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;...@@ -14,7 +14,8 @@ const mem = std.mem;
14const elf = std.elf;14const elf = std.elf;
15const math = std.math;15const math = std.math;
16const assert = std.debug.assert;16const assert = std.debug.assert;
17const native_arch = @import("builtin").cpu.arch;17const builtin = @import("builtin");
18const native_arch = builtin.cpu.arch;
18const linux = std.os.linux;19const linux = std.os.linux;
19const page_size_min = std.heap.page_size_min;20const page_size_min = std.heap.page_size_min;
2021
...@@ -41,13 +42,13 @@ const Variant = enum {...@@ -41,13 +42,13 @@ const Variant = enum {
41 I_original,42 I_original,
42 /// The modified Variant I:43 /// The modified Variant I:
43 ///44 ///
44 /// ---------------------------------------------------45 /// --------------------------------------------
45 /// | DTV | Zig TCB | ABI TCB | [Offset] | TLS Blocks |46 /// | DTV | Zig TCB | ABI TCB | TLS Blocks |
46 /// -------------------------------------^-------------47 /// ------------------------------^-------------
47 /// `-- The TP register points here.48 /// `-- The TP register points here (*inside* the TLS blocks).
48 ///49 ///
49 /// The offset (which can be zero) is applied to the TP only; there is never a physical gap50 /// The offset from the start of the TLS blocks to the TP register is `current_tp_offset`. It
50 /// between the ABI TCB and the TLS blocks. This implies that we only need to align the TP.51 /// may be zero, in which case the TP register points to the start of the TLS blocks.
51 ///52 ///
52 /// The first (and only) word in the ABI TCB points to the DTV.53 /// The first (and only) word in the ABI TCB points to the DTV.
53 I_modified,54 I_modified,
...@@ -106,7 +107,7 @@ const current_variant: Variant = switch (native_arch) {...@@ -106,7 +107,7 @@ const current_variant: Variant = switch (native_arch) {
106 else => @compileError("undefined TLS variant for this architecture"),107 else => @compileError("undefined TLS variant for this architecture"),
107};108};
108109
109/// The Offset value for the modified Variant I.110/// The offset value for the modified Variant I.
110const current_tp_offset = switch (native_arch) {111const current_tp_offset = switch (native_arch) {
111 .m68k,112 .m68k,
112 .mips,113 .mips,
...@@ -379,6 +380,106 @@ pub fn setThreadPointer(addr: usize) void {...@@ -379,6 +380,106 @@ pub fn setThreadPointer(addr: usize) void {
379 }380 }
380}381}
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
382fn computeAreaDesc(phdrs: []elf.Phdr) void {483fn computeAreaDesc(phdrs: []elf.Phdr) void {
383 @setRuntimeSafety(false);484 @setRuntimeSafety(false);
384 @disableInstrumentation();485 @disableInstrumentation();
...@@ -616,3 +717,32 @@ inline fn mmap_tls(length: usize) usize {...@@ -616,3 +717,32 @@ inline fn mmap_tls(length: usize) usize {
616 });717 });
617 }718 }
618}719}
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,...@@ -100,13 +100,15 @@ dynstr: StringTable,
100///100///
101/// Value is the output relocation in `.rela.dyn` for the GOT entry.101/// Value is the output relocation in `.rela.dyn` for the GOT entry.
102got: std.array_hash_map.Auto(GotKey, Section.RelaIndex.Optional),102got: std.array_hash_map.Auto(GotKey, Section.RelaIndex.Optional),
103/// Key is the name of a global.
104///
103/// Indices map 1--1 to indices into the actual `.got.plt` section. These also equal indices into105/// Indices map 1--1 to indices into the actual `.got.plt` section. These also equal indices into
104/// the relocations in `.rela.plt`, because every PLT entry has one output relocation (if a runtime106/// the relocations in `.rela.plt`, because every PLT entry has one output relocation (if a runtime
105/// relocation is no longer necessary, then neither is the corresponding PLT entry!).107/// relocation is no longer necessary, then neither is the corresponding PLT entry!).
106///108///
107/// PLT entries in this map may be "dead", meaning the PLT entry has been deemed unnecessary so is109/// PLT entries in this map may be "dead", meaning the PLT entry has been deemed unnecessary so is
108/// available for reuse---see `Elf.pltEntryIsDead`. Such entries must not be targeted by relocs.110/// available for reuse---see `Elf.pltEntryIsDead`. Such entries must not be targeted by relocs.
109plt: std.array_hash_map.Auto(Symbol.Id, void),111plt: std.array_hash_map.Auto(String(.strtab), void),
110/// The `.plt` section contains zero or more symbol relocations starting at this index.112/// The `.plt` section contains zero or more symbol relocations starting at this index.
111plt_first_symbol_reloc: SymbolReloc.Index,113plt_first_symbol_reloc: SymbolReloc.Index,
112/// The `.dynamic` section contains zero or more symbol relocations starting at this index.114/// The `.dynamic` section contains zero or more symbol relocations starting at this index.
...@@ -152,6 +154,9 @@ tls_size_symbol_relocs: std.array_hash_map.Auto(SymbolReloc.Index, void),...@@ -152,6 +154,9 @@ tls_size_symbol_relocs: std.array_hash_map.Auto(SymbolReloc.Index, void),
152section_by_name: std.array_hash_map.Auto(String(.shstrtab), void),154section_by_name: std.array_hash_map.Auto(String(.shstrtab), void),
153/// Key is the name of a global symbol which has been moved to a new symtab index. Any relocation155/// Key is the name of a global symbol which has been moved to a new symtab index. Any relocation
154/// entries which target that symbol must be updated to reference the correct symbol index.156/// entries which target that symbol must be updated to reference the correct symbol index.
157///
158/// When emitting a relocatable (`ET_REL`), this refers to the index in `.symtab`. Otherwise, it
159/// refers to the index in `.dynsym`.
155changed_symtab_index: std.array_hash_map.Auto(String(.strtab), void),160changed_symtab_index: std.array_hash_map.Auto(String(.strtab), void),
156/// Counts how many relocations are currently in `.rela.dyn` which would require a `DT_TEXTREL`161/// Counts how many relocations are currently in `.rela.dyn` which would require a `DT_TEXTREL`
157/// entry in the `.dynamic` section. This allows adding `DT_TEXTREL` to the output `.dynamic`162/// entry in the `.dynamic` section. This allows adding `DT_TEXTREL` to the output `.dynamic`
...@@ -474,9 +479,14 @@ const Section = struct {...@@ -474,9 +479,14 @@ const Section = struct {
474 }479 }
475480
476 fn vaddr(s: Index, elf: *Elf) u64 {481 fn vaddr(s: Index, elf: *Elf) u64 {
477 return switch (s.get(elf).lsi) {482 return switch (elf.shdrPtr(s)) {
478 .null => 0,483 inline else => |shdr| elf.targetLoad(&shdr.addr),
479 else => |lsi| Symbol.Id.local(lsi).value(elf),484 };
485 }
486
487 fn flags(s: Index, elf: *Elf) std.elf.SHF {
488 return switch (elf.shdrPtr(s)) {
489 inline else => |shdr| elf.targetLoad(&shdr.flags).shf,
480 };490 };
481 }491 }
482492
...@@ -487,8 +497,8 @@ const Section = struct {...@@ -487,8 +497,8 @@ const Section = struct {
487 }497 }
488 }498 }
489499
490 /// Asserts that `shndx` is a `SHT_RELA` section and ensures that its node has enough unused500 /// Asserts that `rela_shndx` is a `SHT_RELA` section and ensures that its node has enough
491 /// space to hold `n` additional `ElfN.Rela` entries.501 /// unused space to hold `n` additional `ElfN.Rela` entries.
492 fn relaEnsureAdditionalCapacity(rela_shndx: Index, elf: *Elf, n: usize) Error!void {502 fn relaEnsureAdditionalCapacity(rela_shndx: Index, elf: *Elf, n: usize) Error!void {
493 const node = rela_shndx.get(elf).ni;503 const node = rela_shndx.get(elf).ni;
494 const need_size: u64 = switch (elf.shdrPtr(rela_shndx)) {504 const need_size: u64 = switch (elf.shdrPtr(rela_shndx)) {
...@@ -514,9 +524,9 @@ const Section = struct {...@@ -514,9 +524,9 @@ const Section = struct {
514 try elf.ensureNodeSize(node, need_size);524 try elf.ensureNodeSize(node, need_size);
515 }525 }
516526
517 /// Asserts that `shndx` is a `SHT_RELA` section and deletes the `ElfN.Rela` entry at the527 /// Asserts that `rela_shndx` is a `SHT_RELA` section and deletes the `ElfN.Rela` entry at
518 /// given `index` in it. The entry is added to the free-list for reuse later. Asserts that528 /// the given `index` in it. The entry is added to the free-list for reuse later. Asserts
519 /// the relocation entry at `index` is not already free.529 /// that the relocation entry at `index` is not already free.
520 fn relaDeleteOne(rela_shndx: Index, elf: *Elf, index: RelaIndex) void {530 fn relaDeleteOne(rela_shndx: Index, elf: *Elf, index: RelaIndex) void {
521 switch (elf.shdrPtr(rela_shndx)) {531 switch (elf.shdrPtr(rela_shndx)) {
522 inline else => |shdr, class| {532 inline else => |shdr, class| {
...@@ -553,9 +563,9 @@ const Section = struct {...@@ -553,9 +563,9 @@ const Section = struct {
553 rela_shndx.get(elf).rela.free_head = index.toOptional();563 rela_shndx.get(elf).rela.free_head = index.toOptional();
554 }564 }
555565
556 /// Asserts that `shndx` is a `SHT_RELA` section and adds a new `ElfN.Rela` entry to it with566 /// Asserts that `rela_shndx` is a `SHT_RELA` section and adds a new `ElfN.Rela` entry to it
557 /// the given field values. Returns the index of the populated entry. Asserts that capacity567 /// with the given field values. Returns the index of the populated entry. Asserts that
558 /// for this operation was already guaranteed using `relaEnsureAdditionalCapacity`.568 /// capacity for this operation was already guaranteed using `relaEnsureAdditionalCapacity`.
559 fn relaAddOneAssumeCapacity(rela_shndx: Index, elf: *Elf, opts: struct {569 fn relaAddOneAssumeCapacity(rela_shndx: Index, elf: *Elf, opts: struct {
560 type: MachineRelocType,570 type: MachineRelocType,
561 offset: u64,571 offset: u64,
...@@ -617,8 +627,8 @@ const Section = struct {...@@ -617,8 +627,8 @@ const Section = struct {
617 }627 }
618 }628 }
619629
620 /// Asserts that `shndx` is a `SHT_RELA` section and updates the `info.sym` field of the630 /// Asserts that `rela_shndx` is a `SHT_RELA` section and updates the `info.sym` field of
621 /// `ElfN.Rela` entry at the given index. As with `relaAddOneAssumeCapacity`, the symbol631 /// the `ElfN.Rela` entry at the given index. As with `relaAddOneAssumeCapacity`, the symbol
622 /// index is a raw `u32`, because it may be an index into `.symtab` or an index into632 /// index is a raw `u32`, because it may be an index into `.symtab` or an index into
623 /// `.dynsym`. Asserts that `index` is not in the free-list (i.e. is not deleted).633 /// `.dynsym`. Asserts that `index` is not in the free-list (i.e. is not deleted).
624 fn relaUpdateSym(rela_shndx: Index, elf: *Elf, index: RelaIndex, raw_sym_index: u32) void {634 fn relaUpdateSym(rela_shndx: Index, elf: *Elf, index: RelaIndex, raw_sym_index: u32) void {
...@@ -642,7 +652,7 @@ const Section = struct {...@@ -642,7 +652,7 @@ const Section = struct {
642 }652 }
643 }653 }
644654
645 /// Asserts that `shndx` is a `SHT_RELA` section and updates the `offset` field of the655 /// Asserts that `rela_shndx` is a `SHT_RELA` section and updates the `offset` field of the
646 /// `ElfN.Rela` entry at the given index. Asserts that `index` is not in the free-list (i.e.656 /// `ElfN.Rela` entry at the given index. Asserts that `index` is not in the free-list (i.e.
647 /// it is not deleted).657 /// it is not deleted).
648 fn relaSetOffset(rela_shndx: Index, elf: *Elf, index: RelaIndex, new_offset: u64) void {658 fn relaSetOffset(rela_shndx: Index, elf: *Elf, index: RelaIndex, new_offset: u64) void {
...@@ -663,7 +673,7 @@ const Section = struct {...@@ -663,7 +673,7 @@ const Section = struct {
663 }673 }
664 }674 }
665675
666 /// Asserts that `shndx` is a `SHT_RELA` section and updates the `offset` field of the676 /// Asserts that `rela_shndx` is a `SHT_RELA` section and updates the `offset` field of the
667 /// `ElfN.Rela` entry at the given index, by subtracting `old_base` and adding `new_base`.677 /// `ElfN.Rela` entry at the given index, by subtracting `old_base` and adding `new_base`.
668 /// Asserts that `index` is not in the free-list (i.e. it is not deleted).678 /// Asserts that `index` is not in the free-list (i.e. it is not deleted).
669 fn relaAdjustOffset(rela_shndx: Index, elf: *Elf, index: RelaIndex, old_base: u64, new_base: u64) void {679 fn relaAdjustOffset(rela_shndx: Index, elf: *Elf, index: RelaIndex, old_base: u64, new_base: u64) void {
...@@ -686,6 +696,28 @@ const Section = struct {...@@ -686,6 +696,28 @@ const Section = struct {
686 },696 },
687 }697 }
688 }698 }
699
700 /// Asserts that `rela_shndx` is a `SHT_RELA` section, and asserts that `index` refers to an
701 /// `R_*_RELATIVE` relocation inside of it; then, updates that relocation's addend (which is
702 /// an address in this DSO without the runtime load offset applied) to the given value.
703 fn relaSetRelativeOffset(rela_shndx: Index, elf: *Elf, index: RelaIndex, new_addend: u64) void {
704 switch (elf.shdrPtr(rela_shndx)) {
705 inline else => |shdr, class| {
706 assert(elf.targetLoad(&shdr.type) == .RELA);
707 assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela));
708 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
709 rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))],
710 ));
711 {
712 const rela_info = elf.targetLoad(&relas[@intFromEnum(index)].info);
713 const none_reloc_type = MachineRelocType.none(elf).unwrap(elf);
714 assert(rela_info.type != none_reloc_type); // bug: `index` is in the free-list
715 }
716 const unsigned: class.ElfN().Addr = @intCast(new_addend);
717 elf.targetStore(&relas[@intFromEnum(index)].addend, @bitCast(unsigned));
718 },
719 }
720 }
689 };721 };
690};722};
691723
...@@ -891,6 +923,16 @@ pub const MachineRelocType = union {...@@ -891,6 +923,16 @@ pub const MachineRelocType = union {
891 .X86_64 => .{ .X86_64 = .COPY },923 .X86_64 => .{ .X86_64 = .COPY },
892 };924 };
893 }925 }
926 pub fn relative(elf: *Elf) MachineRelocType {
927 return switch (elf.ehdrField(.machine)) {
928 else => unreachable,
929 .AARCH64 => .{ .AARCH64 = .RELATIVE },
930 .LOONGARCH => .{ .LOONGARCH = .RELATIVE },
931 .PPC64 => .{ .PPC64 = .RELATIVE },
932 .RISCV => .{ .RISCV = .RELATIVE },
933 .X86_64 => .{ .X86_64 = .RELATIVE },
934 };
935 }
894 pub fn jumpSlot(elf: *Elf) MachineRelocType {936 pub fn jumpSlot(elf: *Elf) MachineRelocType {
895 return switch (elf.ehdrField(.machine)) {937 return switch (elf.ehdrField(.machine)) {
896 else => unreachable,938 else => unreachable,
...@@ -1022,6 +1064,15 @@ const SymbolReloc = struct {...@@ -1022,6 +1064,15 @@ const SymbolReloc = struct {
1022 /// do not apply any relocations ourselves). Otherwise, no symbol relocs use this type.1064 /// do not apply any relocations ourselves). Otherwise, no symbol relocs use this type.
1023 write_rela,1065 write_rela,
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
1025 abs64,1076 abs64,
1026 abs32,1077 abs32,
1027 abs32s,1078 abs32s,
...@@ -1056,21 +1107,39 @@ const SymbolReloc = struct {...@@ -1056,21 +1107,39 @@ const SymbolReloc = struct {
1056 else => false,1107 else => false,
1057 };1108 };
1058 }1109 }
1110
1111 fn isAbsAddr(t: SymbolReloc.Type, elf: *const Elf) bool {
1112 return switch (elf.identClass()) {
1113 .NONE, _ => unreachable,
1114 .@"32" => t == .abs32,
1115 .@"64" => t == .abs64,
1116 };
1117 }
1059 };1118 };
10601119
1061 fn apply(reloc: *const SymbolReloc, elf: *Elf) void {1120 fn apply(reloc: *const SymbolReloc, elf: *Elf) void {
1062 assert(elf.ehdrField(.type) != .REL);1121 assert(elf.ehdrField(.type) != .REL);
1063 assert(reloc.node != .none);1122 assert(reloc.node != .none);
1123
1064 if (reloc.node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) {1124 if (reloc.node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) {
1065 // There's no point applying the relocation now, because it will be re-applied by1125 // There's no point applying the relocation now, because it will be re-applied by
1066 // `flushMoved` at some point anyway.1126 // `flushMoved` at some point anyway.
1067 return;1127 return;
1068 }1128 }
1069 if (reloc.rela_index != .none) {1129
1070 // This relocation has been lowered to a runtime relocation. Until that changes, it is1130 if (reloc.rela_index.unwrap()) |rela_index| switch (elf.classifySymbolValue(reloc.target)) {
1071 // not our job to apply it.1131 .static => unreachable,
1072 return;1132 .dynamic => return, // the relocation happens at runtime
1073 }1133 .static_relative => {
1134 assert(reloc.type.isAbsAddr(elf));
1135 // We have emitted an R_*_RELATIVE relocation to help lower an abs32/abs64 reloc.
1136 // This is a simplified version of the general relocation handling logic, where we
1137 // know we're using '.abs64' or '.abs32' (matching the ELF ident class).
1138 const value = reloc.target.value(elf) +% @as(u64, @bitCast(reloc.addend));
1139 elf.shndx.rela_dyn.relaSetRelativeOffset(elf, rela_index, value);
1140 return;
1141 },
1142 };
1074 const node_vaddr: u64 = switch (elf.getNode(reloc.node)) {1143 const node_vaddr: u64 = switch (elf.getNode(reloc.node)) {
1075 .file => unreachable,1144 .file => unreachable,
1076 .ehdr => unreachable,1145 .ehdr => unreachable,
...@@ -1095,13 +1164,13 @@ const SymbolReloc = struct {...@@ -1095,13 +1164,13 @@ const SymbolReloc = struct {
1095 const target_value = sym_value +% @as(u64, @bitCast(reloc.addend));1164 const target_value = sym_value +% @as(u64, @bitCast(reloc.addend));
1096 type: switch (reloc.type) {1165 type: switch (reloc.type) {
1097 .write_rela => unreachable,1166 .write_rela => unreachable,
1098 .abs64 => std.mem.writeInt(1167 .abs64, .dsorel64 => std.mem.writeInt(
1099 u64,1168 u64,
1100 dest_slice[0..8],1169 dest_slice[0..8],
1101 target_value,1170 target_value,
1102 target_endian,1171 target_endian,
1103 ),1172 ),
1104 .abs32 => std.mem.writeInt(1173 .abs32, .dsorel32 => std.mem.writeInt(
1105 u32,1174 u32,
1106 dest_slice[0..4],1175 dest_slice[0..4],
1107 @intCast(target_value),1176 @intCast(target_value),
...@@ -1126,7 +1195,10 @@ const SymbolReloc = struct {...@@ -1126,7 +1195,10 @@ const SymbolReloc = struct {
1126 target_endian,1195 target_endian,
1127 ),1196 ),
1128 .pltrel64 => {1197 .pltrel64 => {
1129 const plt_index = elf.plt.getIndex(reloc.target) orelse continue :type .rel64;1198 const plt_index = switch (reloc.target.unwrap()) {
1199 .local => continue :type .rel64,
1200 .global => |name| elf.plt.getIndex(name) orelse continue :type .rel64,
1201 };
1130 if (elf.pltEntryIsDead(plt_index)) continue :type .rel64;1202 if (elf.pltEntryIsDead(plt_index)) continue :type .rel64;
1131 const plt_shndx: Section.Index, const plt_entry_size: u64 = switch (elf.ehdrField(.machine)) {1203 const plt_shndx: Section.Index, const plt_entry_size: u64 = switch (elf.ehdrField(.machine)) {
1132 else => |machine| @panic(@tagName(machine)),1204 else => |machine| @panic(@tagName(machine)),
...@@ -1141,7 +1213,10 @@ const SymbolReloc = struct {...@@ -1141,7 +1213,10 @@ const SymbolReloc = struct {
1141 );1213 );
1142 },1214 },
1143 .pltrel32 => {1215 .pltrel32 => {
1144 const plt_index = elf.plt.getIndex(reloc.target) orelse continue :type .rel32;1216 const plt_index = switch (reloc.target.unwrap()) {
1217 .local => continue :type .rel32,
1218 .global => |name| elf.plt.getIndex(name) orelse continue :type .rel32,
1219 };
1145 if (elf.pltEntryIsDead(plt_index)) continue :type .rel32;1220 if (elf.pltEntryIsDead(plt_index)) continue :type .rel32;
1146 const plt_shndx: Section.Index, const plt_entry_size: u64 = switch (elf.ehdrField(.machine)) {1221 const plt_shndx: Section.Index, const plt_entry_size: u64 = switch (elf.ehdrField(.machine)) {
1147 else => |machine| @panic(@tagName(machine)),1222 else => |machine| @panic(@tagName(machine)),
...@@ -1323,12 +1398,9 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe...@@ -1323,12 +1398,9 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe
1323 try elf.symtab.ensureUnusedCapacity(gpa, len);1398 try elf.symtab.ensureUnusedCapacity(gpa, len);
13241399
1325 // If adding locals, we may need to move one global out of the way for each local. If adding1400 // 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 globals1401 // globals, they could all get demoted to STB_LOCAL, meaning we have to move N other globals
1327 // *and* we move up to N other globals out of their way.1402 // around to keep `.dynsym` compact. Either way, the maximum is N.
1328 try elf.changed_symtab_index.ensureUnusedCapacity(gpa, switch (kind) {1403 try elf.changed_symtab_index.ensureUnusedCapacity(gpa, len);
1329 .all_local => len,
1330 .maybe_global => len * 2,
1331 });
13321404
1333 {1405 {
1334 // Ensure the symtab section's node is big enough1406 // Ensure the symtab section's node is big enough
...@@ -1460,7 +1532,7 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L...@@ -1460,7 +1532,7 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L
1460 const global_name: String(.strtab) = @enumFromInt(elf.targetLoad(&new_sym.name));1532 const global_name: String(.strtab) = @enumFromInt(elf.targetLoad(&new_sym.name));
1461 elf.globalByName(global_name).?.symtab_index = new_index;1533 elf.globalByName(global_name).?.symtab_index = new_index;
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) {
1464 // This symbol's index is changing, so queue an update of relocs targeting it.1536 // This symbol's index is changing, so queue an update of relocs targeting it.
1465 elf.changed_symtab_index.putAssumeCapacity(global_name, {});1537 elf.changed_symtab_index.putAssumeCapacity(global_name, {});
1466 }1538 }
...@@ -1722,16 +1794,14 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{...@@ -1722,16 +1794,14 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
1722 elf.moveDemotedGlobal(new_global_ptr);1794 elf.moveDemotedGlobal(new_global_ptr);
1723 }1795 }
17241796
1725 if (new_global_ptr.dynsym_index != 0 and1797 switch (@"type") {
1726 opts.visibility == .DEFAULT and1798 .FUNC, .GNU_IFUNC => if (elf.ehdrField(.type) != .REL and
1727 opts.shndx == .UNDEF and1799 elf.classifySymbolValue(.global(opts.name.strtab)) == .dynamic)
1728 (@"type" == .FUNC or @"type" == std.elf.STT.GNU_IFUNC))1800 {
1729 {1801 // This STT_FUNC symbol might be defined externally, so it needs a PLT entry.
1730 // We're adding an undefined global STT_FUNC symbol which could be resolved by another DSO.1802 elf.addPltEntry(opts.name.strtab, new_global_ptr.dynsym_index);
1731 // We therefore might need a PLT entry, so let's add one now.1803 },
1732 elf.addPltEntry(opts.name.strtab, new_global_ptr.dynsym_index);1804 else => {},
1733 // TODO: we also need to emit a PLT entry if the symbol could be preempted/interposed! By
1734 // not doing that we're basically implementing the behavior of `-Bsymbolic-functions`.
1735 }1805 }
17361806
1737 return .global(opts.name.strtab);1807 return .global(opts.name.strtab);
...@@ -1843,10 +1913,10 @@ fn setGlobalSymbolValue(...@@ -1843,10 +1913,10 @@ fn setGlobalSymbolValue(
1843 // If this symbol was previously undefined, it may have had a PLT entry. If so, we now need to1913 // If this symbol was previously undefined, it may have had a PLT entry. If so, we now need to
1844 // delete its newly-unnecessary runtime relocation to avoid a runtime dynamic linker error.1914 // delete its newly-unnecessary runtime relocation to avoid a runtime dynamic linker error.
1845 // This also allows the PLT entry to be reused---see `pltEntryIsDead`.1915 // This also allows the PLT entry to be reused---see `pltEntryIsDead`.
1846 if (elf.plt.getIndex(.global(global_name))) |plt_index| {1916 if (elf.plt.getIndex(global_name)) |plt_index| {
1847 // TODO: we might still need the PLT entry if the symbol could be preempted/interposed! See1917 if (!elf.pltEntryIsDead(plt_index) and
1848 // matching comment at the end of `addGlobalSymbolAssumeCapacity`.1918 elf.classifySymbolValue(.global(global_name)) != .dynamic)
1849 if (!elf.pltEntryIsDead(plt_index)) {1919 {
1850 elf.shndx.rela_plt.relaDeleteOne(elf, @enumFromInt(plt_index));1920 elf.shndx.rela_plt.relaDeleteOne(elf, @enumFromInt(plt_index));
1851 assert(elf.pltEntryIsDead(plt_index));1921 assert(elf.pltEntryIsDead(plt_index));
1852 }1922 }
...@@ -1942,61 +2012,68 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {...@@ -1942,61 +2012,68 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {
19422012
1943 elf.targetStore(&shdr.info, @intFromEnum(dest_index) + 1);2013 elf.targetStore(&shdr.info, @intFromEnum(dest_index) + 1);
19442014
1945 if (src_index == dest_index) {2015 if (src_index != dest_index) {
1946 // The demoted global was already the first global, so we don't need to do any swap.2016 // The demoted global was not the first global in the symtab, so we need to swap it
1947 return;2017 // to its new location.
1948 }
19492018
1950 const src_sym_ptr = @field(elf.symPtr(src_index), @tagName(class));2019 const src_sym_ptr = @field(elf.symPtr(src_index), @tagName(class));
1951 const dest_sym_ptr = @field(elf.symPtr(dest_index), @tagName(class));2020 const dest_sym_ptr = @field(elf.symPtr(dest_index), @tagName(class));
19522021
1953 const this_name: String(.strtab) = @enumFromInt(elf.targetLoad(&src_sym_ptr.name));2022 const this_name: String(.strtab) = @enumFromInt(elf.targetLoad(&src_sym_ptr.name));
1954 assert(elf.globalByName(this_name).? == global_ptr);2023 assert(elf.globalByName(this_name).? == global_ptr);
1955 if (global_ptr.symtab_index.ptr(elf).first_target_reloc != .none) {
1956 // This symbol's index is changing, so queue an update of relocs targeting it.
1957 elf.changed_symtab_index.putAssumeCapacity(this_name, {});
1958 }
19592024
1960 const other_name: String(.strtab) = @enumFromInt(elf.targetLoad(&dest_sym_ptr.name));2025 const other_name: String(.strtab) = @enumFromInt(elf.targetLoad(&dest_sym_ptr.name));
1961 const other_global_ptr = elf.globalByName(other_name).?;2026 const other_global_ptr = elf.globalByName(other_name).?;
1962 assert(other_global_ptr.symtab_index == dest_index);2027 assert(other_global_ptr.symtab_index == dest_index);
1963 if (other_global_ptr.symtab_index.ptr(elf).first_target_reloc != .none) {2028
1964 // This other symbol's index is changing, so queue an update of relocs targeting it.2029 // First swap the symtab entries...
1965 elf.changed_symtab_index.putAssumeCapacity(other_name, {});2030 std.mem.swap(class.ElfN().Sym, src_sym_ptr, dest_sym_ptr);
2031 // ...then the `elf.symtab` metadata...
2032 std.mem.swap(Symbol, src_index.ptr(elf), dest_index.ptr(elf));
2033 // ...then update the `elf.globals` tracking.
2034 global_ptr.symtab_index = dest_index;
2035 other_global_ptr.symtab_index = src_index;
1966 }2036 }
19672037
1968 // First swap the symtab entries...2038 // We also need to get rid of the dynsym entry if there is one. To keep dynsym compact,
1969 std.mem.swap(class.ElfN().Sym, src_sym_ptr, dest_sym_ptr);2039 // we'll move another symbol into its place just like we did above.
1970 // ...then the `elf.symtab` metadata...
1971 std.mem.swap(Symbol, src_index.ptr(elf), dest_index.ptr(elf));
1972 // ...then update the `elf.globals` tracking.
1973 global_ptr.symtab_index = dest_index;
1974 other_global_ptr.symtab_index = src_index;
1975
1976 // We also need to get rid of the dynsym entry if there is one. For simplicity, just
1977 // replace it with a dummy entry which will never be used and will not cause problems.
1978 // TODO: we should have a free-list of dynsym slots so that other symbols can go here.
1979 // TODO: it would also be best to just avoid having gaps in the dynsym altogether.
1980 if (global_ptr.dynsym_index != 0) {2040 if (global_ptr.dynsym_index != 0) {
1981 const dynsym = @field(elf.dynsymPtr(global_ptr.dynsym_index), @tagName(class));2041 const dynsym_shdr = @field(elf.shdrPtr(elf.shndx.dynsym), @tagName(class));
1982 dynsym.* = .{2042
1983 .name = @intFromEnum(String(.dynstr).empty),2043 const ent_size = @sizeOf(class.ElfN().Sym);
1984 .value = 0,2044 assert(elf.targetLoad(&dynsym_shdr.entsize) == ent_size);
1985 .size = 0,2045
1986 .info = .{2046 // We're going to decrease the size of `.dynsym`, thereby removing its last index.
1987 .type = .NOTYPE,2047 const old_size = elf.targetLoad(&dynsym_shdr.size);
1988 // STB_WEAK is important: we mustn't cause a dynamic linker error if the2048 const new_size = old_size - ent_size;
1989 // symbol can't be resolved.2049 const remove_dynsym_index: u32 = @intCast(@divExact(new_size, ent_size));
1990 .bind = .WEAK,2050
1991 },2051 const free_dynsym_index = global_ptr.dynsym_index;
1992 // SHN_UNDEF is important: we mustn't define this symbol for other DSOs.
1993 .shndx = std.elf.SHN_UNDEF,
1994 .other = .{ .visibility = .DEFAULT },
1995 };
1996 if (elf.targetEndian() != native_endian) {
1997 std.mem.byteSwapAllFields(class.ElfN().Sym, dynsym);
1998 }
1999 global_ptr.dynsym_index = 0;2052 global_ptr.dynsym_index = 0;
2053
2054 if (free_dynsym_index != remove_dynsym_index) {
2055 // The demoted global wasn't the last entry, so move whatever entry we just
2056 // truncated out of dynsym into its place.
2057
2058 const src_dynsym_ptr = @field(elf.dynsymPtr(remove_dynsym_index), @tagName(class));
2059 const dest_dynsym_ptr = @field(elf.dynsymPtr(free_dynsym_index), @tagName(class));
2060
2061 const moved_name_dynstr: String(.dynstr) = @enumFromInt(elf.targetLoad(&src_dynsym_ptr.name));
2062 const moved_name = elf.stringExisting(.strtab, moved_name_dynstr.slice(elf));
2063 const moved_global_ptr = elf.globalByName(moved_name).?;
2064
2065 dest_dynsym_ptr.* = src_dynsym_ptr.*;
2066
2067 assert(moved_global_ptr.dynsym_index == remove_dynsym_index);
2068 moved_global_ptr.dynsym_index = free_dynsym_index;
2069
2070 // Since that symbol's dynsym index has changed, we'll have to update any
2071 // relocation entries targeting it.
2072 elf.changed_symtab_index.putAssumeCapacity(moved_name, {});
2073 }
2074
2075 // Now that we've given that symbol a new home, actually decrease the section size.
2076 elf.targetStore(&dynsym_shdr.size, new_size);
2000 }2077 }
2001 },2078 },
2002 }2079 }
...@@ -2033,13 +2110,13 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void...@@ -2033,13 +2110,13 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
20332110
2034 if (plt_index < elf.plt.count()) {2111 if (plt_index < elf.plt.count()) {
2035 // We reused a free entry, so we're already done!2112 // We reused a free entry, so we're already done!
2036 elf.plt.setKey(plt_index, .global(global_name));2113 elf.plt.setKey(plt_index, global_name);
2037 return;2114 return;
2038 }2115 }
20392116
2040 // We added a new entry, so we now need to extend the PLT sections.2117 // We added a new entry, so we now need to extend the PLT sections.
2041 assert(plt_index == elf.plt.count());2118 assert(plt_index == elf.plt.count());
2042 elf.plt.putAssumeCapacityNoClobber(.global(global_name), {});2119 elf.plt.putAssumeCapacityNoClobber(global_name, {});
20432120
2044 switch (elf.ehdrField(.machine)) {2121 switch (elf.ehdrField(.machine)) {
2045 else => |machine| @panic(@tagName(machine)),2122 else => |machine| @panic(@tagName(machine)),
...@@ -2315,8 +2392,8 @@ const Symbol = struct {...@@ -2315,8 +2392,8 @@ const Symbol = struct {
2315 }2392 }
2316 }2393 }
23172394
2318 /// Scans through all relocations targeting `sym_id` and deletes each one's dynamic2395 /// Scans through all relocations targeting `sym_id` and, for each one with a dynamic
2319 /// relocation entry, if it has one.2396 /// relocation entry, either deletes it or converts it to R_*_RELATIVE as required.
2320 ///2397 ///
2321 /// Asserts we are creating a DSO.2398 /// Asserts we are creating a DSO.
2322 fn deleteDynamicTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void {2399 fn deleteDynamicTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void {
...@@ -2329,6 +2406,45 @@ const Symbol = struct {...@@ -2329,6 +2406,45 @@ const Symbol = struct {
2329 reloc.deleteOutputRel(elf);2406 reloc.deleteOutputRel(elf);
2330 ri = reloc.next;2407 ri = reloc.next;
2331 }2408 }
2409 switch (elf.classifySymbolValue(sym_id)) {
2410 .static => return,
2411 .static_relative => {},
2412 .dynamic => unreachable,
2413 }
2414 // We removed the symbol relocations, now add R_*_RELATIVE relocations where needed.
2415 ri = sym_id.index(elf).ptr(elf).first_target_reloc;
2416 while (ri != .none) {
2417 const reloc = ri.get(elf);
2418 ri = reloc.next;
2419 assert(reloc.target == sym_id);
2420 if (!reloc.type.isAbsAddr(elf)) continue;
2421 switch (elf.nodeWantsDsoRelocation(reloc.node)) {
2422 .no => continue,
2423 .yes_textrel => elf.textrel_count += 1,
2424 .yes => {},
2425 }
2426 const node_vaddr: u64 = switch (elf.getNode(reloc.node)) {
2427 .file => unreachable,
2428 .ehdr => unreachable,
2429 .shdr => unreachable,
2430 .segment => unreachable,
2431 .copied_global => unreachable,
2432 .section => |shndx| shndx.vaddr(elf),
2433 .input_section => |isi| isi.ptrConst(elf).vaddr,
2434 inline .nav,
2435 .uav,
2436 .lazy_code,
2437 .lazy_const_data,
2438 => |i| Symbol.Id.local(i.symbol(elf)).value(elf),
2439 };
2440 // There is capacity for a relocation because we just deleted one earlier.
2441 reloc.rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
2442 .type = .relative(elf),
2443 .offset = node_vaddr + reloc.offset,
2444 .raw_sym_index = 0,
2445 .addend = 0,
2446 }).toOptional();
2447 }
2332 }2448 }
23332449
2334 /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at2450 /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at
...@@ -2357,6 +2473,78 @@ fn globalByName(elf: *const Elf, name: String(.strtab)) ?*Symbol.Global {...@@ -2357,6 +2473,78 @@ fn globalByName(elf: *const Elf, name: String(.strtab)) ?*Symbol.Global {
2357 return null;2473 return null;
2358}2474}
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
2360pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId {2548pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId {
2361 const lsi: Symbol.LocalIndex = switch (elf.getNode(Node.fromAtom(atom))) {2549 const lsi: Symbol.LocalIndex = switch (elf.getNode(Node.fromAtom(atom))) {
2362 .file,2550 .file,
...@@ -2588,6 +2776,11 @@ fn string(elf: *Elf, comptime section: StringSection, key: []const u8) Error!Str...@@ -2588,6 +2776,11 @@ fn string(elf: *Elf, comptime section: StringSection, key: []const u8) Error!Str
2588 const st: *StringTable = &@field(elf, @tagName(section));2776 const st: *StringTable = &@field(elf, @tagName(section));
2589 return @enumFromInt(try st.get(elf, section.shndx(elf), key));2777 return @enumFromInt(try st.get(elf, section.shndx(elf), key));
2590}2778}
2779/// Like `string`, but asserts that the string is already in `section`.
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
2592const StringTable = struct {2785const StringTable = struct {
2593 map: std.HashMapUnmanaged(u32, void, StringTable.Context, std.hash_map.default_max_load_percentage),2786 map: std.HashMapUnmanaged(u32, void, StringTable.Context, std.hash_map.default_max_load_percentage),
...@@ -2618,7 +2811,14 @@ const StringTable = struct {...@@ -2618,7 +2811,14 @@ const StringTable = struct {
2618 }2811 }
2619 };2812 };
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 {
2622 // If we are in `initHeaders` the strtab might not be initalized yet, so we need to special2822 // If we are in `initHeaders` the strtab might not be initalized yet, so we need to special
2623 // case the empty string.2823 // case the empty string.
2624 if (key.len == 0) return 0;2824 if (key.len == 0) return 0;
...@@ -2884,51 +3084,104 @@ fn initHeaders(...@@ -2884,51 +3084,104 @@ fn initHeaders(
2884 .@"64" => .@"8",3084 .@"64" => .@"8",
2885 };3085 };
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
2912 const init_plt_size: std.elf.Xword, const plt_align: std.mem.Alignment, const plt_sec =3087 const init_plt_size: std.elf.Xword, const plt_align: std.mem.Alignment, const plt_sec =
2913 switch (machine) {3088 switch (machine) {
2914 else => @panic(@tagName(machine)),3089 else => @panic(@tagName(machine)),
2915 .X86_64 => .{ 16, .@"16", true },3090 .X86_64 => .{ 16, .@"16", true },
2916 .LOONGARCH => .{ 32, .@"4", false },3091 .LOONGARCH => .{ 32, .@"4", false },
2917 };3092 };
2918 const expected_nodes_len = expected_nodes_len: switch (@"type") {3093
2919 .NONE, .CORE, _ => unreachable,3094 const shnum: u32 = shnum: {
2920 .REL => {3095 var shnum: u32 = 1; // reserved ("null") shdr
2921 // Each phdr is actually going to be an shdr.3096 shnum += 1; // .symtab
2922 defer phnum = 0;3097 shnum += 1; // .shstrtab
2923 break :expected_nodes_len 5 + phnum;3098 shnum += 1; // .strtab
2924 },3099 shnum += @intFromBool(maybe_interp != null); // .interp
2925 .EXEC, .DYN => break :expected_nodes_len 9 +3100 shnum += 1; // .rodata
2926 phnum * 2 - 1 + // each phdr also has a matching shdr, except for the PT_PHDR phdr3101 shnum += 1; // .text
2927 @as(usize, 4) * @intFromBool(have_dynamic_section) + // .dynstr, .dynsym, .rela.dyn, .rela.plt3102 shnum += 1; // .data
2928 @intFromBool(plt_sec),3103 shnum += @intFromBool(comp.config.any_non_single_threaded); // .tdata
3104 shnum += 1; // .data.rel.ro
3105 if (have_dynamic_section) {
3106 shnum += 1; // .dynamic
3107 shnum += 1; // .dynstr
3108 shnum += 1; // .dynsym
3109 shnum += 1; // .rela.dyn
3110 shnum += 1; // .rela.plt
3111 }
3112 if (@"type" != .REL) {
3113 shnum += 1; // .got
3114 shnum += 1; // .got.plt
3115 shnum += 1; // .plt
3116 shnum += @intFromBool(plt_sec); // .plt_sec
3117 }
3118 break :shnum shnum;
3119 };
3120
3121 const phndx: struct {
3122 phdr: u32,
3123 interp: u32,
3124 rodata: u32,
3125 text: u32,
3126 data: u32,
3127 tls: u32,
3128 dynamic: u32,
3129 relro: u32,
3130 gnu_stack: u32,
3131 }, const phnum: u32 = ph: {
3132 switch (@"type") {
3133 .NONE, .CORE, _ => unreachable,
3134 .REL => break :ph .{ undefined, 0 },
3135 .EXEC, .DYN => {},
3136 }
3137 var phnum: u32 = 0;
3138 break :ph .{ .{
3139 .phdr = phndx: {
3140 defer phnum += 1;
3141 break :phndx phnum;
3142 },
3143 .interp = if (maybe_interp) |_| phndx: {
3144 defer phnum += 1;
3145 break :phndx phnum;
3146 } else undefined,
3147 .rodata = phndx: {
3148 defer phnum += 1;
3149 break :phndx phnum;
3150 },
3151 .text = phndx: {
3152 defer phnum += 1;
3153 break :phndx phnum;
3154 },
3155 .data = phndx: {
3156 defer phnum += 1;
3157 break :phndx phnum;
3158 },
3159 .tls = if (comp.config.any_non_single_threaded) phndx: {
3160 defer phnum += 1;
3161 break :phndx phnum;
3162 } else undefined,
3163 .dynamic = if (have_dynamic_section) phndx: {
3164 defer phnum += 1;
3165 break :phndx phnum;
3166 } else undefined,
3167 .relro = phndx: {
3168 defer phnum += 1;
3169 break :phndx phnum;
3170 },
3171 .gnu_stack = phndx: {
3172 defer phnum += 1;
3173 break :phndx phnum;
3174 },
3175 }, phnum };
2929 };3176 };
3177
3178 const expected_nodes_len = 3 + // `.file`, `.ehdr`, and `.shdr` nodes
3179 (shnum - 1) + // -1 because the null shdr does not have a `.section` node
3180 (phnum -| 1); // -1 because the GNU_STACK phdr does not have a `.segment` node
3181
2930 try elf.nodes.ensureTotalCapacity(gpa, expected_nodes_len);3182 try elf.nodes.ensureTotalCapacity(gpa, expected_nodes_len);
2931 try elf.shdrs.ensureTotalCapacity(gpa, shnum);3183 try elf.shdrs.ensureTotalCapacity(gpa, shnum);
3184 try elf.section_by_name.ensureUnusedCapacity(gpa, shnum);
2932 try elf.phdrs.resize(gpa, phnum);3185 try elf.phdrs.resize(gpa, phnum);
2933 try elf.symtab.ensureTotalCapacity(gpa, 1);3186 try elf.symtab.ensureTotalCapacity(gpa, 1);
2934 elf.nodes.appendAssumeCapacity(.file);3187 elf.nodes.appendAssumeCapacity(.file);
...@@ -2980,7 +3233,7 @@ fn initHeaders(...@@ -2980,7 +3233,7 @@ fn initHeaders(
2980 ehdr.phentsize = @sizeOf(ElfN.Phdr);3233 ehdr.phentsize = @sizeOf(ElfN.Phdr);
2981 ehdr.phnum = @min(phnum, std.elf.PN_XNUM);3234 ehdr.phnum = @min(phnum, std.elf.PN_XNUM);
2982 ehdr.shentsize = @sizeOf(ElfN.Shdr);3235 ehdr.shentsize = @sizeOf(ElfN.Shdr);
2983 ehdr.shnum = if (shnum < std.elf.SHN_LORESERVE) shnum else 0;3236 ehdr.shnum = 1; // Only the null shdr initially---will be incremented by `addSection`
2984 ehdr.shstrndx = std.elf.SHN_UNDEF;3237 ehdr.shstrndx = std.elf.SHN_UNDEF;
2985 if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr);3238 if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr);
2986 },3239 },
...@@ -3000,8 +3253,8 @@ fn initHeaders(...@@ -3000,8 +3253,8 @@ fn initHeaders(
3000 .moved = true,3253 .moved = true,
3001 .bubbles_moved = false,3254 .bubbles_moved = false,
3002 }));3255 }));
3003 elf.nodes.appendAssumeCapacity(.{ .segment = rodata_phndx });3256 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.rodata });
3004 elf.phdrs.items[rodata_phndx] = elf.ni.rodata;3257 elf.phdrs.items[phndx.rodata] = elf.ni.rodata;
30053258
3006 assert(elf.ni.phdr == try elf.mf.addOnlyChildNode(gpa, elf.ni.rodata, .{3259 assert(elf.ni.phdr == try elf.mf.addOnlyChildNode(gpa, elf.ni.rodata, .{
3007 .size = elf.ehdrField(.phentsize) * elf.ehdrField(.phnum),3260 .size = elf.ehdrField(.phentsize) * elf.ehdrField(.phnum),
...@@ -3010,32 +3263,34 @@ fn initHeaders(...@@ -3010,32 +3263,34 @@ fn initHeaders(
3010 .resized = true,3263 .resized = true,
3011 .bubbles_moved = false,3264 .bubbles_moved = false,
3012 }));3265 }));
3013 elf.nodes.appendAssumeCapacity(.{ .segment = phdr_phndx });3266 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.phdr });
3014 elf.phdrs.items[phdr_phndx] = elf.ni.phdr;3267 elf.phdrs.items[phndx.phdr] = elf.ni.phdr;
30153268
3016 assert(elf.ni.text == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{3269 assert(elf.ni.text == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{
3017 .alignment = elf.mf.flags.block_size,3270 .alignment = elf.mf.flags.block_size,
3018 .moved = true,3271 .moved = true,
3019 .bubbles_moved = false,3272 .bubbles_moved = false,
3020 }));3273 }));
3021 elf.nodes.appendAssumeCapacity(.{ .segment = text_phndx });3274 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.text });
3022 elf.phdrs.items[text_phndx] = elf.ni.text;3275 elf.phdrs.items[phndx.text] = elf.ni.text;
30233276
3024 assert(elf.ni.data == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{3277 assert(elf.ni.data == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{
3025 .alignment = elf.mf.flags.block_size,3278 .alignment = elf.mf.flags.block_size,
3026 .moved = true,3279 .moved = true,
3027 .bubbles_moved = false,3280 .bubbles_moved = false,
3028 }));3281 }));
3029 elf.nodes.appendAssumeCapacity(.{ .segment = data_phndx });3282 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.data });
3030 elf.phdrs.items[data_phndx] = elf.ni.data;3283 elf.phdrs.items[phndx.data] = elf.ni.data;
30313284
3032 assert(elf.ni.data_rel_ro == try elf.mf.addOnlyChildNode(gpa, elf.ni.data, .{3285 assert(elf.ni.data_rel_ro == try elf.mf.addOnlyChildNode(gpa, elf.ni.data, .{
3033 .alignment = elf.mf.flags.block_size,3286 .alignment = elf.mf.flags.block_size,
3034 .moved = true,3287 .moved = true,
3035 .bubbles_moved = false,3288 .bubbles_moved = false,
3036 }));3289 }));
3037 elf.nodes.appendAssumeCapacity(.{ .segment = relro_phndx });3290 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.relro });
3038 elf.phdrs.items[relro_phndx] = elf.ni.data_rel_ro;3291 elf.phdrs.items[phndx.relro] = elf.ni.data_rel_ro;
3292
3293 elf.phdrs.items[phndx.gnu_stack] = .none;
30393294
3040 break :ph_vaddr switch (elf.ehdrField(.type)) {3295 break :ph_vaddr switch (elf.ehdrField(.type)) {
3041 .NONE, .CORE, _ => unreachable,3296 .NONE, .CORE, _ => unreachable,
...@@ -3058,7 +3313,7 @@ fn initHeaders(...@@ -3058,7 +3313,7 @@ fn initHeaders(
30583313
3059 if (@"type" != .REL) {3314 if (@"type" != .REL) {
3060 const phdr: []ElfN.Phdr = @ptrCast(@alignCast(elf.ni.phdr.slice(&elf.mf)));3315 const phdr: []ElfN.Phdr = @ptrCast(@alignCast(elf.ni.phdr.slice(&elf.mf)));
3061 const ph_phdr = &phdr[phdr_phndx];3316 const ph_phdr = &phdr[phndx.phdr];
3062 ph_phdr.* = .{3317 ph_phdr.* = .{
3063 .type = .PHDR,3318 .type = .PHDR,
3064 .offset = 0,3319 .offset = 0,
...@@ -3072,7 +3327,7 @@ fn initHeaders(...@@ -3072,7 +3327,7 @@ fn initHeaders(
3072 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_phdr);3327 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_phdr);
30733328
3074 if (maybe_interp) |_| {3329 if (maybe_interp) |_| {
3075 const ph_interp = &phdr[interp_phndx];3330 const ph_interp = &phdr[phndx.interp];
3076 ph_interp.* = .{3331 ph_interp.* = .{
3077 .type = .INTERP,3332 .type = .INTERP,
3078 .offset = 0,3333 .offset = 0,
...@@ -3087,7 +3342,7 @@ fn initHeaders(...@@ -3087,7 +3342,7 @@ fn initHeaders(
3087 }3342 }
30883343
3089 _, const rodata_size = elf.ni.rodata.location(&elf.mf).resolve(&elf.mf);3344 _, const rodata_size = elf.ni.rodata.location(&elf.mf).resolve(&elf.mf);
3090 const ph_rodata = &phdr[rodata_phndx];3345 const ph_rodata = &phdr[phndx.rodata];
3091 ph_rodata.* = .{3346 ph_rodata.* = .{
3092 .type = if (rodata_size == 0) .NULL else .LOAD,3347 .type = if (rodata_size == 0) .NULL else .LOAD,
3093 .offset = 0,3348 .offset = 0,
...@@ -3102,7 +3357,7 @@ fn initHeaders(...@@ -3102,7 +3357,7 @@ fn initHeaders(
3102 ph_vaddr += @intCast(rodata_size);3357 ph_vaddr += @intCast(rodata_size);
31033358
3104 _, const text_size = elf.ni.text.location(&elf.mf).resolve(&elf.mf);3359 _, const text_size = elf.ni.text.location(&elf.mf).resolve(&elf.mf);
3105 const ph_text = &phdr[text_phndx];3360 const ph_text = &phdr[phndx.text];
3106 ph_text.* = .{3361 ph_text.* = .{
3107 .type = if (text_size == 0) .NULL else .LOAD,3362 .type = if (text_size == 0) .NULL else .LOAD,
3108 .offset = 0,3363 .offset = 0,
...@@ -3117,7 +3372,7 @@ fn initHeaders(...@@ -3117,7 +3372,7 @@ fn initHeaders(
3117 ph_vaddr += @intCast(text_size);3372 ph_vaddr += @intCast(text_size);
31183373
3119 _, const data_size = elf.ni.data.location(&elf.mf).resolve(&elf.mf);3374 _, const data_size = elf.ni.data.location(&elf.mf).resolve(&elf.mf);
3120 const ph_data = &phdr[data_phndx];3375 const ph_data = &phdr[phndx.data];
3121 ph_data.* = .{3376 ph_data.* = .{
3122 .type = if (data_size == 0) .NULL else .LOAD,3377 .type = if (data_size == 0) .NULL else .LOAD,
3123 .offset = 0,3378 .offset = 0,
...@@ -3132,7 +3387,7 @@ fn initHeaders(...@@ -3132,7 +3387,7 @@ fn initHeaders(
3132 ph_vaddr += @intCast(data_size);3387 ph_vaddr += @intCast(data_size);
31333388
3134 if (comp.config.any_non_single_threaded) {3389 if (comp.config.any_non_single_threaded) {
3135 const ph_tls = &phdr[tls_phndx];3390 const ph_tls = &phdr[phndx.tls];
3136 ph_tls.* = .{3391 ph_tls.* = .{
3137 .type = .TLS,3392 .type = .TLS,
3138 .offset = 0,3393 .offset = 0,
...@@ -3147,7 +3402,7 @@ fn initHeaders(...@@ -3147,7 +3402,7 @@ fn initHeaders(
3147 }3402 }
31483403
3149 if (have_dynamic_section) {3404 if (have_dynamic_section) {
3150 const ph_dynamic = &phdr[dynamic_phndx];3405 const ph_dynamic = &phdr[phndx.dynamic];
3151 ph_dynamic.* = .{3406 ph_dynamic.* = .{
3152 .type = .DYNAMIC,3407 .type = .DYNAMIC,
3153 .offset = 0,3408 .offset = 0,
...@@ -3161,7 +3416,7 @@ fn initHeaders(...@@ -3161,7 +3416,7 @@ fn initHeaders(
3161 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_dynamic);3416 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_dynamic);
3162 }3417 }
31633418
3164 const ph_relro = &phdr[relro_phndx];3419 const ph_relro = &phdr[phndx.relro];
3165 ph_relro.* = .{3420 ph_relro.* = .{
3166 .type = .GNU_RELRO,3421 .type = .GNU_RELRO,
3167 .offset = 0,3422 .offset = 0,
...@@ -3173,6 +3428,19 @@ fn initHeaders(...@@ -3173,6 +3428,19 @@ fn initHeaders(
3173 .@"align" = @intCast(elf.mf.flags.block_size.toByteUnits()),3428 .@"align" = @intCast(elf.mf.flags.block_size.toByteUnits()),
3174 };3429 };
3175 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_relro);3430 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_relro);
3431
3432 const ph_gnu_stack = &phdr[phndx.gnu_stack];
3433 ph_gnu_stack.* = .{
3434 .type = .GNU_STACK,
3435 .offset = 0,
3436 .vaddr = 0,
3437 .paddr = 0,
3438 .filesz = 0,
3439 .memsz = @intCast(elf.options.stack_size orelse 0),
3440 .flags = .{ .R = true, .W = true },
3441 .@"align" = 1,
3442 };
3443 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_gnu_stack);
3176 }3444 }
31773445
3178 const sh_undef: *ElfN.Shdr = @ptrCast(@alignCast(elf.ni.shdr.slice(&elf.mf)));3446 const sh_undef: *ElfN.Shdr = @ptrCast(@alignCast(elf.ni.shdr.slice(&elf.mf)));
...@@ -3312,8 +3580,8 @@ fn initHeaders(...@@ -3312,8 +3580,8 @@ fn initHeaders(
3312 .resized = true,3580 .resized = true,
3313 .bubbles_moved = false,3581 .bubbles_moved = false,
3314 });3582 });
3315 elf.nodes.appendAssumeCapacity(.{ .segment = interp_phndx });3583 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.interp });
3316 elf.phdrs.items[interp_phndx] = interp_ni;3584 elf.phdrs.items[phndx.interp] = interp_ni;
33173585
3318 const sec_interp_shndx = try elf.addSection(interp_ni, .{3586 const sec_interp_shndx = try elf.addSection(interp_ni, .{
3319 .name = ".interp",3587 .name = ".interp",
...@@ -3331,8 +3599,8 @@ fn initHeaders(...@@ -3331,8 +3599,8 @@ fn initHeaders(
3331 .moved = true,3599 .moved = true,
3332 .bubbles_moved = false,3600 .bubbles_moved = false,
3333 });3601 });
3334 elf.nodes.appendAssumeCapacity(.{ .segment = dynamic_phndx });3602 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.dynamic });
3335 elf.phdrs.items[dynamic_phndx] = dynamic_ni;3603 elf.phdrs.items[phndx.dynamic] = dynamic_ni;
33363604
3337 const dynstr_shndx = try elf.addSection(elf.ni.rodata, .{3605 const dynstr_shndx = try elf.addSection(elf.ni.rodata, .{
3338 .name = ".dynstr",3606 .name = ".dynstr",
...@@ -3418,19 +3686,19 @@ fn initHeaders(...@@ -3418,19 +3686,19 @@ fn initHeaders(
3418 });3686 });
3419 elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);3687 elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);
3420 try elf.ensureUnusedRelocCapacity(plt_ni, 2);3688 try elf.ensureUnusedRelocCapacity(plt_ni, 2);
3421 try elf.addRelocAssumeCapacity(3689 try elf.addSymbolRelocAssumeCapacity(
3422 plt_ni,3690 plt_ni,
3423 2,3691 2,
3424 got_plt_sym,3692 got_plt_sym,
3425 8 * 1 - 4,3693 8 * 1 - 4,
3426 .{ .X86_64 = .PC32 },3694 .rel32,
3427 );3695 );
3428 try elf.addRelocAssumeCapacity(3696 try elf.addSymbolRelocAssumeCapacity(
3429 plt_ni,3697 plt_ni,
3430 8,3698 8,
3431 got_plt_sym,3699 got_plt_sym,
3432 8 * 2 - 4,3700 8 * 2 - 4,
3433 .{ .X86_64 = .PC32 },3701 .rel32,
3434 );3702 );
3435 },3703 },
3436 .LOONGARCH => {3704 .LOONGARCH => {
...@@ -3461,9 +3729,9 @@ fn initHeaders(...@@ -3461,9 +3729,9 @@ fn initHeaders(
3461 });3729 });
3462 elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);3730 elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);
3463 try elf.ensureUnusedRelocCapacity(plt_ni, 3);3731 try elf.ensureUnusedRelocCapacity(plt_ni, 3);
3464 try elf.addRelocAssumeCapacity(plt_ni, 0, got_plt_sym, 0, .{ .LOONGARCH = .PCALA_HI20 });3732 try elf.addSymbolRelocAssumeCapacity(plt_ni, 0, got_plt_sym, 0, .rel32_hi20);
3465 try elf.addRelocAssumeCapacity(plt_ni, 8, got_plt_sym, 0, .{ .LOONGARCH = .PCALA_LO12 });3733 try elf.addSymbolRelocAssumeCapacity(plt_ni, 8, got_plt_sym, 0, .abs32_lo12);
3466 try elf.addRelocAssumeCapacity(plt_ni, 16, got_plt_sym, 0, .{ .LOONGARCH = .PCALA_LO12 });3734 try elf.addSymbolRelocAssumeCapacity(plt_ni, 16, got_plt_sym, 0, .abs32_lo12);
3467 },3735 },
3468 }3736 }
3469 }3737 }
...@@ -3473,8 +3741,8 @@ fn initHeaders(...@@ -3473,8 +3741,8 @@ fn initHeaders(
3473 .moved = true,3741 .moved = true,
3474 .bubbles_moved = false,3742 .bubbles_moved = false,
3475 });3743 });
3476 elf.nodes.appendAssumeCapacity(.{ .segment = tls_phndx });3744 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.tls });
3477 elf.phdrs.items[tls_phndx] = elf.ni.tls;3745 elf.phdrs.items[phndx.tls] = elf.ni.tls;
3478 }3746 }
34793747
3480 // Populate reserved GOT words.3748 // Populate reserved GOT words.
...@@ -3641,10 +3909,11 @@ fn initHeaders(...@@ -3641,10 +3909,11 @@ fn initHeaders(
3641 .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true },3909 .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true },
3642 .addralign = elf.mf.flags.block_size,3910 .addralign = elf.mf.flags.block_size,
3643 });3911 });
3912
3644 assert(elf.nodes.len == expected_nodes_len);3913 assert(elf.nodes.len == expected_nodes_len);
3914 assert(elf.shdrs.items.len == shnum);
36453915
3646 try elf.section_by_name.ensureUnusedCapacity(gpa, elf.shdrs.items.len);3916 for (0..shnum) |shndx_raw| {
3647 for (0..elf.shdrs.items.len) |shndx_raw| {
3648 const shndx: Section.Index = @enumFromInt(shndx_raw);3917 const shndx: Section.Index = @enumFromInt(shndx_raw);
3649 elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {});3918 elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {});
3650 }3919 }
...@@ -3780,13 +4049,11 @@ fn flushMovedNodeRelocs(...@@ -3780,13 +4049,11 @@ fn flushMovedNodeRelocs(
3780 for (elf.symbol_relocs.items[@intFromEnum(first_symbol_reloc)..]) |*reloc| {4049 for (elf.symbol_relocs.items[@intFromEnum(first_symbol_reloc)..]) |*reloc| {
3781 if (reloc.node != node) break;4050 if (reloc.node != node) break;
3782 if (reloc.rela_index.unwrap()) |rela_index| {4051 if (reloc.rela_index.unwrap()) |rela_index| {
3783 // Update the offsets of any `ElfN.Rela` entry we've emitted, since the node they're4052 // The node has moved, so the offset of the relocation within the section might have
3784 // in has moved, so their offset within the section might also have moved.4053 // changed, so update the `offset` field of the `ElfN.Rela` entry.
3785 reloc.relaSection(elf).relaSetOffset(elf, rela_index, node_vaddr + reloc.offset);4054 reloc.relaSection(elf).relaSetOffset(elf, rela_index, node_vaddr + reloc.offset);
3786 } else {
3787 // We've applied this relocation ourselves! Just re-apply it now.
3788 reloc.apply(elf);
3789 }4055 }
4056 reloc.apply(elf);
3790 }4057 }
3791 }4058 }
37924059
...@@ -4108,7 +4375,7 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node...@@ -4108,7 +4375,7 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node
4108 } else if (ip.isFunctionType(nav.resolved.?.type)) {4375 } else if (ip.isFunctionType(nav.resolved.?.type)) {
4109 break :section .text;4376 break :section .text;
4110 } else {4377 } else {
4111 break :section .rodata;4378 break :section .data_rel_ro; // TODO: it would be better to use `.rodata` if the NAV value doesn't have relocs
4112 }4379 }
4113 };4380 };
4114 const alignment: InternPool.Alignment = switch (Type.fromInterned(nav.resolved.?.type).zigTypeTag(zcu)) {4381 const alignment: InternPool.Alignment = switch (Type.fromInterned(nav.resolved.?.type).zigTypeTag(zcu)) {
...@@ -4174,7 +4441,7 @@ fn uavMapIndex(...@@ -4174,7 +4441,7 @@ fn uavMapIndex(
4174 const uav_gop = elf.uavs.getOrPutAssumeCapacity(uav_val);4441 const uav_gop = elf.uavs.getOrPutAssumeCapacity(uav_val);
4175 const umi: Node.UavMapIndex = @enumFromInt(uav_gop.index);4442 const umi: Node.UavMapIndex = @enumFromInt(uav_gop.index);
4176 if (!uav_gop.found_existing) {4443 if (!uav_gop.found_existing) {
4177 const shndx: Section.Index = .data;4444 const shndx: Section.Index = .data_rel_ro; // TODO: it would be better to use `.rodata` if the UAV value doesn't have relocs
4178 const node = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{4445 const node = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{
4179 .moved = true, // see assert at end of `flushUav`4446 .moved = true, // see assert at end of `flushUav`
4180 .alignment = resolved_align.toStdMem(),4447 .alignment = resolved_align.toStdMem(),
...@@ -5124,7 +5391,7 @@ fn updateInitFiniArraySectionSize(...@@ -5124,7 +5391,7 @@ fn updateInitFiniArraySectionSize(
5124 const end_vaddr: u64 = switch (elf.shdrPtr(shndx)) {5391 const end_vaddr: u64 = switch (elf.shdrPtr(shndx)) {
5125 inline else => |shdr| shndx.vaddr(elf) + elf.targetLoad(&shdr.size),5392 inline else => |shdr| shndx.vaddr(elf) + elf.targetLoad(&shdr.size),
5126 };5393 };
5127 const end_sym_name = elf.string(.strtab, "__" ++ name ++ "_end") catch unreachable; // string definitely already exists5394 const end_sym_name = elf.stringExisting(.strtab, "__" ++ name ++ "_end");
5128 Symbol.Id.global(end_sym_name).flushMoved(elf, end_vaddr);5395 Symbol.Id.global(end_sym_name).flushMoved(elf, end_vaddr);
5129}5396}
51305397
...@@ -5178,6 +5445,10 @@ fn prelinkInner(elf: *Elf) Error!void {...@@ -5178,6 +5445,10 @@ fn prelinkInner(elf: *Elf) Error!void {
5178 }5445 }
5179 break :rpath try elf.string(.dynstr, buf.items);5446 break :rpath try elf.string(.dynstr, buf.items);
5180 };5447 };
5448 // Static PIEs don't need a PLT, so we shouldn't emit the associated dynamic entries.
5449 const use_plt = !(comp.config.output_mode == .Exe and
5450 comp.config.link_mode == .static and
5451 comp.config.pie);
5181 const soname: ?String(.dynstr) = if (elf.options.soname) |soname_slice| str: {5452 const soname: ?String(.dynstr) = if (elf.options.soname) |soname_slice| str: {
5182 break :str try elf.string(.dynstr, soname_slice);5453 break :str try elf.string(.dynstr, soname_slice);
5183 } else null;5454 } else null;
...@@ -5188,7 +5459,8 @@ fn prelinkInner(elf: *Elf) Error!void {...@@ -5188,7 +5459,8 @@ fn prelinkInner(elf: *Elf) Error!void {
5188 @as(usize, @intFromBool(elf.shndx.init_array != .UNDEF)) * 2 +5459 @as(usize, @intFromBool(elf.shndx.init_array != .UNDEF)) * 2 +
5189 @as(usize, @intFromBool(elf.shndx.fini_array != .UNDEF)) * 2 +5460 @as(usize, @intFromBool(elf.shndx.fini_array != .UNDEF)) * 2 +
5190 @as(usize, @intFromBool(elf.shndx.preinit_array != .UNDEF)) * 2 +5461 @as(usize, @intFromBool(elf.shndx.preinit_array != .UNDEF)) * 2 +
5191 @intFromBool(comp.config.output_mode == .Exe) + 12;5462 @as(usize, @intFromBool(use_plt)) * 4 +
5463 @intFromBool(comp.config.output_mode == .Exe) + 8;
5192 const dynamic_size: u32 = @intCast(@sizeOf(ElfN.Addr) * 2 * dynamic_len);5464 const dynamic_size: u32 = @intCast(@sizeOf(ElfN.Addr) * 2 * dynamic_len);
5193 const dynamic_ni = elf.shndx.dynamic.get(elf).ni;5465 const dynamic_ni = elf.shndx.dynamic.get(elf).ni;
5194 try dynamic_ni.resize(&elf.mf, gpa, dynamic_size);5466 try dynamic_ni.resize(&elf.mf, gpa, dynamic_size);
...@@ -5200,6 +5472,8 @@ fn prelinkInner(elf: *Elf) Error!void {...@@ -5200,6 +5472,8 @@ fn prelinkInner(elf: *Elf) Error!void {
5200 init_array: ?usize,5472 init_array: ?usize,
5201 fini_array: ?usize,5473 fini_array: ?usize,
5202 preinit_array: ?usize,5474 preinit_array: ?usize,
5475 jmprel: ?usize,
5476 pltgot: ?usize,
5203 } = indices: {5477 } = indices: {
5204 const sec_dynamic = dynamic_ni.slice(&elf.mf);5478 const sec_dynamic = dynamic_ni.slice(&elf.mf);
5205 const dynamic_entries: [][2]ElfN.Addr = @ptrCast(@alignCast(sec_dynamic));5479 const dynamic_entries: [][2]ElfN.Addr = @ptrCast(@alignCast(sec_dynamic));
...@@ -5232,7 +5506,7 @@ fn prelinkInner(elf: *Elf) Error!void {...@@ -5232,7 +5506,7 @@ fn prelinkInner(elf: *Elf) Error!void {
5232 }5506 }
5233 const init_array_index: ?usize = if (elf.shndx.init_array != .UNDEF) i: {5507 const init_array_index: ?usize = if (elf.shndx.init_array != .UNDEF) i: {
5234 dynamic_entries[dynamic_index..][0..2].* = .{5508 dynamic_entries[dynamic_index..][0..2].* = .{
5235 .{ std.elf.DT_INIT_ARRAY, @intCast(elf.shndx.init_array.vaddr(elf)) },5509 .{ std.elf.DT_INIT_ARRAY, 0 }, // reloc added below
5236 .{ std.elf.DT_INIT_ARRAYSZ, elf.targetLoad(5510 .{ std.elf.DT_INIT_ARRAYSZ, elf.targetLoad(
5237 &@field(elf.shdrPtr(elf.shndx.init_array), @tagName(ct_class)).size,5511 &@field(elf.shdrPtr(elf.shndx.init_array), @tagName(ct_class)).size,
5238 ) },5512 ) },
...@@ -5242,7 +5516,7 @@ fn prelinkInner(elf: *Elf) Error!void {...@@ -5242,7 +5516,7 @@ fn prelinkInner(elf: *Elf) Error!void {
5242 } else null;5516 } else null;
5243 const fini_array_index: ?usize = if (elf.shndx.fini_array != .UNDEF) i: {5517 const fini_array_index: ?usize = if (elf.shndx.fini_array != .UNDEF) i: {
5244 dynamic_entries[dynamic_index..][0..2].* = .{5518 dynamic_entries[dynamic_index..][0..2].* = .{
5245 .{ std.elf.DT_FINI_ARRAY, @intCast(elf.shndx.fini_array.vaddr(elf)) },5519 .{ std.elf.DT_FINI_ARRAY, 0 }, // reloc added below
5246 .{ std.elf.DT_FINI_ARRAYSZ, elf.targetLoad(5520 .{ std.elf.DT_FINI_ARRAYSZ, elf.targetLoad(
5247 &@field(elf.shdrPtr(elf.shndx.fini_array), @tagName(ct_class)).size,5521 &@field(elf.shdrPtr(elf.shndx.fini_array), @tagName(ct_class)).size,
5248 ) },5522 ) },
...@@ -5252,7 +5526,7 @@ fn prelinkInner(elf: *Elf) Error!void {...@@ -5252,7 +5526,7 @@ fn prelinkInner(elf: *Elf) Error!void {
5252 } else null;5526 } else null;
5253 const preinit_array_index: ?usize = if (elf.shndx.preinit_array != .UNDEF) i: {5527 const preinit_array_index: ?usize = if (elf.shndx.preinit_array != .UNDEF) i: {
5254 dynamic_entries[dynamic_index..][0..2].* = .{5528 dynamic_entries[dynamic_index..][0..2].* = .{
5255 .{ std.elf.DT_PREINIT_ARRAY, @intCast(elf.shndx.preinit_array.vaddr(elf)) },5529 .{ std.elf.DT_PREINIT_ARRAY, 0 }, // reloc added below
5256 .{ std.elf.DT_PREINIT_ARRAYSZ, elf.targetLoad(5530 .{ std.elf.DT_PREINIT_ARRAYSZ, elf.targetLoad(
5257 &@field(elf.shdrPtr(elf.shndx.preinit_array), @tagName(ct_class)).size,5531 &@field(elf.shdrPtr(elf.shndx.preinit_array), @tagName(ct_class)).size,
5258 ) },5532 ) },
...@@ -5260,27 +5534,33 @@ fn prelinkInner(elf: *Elf) Error!void {...@@ -5260,27 +5534,33 @@ fn prelinkInner(elf: *Elf) Error!void {
5260 defer dynamic_index += 2;5534 defer dynamic_index += 2;
5261 break :i dynamic_index;5535 break :i dynamic_index;
5262 } else null;5536 } else null;
5263 dynamic_entries[dynamic_index..][0..12].* = .{5537 const jmprel_index: ?usize, const pltgot_index: ?usize = if (use_plt) i: {
5264 .{ std.elf.DT_RELA, @intCast(elf.shndx.rela_dyn.vaddr(elf)) },5538 dynamic_entries[dynamic_index..][0..4].* = .{
5539 .{ std.elf.DT_JMPREL, 0 }, // reloc added below
5540 .{ std.elf.DT_PLTGOT, 0 }, // reloc added below
5541 .{ std.elf.DT_PLTRELSZ, elf.targetLoad(
5542 &@field(elf.shdrPtr(elf.shndx.rela_plt), @tagName(ct_class)).size,
5543 ) },
5544 .{ std.elf.DT_PLTREL, std.elf.DT_RELA },
5545 };
5546 defer dynamic_index += 4;
5547 break :i .{ dynamic_index, dynamic_index + 1 };
5548 } else .{ null, null };
5549 dynamic_entries[dynamic_index..][0..8].* = .{
5550 .{ std.elf.DT_RELA, 0 }, // reloc added below
5265 .{ std.elf.DT_RELASZ, elf.targetLoad(5551 .{ std.elf.DT_RELASZ, elf.targetLoad(
5266 &@field(elf.shdrPtr(elf.shndx.rela_dyn), @tagName(ct_class)).size,5552 &@field(elf.shdrPtr(elf.shndx.rela_dyn), @tagName(ct_class)).size,
5267 ) },5553 ) },
5268 .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) },5554 .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) },
5269 .{ std.elf.DT_JMPREL, @intCast(elf.shndx.rela_plt.vaddr(elf)) },5555 .{ std.elf.DT_SYMTAB, 0 }, // reloc added below
5270 .{ std.elf.DT_PLTRELSZ, elf.targetLoad(
5271 &@field(elf.shdrPtr(elf.shndx.rela_plt), @tagName(ct_class)).size,
5272 ) },
5273 .{ std.elf.DT_PLTGOT, @intCast(elf.shndx.got_plt.vaddr(elf)) },
5274 .{ std.elf.DT_PLTREL, std.elf.DT_RELA },
5275 .{ std.elf.DT_SYMTAB, @intCast(elf.shndx.dynsym.vaddr(elf)) },
5276 .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) },5556 .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) },
5277 .{ std.elf.DT_STRTAB, @intCast(elf.shndx.dynstr.vaddr(elf)) },5557 .{ std.elf.DT_STRTAB, 0 }, // reloc added below
5278 .{ std.elf.DT_STRSZ, elf.targetLoad(5558 .{ std.elf.DT_STRSZ, elf.targetLoad(
5279 &@field(elf.shdrPtr(elf.shndx.dynstr), @tagName(ct_class)).size,5559 &@field(elf.shdrPtr(elf.shndx.dynstr), @tagName(ct_class)).size,
5280 ) },5560 ) },
5281 .{ std.elf.DT_NULL, 0 },5561 .{ std.elf.DT_NULL, 0 },
5282 };5562 };
5283 dynamic_index += 12;5563 dynamic_index += 8;
5284 assert(dynamic_index == dynamic_len);5564 assert(dynamic_index == dynamic_len);
5285 if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry|5565 if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry|
5286 std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry);5566 std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry);
...@@ -5289,66 +5569,74 @@ fn prelinkInner(elf: *Elf) Error!void {...@@ -5289,66 +5569,74 @@ fn prelinkInner(elf: *Elf) Error!void {
5289 .init_array = init_array_index,5569 .init_array = init_array_index,
5290 .fini_array = fini_array_index,5570 .fini_array = fini_array_index,
5291 .preinit_array = preinit_array_index,5571 .preinit_array = preinit_array_index,
5572 .jmprel = jmprel_index,
5573 .pltgot = pltgot_index,
5292 };5574 };
5293 };5575 };
52945576
5577 const dsorel: SymbolReloc.Type = switch (ct_class) {
5578 .NONE, _ => comptime unreachable,
5579 .@"32" => .dsorel32,
5580 .@"64" => .dsorel64,
5581 };
5582
5295 elf.dynamic_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);5583 elf.dynamic_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);
5296 try elf.ensureUnusedRelocCapacity(dynamic_ni, 8);5584 try elf.ensureUnusedRelocCapacity(dynamic_ni, 8);
5297 if (dynamic_indices.init_array) |index| try elf.addRelocAssumeCapacity(5585 if (dynamic_indices.init_array) |index| try elf.addSymbolRelocAssumeCapacity(
5298 dynamic_ni,5586 dynamic_ni,
5299 @sizeOf(ElfN.Addr) * (2 * index + 1),5587 @sizeOf(ElfN.Addr) * (2 * index + 1),
5300 .local(elf.shndx.init_array.get(elf).lsi),5588 .local(elf.shndx.init_array.get(elf).lsi),
5301 0,5589 0,
5302 .absAddr(elf),5590 dsorel,
5303 );5591 );
5304 if (dynamic_indices.fini_array) |index| try elf.addRelocAssumeCapacity(5592 if (dynamic_indices.fini_array) |index| try elf.addSymbolRelocAssumeCapacity(
5305 dynamic_ni,5593 dynamic_ni,
5306 @sizeOf(ElfN.Addr) * (2 * index + 1),5594 @sizeOf(ElfN.Addr) * (2 * index + 1),
5307 .local(elf.shndx.fini_array.get(elf).lsi),5595 .local(elf.shndx.fini_array.get(elf).lsi),
5308 0,5596 0,
5309 .absAddr(elf),5597 dsorel,
5310 );5598 );
5311 if (dynamic_indices.preinit_array) |index| try elf.addRelocAssumeCapacity(5599 if (dynamic_indices.preinit_array) |index| try elf.addSymbolRelocAssumeCapacity(
5312 dynamic_ni,5600 dynamic_ni,
5313 @sizeOf(ElfN.Addr) * (2 * index + 1),5601 @sizeOf(ElfN.Addr) * (2 * index + 1),
5314 .local(elf.shndx.preinit_array.get(elf).lsi),5602 .local(elf.shndx.preinit_array.get(elf).lsi),
5315 0,5603 0,
5316 .absAddr(elf),5604 dsorel,
5317 );5605 );
5318 try elf.addRelocAssumeCapacity(5606 if (dynamic_indices.jmprel) |index| try elf.addSymbolRelocAssumeCapacity(
5319 dynamic_ni,5607 dynamic_ni,
5320 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 12) + 1),5608 @sizeOf(ElfN.Addr) * (2 * index + 1),
5321 .local(elf.shndx.rela_dyn.get(elf).lsi),5609 .local(elf.shndx.rela_plt.get(elf).lsi),
5322 0,5610 0,
5323 .absAddr(elf),5611 dsorel,
5324 );5612 );
5325 try elf.addRelocAssumeCapacity(5613 if (dynamic_indices.pltgot) |index| try elf.addSymbolRelocAssumeCapacity(
5326 dynamic_ni,5614 dynamic_ni,
5327 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 9) + 1),5615 @sizeOf(ElfN.Addr) * (2 * index + 1),
5328 .local(elf.shndx.rela_plt.get(elf).lsi),5616 .local(elf.shndx.got_plt.get(elf).lsi),
5329 0,5617 0,
5330 .absAddr(elf),5618 dsorel,
5331 );5619 );
5332 try elf.addRelocAssumeCapacity(5620 try elf.addSymbolRelocAssumeCapacity(
5333 dynamic_ni,5621 dynamic_ni,
5334 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 7) + 1),5622 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 8) + 1),
5335 .local(elf.shndx.got_plt.get(elf).lsi),5623 .local(elf.shndx.rela_dyn.get(elf).lsi),
5336 0,5624 0,
5337 .absAddr(elf),5625 dsorel,
5338 );5626 );
5339 try elf.addRelocAssumeCapacity(5627 try elf.addSymbolRelocAssumeCapacity(
5340 dynamic_ni,5628 dynamic_ni,
5341 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 5) + 1),5629 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 5) + 1),
5342 .local(elf.shndx.dynsym.get(elf).lsi),5630 .local(elf.shndx.dynsym.get(elf).lsi),
5343 0,5631 0,
5344 .absAddr(elf),5632 dsorel,
5345 );5633 );
5346 try elf.addRelocAssumeCapacity(5634 try elf.addSymbolRelocAssumeCapacity(
5347 dynamic_ni,5635 dynamic_ni,
5348 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 3) + 1),5636 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 3) + 1),
5349 .local(elf.shndx.dynstr.get(elf).lsi),5637 .local(elf.shndx.dynstr.get(elf).lsi),
5350 0,5638 0,
5351 .absAddr(elf),5639 dsorel,
5352 );5640 );
5353 },5641 },
5354 };5642 };
...@@ -5687,18 +5975,34 @@ fn addSymbolRelocAssumeCapacity(...@@ -5687,18 +5975,34 @@ fn addSymbolRelocAssumeCapacity(
5687 @"type": SymbolReloc.Type,5975 @"type": SymbolReloc.Type,
5688) Error!void {5976) Error!void {
5689 assert(elf.ehdrField(.type) != .REL);5977 assert(elf.ehdrField(.type) != .REL);
5978 assert(node != .none);
56905979
5691 const rela_index: Section.RelaIndex.Optional = r: {5980 const rela_index: Section.RelaIndex.Optional = r: {
5692 if (elf.shndx.dynamic == .UNDEF) break :r .none;5981 // If we emit a runtime relocation entry, its `offset` is a virtual address, so we need to
5693 const global_name = switch (target.unwrap()) {5982 // determine the vaddr of `node`.
5694 .local => break :r .none,5983 const node_vaddr: u64 = switch (elf.getNode(node)) {
5695 .global => |name| name,5984 .file => unreachable,
5985 .ehdr => unreachable,
5986 .shdr => unreachable,
5987 .segment => unreachable,
5988 .copied_global => unreachable,
5989 .section => |shndx| shndx.vaddr(elf),
5990 .input_section => |isi| isi.ptrConst(elf).vaddr,
5991 inline .nav,
5992 .uav,
5993 .lazy_code,
5994 .lazy_const_data,
5995 => |i| Symbol.Id.local(i.symbol(elf)).value(elf),
5696 };5996 };
56975997
5698 const rela_type: MachineRelocType = switch (elf.ehdrField(.machine)) {5998 const rela_type: MachineRelocType = switch (elf.ehdrField(.machine)) {
5699 else => |machine| @panic(@tagName(machine)),5999 else => |machine| @panic(@tagName(machine)),
5700 .X86_64 => .{ .X86_64 = switch (@"type") {6000 .X86_64 => .{ .X86_64 = switch (@"type") {
5701 .write_rela => unreachable,6001 .write_rela => unreachable,
6002 .dsorel64, .dsorel32 => {
6003 assert(target.unwrap() == .local);
6004 break :r .none;
6005 },
5702 .abs64 => .@"64",6006 .abs64 => .@"64",
5703 .abs32 => .@"32",6007 .abs32 => .@"32",
5704 .abs32s => .@"32S",6008 .abs32s => .@"32S",
...@@ -5726,72 +6030,79 @@ fn addSymbolRelocAssumeCapacity(...@@ -5726,72 +6030,79 @@ fn addSymbolRelocAssumeCapacity(
5726 .tpoff64_hi12,6030 .tpoff64_hi12,
5727 => unreachable,6031 => unreachable,
5728 } },6032 } },
5729 .LOONGARCH => .{6033 .LOONGARCH => .{ .LOONGARCH = switch (@"type") {
5730 .LOONGARCH = switch (@"type") {6034 .write_rela => unreachable,
5731 .write_rela => unreachable,6035 .dsorel64, .dsorel32 => {
5732 .abs64 => .@"64",6036 assert(target.unwrap() == .local);
5733 .abs32 => .@"32",6037 break :r .none;
5734 .abs32s, .size64, .size32 => unreachable,
5735 .rel64 => .@"64_PCREL",
5736 .rel32 => .@"32_PCREL",
5737 .pltrel64, .pltrel32 => break :r .none,
5738 .dtpoff64 => .TLS_DTPREL64,
5739 .dtpoff32 => .TLS_DTPREL32,
5740 .tpoff64 => .TLS_TPREL64,
5741 .tpoff32 => .TLS_TPREL32,
5742 .abs32_lo12 => .PCALA_LO12,
5743 .rel32_hi20 => .PCALA_HI20,
5744 .rel64_lo20 => .PCALA64_LO20,
5745 .rel64_hi12 => .PCALA64_HI12,
5746 .branch_rel18 => .B16,
5747 .branch_rel23 => .B21,
5748 .branch_rel28 => .B26,
5749 .call_rel38 => .CALL36,
5750 .tpoff32_lo12 => .TLS_LE_LO12,
5751 .tpoff32_hi20 => .TLS_LE_HI20,
5752 .tpoff64_lo20 => .TLS_LE64_LO20,
5753 .tpoff64_hi12 => .TLS_LE64_HI12,
5754 },6038 },
5755 },6039 .abs64 => .@"64",
6040 .abs32 => .@"32",
6041 .abs32s, .size64, .size32 => unreachable,
6042 .rel64 => .@"64_PCREL",
6043 .rel32 => .@"32_PCREL",
6044 .pltrel64, .pltrel32 => break :r .none,
6045 .dtpoff64 => .TLS_DTPREL64,
6046 .dtpoff32 => .TLS_DTPREL32,
6047 .tpoff64 => .TLS_TPREL64,
6048 .tpoff32 => .TLS_TPREL32,
6049 .abs32_lo12 => .PCALA_LO12,
6050 .rel32_hi20 => .PCALA_HI20,
6051 .rel64_lo20 => .PCALA64_LO20,
6052 .rel64_hi12 => .PCALA64_HI12,
6053 .branch_rel18 => .B16,
6054 .branch_rel23 => .B21,
6055 .branch_rel28 => .B26,
6056 .call_rel38 => .CALL36,
6057 .tpoff32_lo12 => .TLS_LE_LO12,
6058 .tpoff32_hi20 => .TLS_LE_HI20,
6059 .tpoff64_lo20 => .TLS_LE64_LO20,
6060 .tpoff64_hi12 => .TLS_LE64_HI12,
6061 } },
5756 };6062 };
5757 // TODO: even if the symbol is locally defined, preemption/interposition is a
5758 // possibility, which this condition does not currently consider!
5759 if (elf.globals.strong_def.contains(global_name) or
5760 elf.globals.weak_def.contains(global_name))
5761 {
5762 break :r .none;
5763 }
57646063
5765 const dynsym_index = elf.globalByName(global_name).?.dynsym_index;6064 class: switch (elf.classifySymbolValue(target)) {
5766 if (dynsym_index == 0) break :r .none;6065 .static => break :r .none,
57676066 .static_relative => {
5768 switch (elf.nodeWantsDsoRelocation(node)) {6067 if (!@"type".isAbsAddr(elf)) break :r .none;
5769 .no => break :r .none,6068 switch (elf.nodeWantsDsoRelocation(node)) {
5770 .yes => {},6069 .no => break :r .none,
5771 .yes_textrel => if (try elf.maybeAddCopyRelocation(global_name)) {6070 .yes => {},
5772 // We were able to use a copy relocation on this symbol to avoid a text relocation,6071 .yes_textrel => elf.textrel_count += 1,
5773 // which is apparently considered a good thing despite copy relocations being an6072 }
5774 // abomination. (This is necessary for correctness in some cases, because e.g. a6073 break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
5775 // 32-bit runtime relocation on a 64-bit target will often cause rtld errors due to6074 .type = .relative(elf),
5776 // the DSOs being loaded too far apart.)6075 .offset = node_vaddr + offset,
5777 break :r .none;6076 .raw_sym_index = 0,
5778 } else {6077 .addend = 0,
5779 // At least for now, our only choice is a text relocation.6078 }).toOptional();
5780 elf.textrel_count += 1;6079 },
6080 .dynamic => dso_reloc: switch (elf.nodeWantsDsoRelocation(node)) {
6081 .no => break :r .none,
6082 .yes_textrel => if (try elf.maybeAddCopyRelocation(target.unwrap().global)) {
6083 // We were able to use a copy relocation on this symbol to avoid a text relocation,
6084 // which is apparently considered a good thing despite copy relocations being an
6085 // abomination. (This is necessary for correctness in some cases, because e.g. a
6086 // 32-bit runtime relocation on a 64-bit target will often cause rtld errors due to
6087 // the DSOs being loaded too far apart.)
6088 switch (elf.classifySymbolValue(target)) {
6089 .dynamic => unreachable, // we just added a copy relocation
6090 .static => continue :class .static,
6091 .static_relative => continue :class .static_relative,
6092 }
6093 } else {
6094 // At least for now, our only choice is a text relocation.
6095 elf.textrel_count += 1;
6096 continue :dso_reloc .yes;
6097 },
6098 .yes => break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
6099 .type = rela_type,
6100 .offset = node_vaddr + offset,
6101 .raw_sym_index = elf.globalByName(target.unwrap().global).?.dynsym_index,
6102 .addend = addend,
6103 }).toOptional(),
5781 },6104 },
5782 }6105 }
5783
5784 // It currently looks like we need a runtime relocation for this.
5785 break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
5786 .type = rela_type,
5787 // This field needs to equal the offset into the section, which is *not* necessarily
5788 // the same thing as our `offset`, which is the offset into `node`. We could compute
5789 // the section offset now, but there's no point, because `flushMovedNodeRelocs` will
5790 // eventually do it for us anyway, so just init to 0.
5791 .offset = 0,
5792 .raw_sym_index = dynsym_index,
5793 .addend = addend,
5794 }).toOptional();
5795 };6106 };
57966107
5797 const ri: SymbolReloc.Index = @enumFromInt(elf.symbol_relocs.items.len);6108 const ri: SymbolReloc.Index = @enumFromInt(elf.symbol_relocs.items.len);
...@@ -5814,6 +6125,9 @@ fn addSymbolRelocAssumeCapacity(...@@ -5814,6 +6125,9 @@ fn addSymbolRelocAssumeCapacity(
5814 if (@"type".dependsOnTlsSize()) {6125 if (@"type".dependsOnTlsSize()) {
5815 elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {});6126 elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {});
5816 }6127 }
6128
6129 // Actually apply the new relocation!
6130 ri.get(elf).apply(elf);
5817}6131}
5818fn addGotRelocAssumeCapacity(6132fn addGotRelocAssumeCapacity(
5819 elf: *Elf,6133 elf: *Elf,
...@@ -5885,29 +6199,13 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -5885,29 +6199,13 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
5885 reloc: struct {6199 reloc: struct {
5886 type: MachineRelocType,6200 type: MachineRelocType,
5887 dynsym_index: u32,6201 dynsym_index: u32,
6202 addend: i64,
5888 },6203 },
5889 } = switch (elf.got.keys()[got_index]) {6204 } = switch (elf.got.keys()[got_index]) {
5890 .reserved => .{ .unsigned = 0 },6205 .reserved => .{ .unsigned = 0 },
5891 .tpoff => |sym_id| val: {6206 .tpoff => |sym_id| val: {
5892 // We will break from this block if we require a relocation.6207 // Only the executable's per-module TLS block is at a known offset from the TLS pointer.
5893 known: {6208 if (elf.base.comp.config.output_mode == .Exe and elf.classifySymbolValue(sym_id) != .dynamic) {
5894 if (elf.base.comp.config.output_mode != .Exe) {
5895 // Only the executable's per-module TLS block is at a known offset from the
5896 // general TLS pointer.
5897 break :known;
5898 }
5899 switch (sym_id.unwrap()) {
5900 .local => {},
5901 .global => |name| if (elf.globals.strong_undef.contains(name) or
5902 elf.globals.weak_undef.contains(name))
5903 {
5904 // This is an external TLS symbol, so we don't know its offset.
5905 break :known;
5906 },
5907 }
5908 // It's a symbol which we define, the symbol is not interposable because we're the
5909 // executable, and we know our per-module TLS block's offset because we're the
5910 // executable. We therefore know this value!
5911 const tls_phndx = elf.getNode(elf.ni.tls).segment;6209 const tls_phndx = elf.getNode(elf.ni.tls).segment;
5912 const tls_size: u64 = switch (elf.phdrSlice()) {6210 const tls_size: u64 = switch (elf.phdrSlice()) {
5913 inline else => |phdr| tls_size: {6211 inline else => |phdr| tls_size: {
...@@ -5918,110 +6216,80 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -5918,110 +6216,80 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
5918 const sym_value = sym_id.value(elf);6216 const sym_value = sym_id.value(elf);
5919 break :val .{ .signed = @bitCast(sym_value -% tls_size) };6217 break :val .{ .signed = @bitCast(sym_value -% tls_size) };
5920 }6218 }
5921 break :val .{6219 const reloc_type: MachineRelocType = switch (elf.ehdrField(.machine)) {
5922 .reloc = .{6220 else => |machine| @panic(@tagName(machine)),
5923 .type = switch (elf.ehdrField(.machine)) {6221 .X86_64 => .{ .X86_64 = .TPOFF64 },
5924 else => |machine| @panic(@tagName(machine)),6222 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_TPREL64 else .TLS_TPREL32 },
5925 .X86_64 => .{ .X86_64 = .TPOFF64 },
5926 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_TPREL64 else .TLS_TPREL32 },
5927 },
5928 .dynsym_index = switch (sym_id.unwrap()) {
5929 .global => |name| elf.globalByName(name).?.dynsym_index,
5930 // TODO: I have no idea if compilers are even allowed to emit this, but if they
5931 // are then I guess we need to add this local symbol to `.dynsym`?
5932 .local => @panic("TODO(Elf2): GOT tpoff entry referencing local symbol"),
5933 },
5934 },
5935 };
5936 },
5937 .symbol, .tlsgd1 => |sym_id, tag| val: {
5938 const name = switch (sym_id.unwrap()) {
5939 .local => break :val .{ .unsigned = sym_id.value(elf) },
5940 .global => |name| name,
5941 };6223 };
5942 // If the symbol is *defined* in this module, we might be able to avoid the relocation.6224 break :val switch (sym_id.unwrap()) {
5943 const need_reloc: bool = need_reloc: {6225 // For global symbols, just target the right dynsym with no addend.
5944 const global = g: {6226 .global => |name| .{ .reloc = .{
5945 if (elf.globals.strong_def.getPtr(name)) |g| break :g g;6227 .type = reloc_type,
5946 if (elf.globals.weak_def.getPtr(name)) |g| break :g g;6228 .dynsym_index = elf.globalByName(name).?.dynsym_index,
5947 // The global is undefined, which probably means we need a relocation---unless6229 .addend = 0,
5948 // we have created a copy relocation for it, in which case we own the canonical6230 } },
5949 // address of this symbol in this DSO!6231 // For local symbols, target the null symbol (index 0) so we get the offset to the
5950 break :need_reloc !elf.copied_globals.contains(name);6232 // base of our TLS block, and then use `addend` to offset to the right symbol.
5951 };6233 .local => .{ .reloc = .{
59526234 .type = reloc_type,
5953 // We have a definition, but it might be interposable (aka preemptible). There6235 .dynsym_index = 0,
5954 // are two cases where it is not and so we can (and, in fact, must) elide the6236 .addend = @intCast(sym_id.value(elf)),
5955 // runtime relocation:6237 } },
5956 // * We are the executable. Symbols from executables cannot be interposed.
5957 // * The symbol's visibility disallows interposition.
5958 if (elf.base.comp.config.output_mode == .Exe) {
5959 break :need_reloc false;
5960 }
5961 const visibility: std.elf.STV = switch (elf.symPtr(global.symtab_index)) {
5962 inline else => |sym| elf.targetLoad(&sym.other).visibility,
5963 };
5964 break :need_reloc switch (visibility) {
5965 .DEFAULT => true,
5966 .INTERNAL, .HIDDEN, .PROTECTED => false,
5967 };
5968 };6238 };
5969
5970 if (!need_reloc) {
5971 break :val .{ .unsigned = sym_id.value(elf) };
5972 }
5973
5974 break :val .{ .reloc = .{
5975 .type = if (tag == .symbol) .globDat(elf) else .dtpOffAddr(elf),
5976 .dynsym_index = elf.globalByName(name).?.dynsym_index,
5977 } };
5978 },6239 },
5979 .tlsgd0 => |sym| switch (elf.shndx.dynamic) {6240 .symbol => |sym| switch (elf.classifySymbolValue(sym)) {
5980 .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable6241 .static => .{ .unsigned = sym.value(elf) },
5981 else => .{6242 .static_relative => .{ .reloc = .{
5982 .reloc = .{6243 .type = .relative(elf),
5983 .type = switch (elf.ehdrField(.machine)) {6244 .dynsym_index = 0,
5984 else => |machine| @panic(@tagName(machine)),6245 .addend = @bitCast(sym.value(elf)),
5985 .X86_64 => .{ .X86_64 = .DTPMOD64 },6246 } },
5986 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 },6247 .dynamic => .{ .reloc = .{
5987 },6248 .type = .globDat(elf),
5988 .dynsym_index = switch (sym.unwrap()) {6249 .dynsym_index = elf.globalByName(sym.unwrap().global).?.dynsym_index,
5989 .local => 0,6250 .addend = 0,
5990 .global => |name| dsi: {6251 } },
5991 // Like in the `.tlsgd1` case, we need to check for a non-interposable definition.6252 },
5992 if (elf.globals.strong_def.getPtr(name) orelse6253 .tlsgd1 => |sym| switch (elf.classifySymbolValue(sym)) {
5993 elf.globals.weak_def.getPtr(name)) |global|6254 .static => .{ .unsigned = sym.value(elf) },
5994 {6255 .static_relative => unreachable, // TLS variables should be in TLS sections, which do not return `.static_relative`
5995 if (elf.base.comp.config.output_mode == .Exe) {6256 .dynamic => .{ .reloc = .{
5996 break :dsi 0; // non-interposable definition6257 .type = .dtpOffAddr(elf),
5997 }6258 .dynsym_index = elf.globalByName(sym.unwrap().global).?.dynsym_index,
5998 const visibility: std.elf.STV = switch (elf.symPtr(global.symtab_index)) {6259 .addend = 0,
5999 inline else => |sym_ptr| elf.targetLoad(&sym_ptr.other).visibility,6260 } },
6000 };6261 },
6001 switch (visibility) {6262 .tlsgd0 => |sym| switch (elf.base.comp.config.link_mode) {
6002 .DEFAULT => {},6263 .static => val: {
6003 .INTERNAL, .HIDDEN, .PROTECTED => {6264 assert(elf.base.comp.config.output_mode == .Exe); // static libraries don't have GOTs
6004 break :dsi 0; // non-interposable definition6265 break :val .{ .unsigned = 1 }; // TLS module ID for executable
6005 },
6006 }
6007 }
6008 // `sym` is either undefined or an interposable definition, so use its
6009 // actual dynsym index.
6010 break :dsi elf.globalByName(name).?.dynsym_index;
6011 },
6012 },
6013 },
6014 },6266 },
6267 .dynamic => .{ .reloc = .{
6268 .type = switch (elf.ehdrField(.machine)) {
6269 else => |machine| @panic(@tagName(machine)),
6270 .X86_64 => .{ .X86_64 = .DTPMOD64 },
6271 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 },
6272 },
6273 .dynsym_index = switch (elf.classifySymbolValue(sym)) {
6274 .static, .static_relative => 0,
6275 .dynamic => elf.globalByName(sym.unwrap().global).?.dynsym_index,
6276 },
6277 .addend = 0,
6278 } },
6015 },6279 },
6016 .tlsld0 => switch (elf.shndx.dynamic) {6280 .tlsld0 => switch (elf.base.comp.config.link_mode) {
6017 .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable6281 .static => val: {
6018 else => .{ .reloc = .{6282 assert(elf.base.comp.config.output_mode == .Exe); // static libraries don't have GOTs
6283 break :val .{ .unsigned = 1 }; // TLS module ID for executable
6284 },
6285 .dynamic => .{ .reloc = .{
6019 .type = switch (elf.ehdrField(.machine)) {6286 .type = switch (elf.ehdrField(.machine)) {
6020 else => |machine| @panic(@tagName(machine)),6287 else => |machine| @panic(@tagName(machine)),
6021 .X86_64 => .{ .X86_64 = .DTPMOD64 },6288 .X86_64 => .{ .X86_64 = .DTPMOD64 },
6022 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 },6289 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 },
6023 },6290 },
6024 .dynsym_index = 0,6291 .dynsym_index = 0,
6292 .addend = 0,
6025 } },6293 } },
6026 },6294 },
6027 .tlsld1 => .{ .unsigned = 0 },6295 .tlsld1 => .{ .unsigned = 0 },
...@@ -6065,7 +6333,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -6065,7 +6333,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
6065 .type = reloc.type,6333 .type = reloc.type,
6066 .offset = got_entry_addr,6334 .offset = got_entry_addr,
6067 .raw_sym_index = reloc.dynsym_index,6335 .raw_sym_index = reloc.dynsym_index,
6068 .addend = 0,6336 .addend = reloc.addend,
6069 }).toOptional(),6337 }).toOptional(),
6070 };6338 };
6071}6339}
...@@ -6405,26 +6673,80 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {...@@ -6405,26 +6673,80 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
6405 };6673 };
6406 break :task;6674 break :task;
6407 }6675 }
6408 while (elf.changed_symtab_index.pop()) |kv| {6676 if (elf.changed_symtab_index.pop()) |kv| {
6409 // We only need to do work in relocatables, because in ELF modules (non-relocatables)6677 const sub_prog_node = elf.mf.update_prog_node.start(kv.key.slice(elf), 0);
6410 // our `ElfN.Rela` entries use `.dynsym` indices rather than `.symtab` indices, and6678 defer sub_prog_node.end();
6411 // `.dynsym` indices are (at the time of writing) always immutable.6679
6412 if (elf.ehdrField(.type) == .REL) {6680 const global_name = kv.key;
6413 const sub_prog_node = elf.mf.update_prog_node.start(kv.key.slice(elf), 0);6681 const global = elf.globalByName(global_name).?;
6414 defer sub_prog_node.end();6682 const sym_id: Symbol.Id = .global(global_name);
6415 const sym = elf.globalByName(kv.key).?.symtab_index.ptr(elf);6683 const sym = global.symtab_index.ptr(elf);
6416 var ri = sym.first_target_reloc;6684
6417 while (ri != .none) {6685 switch (elf.ehdrField(.type)) {
6418 const reloc = ri.get(elf);6686 .REL => {
6419 reloc.relaSection(elf).relaUpdateSym(6687 // Index in `.symtab` has changed. Relocatables are easy, we just need to update
6420 elf,6688 // all of the output relocations.
6421 reloc.rela_index.unwrap().?,6689 const symtab_index = @intFromEnum(global.symtab_index);
6422 @intFromEnum(reloc.target.index(elf)),6690 var ri = sym.first_target_reloc;
6423 );6691 while (ri != .none) {
6424 ri = reloc.next;6692 const reloc = ri.get(elf);
6425 }6693 assert(reloc.target == sym_id);
6426 break :task;6694 // In relocatables, every symbol relocation has an output relocation.
6695 const rela_index = reloc.rela_index.unwrap().?;
6696 reloc.relaSection(elf).relaUpdateSym(elf, rela_index, symtab_index);
6697 ri = reloc.next;
6698 }
6699 },
6700 // For other `ET_*` values, the index in `.dynsym` has changed. There are a few
6701 // places we might have emitted output relocations, depending on whether or not the
6702 // symbol's value is statically known.
6703 else => switch (elf.classifySymbolValue(sym_id)) {
6704 .static, .static_relative => {
6705 // Since the symbol value is statically known, we definitely aren't emitting
6706 // any relocation targeting it (we might have `R_*_RELATIVE` relocs but they
6707 // don't care about the dynsym index). The only exception is a copy reloc
6708 // could exist (and be the *reason* the symbol value is statically known).
6709 if (elf.copied_globals.get(global_name)) |copied| {
6710 elf.shndx.rela_dyn.relaUpdateSym(elf, copied.rela_index, global.dynsym_index);
6711 }
6712 },
6713 .dynamic => {
6714 assert(!elf.copied_globals.contains(global_name)); // value would be statically known
6715
6716 // Update symbol relocs:
6717 var ri = sym.first_target_reloc;
6718 while (ri != .none) {
6719 const reloc = ri.get(elf);
6720 assert(reloc.target == sym_id);
6721 // There may or may not be a runtime relocation for this symbol reloc.
6722 if (reloc.rela_index.unwrap()) |rela_index| {
6723 elf.shndx.rela_dyn.relaUpdateSym(elf, rela_index, global.dynsym_index);
6724 }
6725 ri = reloc.next;
6726 }
6727
6728 // Update the PLT entry's reloc if there is one:
6729 if (elf.plt.getIndex(global_name)) |plt_index| {
6730 // PLT indices exactly match `.rela.plt` relocation indices.
6731 elf.shndx.rela_plt.relaUpdateSym(elf, @enumFromInt(plt_index), global.dynsym_index);
6732 }
6733
6734 // Update relocs for any relevant GOT entries:
6735 if (elf.got.getIndex(.{ .symbol = sym_id })) |got_index| {
6736 elf.updateGotEntry(got_index);
6737 }
6738 if (elf.got.getIndex(.{ .tpoff = sym_id })) |got_index| {
6739 elf.updateGotEntry(got_index);
6740 }
6741 if (elf.got.getIndex(.{ .tlsgd0 = sym_id })) |got_index| {
6742 elf.updateGotEntry(got_index);
6743 elf.updateGotEntry(got_index + 1); // tlsgd1
6744 }
6745 },
6746 },
6427 }6747 }
6748
6749 break :task;
6428 }6750 }
6429 while (elf.mf.updates.pop()) |ni| {6751 while (elf.mf.updates.pop()) |ni| {
6430 const clean_moved = ni.cleanMoved(&elf.mf);6752 const clean_moved = ni.cleanMoved(&elf.mf);
...@@ -6838,7 +7160,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo...@@ -6838,7 +7160,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
6838 switch (elf.targetLoad(&next_ph.type)) {7160 switch (elf.targetLoad(&next_ph.type)) {
6839 else => unreachable,7161 else => unreachable,
6840 .NULL, .LOAD => {},7162 .NULL, .LOAD => {},
6841 .DYNAMIC, .INTERP, .PHDR, .TLS, std.elf.PT.GNU_RELRO => break,7163 .DYNAMIC, .INTERP, .PHDR, .TLS, .GNU_RELRO, .GNU_STACK => break,
6842 }7164 }
6843 const next_vaddr = elf.targetLoad(&next_ph.vaddr);7165 const next_vaddr = elf.targetLoad(&next_ph.vaddr);
6844 if (vaddr + memsz <= next_vaddr) break;7166 if (vaddr + memsz <= next_vaddr) break;
...@@ -6917,8 +7239,8 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad...@@ -6917,8 +7239,8 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad
6917 // its relocations are probably going through the PLT, so we don't bother with7239 // its relocations are probably going through the PLT, so we don't bother with
6918 // specific tracking for PLT relocations---instead just re-apply all relocations7240 // specific tracking for PLT relocations---instead just re-apply all relocations
6919 // targeting symbols with PLT entries.7241 // targeting symbols with PLT entries.
6920 for (elf.plt.keys()) |sym| {7242 for (elf.plt.keys()) |name| {
6921 sym.applyTargetRelocs(elf);7243 Symbol.Id.global(name).applyTargetRelocs(elf);
6922 }7244 }
6923 // We also need to update all of the references from `.plt.sec` to `.got.plt`.7245 // We also need to update all of the references from `.plt.sec` to `.got.plt`.
6924 // However, if there's also a flush pending for `.got.plt`, don't bother doing7246 // 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 {...@@ -3,18 +3,21 @@ pub fn build(b: *Build) void {
3 b.default_step = test_step;3 b.default_step = test_step;
44
5 if (b.graph.host.result.cpu.arch == .x86_64 and b.graph.host.result.os.tag == .linux) {5 if (b.graph.host.result.cpu.arch == .x86_64 and b.graph.host.result.os.tag == .linux) {
6 addOne(b, test_step, b.graph.host, false, .static, "elf2-hello-native-selfhosted-static");6 addOne(b, test_step, b.graph.host, false, .static, false, "elf2-hello-native-selfhosted-static");
7 addOne(b, test_step, b.graph.host, false, .dynamic, "elf2-hello-native-selfhosted-dynamic");7 addOne(b, test_step, b.graph.host, false, .dynamic, false, "elf2-hello-native-selfhosted-dynamic");
8 addOne(b, test_step, b.graph.host, true, .static, "elf2-hello-native-llvm-static");8 addOne(b, test_step, b.graph.host, false, .static, true, "elf2-hello-native-selfhosted-static-pie");
9 addOne(b, test_step, b.graph.host, true, .dynamic, "elf2-hello-native-llvm-dynamic");9 addOne(b, test_step, b.graph.host, false, .dynamic, true, "elf2-hello-native-selfhosted-dynamic-pie");
10 addOne(b, test_step, b.graph.host, true, .static, false, "elf2-hello-native-llvm-static");
11 addOne(b, test_step, b.graph.host, true, .dynamic, false, "elf2-hello-native-llvm-dynamic");
10 }12 }
1113
12 const x86_64_linux_target: Build.ResolvedTarget = b.resolveTargetQuery(.{14 const x86_64_linux_target: Build.ResolvedTarget = b.resolveTargetQuery(.{
13 .cpu_arch = .x86_64,15 .cpu_arch = .x86_64,
14 .os_tag = .linux,16 .os_tag = .linux,
15 });17 });
16 addOne(b, test_step, x86_64_linux_target, false, .static, "elf2-hello-selfhosted-static");18 addOne(b, test_step, x86_64_linux_target, false, .static, false, "elf2-hello-selfhosted-static");
17 addOne(b, test_step, x86_64_linux_target, true, .static, "elf2-hello-llvm-static");19 addOne(b, test_step, x86_64_linux_target, false, .static, true, "elf2-hello-selfhosted-static-pie");
20 addOne(b, test_step, x86_64_linux_target, true, .static, false, "elf2-hello-llvm-static");
18}21}
1922
20fn addOne(23fn addOne(
...@@ -23,6 +26,7 @@ fn addOne(...@@ -23,6 +26,7 @@ fn addOne(
23 target: Build.ResolvedTarget,26 target: Build.ResolvedTarget,
24 use_llvm: bool,27 use_llvm: bool,
25 link_mode: std.lang.LinkMode,28 link_mode: std.lang.LinkMode,
29 pie: bool,
26 name: []const u8,30 name: []const u8,
27) void {31) void {
28 const mod = b.createModule(.{32 const mod = b.createModule(.{
...@@ -38,6 +42,7 @@ fn addOne(...@@ -38,6 +42,7 @@ fn addOne(
38 });42 });
39 exe.use_new_linker = true;43 exe.use_new_linker = true;
40 exe.use_llvm = use_llvm;44 exe.use_llvm = use_llvm;
45 if (pie) exe.pie = true;
4146
42 const run = b.addRunArtifact(exe);47 const run = b.addRunArtifact(exe);
43 run.expectExitCode(0);48 run.expectExitCode(0);