authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-07-15 18:53:50+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-07-17 15:57:25+02:00
log58a94eaae24e955d4a4a69238617e1bdd90270e7
tree0c49216530b74d5af7f747ae4de7b9fe4add005a
parent8f7fd5c7f363315f0f014cb9f1b8edac264bfb14

Elf2: big refactors and enhancements

These mainly concern relocation handling. This introduces a way to represent most relocation types---those which write to a single bit-field in a 8/16/32/64-bit backing integer---in a target-agnostic manner, without tons of copy-and-pasted logic. It isn't quite as generalized as what GNU ld does, but it can still handle *most* common cases, and it represents these cases in 16 bits of state without any lookup table. I've documented the new relocation types in detail in doc comments on `SymbolReloc.Type`, so take a look at those if you're interested. Also see the new code in `addRelocAssumeCapacity`, which is responsible for mapping the ELF relocation enums to this system. The logic for emitting runtime relocations has been greatly simplified. It no longer requires any target-specific logic, because even on targets with many complex static relocations, there are usually only a handful of dynamic relocations, so the code emitting them can quite easily be abstracted across target architectures. More generally, target-specific logic has been cleaned up and pulled together to make it easier to add support for new targets. For instance, a new function `targetPltInfo` is introduced which just returns a bunch of information about the structure of the PLT on this particular target. (I also filled in a bit more target-specific logic, e.g. lowerings for a few relocations and some missing cases in `MachineRelocType`.) `ehdrField` is replaced with more specialized functions, which return slightly-modified enum types with impossible tags omitted. This makes it much easier to use exhaustive `switch` statements in the linker when branching on things like the target ELF machine. The linker now has basic detection and error reporting for misaligned or overflowed relocation values. The error reporting isn't very good yet (you just get told that the overflow/misalignment happened and for how many relocations), but it's there! There are probably a few other smaller refactors and bugfixes here which I don't remember. Awfully sorry to throw all of this in one commit, I kept finding yaks to shave mid-way through the relocation type stuff! Resolves: https://codeberg.org/ziglang/zig/issues/36066

5 files changed, 1887 insertions(+), 1974 deletions(-)

lib/std/elf.zig+10-9
...@@ -1053,7 +1053,7 @@ pub const Elf32 = struct {...@@ -1053,7 +1053,7 @@ pub const Elf32 = struct {
1053 entry: Elf32.Addr,1053 entry: Elf32.Addr,
1054 phoff: Elf32.Off,1054 phoff: Elf32.Off,
1055 shoff: Elf32.Off,1055 shoff: Elf32.Off,
1056 flags: Word,1056 flags: EhdrFlags,
1057 ehsize: Half,1057 ehsize: Half,
1058 phentsize: Half,1058 phentsize: Half,
1059 phnum: Half,1059 phnum: Half,
...@@ -1143,7 +1143,7 @@ pub const Elf64 = struct {...@@ -1143,7 +1143,7 @@ pub const Elf64 = struct {
1143 entry: Elf64.Addr,1143 entry: Elf64.Addr,
1144 phoff: Elf64.Off,1144 phoff: Elf64.Off,
1145 shoff: Elf64.Off,1145 shoff: Elf64.Off,
1146 flags: Word,1146 flags: EhdrFlags,
1147 ehsize: Half,1147 ehsize: Half,
1148 phentsize: Half,1148 phentsize: Half,
1149 phnum: Half,1149 phnum: Half,
...@@ -1644,7 +1644,7 @@ pub const CLASS = enum(u8) {...@@ -1644,7 +1644,7 @@ pub const CLASS = enum(u8) {
16441644
1645 pub const NUM = @typeInfo(CLASS).@"enum".field_names.len;1645 pub const NUM = @typeInfo(CLASS).@"enum".field_names.len;
16461646
1647 pub inline fn size(class: CLASS) u32 {1647 pub inline fn size(class: CLASS) u8 {
1648 return switch (class) {1648 return switch (class) {
1649 .NONE, _ => unreachable,1649 .NONE, _ => unreachable,
1650 .@"32" => 4,1650 .@"32" => 4,
...@@ -3377,9 +3377,12 @@ pub const gnu_hash = struct {...@@ -3377,9 +3377,12 @@ pub const gnu_hash = struct {
3377 }3377 }
3378};3378};
33793379
3380pub const loongarch = struct {3380pub const EhdrFlags = packed union(Word) {
3381 /// Ehdr.e_flags bits of LoongArch3381 int: u32,
3382 pub const EFlags = packed struct(Word) {3382 loongarch: Loongarch,
3383 sparc: Sparc,
3384
3385 pub const Loongarch = packed struct(u32) {
3383 base_abi_modifier: BaseAbiModifier,3386 base_abi_modifier: BaseAbiModifier,
3384 abi_extension: AbiExtension,3387 abi_extension: AbiExtension,
3385 abi_version: u2,3388 abi_version: u2,
...@@ -3393,10 +3396,8 @@ pub const loongarch = struct {...@@ -3393,10 +3396,8 @@ pub const loongarch = struct {
3393 };3396 };
3394 pub const AbiExtension = enum(u3) { base = 0, _ };3397 pub const AbiExtension = enum(u3) { base = 0, _ };
3395 };3398 };
3396};
33973399
3398pub const sparc = struct {3400 pub const Sparc = packed struct(u32) {
3399 pub const EFlags = packed struct(Word) {
3400 mm: MemoryModel,3401 mm: MemoryModel,
3401 _reserved1: u6 = 0,3402 _reserved1: u6 = 0,
3402 ext: Extensions,3403 ext: Extensions,
src/link.zig-1
...@@ -32,7 +32,6 @@ pub const ConstPool = @import("link/ConstPool.zig");...@@ -32,7 +32,6 @@ pub const ConstPool = @import("link/ConstPool.zig");
3232
33pub const aarch64 = @import("link/aarch64.zig");33pub const aarch64 = @import("link/aarch64.zig");
34pub const loongarch = @import("link/loongarch.zig");34pub const loongarch = @import("link/loongarch.zig");
35pub const sparc = @import("link/sparc.zig");
3635
37pub const Error = Allocator.Error || Io.Cancelable || error{36pub const Error = Allocator.Error || Io.Cancelable || error{
38 /// An error message has already been stored in persistent state on `Compilation` or `Zcu`, for37 /// An error message has already been stored in persistent state on `Compilation` or `Zcu`, for
src/link/Elf2.zig+1869-1725
...@@ -44,6 +44,12 @@ shndx: struct {...@@ -44,6 +44,12 @@ shndx: struct {
44 fini_array: Section.Index,44 fini_array: Section.Index,
45 preinit_array: Section.Index,45 preinit_array: Section.Index,
46},46},
47dynamic: struct {
48 flags: u32,
49 flags_1: u32,
50 rpath: String(.dynstr),
51 soname: String(.dynstr),
52},
47symtab: std.ArrayList(Symbol),53symtab: std.ArrayList(Symbol),
48globals: struct {54globals: struct {
49 strong_def: std.array_hash_map.Auto(String(.strtab), Symbol.Global),55 strong_def: std.array_hash_map.Auto(String(.strtab), Symbol.Global),
...@@ -58,7 +64,7 @@ copied_globals: std.array_hash_map.Auto(String(.strtab), struct {...@@ -58,7 +64,7 @@ copied_globals: std.array_hash_map.Auto(String(.strtab), struct {
58 rela_index: Section.RelaIndex,64 rela_index: Section.RelaIndex,
59}),65}),
60/// Key is the name of an undef global for which we would *like* to create a copy relocation66/// Key is the name of an undef global for which we would *like* to create a copy relocation
61/// (`R_*_COPY`), but cannot because we have not seen an appropriate definition in a linked DSO yet.67/// (`R_*_COPY`),but cannot because we have not seen an appropriate definition in a linked DSO yet.
62///68///
63/// Therefore, if, when scanning a DSO input, we discover a definition for one of these symbols, we69/// Therefore, if, when scanning a DSO input, we discover a definition for one of these symbols, we
64/// will remove it from this map and call `maybeAddCopyRelocation`.70/// will remove it from this map and call `maybeAddCopyRelocation`.
...@@ -165,6 +171,9 @@ changed_symtab_index: std.array_hash_map.Auto(String(.strtab), void),...@@ -165,6 +171,9 @@ changed_symtab_index: std.array_hash_map.Auto(String(.strtab), void),
165/// section in `flush` only when it is actually necessary. See also `nodeWantsDsoRelocation`.171/// section in `flush` only when it is actually necessary. See also `nodeWantsDsoRelocation`.
166textrel_count: u32,172textrel_count: u32,
167173
174overflowed_reloc_count: u32,
175misaligned_reloc_count: u32,
176
168const_prog_node: std.Progress.Node,177const_prog_node: std.Progress.Node,
169synth_prog_node: std.Progress.Node,178synth_prog_node: std.Progress.Node,
170input_prog_node: std.Progress.Node,179input_prog_node: std.Progress.Node,
...@@ -486,6 +495,12 @@ const Section = struct {...@@ -486,6 +495,12 @@ const Section = struct {
486 };495 };
487 }496 }
488497
498 fn size(s: Index, elf: *Elf) u64 {
499 return switch (elf.shdrPtr(s)) {
500 inline else => |shdr| elf.targetLoad(&shdr.size),
501 };
502 }
503
489 fn flags(s: Index, elf: *Elf) std.elf.SHF {504 fn flags(s: Index, elf: *Elf) std.elf.SHF {
490 return switch (elf.shdrPtr(s)) {505 return switch (elf.shdrPtr(s)) {
491 inline else => |shdr| elf.targetLoad(&shdr.flags).shf,506 inline else => |shdr| elf.targetLoad(&shdr.flags).shf,
...@@ -769,36 +784,111 @@ const GotReloc = struct {...@@ -769,36 +784,111 @@ const GotReloc = struct {
769 target: GotKey,784 target: GotKey,
770 addend: i64,785 addend: i64,
771 type: GotReloc.Type,786 type: GotReloc.Type,
787 result: enum(u8) { ok, overflowed, misaligned },
788
789 /// `GotReloc.Type` has the same structure as `SymbolReloc.Type`, just with different `Target`
790 /// and `Special` enums---consult doc comments on `SymbolReloc.Type` for an overview.
791 const Type = packed struct(u16) {
792 fn simple(target: Target, action: Simple) GotReloc.Type {
793 assert(target != .special);
794 return .{ .target = target, .action = .{ .simple = action } };
795 }
772796
773 const deleted: GotReloc = .{797 fn special(s: Special) GotReloc.Type {
774 .node = .none,798 return .{ .target = .special, .action = .{ .special = s } };
775 .offset = undefined,799 }
776 .target = undefined,800
777 .addend = undefined,801 target: Target,
778 .type = undefined,802 action: packed union {
779 };803 simple: Simple,
804 special: Special,
805 },
780806
781 const Type = enum(u8) {807 /// Like `SymbolReloc.Target`, but for GOT relocations. There are fewer tags because there
782 offset32,808 /// are fewer different kinds of GOT relocation.
783 offset64,809 const Target = enum(u3) {
784 rel32,810 /// This is a "special" relocation whose specific type is in the `action.special` field.
785 rel64,811 special,
786812
787 larch_rel32_hi20,813 /// Absolute address of the GOT entry.
788 larch_rel64_lo20,814 abs,
789 larch_rel64_hi12,815 /// Offset from the relocation itself to the GOT entry ("PC-relative").
790 larch_abs32_lo12,816 rel,
791 larch_abs32_hi20,817 /// Offset from the base of the GOT to the GOT entry.
792 larch_abs64_lo20,818 offset,
793 larch_abs64_hi12,819 };
794820
795 sparc_10,821 const Simple = SymbolReloc.Type.Simple;
796 sparc_13,822
797 sparc_22,823 /// Like `SymbolReloc.Special`, but for GOT relocations.
798 sparc_ldm_hi22,824 const Special = enum(u13) {
799 sparc_ldm_lo10,825 larch_pcala_hi20,
800 sparc_op_hix22,826 larch_pcala64_lo20,
801 sparc_op_lox10,827 larch_pcala64_hi12,
828
829 sparc_op_lox10,
830 sparc_op_hix22,
831
832 fn applyInner(
833 s: Special,
834 elf: *Elf,
835 got_vaddr: u64,
836 got_offset: u64,
837 addend: u64,
838 dest_vaddr: u64,
839 dest_slice: []u8,
840 ) error{ RelocationMisaligned, RelocationOverflow }!void {
841 switch (s) {
842 .larch_pcala_hi20 => {
843 const val = got_vaddr +% got_offset +% addend;
844 const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]);
845 elf.targetStore(inst, .{
846 .b0_4 = elf.targetLoad(inst).b0_4,
847 .j20 = link.loongarch.pcalaHi20(val, dest_vaddr),
848 .b25_31 = elf.targetLoad(inst).b25_31,
849 });
850 },
851 .larch_pcala64_lo20 => {
852 const val = got_vaddr +% got_offset +% addend;
853 const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]);
854 elf.targetStore(inst, .{
855 .b0_4 = elf.targetLoad(inst).b0_4,
856 .j20 = link.loongarch.pcala64Lo20(val, dest_vaddr),
857 .b25_31 = elf.targetLoad(inst).b25_31,
858 });
859 },
860 .larch_pcala64_hi12 => {
861 const val = got_vaddr +% got_offset +% addend;
862 const inst: *align(1) link.loongarch.K12 = @ptrCast(dest_slice[0..4]);
863 elf.targetStore(inst, .{
864 .b0_9 = elf.targetLoad(inst).b0_9,
865 .k12 = link.loongarch.pcala64Hi12(val, dest_vaddr),
866 .b22_31 = elf.targetLoad(inst).b22_31,
867 });
868 },
869 .sparc_op_lox10 => {
870 const dest_ptr: *align(1) packed struct(u32) {
871 imm13: u13,
872 b13_31: u19,
873 } = @ptrCast(dest_slice);
874 elf.targetStore(dest_ptr, .{
875 .imm13 = @as(u10, @truncate(got_offset)),
876 .b13_31 = elf.targetLoad(dest_ptr).b13_31,
877 });
878 },
879 .sparc_op_hix22 => {
880 const dest_ptr: *align(1) packed struct(u32) {
881 imm22: u22,
882 b22_31: u10,
883 } = @ptrCast(dest_slice);
884 elf.targetStore(dest_ptr, .{
885 .imm22 = @truncate(got_offset >> 10),
886 .b22_31 = elf.targetLoad(dest_ptr).b22_31,
887 });
888 },
889 }
890 }
891 };
802 };892 };
803893
804 const Index = enum(u32) {894 const Index = enum(u32) {
...@@ -810,14 +900,34 @@ const GotReloc = struct {...@@ -810,14 +900,34 @@ const GotReloc = struct {
810 }900 }
811 };901 };
812902
813 fn apply(reloc: *const GotReloc, elf: *Elf) void {903 fn apply(reloc: *GotReloc, elf: *Elf) void {
814 assert(elf.ehdrField(.type) != .REL);904 assert(elf.ehdrType() != .REL);
815 if (reloc.node == .none) return; // deleted905 if (reloc.node == .none) return; // deleted
816 if (reloc.node.hasMoved(&elf.mf) or elf.shndx.got.get(elf).ni.hasMoved(&elf.mf)) {906 if (reloc.node.hasMoved(&elf.mf) or elf.shndx.got.get(elf).ni.hasMoved(&elf.mf)) {
817 // There's no point applying the relocation now, because it will be re-applied by907 // There's no point applying the relocation now, because it will be re-applied by
818 // `flushMoved` at some point anyway.908 // `flushMoved` at some point anyway.
819 return;909 return;
820 }910 }
911 switch (reloc.result) {
912 .ok => {},
913 .overflowed => elf.overflowed_reloc_count -= 1,
914 .misaligned => elf.misaligned_reloc_count -= 1,
915 }
916 if (reloc.applyInner(elf)) {
917 @branchHint(.likely);
918 reloc.result = .ok;
919 } else |err| switch (err) {
920 error.RelocationOverflow => {
921 reloc.result = .overflowed;
922 elf.overflowed_reloc_count += 1;
923 },
924 error.RelocationMisaligned => {
925 reloc.result = .misaligned;
926 elf.misaligned_reloc_count += 1;
927 },
928 }
929 }
930 fn applyInner(reloc: *const GotReloc, elf: *Elf) error{ RelocationOverflow, RelocationMisaligned }!void {
821 const node_vaddr: u64 = switch (elf.getNode(reloc.node)) {931 const node_vaddr: u64 = switch (elf.getNode(reloc.node)) {
822 .file => unreachable,932 .file => unreachable,
823 .ehdr => unreachable,933 .ehdr => unreachable,
...@@ -834,7 +944,7 @@ const GotReloc = struct {...@@ -834,7 +944,7 @@ const GotReloc = struct {
834 };944 };
835 const dest_vaddr = node_vaddr + reloc.offset;945 const dest_vaddr = node_vaddr + reloc.offset;
836 const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..];946 const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..];
837 const target_endian = elf.targetEndian();947
838 const got_vaddr = elf.shndx.got.vaddr(elf);948 const got_vaddr = elf.shndx.got.vaddr(elf);
839 const got_index: u64 = elf.got.getIndex(reloc.target).?;949 const got_index: u64 = elf.got.getIndex(reloc.target).?;
840 const got_offset: u64 = switch (elf.identClass()) {950 const got_offset: u64 = switch (elf.identClass()) {
...@@ -842,228 +952,201 @@ const GotReloc = struct {...@@ -842,228 +952,201 @@ const GotReloc = struct {
842 inline else => |class| @sizeOf(class.ElfN().Addr) * got_index,952 inline else => |class| @sizeOf(class.ElfN().Addr) * got_index,
843 };953 };
844 const addend: u64 = @bitCast(reloc.addend);954 const addend: u64 = @bitCast(reloc.addend);
845 switch (reloc.type) {
846 .offset64 => std.mem.writeInt(
847 u64,
848 dest_slice[0..8],
849 got_offset +% addend,
850 target_endian,
851 ),
852 .offset32 => std.mem.writeInt(
853 u32,
854 dest_slice[0..4],
855 @intCast(got_offset +% addend),
856 target_endian,
857 ),
858 .rel64 => std.mem.writeInt(
859 i64,
860 dest_slice[0..8],
861 @bitCast(got_vaddr +% got_offset +% addend -% dest_vaddr),
862 target_endian,
863 ),
864 .rel32 => std.mem.writeInt(
865 i32,
866 dest_slice[0..4],
867 @intCast(@as(i64, @bitCast(got_vaddr +% got_offset +% addend -% dest_vaddr))),
868 target_endian,
869 ),
870955
871 .larch_rel32_hi20 => {956 const target_val: u64 = switch (reloc.type.target) {
872 assert(elf.ehdrField(.machine) == .LOONGARCH);957 .abs => got_vaddr +% got_offset +% addend,
873 const target_value = got_vaddr +% got_offset +% addend;958 .rel => got_vaddr +% got_offset +% addend -% dest_vaddr,
874 link.loongarch.writeJ20(dest_slice[0..4], link.loongarch.toPcalaHi20(target_value, dest_vaddr));959 .offset => got_offset +% addend,
875 },960 .special => return reloc.type.action.special.applyInner(
876 .larch_rel64_lo20 => {961 elf,
877 assert(elf.ehdrField(.machine) == .LOONGARCH);962 got_vaddr,
878 const target_value = got_vaddr +% got_offset +% addend;963 got_offset,
879 link.loongarch.writeJ20(dest_slice[0..4], link.loongarch.toPcala64Lo20(target_value, dest_vaddr));964 addend,
880 },965 dest_vaddr,
881 .larch_rel64_hi12 => {966 dest_slice,
882 assert(elf.ehdrField(.machine) == .LOONGARCH);967 ),
883 const target_value = got_vaddr +% got_offset +% addend;968 };
884 link.loongarch.writeK12(dest_slice[0..4], link.loongarch.toPcala64Hi12(target_value, dest_vaddr));969 try reloc.type.action.simple.write(target_val, dest_slice, elf.targetEndian());
885 },970 }
886 .larch_abs32_lo12 => {
887 assert(elf.ehdrField(.machine) == .LOONGARCH);
888 const target_value = got_vaddr +% got_offset +% addend;
889 link.loongarch.writeK12(dest_slice[0..4], @truncate(target_value));
890 },
891 .larch_abs32_hi20 => {
892 assert(elf.ehdrField(.machine) == .LOONGARCH);
893 const target_value = got_vaddr +% got_offset +% addend;
894 link.loongarch.writeJ20(dest_slice[0..4], @truncate(target_value >> 12));
895 },
896 .larch_abs64_lo20 => {
897 assert(elf.ehdrField(.machine) == .LOONGARCH);
898 const target_value = got_vaddr +% got_offset +% addend;
899 link.loongarch.writeJ20(dest_slice[0..4], @truncate(target_value >> 32));
900 },
901 .larch_abs64_hi12 => {
902 assert(elf.ehdrField(.machine) == .LOONGARCH);
903 const target_value = got_vaddr +% got_offset +% addend;
904 link.loongarch.writeK12(dest_slice[0..4], @truncate(target_value >> 52));
905 },
906971
907 .sparc_10 => {972 fn delete(reloc: *GotReloc, elf: *Elf) void {
908 const dest_ptr: *link.sparc.reloc.Simm13 = @ptrCast(@alignCast(dest_slice));973 switch (reloc.result) {
909 var result = elf.targetLoad(dest_ptr);974 .ok => {},
910 result.simm13 = @as(u10, @truncate(got_offset));975 .overflowed => elf.overflowed_reloc_count -= 1,
911 elf.targetStore(dest_ptr, result);976 .misaligned => elf.misaligned_reloc_count -= 1,
912 },
913 .sparc_13 => {
914 const dest_ptr: *link.sparc.reloc.Simm13 = @ptrCast(@alignCast(dest_slice));
915 var result = elf.targetLoad(dest_ptr);
916 result.simm13 = @truncate(got_offset);
917 elf.targetStore(dest_ptr, result);
918 },
919 .sparc_22 => {
920 const dest_ptr: *link.sparc.reloc.Simm22 = @ptrCast(@alignCast(dest_slice));
921 var result = elf.targetLoad(dest_ptr);
922 result.simm22 = @truncate(got_offset >> 10);
923 elf.targetStore(dest_ptr, result);
924 },
925 .sparc_ldm_hi22 => {
926 const dest_ptr: *link.sparc.reloc.Simm22 = @ptrCast(@alignCast(dest_slice));
927 var result = elf.targetLoad(dest_ptr);
928 result.simm22 = @truncate((got_offset +% addend) >> 10);
929 elf.targetStore(dest_ptr, result);
930 },
931 .sparc_ldm_lo10 => {
932 const dest_ptr: *link.sparc.reloc.Simm13 = @ptrCast(@alignCast(dest_slice));
933 var result = elf.targetLoad(dest_ptr);
934 result.simm13 = @as(u10, @truncate(got_offset +% addend));
935 elf.targetStore(dest_ptr, result);
936 },
937 .sparc_op_hix22 => {
938 const dest_ptr: *link.sparc.reloc.Imm22 = @ptrCast(@alignCast(dest_slice));
939 var result = elf.targetLoad(dest_ptr);
940 result.imm22 = @truncate(got_offset >> 10);
941 elf.targetStore(dest_ptr, result);
942 },
943 .sparc_op_lox10 => {
944 const dest_ptr: *link.sparc.reloc.Imm13 = @ptrCast(@alignCast(dest_slice));
945 var result = elf.targetLoad(dest_ptr);
946 result.imm13 = @as(u10, @truncate(got_offset));
947 elf.targetStore(dest_ptr, result);
948 },
949 }977 }
978 reloc.* = .{
979 .node = .none,
980 .offset = undefined,
981 .target = undefined,
982 .addend = undefined,
983 .type = undefined,
984 .result = undefined,
985 };
950 }986 }
951};987};
952988
953pub const MachineRelocType = union {989pub const MachineRelocType = union {
954 AARCH64: std.elf.R_AARCH64,990 AARCH64: std.elf.R_AARCH64,
955 LOONGARCH: std.elf.R_LARCH,991 LARCH: std.elf.R_LARCH,
956 PPC64: std.elf.R_PPC64,992 PPC64: std.elf.R_PPC64,
957 RISCV: std.elf.R_RISCV,993 RISCV: std.elf.R_RISCV,
958 SPARC: std.elf.R_SPARC,994 SPARC: std.elf.R_SPARC,
959 X86_64: std.elf.R_X86_64,995 X86_64: std.elf.R_X86_64,
960996
961 pub fn none(elf: *Elf) MachineRelocType {997 pub const Format = struct {
962 return switch (elf.ehdrField(.machine)) {998 rt: MachineRelocType,
963 else => unreachable,999 elf: *const Elf,
1000
1001 pub fn format(f: Format, w: *Io.Writer) Io.Writer.Error!void {
1002 switch (f.elf.ehdrMachine()) {
1003 .AARCH64 => try w.print("R_AARCH64_{t}", .{f.rt.AARCH64}),
1004 .LOONGARCH => try w.print("R_LARCH_{t}", .{f.rt.LARCH}),
1005 .PPC64 => try w.print("R_PPC64_{t}", .{f.rt.PPC64}),
1006 .RISCV => try w.print("R_RISCV_{t}", .{f.rt.RISCV}),
1007 .SPARCV9 => try w.print("R_SPARC_{t}", .{f.rt.SPARC}),
1008 .X86_64 => try w.print("R_X86_64_{t}", .{f.rt.X86_64}),
1009 }
1010 }
1011 };
1012
1013 pub fn fmt(rt: MachineRelocType, elf: *const Elf) Format {
1014 return .{ .rt = rt, .elf = elf };
1015 }
1016
1017 pub fn none(elf: *const Elf) MachineRelocType {
1018 return switch (elf.ehdrMachine()) {
964 .AARCH64 => .{ .AARCH64 = .NONE },1019 .AARCH64 => .{ .AARCH64 = .NONE },
965 .LOONGARCH => .{ .LOONGARCH = .NONE },1020 .LOONGARCH => .{ .LARCH = .NONE },
966 .PPC64 => .{ .PPC64 = .NONE },1021 .PPC64 => .{ .PPC64 = .NONE },
967 .RISCV => .{ .RISCV = .NONE },1022 .RISCV => .{ .RISCV = .NONE },
968 .SPARCV9 => .{ .SPARC = .NONE },1023 .SPARCV9 => .{ .SPARC = .NONE },
969 .X86_64 => .{ .X86_64 = .NONE },1024 .X86_64 => .{ .X86_64 = .NONE },
970 };1025 };
971 }1026 }
972 pub fn copy(elf: *Elf) MachineRelocType {1027 pub fn copy(elf: *const Elf) MachineRelocType {
973 return switch (elf.ehdrField(.machine)) {1028 return switch (elf.ehdrMachine()) {
974 else => unreachable,
975 .AARCH64 => .{ .AARCH64 = .COPY },1029 .AARCH64 => .{ .AARCH64 = .COPY },
976 .LOONGARCH => .{ .LOONGARCH = .COPY },1030 .LOONGARCH => .{ .LARCH = .COPY },
977 .PPC64 => .{ .PPC64 = .COPY },1031 .PPC64 => .{ .PPC64 = .COPY },
978 .RISCV => .{ .RISCV = .COPY },1032 .RISCV => .{ .RISCV = .COPY },
979 .SPARCV9 => .{ .SPARC = .COPY },1033 .SPARCV9 => .{ .SPARC = .COPY },
980 .X86_64 => .{ .X86_64 = .COPY },1034 .X86_64 => .{ .X86_64 = .COPY },
981 };1035 };
982 }1036 }
983 pub fn relative(elf: *Elf) MachineRelocType {1037 pub fn relative(elf: *const Elf) MachineRelocType {
984 return switch (elf.ehdrField(.machine)) {1038 return switch (elf.ehdrMachine()) {
985 else => unreachable,
986 .AARCH64 => .{ .AARCH64 = .RELATIVE },1039 .AARCH64 => .{ .AARCH64 = .RELATIVE },
987 .LOONGARCH => .{ .LOONGARCH = .RELATIVE },1040 .LOONGARCH => .{ .LARCH = .RELATIVE },
988 .PPC64 => .{ .PPC64 = .RELATIVE },1041 .PPC64 => .{ .PPC64 = .RELATIVE },
989 .RISCV => .{ .RISCV = .RELATIVE },1042 .RISCV => .{ .RISCV = .RELATIVE },
990 .SPARCV9 => .{ .SPARC = .RELATIVE },1043 .SPARCV9 => .{ .SPARC = .RELATIVE },
991 .X86_64 => .{ .X86_64 = .RELATIVE },1044 .X86_64 => .{ .X86_64 = .RELATIVE },
992 };1045 };
993 }1046 }
994 pub fn jumpSlot(elf: *Elf) MachineRelocType {1047 pub fn jumpSlot(elf: *const Elf) MachineRelocType {
995 return switch (elf.ehdrField(.machine)) {1048 return switch (elf.ehdrMachine()) {
996 else => unreachable,
997 .AARCH64 => .{ .AARCH64 = .JUMP_SLOT },1049 .AARCH64 => .{ .AARCH64 = .JUMP_SLOT },
998 .LOONGARCH => .{ .LOONGARCH = .JUMP_SLOT },1050 .LOONGARCH => .{ .LARCH = .JUMP_SLOT },
999 .PPC64 => .{ .PPC64 = .JMP_SLOT },1051 .PPC64 => .{ .PPC64 = .JMP_SLOT },
1000 .RISCV => .{ .RISCV = .JUMP_SLOT },1052 .RISCV => .{ .RISCV = .JUMP_SLOT },
1001 .SPARCV9 => .{ .SPARC = .JMP_SLOT },1053 .SPARCV9 => .{ .SPARC = .JMP_SLOT },
1002 .X86_64 => .{ .X86_64 = .JUMP_SLOT },1054 .X86_64 => .{ .X86_64 = .JUMP_SLOT },
1003 };1055 };
1004 }1056 }
1005 pub fn globDat(elf: *Elf) MachineRelocType {1057 pub fn globDat(elf: *const Elf) MachineRelocType {
1006 return switch (elf.ehdrField(.machine)) {1058 return switch (elf.ehdrMachine()) {
1007 else => unreachable,
1008 .AARCH64 => .{ .AARCH64 = .GLOB_DAT },1059 .AARCH64 => .{ .AARCH64 = .GLOB_DAT },
1009 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .@"64" else .@"32" },1060 .LOONGARCH => .{ .LARCH = if (elf.identClass() == .@"64") .@"64" else .@"32" },
1010 .PPC64 => .{ .PPC64 = .GLOB_DAT },1061 .PPC64 => .{ .PPC64 = .GLOB_DAT },
1011 .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .@"64" else .@"32" },1062 .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .@"64" else .@"32" },
1012 .SPARCV9 => .{ .SPARC = .GLOB_DAT },1063 .SPARCV9 => .{ .SPARC = .GLOB_DAT },
1013 .X86_64 => .{ .X86_64 = .GLOB_DAT },1064 .X86_64 => .{ .X86_64 = .GLOB_DAT },
1014 };1065 };
1015 }1066 }
1016 pub fn dtpOffAddr(elf: *Elf) MachineRelocType {1067 pub fn dtpMod(elf: *const Elf) MachineRelocType {
1017 return switch (elf.ehdrField(.machine)) {1068 return switch (elf.ehdrMachine()) {
1018 else => unreachable,1069 .AARCH64 => .{ .AARCH64 = if (elf.identClass() == .@"64") .TLS_DTPMOD else .P32_TLS_DTPMOD },
1019 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_DTPREL64 else .TLS_DTPREL32 },1070 .LOONGARCH => .{ .LARCH = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 },
1071 .PPC64 => .{ .PPC64 = .DTPMOD64 },
1072 .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 },
1073 .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 },
1074 .X86_64 => .{ .X86_64 = .DTPMOD64 },
1075 };
1076 }
1077 pub fn dtpOff(elf: *const Elf) MachineRelocType {
1078 return switch (elf.ehdrMachine()) {
1079 .AARCH64 => .{ .AARCH64 = if (elf.identClass() == .@"64") .TLS_DTPREL else .P32_TLS_DTPREL },
1080 .LOONGARCH => .{ .LARCH = if (elf.identClass() == .@"64") .TLS_DTPREL64 else .TLS_DTPREL32 },
1020 .PPC64 => .{ .PPC64 = .DTPREL64 },1081 .PPC64 => .{ .PPC64 = .DTPREL64 },
1021 .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .TLS_DTPREL64 else .TLS_DTPREL32 },1082 .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .TLS_DTPREL64 else .TLS_DTPREL32 },
1022 .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .TLS_DTPOFF64 else .TLS_DTPOFF32 },1083 .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .TLS_DTPOFF64 else .TLS_DTPOFF32 },
1023 .X86_64 => .{ .X86_64 = if (elf.identClass() == .@"64") .DTPOFF64 else .DTPOFF32 },1084 .X86_64 => .{ .X86_64 = .DTPOFF64 },
1024 };1085 };
1025 }1086 }
1026 pub fn absAddr(elf: *Elf) MachineRelocType {1087 pub fn tpOff(elf: *const Elf) MachineRelocType {
1027 return switch (elf.ehdrField(.machine)) {1088 return switch (elf.ehdrMachine()) {
1028 else => unreachable,1089 .AARCH64 => .{ .AARCH64 = if (elf.identClass() == .@"64") .TLS_TPREL else .P32_TLS_TPREL },
1090 .LOONGARCH => .{ .LARCH = if (elf.identClass() == .@"64") .TLS_TPREL64 else .TLS_TPREL32 },
1091 .PPC64 => .{ .PPC64 = .TPREL64 },
1092 .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .TLS_TPREL64 else .TLS_TPREL32 },
1093 .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .TLS_TPOFF64 else .TLS_TPOFF32 },
1094 .X86_64 => .{ .X86_64 = .TPOFF64 },
1095 };
1096 }
1097 pub fn absAddr(elf: *const Elf) MachineRelocType {
1098 return switch (elf.ehdrMachine()) {
1029 .AARCH64 => .{ .AARCH64 = if (elf.identClass() == .@"64") .ABS64 else .P32_ABS32 },1099 .AARCH64 => .{ .AARCH64 = if (elf.identClass() == .@"64") .ABS64 else .P32_ABS32 },
1030 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .@"64" else .@"32" },1100 .LOONGARCH => .{ .LARCH = if (elf.identClass() == .@"64") .@"64" else .@"32" },
1031 .PPC64 => .{ .PPC64 = .ADDR64 },1101 .PPC64 => .{ .PPC64 = .ADDR64 },
1032 .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .@"64" else .@"32" },1102 .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .@"64" else .@"32" },
1033 .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .@"64" else .@"32" },1103 .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .@"64" else .@"32" },
1034 .X86_64 => .{ .X86_64 = if (elf.identClass() == .@"64") .@"64" else .@"32" },1104 .X86_64 => .{ .X86_64 = if (elf.identClass() == .@"64") .@"64" else .@"32" },
1035 };1105 };
1036 }1106 }
1037 pub fn sizeAddr(elf: *Elf) MachineRelocType {1107 pub fn size32(elf: *const Elf) ?MachineRelocType {
1038 return switch (elf.ehdrField(.machine)) {1108 return switch (elf.ehdrMachine()) {
1039 else => unreachable,1109 .AARCH64,
1040 .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .SIZE64 else .SIZE32 },
1041 .X86_64 => .{ .X86_64 = .SIZE64 },
1042 };
1043 }
1044
1045 pub fn wrap(int: u32, elf: *Elf) MachineRelocType {
1046 return switch (elf.ehdrField(.machine)) {
1047 else => unreachable,
1048 .SPARCV9 => .{ .SPARC = @enumFromInt(int) },
1049 inline .AARCH64,
1050 .LOONGARCH,1110 .LOONGARCH,
1051 .PPC64,1111 .PPC64,
1052 .RISCV,1112 .RISCV,
1053 .X86_64,1113 => null,
1054 => |machine| @unionInit(MachineRelocType, @tagName(machine), @enumFromInt(int)),1114
1115 .SPARCV9 => .{ .SPARC = .SIZE32 },
1116 .X86_64 => .{ .X86_64 = .SIZE32 },
1055 };1117 };
1056 }1118 }
1057 pub fn unwrap(rt: MachineRelocType, elf: *Elf) u32 {1119 pub fn size64(elf: *const Elf) ?MachineRelocType {
1058 return switch (elf.ehdrField(.machine)) {1120 return switch (elf.ehdrMachine()) {
1059 else => unreachable,1121 .AARCH64,
1060 .SPARCV9 => @intFromEnum(rt.SPARC),
1061 inline .AARCH64,
1062 .LOONGARCH,1122 .LOONGARCH,
1063 .PPC64,1123 .PPC64,
1064 .RISCV,1124 .RISCV,
1065 .X86_64,1125 => null,
1066 => |machine| @intFromEnum(@field(rt, @tagName(machine))),1126
1127 .SPARCV9 => .{ .SPARC = .SIZE64 },
1128 .X86_64 => .{ .X86_64 = .SIZE64 },
1129 };
1130 }
1131
1132 pub fn wrap(int: u32, elf: *const Elf) MachineRelocType {
1133 return switch (elf.ehdrMachine()) {
1134 .AARCH64 => .{ .AARCH64 = @enumFromInt(int) },
1135 .LOONGARCH => .{ .LARCH = @enumFromInt(int) },
1136 .PPC64 => .{ .PPC64 = @enumFromInt(int) },
1137 .RISCV => .{ .RISCV = @enumFromInt(int) },
1138 .SPARCV9 => .{ .SPARC = @enumFromInt(int) },
1139 .X86_64 => .{ .X86_64 = @enumFromInt(int) },
1140 };
1141 }
1142 pub fn unwrap(rt: MachineRelocType, elf: *const Elf) u32 {
1143 return switch (elf.ehdrMachine()) {
1144 .AARCH64 => @intFromEnum(rt.AARCH64),
1145 .LOONGARCH => @intFromEnum(rt.LARCH),
1146 .PPC64 => @intFromEnum(rt.PPC64),
1147 .RISCV => @intFromEnum(rt.RISCV),
1148 .SPARCV9 => @intFromEnum(rt.SPARC),
1149 .X86_64 => @intFromEnum(rt.X86_64),
1067 };1150 };
1068 }1151 }
1069};1152};
...@@ -1082,6 +1165,8 @@ const SymbolReloc = struct {...@@ -1082,6 +1165,8 @@ const SymbolReloc = struct {
1082 /// A signed constant used to compute the relocated value. Precise meaning depends on `@"type"`.1165 /// A signed constant used to compute the relocated value. Precise meaning depends on `@"type"`.
1083 addend: i64,1166 addend: i64,
1084 /// Specifies how to apply the relocation.1167 /// Specifies how to apply the relocation.
1168 ///
1169 /// When emitting a relocatable, this field is `undefined`.
1085 type: SymbolReloc.Type,1170 type: SymbolReloc.Type,
1086 /// Forms a linked list of all symbol relocations with the same `target`. This list exists so1171 /// Forms a linked list of all symbol relocations with the same `target`. This list exists so
1087 /// that all relocations targeting a particular symbol can be re-applied if that symbol moves.1172 /// that all relocations targeting a particular symbol can be re-applied if that symbol moves.
...@@ -1100,6 +1185,7 @@ const SymbolReloc = struct {...@@ -1100,6 +1185,7 @@ const SymbolReloc = struct {
1100 /// relocation entry. The entry will be removed if we discover a definition which allows us to1185 /// relocation entry. The entry will be removed if we discover a definition which allows us to
1101 /// statically resolve the relocation.1186 /// statically resolve the relocation.
1102 rela_index: Section.RelaIndex.Optional,1187 rela_index: Section.RelaIndex.Optional,
1188 result: enum(u8) { ok, overflowed, misaligned },
11031189
1104 /// Determines the section in which this relocation will be placed if it is outstanding.1190 /// Determines the section in which this relocation will be placed if it is outstanding.
1105 ///1191 ///
...@@ -1110,8 +1196,7 @@ const SymbolReloc = struct {...@@ -1110,8 +1196,7 @@ const SymbolReloc = struct {
1110 /// When producing a DSO, the relocation section is always `.rela.dyn`. It is not `.rela.plt`1196 /// When producing a DSO, the relocation section is always `.rela.dyn`. It is not `.rela.plt`
1111 /// because relocations in the GOTPLT are handled specially, without `SymbolReloc` entries.1197 /// because relocations in the GOTPLT are handled specially, without `SymbolReloc` entries.
1112 fn relaSection(sr: *const SymbolReloc, elf: *Elf) Section.Index {1198 fn relaSection(sr: *const SymbolReloc, elf: *Elf) Section.Index {
1113 const shndx = switch (elf.ehdrField(.type)) {1199 const shndx = switch (elf.ehdrType()) {
1114 .NONE, .CORE, _ => unreachable,
1115 .REL => elf.getNodeShndx(sr.node).get(elf).rela.shndx,1200 .REL => elf.getNodeShndx(sr.node).get(elf).rela.shndx,
1116 .EXEC, .DYN => elf.shndx.rela_dyn,1201 .EXEC, .DYN => elf.shndx.rela_dyn,
1117 };1202 };
...@@ -1128,151 +1213,439 @@ const SymbolReloc = struct {...@@ -1128,151 +1213,439 @@ const SymbolReloc = struct {
1128 }1213 }
1129 };1214 };
11301215
1131 const Type = enum {1216 /// Instead of using the ELF relocation enums, we have our own internal representation for
1132 /// This input relocation is being directly forwarded to an `ElfN.Rela` entry in the output1217 /// relocation types. This representation is more compact (requiring only 16 bits), and allows
1133 /// file. `rela_index` is guaranteed to be populated. The ELF relocation type is available1218 /// sharing a lot of relocation handling between multiple relocs and target architectures.
1134 /// in the `ElfN.Rela` entry.1219 ///
1135 ///1220 /// A relocation type can be "simple" or "special".
1136 /// If we are emitting a relocatable (`ET_REL`), all symbol relocs use this type (since we1221 ///
1137 /// do not apply any relocations ourselves). Otherwise, no symbol relocs use this type.1222 /// "Simple" relocations are designed to cover the majority of cases. They can represent most
1138 write_rela,1223 /// relocations which either write 8-bit, 16-bit, 32-bit, or 64-bit integers, or which write one
1224 /// contiguous bit-field within such an integer (e.g. an instruction operand). For more details,
1225 /// see `Simple`.
1226 ///
1227 /// "Special" relocations handle anything which does not fit into the above category, such as
1228 /// relocations which write multiple sequences of bits or which need to do unusual arithmetic on
1229 /// a symbol value. The representation is simply a big enum containing all of these exceptional
1230 /// cases---see `Special`. This representation is in use when `Type.target == .special`.
1231 const Type = packed struct(u16) {
1232 /// Helper function for constructing a "simple" relocation type. This mainly exists to
1233 /// improve readability in the relocation lowering logic in `addRelocAssumeCapacity`.
1234 fn simple(target: Target, action: Simple) SymbolReloc.Type {
1235 assert(target != .special);
1236 return .{ .target = target, .action = .{ .simple = action } };
1237 }
1238
1239 /// Helper function for constructing a "special" relocation type. This mainly exists to
1240 /// improve readability in the relocation lowering logic in `addRelocAssumeCapacity`.
1241 fn special(s: Special) SymbolReloc.Type {
1242 return .{ .target = .special, .action = .{ .special = s } };
1243 }
11391244
1140 /// Address relative to the DSO base. Like `.abs64` but does not emit `R_*_RELATIVE` relocs.1245 /// See doc comment on `Target`.
1246 target: Target,
1247 /// If `target == .special`, the `special` field is used.
1141 ///1248 ///
1142 /// This is only used targeting local symbols so can always be statically resolved.1249 /// Otherwise, the `.simple` field is used.
1143 dsorel64,1250 action: packed union {
1144 /// Address relative to the DSO base. Like `.abs32` but does not emit `R_*_RELATIVE` relocs.1251 simple: Simple,
1252 special: Special,
1253 },
1254
1255 /// If a relocation is "special", indicates that using the value `.@"special"`.
1145 ///1256 ///
1146 /// This is only used targeting local symbols so can always be statically resolved.1257 /// Otherwise (for "simple" relocations), `Target` indicates the first step in computing the
1147 dsorel32,1258 /// relocation---whether we care about the target symbol's absolute address, its PC-relative
11481259 /// address, its PLT entry, etc.
1149 abs8,1260 const Target = enum(u3) {
1150 abs16,1261 /// This is a "special" relocation whose specific type is in the `action.special` field.
1151 abs32,1262 special,
1152 abs32s,1263
1153 abs64,1264 /// Absolute value of the target symbol.
1154 rel8,1265 abs,
1155 rel16,1266 /// Offset from the relocation itself to the target symbol ("PC-relative").
1156 rel32,1267 rel,
1157 rel64,1268 /// Address of the target symbol's PLT entry.
1158 pltabs32,1269 ///
1159 pltabs64,1270 /// If the target symbol does not have a PLT entry, equivalent to `.abs`.
1160 pltrel32,1271 pltabs,
1161 pltrel64,1272 /// Offset from the relocation itself to the target symbol's PLT entry ("PC-relative").
1162 dtpoff32,1273 ///
1163 dtpoff64,1274 /// If the target symbol does not have a PLT entry, equivalent to `.rel`.
1164 tpoff32,1275 pltrel,
1165 tpoff64,1276 /// Offset of the target TLS symbol from the base of this DSO's own TLS region.
1166 size32,1277 dtpoff,
1167 size64,1278 /// Offset of the target TLS symbol from the raw thread pointer.
11681279 tpoff,
1169 larch_abs32_lo12,1280 /// Size of the target symbol.
1170 larch_rel32_hi20,1281 size,
1171 larch_rel64_lo20,1282 };
1172 larch_rel64_hi12,
1173 larch_branch_rel18,
1174 larch_branch_rel23,
1175 larch_branch_rel28,
1176 larch_call_rel38,
1177 larch_tpoff32_lo12,
1178 larch_tpoff32_hi20,
1179 larch_tpoff64_lo20,
1180 larch_tpoff64_hi12,
1181
1182 sparc_wdisp30,
1183 sparc_pc10,
1184 sparc_pc22,
1185 sparc_wplt30,
1186 sparc_h44,
1187 sparc_m44,
1188 sparc_l44,
1189 sparc_ldo_hix22,
1190 sparc_ldo_lox10,
1191 sparc_le_hix22,
1192 sparc_le_lox10,
1193
1194 fn dependsOnTlsSize(t: SymbolReloc.Type) bool {
1195 return switch (t) {
1196 .tpoff32,
1197 .tpoff64,
1198 => true,
11991283
1200 .larch_tpoff32_lo12,1284 /// For a "simple" relocation, after the initial value is computed according to `Target`, a
1201 .larch_tpoff32_hi20,1285 /// `Simple` value communicates how to shift, truncate, and store that value into memory.
1202 .larch_tpoff64_lo20,1286 const Simple = packed struct(u13) {
1203 .larch_tpoff64_hi12,1287 /// The field being written to, represented as a sequence of bits in a backing integer
1204 => true,1288 /// of 8, 16, 32, or 64 bits.
1289 ///
1290 /// The `.@"8"`, `.@"16"`, `.@"32"`, and `.@"64"` fields simply write to all bits of the
1291 /// backing integer; i.e. the existing value is entirely overwritten.
1292 ///
1293 /// Other fields are named like "B[H:L]", where "B" is the backing integer type, and
1294 /// "H" and "L" are the indices of the highest and lowest bits in the bit field (in
1295 /// other words, an inclusive bit range). This notation was chosen because it seems to
1296 /// be one of the more common ways that bit relocations are written in ABIs.
1297 ///
1298 /// e.g. 8[6:3] writes the relocated value to this 4-bit field in an 8-bit integer:
1299 ///
1300 /// MSB ___ ### ### ### ### ___ ___ ___ LSB
1301 /// 7 6 5 4 3 2 1 0
1302 /// bit index
1303 ///
1304 /// This enum is not intended to be able to represent every possible bit field in the
1305 /// backing integer types. Instead, to keep `SymbolReloc.Type` compact, fields are added
1306 /// to this enum only as needed. If the enum ever becomes full, some lesser-used tags
1307 /// can have their handling moved into `Special` to free up space.
1308 dest: enum(u6) {
1309 @"8",
1310 @"16",
1311 @"32",
1312 @"64",
1313
1314 @"32[4:0]",
1315 @"32[5:0]",
1316 @"32[6:0]",
1317 @"32[9:0]",
1318 @"32[10:0]",
1319 @"32[11:0]",
1320 @"32[12:0]",
1321 @"32[21:0]",
1322 @"32[21:10]",
1323 @"32[24:5]",
1324 @"32[25:10]",
1325 @"32[29:0]",
1326
1327 /// Returns `true` iff `dest` writes a full address for the target.
1328 ///
1329 /// i.e. checks for `.@"32"` on 32-bit targets; for `.@"64"` on 64-bit targets.
1330 fn isAddr(dest: @This(), elf: *const Elf) bool {
1331 return switch (elf.identClass()) {
1332 .NONE, _ => unreachable,
1333 .@"32" => dest == .@"32",
1334 .@"64" => dest == .@"64",
1335 };
1336 }
1337 },
12051338
1206 .sparc_le_hix22,1339 /// After the relocation value is shifted (see `shift`), it is truncated to the size of
1207 .sparc_le_lox10,1340 /// the bit field (see `dest`). This field specifies whether the linker will check for,
1208 => true,1341 /// and error in the case of, truncated bits (in other words, relocation overflow).
1342 cast: enum(u2) {
1343 /// Do not perform any check when truncating unused bits.
1344 trunc,
1345 /// Error if the truncated value cannot be zero-extended back to the original value,
1346 /// i.e. if the truncated value is different when interpreted as unsigned.
1347 unsigned,
1348 /// Error if the truncated value cannot be sign-extended back to the original value.
1349 /// i.e. if the truncated value is different when interpreted as signed.
1350 signed,
1351 },
12091352
1210 else => false,1353 /// The relocation value (computed based on the `Target`) gets shifted to the right by
1211 };1354 /// this amount. By default, the shifted-out bits can be anything, but tags ending in
1212 }1355 /// "_exact" introduce a check that the shifted-out bits are all zeroes (an error is
1356 /// emitted if not), similar to the behavior of `@shrExact`.
1357 shift: enum(u5) {
1358 @"0",
1359 @"2_exact",
1360 @"10",
1361 @"12",
1362 @"22",
1363 @"32",
1364 @"52",
1365 },
12131366
1214 fn isAbsAddr(t: SymbolReloc.Type, elf: *const Elf) bool {1367 /// Given a value (computed based on the `Target`), applies the shift and truncation
1215 return switch (elf.identClass()) {1368 /// operations specified by `s`, then writes the result to the start of `dest_slice` as
1216 .NONE, _ => unreachable,1369 /// specified by `s.dest`.
1217 .@"32" => switch (t) {1370 fn write(
1218 .abs32,1371 s: Simple,
1219 .pltabs32,1372 val: u64,
1220 => true,1373 dest_slice: []u8,
1221 else => false,1374 target_endian: std.lang.Endian,
1222 },1375 ) error{ RelocationMisaligned, RelocationOverflow }!void {
1223 .@"64" => switch (t) {1376 const shift: u6, const shift_exact: bool = switch (s.shift) {
1224 .abs64,1377 .@"0" => .{ 0, false },
1225 .pltabs64,1378 .@"2_exact" => .{ 2, true },
1226 => true,1379 .@"10" => .{ 10, true },
1227 else => false,1380 .@"12" => .{ 12, false },
1381 .@"22" => .{ 22, false },
1382 .@"32" => .{ 32, false },
1383 .@"52" => .{ 52, false },
1384 };
1385
1386 if (shift_exact and (val >> shift) << shift != val) {
1387 return error.RelocationMisaligned;
1388 }
1389
1390 const dest_word_bits: u8, const dest_high_bit: u6, const dest_low_bit: u6 = switch (s.dest) {
1391 // zig fmt: off
1392 .@"8" => .{ 8, 7, 0 },
1393 .@"16" => .{ 16, 15, 0 },
1394 .@"32" => .{ 32, 31, 0 },
1395 .@"64" => .{ 64, 63, 0 },
1396 .@"32[4:0]" => .{ 32, 4, 0 },
1397 .@"32[5:0]" => .{ 32, 5, 0 },
1398 .@"32[6:0]" => .{ 32, 6, 0 },
1399 .@"32[9:0]" => .{ 32, 9, 0 },
1400 .@"32[10:0]" => .{ 32, 10, 0 },
1401 .@"32[11:0]" => .{ 32, 11, 0 },
1402 .@"32[12:0]" => .{ 32, 12, 0 },
1403 .@"32[21:0]" => .{ 32, 21, 0 },
1404 .@"32[21:10]" => .{ 32, 21, 10 },
1405 .@"32[24:5]" => .{ 32, 24, 5 },
1406 .@"32[25:10]" => .{ 32, 25, 10 },
1407 .@"32[29:0]" => .{ 32, 29, 0 },
1408 // zig fmt: on
1409 };
1410
1411 // The number of bits we are truncating from the full 64-bit relocation value.
1412 const trunc_bits: u6 = 63 - dest_high_bit + dest_low_bit;
1413
1414 // When we shift, whether we do an arithmetic or logical shift depends on what cast
1415 // behavior we are going to use. If we'll be doing a signed int cast, we must shift
1416 // in sign bits so that we don't incorrectly cause a failure, and vice versa for an
1417 // unsigned int cast. Either is fine when truncating (here we pick logical shift).
1418 const shifted_val: u64 = switch (s.cast) {
1419 .trunc => val >> shift,
1420 inline else => |cast| shifted: {
1421 const ShiftInt = if (cast == .signed) i64 else u64;
1422 const x: ShiftInt = @bitCast(val);
1423 const shifted: ShiftInt = x >> shift;
1424
1425 if ((shifted << trunc_bits) >> trunc_bits != shifted) {
1426 return error.RelocationOverflow;
1427 }
1428
1429 break :shifted @bitCast(shifted);
1430 },
1431 };
1432
1433 // Create a bit-mask for the field being populated, e.g. 8[3:1] -> 0b00001110
1434 const field_mask = (~@as(u64, 0) >> trunc_bits) << dest_low_bit;
1435
1436 // Shift and mask the value to be in the correct bits, leaving the others zeroed.
1437 const masked_field: u64 = (shifted_val << dest_low_bit) & field_mask;
1438
1439 // Now we just need to actually apply the relocation by loading a word, replacing
1440 // the field bits with those in `masked_field`, and storing the result back.
1441 switch (dest_word_bits) {
1442 inline 8, 16, 32, 64 => |bits| {
1443 const word_slice = dest_slice[0..@divExact(bits, 8)];
1444 const Int = @Int(.unsigned, bits);
1445 const old: u64 = std.mem.readInt(Int, word_slice, target_endian);
1446 const new: u64 = (old & ~field_mask) | masked_field;
1447 std.mem.writeInt(Int, word_slice, @intCast(new), target_endian);
1448 },
1449 else => unreachable,
1450 }
1451 }
1452 };
1453
1454 /// Enum representing "special" relocation types, i.e. those which cannot be represented
1455 /// just with `Target` and `Simple`. These relocations have completely custom handling in
1456 /// the `Special.applyInner` function.
1457 const Special = enum(u13) {
1458 larch_pcala_hi20,
1459 larch_pcala64_lo20,
1460 larch_pcala64_hi12,
1461 larch_b21,
1462 larch_b26,
1463 larch_call36,
1464
1465 sparc_le_hix22,
1466
1467 fn applyInner(
1468 s: Special,
1469 elf: *Elf,
1470 target: Symbol.Id,
1471 addend: u64,
1472 dest_vaddr: u64,
1473 dest_slice: []u8,
1474 ) error{ RelocationMisaligned, RelocationOverflow }!void {
1475 switch (s) {
1476 .larch_pcala_hi20 => {
1477 const val = target.value(elf) +% addend;
1478 const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]);
1479 elf.targetStore(inst, .{
1480 .b0_4 = elf.targetLoad(inst).b0_4,
1481 .j20 = link.loongarch.pcalaHi20(val, dest_vaddr),
1482 .b25_31 = elf.targetLoad(inst).b25_31,
1483 });
1484 },
1485 .larch_pcala64_lo20 => {
1486 const val = target.value(elf) +% addend;
1487 const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]);
1488 elf.targetStore(inst, .{
1489 .b0_4 = elf.targetLoad(inst).b0_4,
1490 .j20 = link.loongarch.pcala64Lo20(val, dest_vaddr),
1491 .b25_31 = elf.targetLoad(inst).b25_31,
1492 });
1493 },
1494 .larch_pcala64_hi12 => {
1495 const val = target.value(elf) +% addend;
1496 const inst: *align(1) link.loongarch.K12 = @ptrCast(dest_slice[0..4]);
1497 elf.targetStore(inst, .{
1498 .b0_9 = elf.targetLoad(inst).b0_9,
1499 .k12 = link.loongarch.pcala64Hi12(val, dest_vaddr),
1500 .b22_31 = elf.targetLoad(inst).b22_31,
1501 });
1502 },
1503 .larch_b21, .larch_b26, .larch_call36 => {
1504 const target_vaddr: u64 = elf.pltEntryTargetAddr(target) orelse target.value(elf);
1505 const jump_offset: i64 = @bitCast(target_vaddr +% addend -% dest_vaddr);
1506 if ((jump_offset >> 2) << 2 != jump_offset) {
1507 return error.RelocationMisaligned;
1508 }
1509 const shifted_jump_offset: i64 = @shrExact(jump_offset, 2);
1510 switch (s) {
1511 .larch_b21 => {
1512 if ((shifted_jump_offset << (64 - 21)) >> (64 - 21) != shifted_jump_offset) {
1513 return error.RelocationOverflow;
1514 }
1515 const truncated: i21 = @intCast(shifted_jump_offset);
1516 const parts: packed struct { lo16: u16, hi5: u5 } = @bitCast(truncated);
1517 const inst: *align(1) link.loongarch.D5K16 = @ptrCast(dest_slice[0..4]);
1518 elf.targetStore(inst, .{
1519 .d5 = parts.hi5,
1520 .b5_9 = elf.targetLoad(inst).b5_9,
1521 .k16 = parts.lo16,
1522 .b26_31 = elf.targetLoad(inst).b26_31,
1523 });
1524 },
1525 .larch_b26 => {
1526 if ((shifted_jump_offset << (64 - 26)) >> (64 - 26) != shifted_jump_offset) {
1527 return error.RelocationOverflow;
1528 }
1529 const truncated: i26 = @intCast(shifted_jump_offset);
1530 const parts: packed struct { lo16: u16, hi10: u10 } = @bitCast(truncated);
1531 const inst: *align(1) link.loongarch.D10K16 = @ptrCast(dest_slice[0..4]);
1532 elf.targetStore(inst, .{
1533 .d10 = parts.hi10,
1534 .k16 = parts.lo16,
1535 .b26_31 = elf.targetLoad(inst).b26_31,
1536 });
1537 },
1538 .larch_call36 => {
1539 // The allowed range of destination addresses here is non-trivial:
1540 // [PC - 128 GiB - 0x20_000, PC + 128 GiB - 0x20_000 - 4]
1541 const gib = 1024 * 1024 * 1024;
1542 if (jump_offset < -128 * gib - 0x20_000 or
1543 jump_offset > 128 * gib - 0x20_000 - 4)
1544 {
1545 return error.RelocationOverflow;
1546 }
1547 // The values we write into the instructions are a little weird too:
1548 const hi: i20 = @intCast((shifted_jump_offset +% 0x8000) >> 16);
1549 const lo: i16 = @truncate(shifted_jump_offset);
1550
1551 const inst0: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]);
1552 const inst1: *align(1) link.loongarch.K16 = @ptrCast(dest_slice[4..8]);
1553
1554 const old0 = elf.targetLoad(inst0);
1555 elf.targetStore(inst0, .{ .b0_4 = old0.b0_4, .j20 = @bitCast(hi), .b25_31 = old0.b25_31 });
1556
1557 const old1 = elf.targetLoad(inst1);
1558 elf.targetStore(inst1, .{ .b0_9 = old1.b0_9, .k16 = @bitCast(lo), .b26_31 = old1.b26_31 });
1559 },
1560 else => unreachable,
1561 }
1562 },
1563 .sparc_le_hix22 => {
1564 const tls_phndx = elf.getNode(elf.ni.tls).segment;
1565 const tls_size: u64 = switch (elf.phdrSlice()) {
1566 inline else => |phdr| tls_size: {
1567 assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS);
1568 break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz);
1569 },
1570 };
1571 const dest_ptr: *align(1) packed struct(u32) {
1572 imm22: u22,
1573 b22_31: u10,
1574 } = @ptrCast(dest_slice);
1575 elf.targetStore(dest_ptr, .{
1576 .imm22 = @truncate(~(target.value(elf) +% addend -% tls_size) >> 10),
1577 .b22_31 = elf.targetLoad(dest_ptr).b22_31,
1578 });
1579 },
1580 }
1581 }
1582 };
1583
1584 fn dependsOnTlsSize(t: SymbolReloc.Type, elf: *const Elf) bool {
1585 return switch (elf.targetTlsVariant()) {
1586 // In TLS variant I, the executable's TLS block starts at a fixed offset from the
1587 // thread pointer, so everything is fine...
1588 .I_original, .I_modified => false,
1589 // ...but in variant II, the executable's TLS block *ends* at a fixed offset from
1590 // the thread pointer, so the offset from the thread pointer to the *start* of the
1591 // TLS block depends on the size of the block, and we need that offset to resolve
1592 // 'tpoff' relocations.
1593 .II => switch (t.target) {
1594 .abs,
1595 .rel,
1596 .pltabs,
1597 .pltrel,
1598 .dtpoff,
1599 .size,
1600 => false,
1601
1602 .tpoff => true,
1603
1604 .special => switch (t.action.special) {
1605 .sparc_le_hix22,
1606 => true,
1607
1608 .larch_pcala_hi20,
1609 .larch_pcala64_lo20,
1610 .larch_pcala64_hi12,
1611 .larch_b21,
1612 .larch_b26,
1613 .larch_call36,
1614 => false,
1615 },
1228 },1616 },
1229 };1617 };
1230 }1618 }
1231 };1619 };
12321620
1233 fn apply(reloc: *const SymbolReloc, elf: *Elf) void {1621 fn apply(reloc: *SymbolReloc, elf: *Elf) void {
1234 assert(elf.ehdrField(.type) != .REL);1622 assert(elf.ehdrType() != .REL);
1235 assert(reloc.node != .none);1623 assert(reloc.node != .none);
1236
1237 if (reloc.node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) {1624 if (reloc.node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) {
1238 // There's no point applying the relocation now, because it will be re-applied by1625 // There's no point applying the relocation now, because it will be re-applied by
1239 // `flushMoved` at some point anyway.1626 // `flushMoved` at some point anyway.
1240 return;1627 return;
1241 }1628 }
12421629 switch (reloc.result) {
1243 if (reloc.rela_index.unwrap()) |rela_index| switch (elf.classifySymbolValue(reloc.target)) {1630 .ok => {},
1244 .static => unreachable,1631 .overflowed => elf.overflowed_reloc_count -= 1,
1245 .dynamic => return, // the relocation happens at runtime1632 .misaligned => elf.misaligned_reloc_count -= 1,
1246 .static_relative => {1633 }
1247 // We have emitted an R_*_RELATIVE relocation to help lower an abs32/abs64 reloc.1634 if (reloc.applyInner(elf)) {
1248 // This is a simplified version of the general relocation handling logic, where we1635 @branchHint(.likely);
1249 // know we're using '.abs64' or '.abs32' (matching the ELF ident class).1636 reloc.result = .ok;
1250 const value = type: switch (reloc.type) {1637 } else |err| switch (err) {
1251 .abs32,1638 error.RelocationOverflow => {
1252 .abs64,1639 reloc.result = .overflowed;
1253 => reloc.target.value(elf) +% @as(u64, @bitCast(reloc.addend)),1640 elf.overflowed_reloc_count += 1;
1254 .pltabs32,
1255 .pltabs64,
1256 => value: {
1257 const plt_index = switch (reloc.target.unwrap()) {
1258 .local => continue :type .abs32,
1259 .global => |name| elf.plt.getIndex(name) orelse continue :type .abs32,
1260 };
1261 if (elf.pltEntryIsDead(plt_index)) continue :type .abs32;
1262 const plt_shndx: Section.Index, const plt_header_entries: u64, const plt_entry_size: u64 = switch (elf.ehdrField(.machine)) {
1263 else => |machine| @panic(@tagName(machine)),
1264 .SPARCV9 => .{ elf.shndx.plt, 4, 32 },
1265 .X86_64 => .{ elf.shndx.plt_sec, 0, 16 },
1266 };
1267 const plt_entry = plt_shndx.vaddr(elf) +% (plt_header_entries + plt_index) * plt_entry_size;
1268 break :value plt_entry +% @as(u64, @bitCast(reloc.addend));
1269 },
1270 else => unreachable,
1271 };
1272 elf.shndx.rela_dyn.relaSetRelativeOffset(elf, rela_index, value);
1273 return;
1274 },1641 },
1275 };1642 error.RelocationMisaligned => {
1643 reloc.result = .misaligned;
1644 elf.misaligned_reloc_count += 1;
1645 },
1646 }
1647 }
1648 fn applyInner(reloc: *const SymbolReloc, elf: *Elf) error{ RelocationOverflow, RelocationMisaligned }!void {
1276 const node_vaddr: u64 = switch (elf.getNode(reloc.node)) {1649 const node_vaddr: u64 = switch (elf.getNode(reloc.node)) {
1277 .file => unreachable,1650 .file => unreachable,
1278 .ehdr => unreachable,1651 .ehdr => unreachable,
...@@ -1289,349 +1662,75 @@ const SymbolReloc = struct {...@@ -1289,349 +1662,75 @@ const SymbolReloc = struct {
1289 };1662 };
1290 const dest_vaddr = node_vaddr + reloc.offset;1663 const dest_vaddr = node_vaddr + reloc.offset;
1291 const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..];1664 const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..];
1292 const target_endian = elf.targetEndian();1665
1293 const sym_value: u64 = reloc.target.value(elf);1666 const addend: u64 = @bitCast(reloc.addend);
1294 const sym_size: u64 = switch (elf.symPtr(reloc.target.index(elf))) {1667 const target_val: u64 = type: switch (reloc.type.target) {
1295 inline else => |target_sym| elf.targetLoad(&target_sym.size),1668 .abs => reloc.target.value(elf) +% addend,
1296 };1669 .rel => reloc.target.value(elf) +% addend -% dest_vaddr,
1297 const target_value = sym_value +% @as(u64, @bitCast(reloc.addend));1670 .pltabs => {
1298 type: switch (reloc.type) {1671 const plt_entry_addr = elf.pltEntryTargetAddr(reloc.target) orelse continue :type .abs;
1299 .write_rela => unreachable,1672 break :type plt_entry_addr +% addend;
1300 .abs64, .dsorel64 => std.mem.writeInt(
1301 u64,
1302 dest_slice[0..8],
1303 target_value,
1304 target_endian,
1305 ),
1306 .abs32, .dsorel32 => std.mem.writeInt(
1307 u32,
1308 dest_slice[0..4],
1309 @intCast(target_value),
1310 target_endian,
1311 ),
1312 .abs32s => std.mem.writeInt(
1313 i32,
1314 dest_slice[0..4],
1315 @intCast(@as(i64, @bitCast(target_value))),
1316 target_endian,
1317 ),
1318 .abs16 => std.mem.writeInt(
1319 u16,
1320 dest_slice[0..2],
1321 @intCast(target_value),
1322 target_endian,
1323 ),
1324 .abs8 => dest_slice[0] = @intCast(target_value),
1325 .rel64 => std.mem.writeInt(
1326 i64,
1327 dest_slice[0..8],
1328 @bitCast(target_value -% dest_vaddr),
1329 target_endian,
1330 ),
1331 .rel32 => std.mem.writeInt(
1332 i32,
1333 dest_slice[0..4],
1334 @intCast(@as(i64, @bitCast(target_value -% dest_vaddr))),
1335 target_endian,
1336 ),
1337 .rel16 => std.mem.writeInt(
1338 i16,
1339 dest_slice[0..2],
1340 @intCast(@as(i64, @bitCast(target_value -% dest_vaddr))),
1341 target_endian,
1342 ),
1343 .rel8 => dest_slice[0] = @bitCast(@as(i8, @intCast(@as(i64, @bitCast(target_value -% dest_vaddr))))),
1344 .pltabs64 => {
1345 const plt_index = switch (reloc.target.unwrap()) {
1346 .local => continue :type .abs64,
1347 .global => |name| elf.plt.getIndex(name) orelse continue :type .abs64,
1348 };
1349 if (elf.pltEntryIsDead(plt_index)) continue :type .abs64;
1350 const plt_shndx: Section.Index, const plt_header_entries: u64, const plt_entry_size: u64 = switch (elf.ehdrField(.machine)) {
1351 else => |machine| @panic(@tagName(machine)),
1352 .SPARCV9 => .{ elf.shndx.plt, 4, 32 },
1353 .X86_64 => .{ elf.shndx.plt_sec, 0, 16 },
1354 };
1355 const plt_entry = plt_shndx.vaddr(elf) +% (plt_header_entries + plt_index) * plt_entry_size;
1356 std.mem.writeInt(
1357 i64,
1358 dest_slice[0..8],
1359 @bitCast(plt_entry +% @as(u64, @bitCast(reloc.addend))),
1360 target_endian,
1361 );
1362 },1673 },
1363 .pltabs32 => {1674 .pltrel => {
1364 const plt_index = switch (reloc.target.unwrap()) {1675 const plt_entry_addr = elf.pltEntryTargetAddr(reloc.target) orelse continue :type .rel;
1365 .local => continue :type .abs32,1676 break :type plt_entry_addr +% addend -% dest_vaddr;
1366 .global => |name| elf.plt.getIndex(name) orelse continue :type .abs32,
1367 };
1368 if (elf.pltEntryIsDead(plt_index)) continue :type .abs32;
1369 const plt_shndx: Section.Index, const plt_header_entries: u64, const plt_entry_size: u64 = switch (elf.ehdrField(.machine)) {
1370 else => |machine| @panic(@tagName(machine)),
1371 .SPARCV9 => .{ elf.shndx.plt, 4, 32 },
1372 .X86_64 => .{ elf.shndx.plt_sec, 0, 16 },
1373 };
1374 const plt_entry = plt_shndx.vaddr(elf) +% (plt_header_entries + plt_index) * plt_entry_size;
1375 std.mem.writeInt(
1376 i32,
1377 dest_slice[0..4],
1378 @intCast(@as(i64, @bitCast(
1379 plt_entry +% @as(u64, @bitCast(reloc.addend)),
1380 ))),
1381 target_endian,
1382 );
1383 },1677 },
1384 .pltrel64 => {1678 .dtpoff => reloc.target.value(elf) +% addend,
1385 const plt_index = switch (reloc.target.unwrap()) {1679 .tpoff => switch (elf.targetTlsVariant()) {
1386 .local => continue :type .rel64,1680 .I_original => |tls| tls.tcb_size +% reloc.target.value(elf) +% addend,
1387 .global => |name| elf.plt.getIndex(name) orelse continue :type .rel64,1681 .I_modified => |tls| 0 -% tls.tp_off +% reloc.target.value(elf) +% addend,
1388 };1682 .II => {
1389 if (elf.pltEntryIsDead(plt_index)) continue :type .rel64;1683 const tls_phndx = elf.getNode(elf.ni.tls).segment;
1390 const plt_shndx: Section.Index, const plt_header_entries: u64, const plt_entry_size: u64 = switch (elf.ehdrField(.machine)) {1684 const tls_size: u64 = switch (elf.phdrSlice()) {
1391 else => |machine| @panic(@tagName(machine)),1685 inline else => |phdr| tls_size: {
1392 .SPARCV9 => .{ elf.shndx.plt, 4, 32 },1686 assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS);
1393 .X86_64 => .{ elf.shndx.plt_sec, 0, 16 },1687 break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz);
1394 };1688 },
1395 const plt_entry = plt_shndx.vaddr(elf) +% (plt_header_entries + plt_index) * plt_entry_size;1689 };
1396 std.mem.writeInt(1690 break :type reloc.target.value(elf) +% addend -% tls_size;
1397 i64,1691 },
1398 dest_slice[0..8],
1399 @bitCast(plt_entry +% @as(u64, @bitCast(reloc.addend)) -% dest_vaddr),
1400 target_endian,
1401 );
1402 },1692 },
1403 .pltrel32 => {1693 .size => switch (elf.symPtr(reloc.target.index(elf))) {
1404 const plt_index = switch (reloc.target.unwrap()) {1694 inline else => |sym| elf.targetLoad(&sym.size),
1405 .local => continue :type .rel32,
1406 .global => |name| elf.plt.getIndex(name) orelse continue :type .rel32,
1407 };
1408 if (elf.pltEntryIsDead(plt_index)) continue :type .rel32;
1409 const plt_shndx: Section.Index, const plt_header_entries: u64, const plt_entry_size: u64 = switch (elf.ehdrField(.machine)) {
1410 else => |machine| @panic(@tagName(machine)),
1411 .SPARCV9 => .{ elf.shndx.plt, 4, 32 },
1412 .X86_64 => .{ elf.shndx.plt_sec, 0, 16 },
1413 };
1414 const plt_entry = plt_shndx.vaddr(elf) +% (plt_header_entries + plt_index) * plt_entry_size;
1415 std.mem.writeInt(
1416 i32,
1417 dest_slice[0..4],
1418 @intCast(@as(i64, @bitCast(
1419 plt_entry +% @as(u64, @bitCast(reloc.addend)) -% dest_vaddr,
1420 ))),
1421 target_endian,
1422 );
1423 },1695 },
1424 .size64 => std.mem.writeInt(1696 .special => return reloc.type.action.special.applyInner(
1425 u64,1697 elf,
1426 dest_slice[0..8],1698 reloc.target,
1427 sym_size +% @as(u64, @bitCast(reloc.addend)),1699 addend,
1428 target_endian,1700 dest_vaddr,
1429 ),1701 dest_slice,
1430 .size32 => std.mem.writeInt(
1431 u32,
1432 dest_slice[0..4],
1433 @intCast(sym_size +% @as(u64, @bitCast(reloc.addend))),
1434 target_endian,
1435 ),
1436 .dtpoff64 => std.mem.writeInt(
1437 i64,
1438 dest_slice[0..8],
1439 @bitCast(target_value),
1440 target_endian,
1441 ),
1442 .dtpoff32 => std.mem.writeInt(
1443 i32,
1444 dest_slice[0..4],
1445 @intCast(@as(i64, @bitCast(target_value))),
1446 target_endian,
1447 ),1702 ),
1448 .tpoff64 => {1703 };
1449 const tls_phndx = elf.getNode(elf.ni.tls).segment;
1450 const tls_size: u64 = switch (elf.phdrSlice()) {
1451 inline else => |phdr| tls_size: {
1452 assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS);
1453 break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz);
1454 },
1455 };
1456 std.mem.writeInt(
1457 i64,
1458 dest_slice[0..8],
1459 @bitCast(target_value -% tls_size),
1460 target_endian,
1461 );
1462 },
1463 .tpoff32 => {
1464 const tls_phndx = elf.getNode(elf.ni.tls).segment;
1465 const tls_size: u64 = switch (elf.phdrSlice()) {
1466 inline else => |phdr| tls_size: {
1467 assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS);
1468 break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz);
1469 },
1470 };
1471 std.mem.writeInt(
1472 i32,
1473 dest_slice[0..4],
1474 @intCast(@as(i64, @bitCast(target_value -% tls_size))),
1475 target_endian,
1476 );
1477 },
14781704
1479 .larch_abs32_lo12 => {1705 // Check for the `R_*_RELATIVE` case now, because it is possible only when no shift or cast
1480 assert(elf.ehdrField(.machine) == .LOONGARCH);1706 // is required, meaning we can handle it now and return early.
1481 link.loongarch.writeK12(dest_slice[0..4], @truncate(target_value));1707 if (reloc.rela_index.unwrap()) |rela_index| switch (elf.classifySymbolValue(reloc.target)) {
1482 },1708 .static => unreachable,
1483 .larch_rel32_hi20 => {1709 .dynamic => return, // the relocation happens at runtime
1484 assert(elf.ehdrField(.machine) == .LOONGARCH);1710 .static_relative => {
1485 link.loongarch.writeJ20(dest_slice[0..4], link.loongarch.toPcalaHi20(target_value, dest_vaddr));1711 // We have emitted an R_*_RELATIVE relocation to help lower an absolute-address
1486 },1712 // relocation. The value computed above is valid, but instead of writing it to the
1487 .larch_rel64_lo20 => {1713 // destination slice, we actually want to write it to the runtime relocation entry.
1488 assert(elf.ehdrField(.machine) == .LOONGARCH);1714 switch (elf.identClass()) {
1489 link.loongarch.writeJ20(dest_slice[0..4], link.loongarch.toPcala64Lo20(target_value, dest_vaddr));1715 .NONE, _ => unreachable,
1490 },1716 .@"32" => assert(reloc.type.action.simple.dest == .@"32"),
1491 .larch_rel64_hi12 => {1717 .@"64" => assert(reloc.type.action.simple.dest == .@"64"),
1492 assert(elf.ehdrField(.machine) == .LOONGARCH);1718 }
1493 link.loongarch.writeK12(dest_slice[0..4], link.loongarch.toPcala64Hi12(target_value, dest_vaddr));1719 assert(reloc.type.action.simple.cast == .unsigned);
1494 },1720 assert(reloc.type.action.simple.shift == .@"0");
1495 // TODO: handle bad alignment and overflow gracefully1721 elf.shndx.rela_dyn.relaSetRelativeOffset(elf, rela_index, target_val);
1496 .larch_branch_rel18 => {1722 return;
1497 assert(elf.ehdrField(.machine) == .LOONGARCH);
1498 const target_rel: i64 = @bitCast(target_value -% dest_vaddr);
1499 const slot_target: i16 = @intCast(@shrExact(target_rel, 2));
1500 link.loongarch.writeK16(dest_slice[0..4], @bitCast(slot_target));
1501 },
1502 .larch_branch_rel23 => {
1503 assert(elf.ehdrField(.machine) == .LOONGARCH);
1504 const target_rel: i64 = @bitCast(target_value -% dest_vaddr);
1505 const slot_target: i21 = @intCast(@shrExact(target_rel, 2));
1506 link.loongarch.writeD5K16(dest_slice[0..4], @bitCast(slot_target));
1507 },
1508 .larch_branch_rel28 => {
1509 assert(elf.ehdrField(.machine) == .LOONGARCH);
1510 const target_rel: i64 = @bitCast(target_value -% dest_vaddr);
1511 const slot_target: i26 = @intCast(@shrExact(target_rel, 2));
1512 link.loongarch.writeD10K16(dest_slice[0..4], @bitCast(slot_target));
1513 },
1514 .larch_call_rel38 => {
1515 assert(elf.ehdrField(.machine) == .LOONGARCH);
1516 const target_rel: i64 = @bitCast(target_value -% dest_vaddr);
1517 // We use i64 instead of i36 here because the allowed range is
1518 // [PC - 128 GiB - 0x20000, PC + 128GiB - 0x20000 - 4].
1519 // The intCast in writeJ20 will do the final check.
1520 const slot_target: i64 = @intCast(@shrExact(target_rel, 2));
1521 link.loongarch.writeJ20(dest_slice[0..4], @bitCast(@as(i20, @intCast((slot_target +% 0x8000) >> 16))));
1522 link.loongarch.writeK16(dest_slice[4..8], @bitCast(@as(i16, @truncate(slot_target))));
1523 },
1524 .larch_tpoff32_lo12 => {
1525 assert(elf.ehdrField(.machine) == .LOONGARCH);
1526 link.loongarch.writeK12(dest_slice[0..4], @truncate(target_value));
1527 },
1528 .larch_tpoff32_hi20 => {
1529 assert(elf.ehdrField(.machine) == .LOONGARCH);
1530 link.loongarch.writeJ20(dest_slice[0..4], @truncate(target_value >> 12));
1531 },
1532 .larch_tpoff64_lo20 => {
1533 assert(elf.ehdrField(.machine) == .LOONGARCH);
1534 link.loongarch.writeJ20(dest_slice[0..4], @truncate(target_value >> 32));
1535 },
1536 .larch_tpoff64_hi12 => {
1537 assert(elf.ehdrField(.machine) == .LOONGARCH);
1538 link.loongarch.writeK12(dest_slice[0..4], @truncate(target_value >> 52));
1539 },1723 },
1724 };
15401725
1541 .sparc_wdisp30 => {1726 try reloc.type.action.simple.write(target_val, dest_slice, elf.targetEndian());
1542 const dest_ptr: *link.sparc.reloc.Disp30 = @ptrCast(@alignCast(dest_slice));
1543 var result = elf.targetLoad(dest_ptr);
1544 result.disp30 = @truncate((target_value -% dest_vaddr) >> 2);
1545 elf.targetStore(dest_ptr, result);
1546 },
1547 .sparc_pc10 => {
1548 const dest_ptr: *link.sparc.reloc.Simm13 = @ptrCast(@alignCast(dest_slice));
1549 var result = elf.targetLoad(dest_ptr);
1550 result.simm13 = @as(u10, @truncate(target_value -% dest_vaddr));
1551 elf.targetStore(dest_ptr, result);
1552 },
1553 .sparc_pc22 => {
1554 const dest_ptr: *link.sparc.reloc.Disp22 = @ptrCast(@alignCast(dest_slice));
1555 var result = elf.targetLoad(dest_ptr);
1556 result.disp22 = @truncate((target_value -% dest_vaddr) >> 10);
1557 elf.targetStore(dest_ptr, result);
1558 },
1559 .sparc_wplt30 => {
1560 const plt_index = switch (reloc.target.unwrap()) {
1561 .local => continue :type .sparc_wdisp30,
1562 .global => |name| elf.plt.getIndex(name) orelse continue :type .sparc_wdisp30,
1563 };
1564 if (elf.pltEntryIsDead(plt_index)) continue :type .sparc_wdisp30;
1565 const plt_entry = elf.shndx.plt.vaddr(elf) +% (4 + plt_index) * 32;
1566 const dest_ptr: *link.sparc.reloc.Disp30 = @ptrCast(@alignCast(dest_slice));
1567 var result = elf.targetLoad(dest_ptr);
1568 result.disp30 = @truncate((plt_entry +% @as(u64, @bitCast(reloc.addend)) -% dest_vaddr) >> 2);
1569 elf.targetStore(dest_ptr, result);
1570 },
1571 .sparc_h44 => {
1572 const dest_ptr: *link.sparc.reloc.Imm22 = @ptrCast(@alignCast(dest_slice));
1573 var result = elf.targetLoad(dest_ptr);
1574 result.imm22 = @truncate(target_value >> 22);
1575 elf.targetStore(dest_ptr, result);
1576 },
1577 .sparc_m44 => {
1578 const dest_ptr: *link.sparc.reloc.Imm10 = @ptrCast(@alignCast(dest_slice));
1579 var result = elf.targetLoad(dest_ptr);
1580 result.imm10 = @truncate(target_value >> 12);
1581 elf.targetStore(dest_ptr, result);
1582 },
1583 .sparc_l44 => {
1584 const dest_ptr: *link.sparc.reloc.Imm13 = @ptrCast(@alignCast(dest_slice));
1585 var result = elf.targetLoad(dest_ptr);
1586 result.imm13 = @as(u12, @truncate(target_value));
1587 elf.targetStore(dest_ptr, result);
1588 },
1589 .sparc_ldo_hix22 => {
1590 const dest_ptr: *link.sparc.reloc.Simm22 = @ptrCast(@alignCast(dest_slice));
1591 var result = elf.targetLoad(dest_ptr);
1592 result.simm22 = @truncate(target_value >> 10);
1593 elf.targetStore(dest_ptr, result);
1594 },
1595 .sparc_ldo_lox10 => {
1596 const dest_ptr: *link.sparc.reloc.Simm13 = @ptrCast(@alignCast(dest_slice));
1597 var result = elf.targetLoad(dest_ptr);
1598 result.simm13 = @as(u10, @truncate(target_value));
1599 elf.targetStore(dest_ptr, result);
1600 },
1601 .sparc_le_hix22 => {
1602 const tls_phndx = elf.getNode(elf.ni.tls).segment;
1603 const tls_size: u64 = switch (elf.phdrSlice()) {
1604 inline else => |phdr| tls_size: {
1605 assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS);
1606 break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz);
1607 },
1608 };
1609 const dest_ptr: *link.sparc.reloc.Imm22 = @ptrCast(@alignCast(dest_slice));
1610 var result = elf.targetLoad(dest_ptr);
1611 result.imm22 = @truncate(~(target_value -% tls_size) >> 10);
1612 elf.targetStore(dest_ptr, result);
1613 },
1614 .sparc_le_lox10 => {
1615 const tls_phndx = elf.getNode(elf.ni.tls).segment;
1616 const tls_size: u64 = switch (elf.phdrSlice()) {
1617 inline else => |phdr| tls_size: {
1618 assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS);
1619 break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz);
1620 },
1621 };
1622 const dest_ptr: *link.sparc.reloc.Simm13 = @ptrCast(@alignCast(dest_slice));
1623 var result = elf.targetLoad(dest_ptr);
1624 result.simm13 = @as(u13, 0b1110000000000) | @as(u10, @truncate(target_value -% tls_size));
1625 elf.targetStore(dest_ptr, result);
1626 },
1627 }
1628 }1727 }
16291728
1630 fn delete(reloc: *SymbolReloc, elf: *Elf, index: SymbolReloc.Index) void {1729 fn delete(reloc: *SymbolReloc, elf: *Elf, index: SymbolReloc.Index) void {
1631 assert(index.get(elf) == reloc);1730 assert(index.get(elf) == reloc);
16321731
1633 reloc.deleteOutputRel(elf);1732 reloc.deleteOutputRel(elf);
1634 if (reloc.type.dependsOnTlsSize()) {1733 if (reloc.type.dependsOnTlsSize(elf)) {
1635 assert(elf.tls_size_symbol_relocs.swapRemove(index));1734 assert(elf.tls_size_symbol_relocs.swapRemove(index));
1636 }1735 }
16371736
...@@ -1647,6 +1746,11 @@ const SymbolReloc = struct {...@@ -1647,6 +1746,11 @@ const SymbolReloc = struct {
1647 .none => {},1746 .none => {},
1648 else => |next| next.get(elf).prev = reloc.prev,1747 else => |next| next.get(elf).prev = reloc.prev,
1649 }1748 }
1749 switch (reloc.result) {
1750 .ok => {},
1751 .overflowed => elf.overflowed_reloc_count -= 1,
1752 .misaligned => elf.misaligned_reloc_count -= 1,
1753 }
16501754
1651 reloc.* = undefined;1755 reloc.* = undefined;
1652 }1756 }
...@@ -1656,8 +1760,7 @@ const SymbolReloc = struct {...@@ -1656,8 +1760,7 @@ const SymbolReloc = struct {
1656 fn deleteOutputRel(reloc: *SymbolReloc, elf: *Elf) void {1760 fn deleteOutputRel(reloc: *SymbolReloc, elf: *Elf) void {
1657 const rela_index = reloc.rela_index.unwrap() orelse return;1761 const rela_index = reloc.rela_index.unwrap() orelse return;
1658 reloc.relaSection(elf).relaDeleteOne(elf, rela_index);1762 reloc.relaSection(elf).relaDeleteOne(elf, rela_index);
1659 switch (elf.ehdrField(.type)) {1763 switch (elf.ehdrType()) {
1660 .NONE, .CORE, _ => unreachable,
1661 .REL => {},1764 .REL => {},
1662 .EXEC, .DYN => switch (elf.nodeWantsDsoRelocation(reloc.node)) {1765 .EXEC, .DYN => switch (elf.nodeWantsDsoRelocation(reloc.node)) {
1663 .no => unreachable, // there *was* a dynamic relocation!1766 .no => unreachable, // there *was* a dynamic relocation!
...@@ -1715,37 +1818,26 @@ fn ensureUnusedPltCapacity(elf: *Elf, len: u32) Error!void {...@@ -1715,37 +1818,26 @@ fn ensureUnusedPltCapacity(elf: *Elf, len: u32) Error!void {
1715 try elf.shndx.rela_plt.relaEnsureAdditionalCapacity(elf, len);1818 try elf.shndx.rela_plt.relaEnsureAdditionalCapacity(elf, len);
17161819
1717 try elf.plt.ensureUnusedCapacity(gpa, len);1820 try elf.plt.ensureUnusedCapacity(gpa, len);
1718 const need_plt_capacity = elf.plt.count() + len;1821 const need_plt_count = elf.plt.count() + len;
17191822
1720 switch (elf.ehdrField(.machine)) {1823 const plt = elf.targetPltInfo();
1721 else => |machine| @panic(@tagName(machine)),
1722 .X86_64 => {
1723 // Ensure the `.plt` section's node is big enough
1724 const plt_need_size: usize = 16 * (1 + need_plt_capacity);
1725 try elf.ensureNodeSize(elf.shndx.plt.get(elf).ni, plt_need_size);
17261824
1727 // Ensure the `.got.plt` section's node is big enough1825 // Ensure the `.plt` section's node is big enough:
1728 const got_plt_need_size: usize = elf.targetPtrSize() * (3 + need_plt_capacity);1826 {
1729 try elf.ensureNodeSize(elf.shndx.got_plt.get(elf).ni, got_plt_need_size);1827 const need_size: usize = plt.entry_size * (1 + need_plt_count);
1828 try elf.ensureNodeSize(elf.shndx.plt.get(elf).ni, need_size);
1829 }
17301830
1731 // Ensure the `.plt.sec` section's node is big enough1831 // If there is a `.got.plt` section, ensure its node is big enough
1732 const plt_sec_need_size: usize = 16 * need_plt_capacity;1832 if (plt.got_plt) |got_plt| {
1733 try elf.ensureNodeSize(elf.shndx.plt_sec.get(elf).ni, plt_sec_need_size);1833 const need_size: usize = elf.targetPtrSize() * (got_plt.header_entries + need_plt_count);
1734 },1834 try elf.ensureNodeSize(elf.shndx.got_plt.get(elf).ni, need_size);
1735 .LOONGARCH => {1835 }
1736 // Ensure the `.plt` section's node is big enough
1737 const plt_need_size: usize = 16 * (2 + need_plt_capacity);
1738 try elf.ensureNodeSize(elf.shndx.plt.get(elf).ni, plt_need_size);
17391836
1740 // Ensure the `.got.plt` section's node is big enough1837 // If there is a `.plt.sec` section, ensure its node is big enough
1741 const got_plt_need_size: usize = elf.targetPtrSize() * (2 + need_plt_capacity);1838 if (plt.plt_sec) |plt_sec| {
1742 try elf.ensureNodeSize(elf.shndx.got_plt.get(elf).ni, got_plt_need_size);1839 const need_size: usize = plt_sec.entry_size * need_plt_count;
1743 },1840 try elf.ensureNodeSize(elf.shndx.plt_sec.get(elf).ni, need_size);
1744 .SPARCV9 => {
1745 // Ensure the `.plt` section's node is big enough
1746 const plt_need_size: usize = 32 * (4 + need_plt_capacity);
1747 try elf.ensureNodeSize(elf.shndx.plt.get(elf).ni, plt_need_size);
1748 },
1749 }1841 }
1750}1842}
1751/// Given an index into the PLT, returns whether that PLT entry is dead, meaning it may be reused at1843/// Given an index into the PLT, returns whether that PLT entry is dead, meaning it may be reused at
...@@ -1808,7 +1900,7 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L...@@ -1808,7 +1900,7 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L
1808 const global_name: String(.strtab) = @enumFromInt(elf.targetLoad(&new_sym.name));1900 const global_name: String(.strtab) = @enumFromInt(elf.targetLoad(&new_sym.name));
1809 elf.globalByName(global_name).?.symtab_index = new_index;1901 elf.globalByName(global_name).?.symtab_index = new_index;
18101902
1811 if (elf.ehdrField(.type) == .REL and target_index.ptr(elf).first_target_reloc != .none) {1903 if (elf.ehdrType() == .REL and target_index.ptr(elf).first_target_reloc != .none) {
1812 // This symbol's index is changing, so queue an update of relocs targeting it.1904 // This symbol's index is changing, so queue an update of relocs targeting it.
1813 elf.changed_symtab_index.putAssumeCapacity(global_name, {});1905 elf.changed_symtab_index.putAssumeCapacity(global_name, {});
1814 }1906 }
...@@ -1972,7 +2064,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{...@@ -1972,7 +2064,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
1972 };2064 };
19732065
1974 const force_local_bind: bool = switch (opts.visibility) {2066 const force_local_bind: bool = switch (opts.visibility) {
1975 .HIDDEN, .INTERNAL => elf.ehdrField(.type) != .REL,2067 .HIDDEN, .INTERNAL => elf.ehdrType() != .REL,
1976 .PROTECTED, .DEFAULT => false,2068 .PROTECTED, .DEFAULT => false,
1977 };2069 };
19782070
...@@ -2071,7 +2163,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{...@@ -2071,7 +2163,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
2071 }2163 }
20722164
2073 switch (@"type") {2165 switch (@"type") {
2074 .FUNC, .GNU_IFUNC => if (elf.ehdrField(.type) != .REL and2166 .FUNC, .GNU_IFUNC => if (elf.ehdrType() != .REL and
2075 elf.classifySymbolValue(.global(opts.name.strtab)) == .dynamic)2167 elf.classifySymbolValue(.global(opts.name.strtab)) == .dynamic)
2076 {2168 {
2077 // This STT_FUNC symbol might be defined externally, so it needs a PLT entry.2169 // This STT_FUNC symbol might be defined externally, so it needs a PLT entry.
...@@ -2240,7 +2332,7 @@ fn mergeGlobalSymbolVisibility(elf: *Elf, global_ptr: *Symbol.Global, other_visi...@@ -2240,7 +2332,7 @@ fn mergeGlobalSymbolVisibility(elf: *Elf, global_ptr: *Symbol.Global, other_visi
2240 // object), then the symbol should have binding STB_LOCAL in the output. Therefore, if we are2332 // object), then the symbol should have binding STB_LOCAL in the output. Therefore, if we are
2241 // putting the global in this state for the first time---let's call it "demoting" the global to2333 // putting the global in this state for the first time---let's call it "demoting" the global to
2242 // STB_LOCAL---we need to update its bind in the symtab.2334 // STB_LOCAL---we need to update its bind in the symtab.
2243 const demote_to_local = newly_hidden and elf.ehdrField(.type) != .REL;2335 const demote_to_local = newly_hidden and elf.ehdrType() != .REL;
2244 switch (elf.symPtr(global_ptr.symtab_index)) {2336 switch (elf.symPtr(global_ptr.symtab_index)) {
2245 inline else => |sym, class| {2337 inline else => |sym, class| {
2246 const old_info = elf.targetLoad(&sym.info);2338 const old_info = elf.targetLoad(&sym.info);
...@@ -2274,7 +2366,7 @@ fn mergeGlobalSymbolVisibility(elf: *Elf, global_ptr: *Symbol.Global, other_visi...@@ -2274,7 +2366,7 @@ fn mergeGlobalSymbolVisibility(elf: *Elf, global_ptr: *Symbol.Global, other_visi
2274/// the symbol must be moved from the "globals" part of the symtab to the "locals" part, because ELF2366/// the symbol must be moved from the "globals" part of the symtab to the "locals" part, because ELF
2275/// requires that all STB_LOCAL symbols in a symbol table appear before any global symbols.2367/// requires that all STB_LOCAL symbols in a symbol table appear before any global symbols.
2276fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {2368fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {
2277 assert(elf.ehdrField(.type) != .REL); // demotion only happens when emitting an ELF module2369 assert(elf.ehdrType() != .REL); // demotion only happens when emitting an ELF module
2278 switch (elf.shdrPtr(.symtab)) {2370 switch (elf.shdrPtr(.symtab)) {
2279 inline else => |shdr, class| {2371 inline else => |shdr, class| {
2280 // `shdr.info` stores the index of the first global symbol. We are going to swap the2372 // `shdr.info` stores the index of the first global symbol. We are going to swap the
...@@ -2292,240 +2384,64 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {...@@ -2292,240 +2384,64 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {
2292 // The demoted global was not the first global in the symtab, so we need to swap it2384 // The demoted global was not the first global in the symtab, so we need to swap it
2293 // to its new location.2385 // to its new location.
22942386
2295 const src_sym_ptr = @field(elf.symPtr(src_index), @tagName(class));2387 const src_sym_ptr = @field(elf.symPtr(src_index), @tagName(class));
2296 const dest_sym_ptr = @field(elf.symPtr(dest_index), @tagName(class));2388 const dest_sym_ptr = @field(elf.symPtr(dest_index), @tagName(class));
2297
2298 const this_name: String(.strtab) = @enumFromInt(elf.targetLoad(&src_sym_ptr.name));
2299 assert(elf.globalByName(this_name).? == global_ptr);
2300
2301 const other_name: String(.strtab) = @enumFromInt(elf.targetLoad(&dest_sym_ptr.name));
2302 const other_global_ptr = elf.globalByName(other_name).?;
2303 assert(other_global_ptr.symtab_index == dest_index);
2304
2305 // First swap the symtab entries...
2306 std.mem.swap(class.ElfN().Sym, src_sym_ptr, dest_sym_ptr);
2307 // ...then the `elf.symtab` metadata...
2308 std.mem.swap(Symbol, src_index.ptr(elf), dest_index.ptr(elf));
2309 // ...then update the `elf.globals` tracking.
2310 global_ptr.symtab_index = dest_index;
2311 other_global_ptr.symtab_index = src_index;
2312 }
2313
2314 // We also need to get rid of the dynsym entry if there is one. To keep dynsym compact,
2315 // we'll move another symbol into its place just like we did above.
2316 if (global_ptr.dynsym_index != 0) {
2317 const dynsym_shdr = @field(elf.shdrPtr(elf.shndx.dynsym), @tagName(class));
2318
2319 const ent_size = @sizeOf(class.ElfN().Sym);
2320 assert(elf.targetLoad(&dynsym_shdr.entsize) == ent_size);
2321
2322 // We're going to decrease the size of `.dynsym`, thereby removing its last index.
2323 const old_size = elf.targetLoad(&dynsym_shdr.size);
2324 const new_size = old_size - ent_size;
2325 const remove_dynsym_index: u32 = @intCast(@divExact(new_size, ent_size));
2326
2327 const free_dynsym_index = global_ptr.dynsym_index;
2328 global_ptr.dynsym_index = 0;
2329
2330 if (free_dynsym_index != remove_dynsym_index) {
2331 // The demoted global wasn't the last entry, so move whatever entry we just
2332 // truncated out of dynsym into its place.
2333
2334 const src_dynsym_ptr = @field(elf.dynsymPtr(remove_dynsym_index), @tagName(class));
2335 const dest_dynsym_ptr = @field(elf.dynsymPtr(free_dynsym_index), @tagName(class));
2336
2337 const moved_name_dynstr: String(.dynstr) = @enumFromInt(elf.targetLoad(&src_dynsym_ptr.name));
2338 const moved_name = elf.stringExisting(.strtab, moved_name_dynstr.slice(elf));
2339 const moved_global_ptr = elf.globalByName(moved_name).?;
2340
2341 dest_dynsym_ptr.* = src_dynsym_ptr.*;
2342
2343 assert(moved_global_ptr.dynsym_index == remove_dynsym_index);
2344 moved_global_ptr.dynsym_index = free_dynsym_index;
2345
2346 // Since that symbol's dynsym index has changed, we'll have to update any
2347 // relocation entries targeting it.
2348 elf.changed_symtab_index.putAssumeCapacity(moved_name, {});
2349 }
2350
2351 // Now that we've given that symbol a new home, actually decrease the section size.
2352 elf.targetStore(&dynsym_shdr.size, new_size);
2353 }
2354 },
2355 }
2356}
2357fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void {
2358 const target_endian = elf.targetEndian();
2359
2360 // We use the existing free-list tracking of the `.rela.plt` section to also behave as a
2361 // free-list for the PLT itself---see `pltEntryIsDead` for details.
2362 const plt_index: u32 = @intFromEnum(elf.shndx.rela_plt.relaAddOneAssumeCapacity(elf, .{
2363 .type = .jumpSlot(elf),
2364 .offset = 0, // populated later
2365 .raw_sym_index = dynsym_index,
2366 .addend = 0,
2367 }));
2368
2369 // Note that some architectures don't have .got.plt (e.g. SPARC), and so
2370 // these values actually refer to .plt.
2371 const got_plt_section, const got_plt_offset = switch (elf.ehdrField(.machine)) {
2372 else => |machine| @panic(@tagName(machine)),
2373 .LOONGARCH => .{ elf.shndx.got_plt, elf.targetPtrSize() * (2 + plt_index) },
2374 .SPARCV9 => .{ elf.shndx.plt, 32 * (4 + plt_index) },
2375 .X86_64 => .{ elf.shndx.got_plt, elf.targetPtrSize() * (3 + plt_index) },
2376 };
2377
2378 // Now that we know the index, we can set the relocation's offset.
2379 elf.shndx.rela_plt.relaSetOffset(elf, @enumFromInt(plt_index), got_plt_section.vaddr(elf) + got_plt_offset);
2380
2381 if (plt_index < elf.plt.count()) {
2382 // We reused a free entry, so we're already done!
2383 elf.plt.setKey(plt_index, global_name);
2384 return;
2385 }
2386
2387 // We added a new entry, so we now need to extend the PLT sections.
2388 assert(plt_index == elf.plt.count());
2389 elf.plt.putAssumeCapacityNoClobber(global_name, {});
2390
2391 switch (elf.ehdrField(.machine)) {
2392 else => |machine| @panic(@tagName(machine)),
2393 .X86_64 => {
2394 const plt_ni = elf.shndx.plt.get(elf).ni;
2395 const plt_addr = plt_addr: switch (elf.shdrPtr(elf.shndx.plt)) {
2396 inline else => |shdr| {
2397 const old_size = 16 * (1 + plt_index);
2398 assert(elf.targetLoad(&shdr.size) == old_size);
2399 elf.targetStore(&shdr.size, old_size + 16);
2400 const plt_slice = plt_ni.slice(&elf.mf)[old_size..][0..16];
2401 @memcpy(plt_slice, &[16]u8{
2402 0xf3, 0x0f, 0x1e, 0xfa, // endbr64
2403 0x68, 0x00, 0x00, 0x00, 0x00, // push $0x0
2404 0xe9, 0x00, 0x00, 0x00, 0x00, // jmp 0
2405 0x66, 0x90, // xchg %ax,%ax
2406 });
2407 std.mem.writeInt(u32, plt_slice[5..][0..4], plt_index, target_endian);
2408 std.mem.writeInt(
2409 i32,
2410 plt_slice[10..][0..4],
2411 -@as(i32, @intCast(old_size + 14)),
2412 target_endian,
2413 );
2414 break :plt_addr elf.targetLoad(&shdr.addr) + old_size;
2415 },
2416 };
2417
2418 const got_plt_ni = elf.shndx.got_plt.get(elf).ni;
2419 switch (elf.shdrPtr(elf.shndx.got_plt)) {
2420 inline else => |shdr, class| {
2421 assert(elf.targetLoad(&shdr.size) == got_plt_offset);
2422 elf.targetStore(&shdr.size, got_plt_offset + @sizeOf(class.ElfN().Addr));
2423 std.mem.writeInt(
2424 class.ElfN().Addr,
2425 got_plt_ni.slice(&elf.mf)[got_plt_offset..][0..@sizeOf(class.ElfN().Addr)],
2426 @intCast(plt_addr),
2427 target_endian,
2428 );
2429 },
2430 }
2431
2432 const plt_sec_ni = elf.shndx.plt_sec.get(elf).ni;
2433 switch (elf.shdrPtr(elf.shndx.plt_sec)) {
2434 inline else => |shdr| {
2435 const old_size = 16 * plt_index;
2436 elf.targetStore(&shdr.size, old_size + 16);
2437 const plt_sec_slice = plt_sec_ni.slice(&elf.mf)[old_size..][0..16];
2438 @memcpy(plt_sec_slice, &[16]u8{
2439 0xf3, 0x0f, 0x1e, 0xfa, // endbr64
2440 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *0x0(%rip)
2441 0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00, // nopw 0x0(%rax,%rax,1)
2442 });
2443 std.mem.writeInt(
2444 i32,
2445 plt_sec_slice[6..][0..4],
2446 @intCast(@as(i64, @bitCast(
2447 (got_plt_section.vaddr(elf) + got_plt_offset) -% (elf.targetLoad(&shdr.addr) + old_size + 10),
2448 ))),
2449 target_endian,
2450 );
2451 },
2452 }
2453 },
2454 .LOONGARCH => {
2455 // add a .PLT entry, writing the template
2456 const plt_ni = elf.shndx.plt.get(elf).ni;
2457 const plt_addr, const plt_slice = plt_entry: switch (elf.shdrPtr(elf.shndx.plt)) {
2458 inline else => |shdr| {
2459 const old_size = 16 * (1 + plt_index);
2460 assert(elf.targetLoad(&shdr.size) == old_size);
2461 elf.targetStore(&shdr.size, old_size + 16);
2462 const plt_slice = plt_ni.slice(&elf.mf)[old_size..][0..16];
2463 @memcpy(plt_slice, source: switch (elf.identClass()) {
2464 .NONE, _ => unreachable,
2465 inline .@"32", .@"64" => |elf_class| {
2466 const ld_byte = if (elf_class == .@"64") 0xc0 else 0x80;
2467 break :source &[16]u8{
2468 0x1a, 0x00, 0x00, 0x0f, // pcalau12i $t3, %pc_hi20(func@.got.plt)
2469 0x28, ld_byte, 0x01, 0xef, // ld.w/d $t3, $t3, %lo12(func@.got.plt)
2470 0x4c, 0x00, 0x01, 0xed, // jirl $t1, $t3, 0
2471 0x00, 0x2a, 0x00, 0x00, // break
2472 };
2473 },
2474 });
2475 break :plt_entry .{ elf.targetLoad(&shdr.addr) + old_size, plt_slice };
2476 },
2477 };
24782389
2479 // add a .GOT.PLT entry, writing the address of the corresponding .PLT entry2390 const this_name: String(.strtab) = @enumFromInt(elf.targetLoad(&src_sym_ptr.name));
2480 const got_plt_ni = elf.shndx.got_plt.get(elf).ni;2391 assert(elf.globalByName(this_name).? == global_ptr);
2481 switch (elf.shdrPtr(elf.shndx.got_plt)) {2392
2482 inline else => |shdr, class| {2393 const other_name: String(.strtab) = @enumFromInt(elf.targetLoad(&dest_sym_ptr.name));
2483 assert(elf.targetLoad(&shdr.size) == got_plt_offset);2394 const other_global_ptr = elf.globalByName(other_name).?;
2484 elf.targetStore(&shdr.size, got_plt_offset + @sizeOf(class.ElfN().Addr));2395 assert(other_global_ptr.symtab_index == dest_index);
2485 std.mem.writeInt(2396
2486 class.ElfN().Addr,2397 // First swap the symtab entries...
2487 got_plt_ni.slice(&elf.mf)[got_plt_offset..][0..@sizeOf(class.ElfN().Addr)],2398 std.mem.swap(class.ElfN().Sym, src_sym_ptr, dest_sym_ptr);
2488 @intCast(plt_addr),2399 // ...then the `elf.symtab` metadata...
2489 target_endian,2400 std.mem.swap(Symbol, src_index.ptr(elf), dest_index.ptr(elf));
2490 );2401 // ...then update the `elf.globals` tracking.
2491 },2402 global_ptr.symtab_index = dest_index;
2403 other_global_ptr.symtab_index = src_index;
2492 }2404 }
24932405
2494 // relocate the PLT entry to point to the .GOT.PLT entry2406 // We also need to get rid of the dynsym entry if there is one. To keep dynsym compact,
2495 const got_plt_abs = got_plt_section.vaddr(elf) + got_plt_offset;2407 // we'll move another symbol into its place just like we did above.
2496 // TODO: handle overflow gracefully2408 if (global_ptr.dynsym_index != 0) {
2497 link.loongarch.writeJ20(plt_slice[0..4], link.loongarch.toPcalaHi20(got_plt_abs, plt_addr));2409 const dynsym_shdr = @field(elf.shdrPtr(elf.shndx.dynsym), @tagName(class));
2498 link.loongarch.writeK12(plt_slice[4..8], @truncate(got_plt_abs));2410
2499 },2411 const ent_size = @sizeOf(class.ElfN().Sym);
2500 .SPARCV9 => {2412 assert(elf.targetLoad(&dynsym_shdr.entsize) == ent_size);
2501 // add a .PLT entry, writing the template2413
2502 const plt_ni = elf.shndx.plt.get(elf).ni;2414 // We're going to decrease the size of `.dynsym`, thereby removing its last index.
2503 switch (elf.shdrPtr(elf.shndx.plt)) {2415 const old_size = elf.targetLoad(&dynsym_shdr.size);
2504 inline else => |shdr| {2416 const new_size = old_size - ent_size;
2505 assert(elf.targetLoad(&shdr.size) == got_plt_offset);2417 const remove_dynsym_index: u32 = @intCast(@divExact(new_size, ent_size));
2506 elf.targetStore(&shdr.size, got_plt_offset + 32);2418
2507 const plt_slice: []u32 = @ptrCast(@alignCast(plt_ni.slice(&elf.mf)[got_plt_offset..][0..32]));2419 const free_dynsym_index = global_ptr.dynsym_index;
2508 // sethi (. - .plt[0]), %g12420 global_ptr.dynsym_index = 0;
2509 // ba,a %xcc, .plt[1]2421
2510 // nop2422 if (free_dynsym_index != remove_dynsym_index) {
2511 // nop2423 // The demoted global wasn't the last entry, so move whatever entry we just
2512 // nop2424 // truncated out of dynsym into its place.
2513 // nop2425
2514 // nop2426 const src_dynsym_ptr = @field(elf.dynsymPtr(remove_dynsym_index), @tagName(class));
2515 // nop2427 const dest_dynsym_ptr = @field(elf.dynsymPtr(free_dynsym_index), @tagName(class));
2516 @memcpy(plt_slice, &([2]u32{2428
2517 // TODO: handle overflow gracefully2429 const moved_name_dynstr: String(.dynstr) = @enumFromInt(elf.targetLoad(&src_dynsym_ptr.name));
2518 @bitCast(link.sparc.reloc.Imm22{2430 const moved_name = elf.stringExisting(.strtab, moved_name_dynstr.slice(elf));
2519 .imm22 = @truncate(got_plt_offset),2431 const moved_global_ptr = elf.globalByName(moved_name).?;
2520 .b22_31 = 0b0000000011,2432
2521 }),2433 dest_dynsym_ptr.* = src_dynsym_ptr.*;
2522 @bitCast(link.sparc.reloc.Disp19{2434
2523 .disp19 = @truncate((got_plt_offset + 4 - 32) >> 2),2435 assert(moved_global_ptr.dynsym_index == remove_dynsym_index);
2524 .b19_31 = 0b1100001101000,2436 moved_global_ptr.dynsym_index = free_dynsym_index;
2525 }),2437
2526 } ++ @as([6]u32, @splat(0x01000000))));2438 // Since that symbol's dynsym index has changed, we'll have to update any
2527 if (elf.targetEndian() != native_endian) std.mem.byteSwapAllElements(u32, plt_slice);2439 // relocation entries targeting it.
2528 },2440 elf.changed_symtab_index.putAssumeCapacity(moved_name, {});
2441 }
2442
2443 // Now that we've given that symbol a new home, actually decrease the section size.
2444 elf.targetStore(&dynsym_shdr.size, new_size);
2529 }2445 }
2530 },2446 },
2531 }2447 }
...@@ -2660,7 +2576,7 @@ const Symbol = struct {...@@ -2660,7 +2576,7 @@ const Symbol = struct {
2660 }2576 }
26612577
2662 // Re-apply relocations targeting this symbol2578 // Re-apply relocations targeting this symbol
2663 if (elf.ehdrField(.type) != .REL) {2579 if (elf.ehdrType() != .REL) {
2664 sym_id.applyTargetRelocs(elf);2580 sym_id.applyTargetRelocs(elf);
2665 }2581 }
26662582
...@@ -2678,7 +2594,7 @@ const Symbol = struct {...@@ -2678,7 +2594,7 @@ const Symbol = struct {
2678 }2594 }
26792595
2680 fn applyTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void {2596 fn applyTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void {
2681 assert(elf.ehdrField(.type) != .REL);2597 assert(elf.ehdrType() != .REL);
2682 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;2598 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;
2683 while (ri != .none) {2599 while (ri != .none) {
2684 const reloc = ri.get(elf);2600 const reloc = ri.get(elf);
...@@ -2693,7 +2609,7 @@ const Symbol = struct {...@@ -2693,7 +2609,7 @@ const Symbol = struct {
2693 ///2609 ///
2694 /// Asserts we are creating a DSO.2610 /// Asserts we are creating a DSO.
2695 fn deleteDynamicTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void {2611 fn deleteDynamicTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void {
2696 assert(elf.ehdrField(.type) != .REL);2612 assert(elf.ehdrType() != .REL);
2697 assert(elf.shndx.dynamic != .UNDEF);2613 assert(elf.shndx.dynamic != .UNDEF);
2698 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;2614 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;
2699 while (ri != .none) {2615 while (ri != .none) {
...@@ -2713,7 +2629,20 @@ const Symbol = struct {...@@ -2713,7 +2629,20 @@ const Symbol = struct {
2713 const reloc = ri.get(elf);2629 const reloc = ri.get(elf);
2714 ri = reloc.next;2630 ri = reloc.next;
2715 assert(reloc.target == sym_id);2631 assert(reloc.target == sym_id);
2716 if (!reloc.type.isAbsAddr(elf)) continue;2632 switch (reloc.type.target) {
2633 // Only relocations which resolve to absolute addresses require runtime
2634 // `R_*_RELATIVE` relocations.
2635 .special,
2636 .pltrel,
2637 .rel,
2638 .dtpoff,
2639 .tpoff,
2640 .size,
2641 => continue,
2642
2643 .abs, .pltabs => {},
2644 }
2645 if (!reloc.type.action.simple.dest.isAddr(elf)) continue;
2717 switch (elf.nodeWantsDsoRelocation(reloc.node)) {2646 switch (elf.nodeWantsDsoRelocation(reloc.node)) {
2718 .no => continue,2647 .no => continue,
2719 .yes_textrel => elf.textrel_count += 1,2648 .yes_textrel => elf.textrel_count += 1,
...@@ -2781,8 +2710,7 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {...@@ -2781,8 +2710,7 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {
2781} {2710} {
2782 const comp = elf.base.comp;2711 const comp = elf.base.comp;
27832712
2784 const runtime_load_addr = switch (elf.ehdrField(.type)) {2713 const runtime_load_addr = switch (elf.ehdrType()) {
2785 .NONE, .CORE, _ => unreachable,
2786 .REL => unreachable,2714 .REL => unreachable,
2787 .DYN => true,2715 .DYN => true,
2788 .EXEC => false,2716 .EXEC => false,
...@@ -2933,10 +2861,10 @@ fn externSymbolInner(elf: *Elf, opts: ExternSymbolOpts) Error!Symbol.Id {...@@ -2933,10 +2861,10 @@ fn externSymbolInner(elf: *Elf, opts: ExternSymbolOpts) Error!Symbol.Id {
2933 .size = 0,2861 .size = 0,
2934 .type = opts.type,2862 .type = opts.type,
2935 .bind = switch (opts.linkage) {2863 .bind = switch (opts.linkage) {
2936 .internal => @panic("TODO internal extern symbol"),
2937 .strong => .strong,2864 .strong => .strong,
2938 .weak => .weak,2865 .weak => .weak,
2939 .link_once => return elf.base.comp.link_diags.fail("TODO(Elf2): link_once is not supported", .{}),2866 .internal => return elf.base.comp.link_diags.fail("TODO(Elf2): '.internal' linkage", .{}),
2867 .link_once => return elf.base.comp.link_diags.fail("TODO(Elf2): '.link_once' linkage", .{}),
2940 },2868 },
2941 .visibility = switch (opts.visibility) {2869 .visibility = switch (opts.visibility) {
2942 .default => .DEFAULT,2870 .default => .DEFAULT,
...@@ -2965,6 +2893,9 @@ pub fn addReloc(...@@ -2965,6 +2893,9 @@ pub fn addReloc(
2965 };2893 };
2966 elf.addRelocAssumeCapacity(node, offset, .fromTypeErased(target), addend, @"type") catch |err| switch (err) {2894 elf.addRelocAssumeCapacity(node, offset, .fromTypeErased(target), addend, @"type") catch |err| switch (err) {
2967 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),2895 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
2896 error.UnknownRelocation => unreachable, // codegen bug
2897 error.NonStaticRelocation => unreachable, // codegen bug
2898 error.UnimplementedRelocation => unreachable, // codegen bug (asking Elf2 for a relocation it does not support)
2968 else => |e| return e,2899 else => |e| return e,
2969 };2900 };
2970}2901}
...@@ -3149,23 +3080,6 @@ const StringTable = struct {...@@ -3149,23 +3080,6 @@ const StringTable = struct {
3149 }3080 }
3150};3081};
31513082
3152const GotIndex = enum(u32) {
3153 none = std.math.maxInt(u32),
3154 _,
3155
3156 pub fn wrap(i: ?u32) GotIndex {
3157 const gi: GotIndex = @enumFromInt(i orelse return .none);
3158 assert(gi != .none);
3159 return gi;
3160 }
3161 pub fn unwrap(gi: GotIndex) ?u32 {
3162 return switch (gi) {
3163 _ => @intFromEnum(gi),
3164 .none => null,
3165 };
3166 }
3167};
3168
3169pub fn open(3083pub fn open(
3170 arena: std.mem.Allocator,3084 arena: std.mem.Allocator,
3171 comp: *Compilation,3085 comp: *Compilation,
...@@ -3212,7 +3126,7 @@ fn create(...@@ -3212,7 +3126,7 @@ fn create(
3212 .amdpal => .AMDGPU_PAL,3126 .amdpal => .AMDGPU_PAL,
3213 .mesa3d => .AMDGPU_MESA3D,3127 .mesa3d => .AMDGPU_MESA3D,
3214 };3128 };
3215 const @"type": std.elf.ET = switch (comp.config.output_mode) {3129 const @"type": EhdrType = switch (comp.config.output_mode) {
3216 .Exe => if (comp.config.pie or target.os.tag == .haiku) .DYN else .EXEC,3130 .Exe => if (comp.config.pie or target.os.tag == .haiku) .DYN else .EXEC,
3217 .Lib => switch (comp.config.link_mode) {3131 .Lib => switch (comp.config.link_mode) {
3218 .static => .REL,3132 .static => .REL,
...@@ -3220,7 +3134,9 @@ fn create(...@@ -3220,7 +3134,9 @@ fn create(
3220 },3134 },
3221 .Obj => .REL,3135 .Obj => .REL,
3222 };3136 };
3223 const machine = target.toElfMachine();3137 const machine = EhdrMachine.fromElf(target.toElfMachine()) orelse {
3138 std.debug.panic("TODO(Elf2): add support for target machine '{t}'", .{target.toElfMachine()});
3139 };
3224 const maybe_interp = switch (comp.config.link_mode) {3140 const maybe_interp = switch (comp.config.link_mode) {
3225 .static => null,3141 .static => null,
3226 .dynamic => switch (comp.config.output_mode) {3142 .dynamic => switch (comp.config.output_mode) {
...@@ -3276,6 +3192,12 @@ fn create(...@@ -3276,6 +3192,12 @@ fn create(
3276 .fini_array = .UNDEF,3192 .fini_array = .UNDEF,
3277 .preinit_array = .UNDEF,3193 .preinit_array = .UNDEF,
3278 },3194 },
3195 .dynamic = .{
3196 .flags = 0,
3197 .flags_1 = 0,
3198 .rpath = .empty,
3199 .soname = .empty,
3200 },
3279 .symtab = .empty,3201 .symtab = .empty,
3280 .globals = .{3202 .globals = .{
3281 .strong_def = .empty,3203 .strong_def = .empty,
...@@ -3310,10 +3232,12 @@ fn create(...@@ -3310,10 +3232,12 @@ fn create(
3310 .tls_size_symbol_relocs = .empty,3232 .tls_size_symbol_relocs = .empty,
3311 .section_by_name = .empty,3233 .section_by_name = .empty,
3312 .changed_symtab_index = .empty,3234 .changed_symtab_index = .empty,
3235 .textrel_count = 0,
3236 .overflowed_reloc_count = 0,
3237 .misaligned_reloc_count = 0,
3313 .const_prog_node = .none,3238 .const_prog_node = .none,
3314 .synth_prog_node = .none,3239 .synth_prog_node = .none,
3315 .input_prog_node = .none,3240 .input_prog_node = .none,
3316 .textrel_count = 0,
3317 };3241 };
3318 errdefer elf.deinit();3242 errdefer elf.deinit();
33193243
...@@ -3362,14 +3286,14 @@ fn initHeaders(...@@ -3362,14 +3286,14 @@ fn initHeaders(
3362 class: std.elf.CLASS,3286 class: std.elf.CLASS,
3363 data: std.elf.DATA,3287 data: std.elf.DATA,
3364 osabi: std.elf.OSABI,3288 osabi: std.elf.OSABI,
3365 @"type": std.elf.ET,3289 @"type": EhdrType,
3366 machine: std.elf.EM,3290 machine: EhdrMachine,
3367 maybe_interp: ?[]const u8,3291 maybe_interp: ?[]const u8,
3368) !void {3292) Error!void {
3369 const comp = elf.base.comp;3293 const comp = elf.base.comp;
3370 const gpa = comp.gpa;3294 const gpa = comp.gpa;
3295
3371 const have_dynamic_section = switch (@"type") {3296 const have_dynamic_section = switch (@"type") {
3372 .NONE, .CORE, _ => unreachable,
3373 .REL => false,3297 .REL => false,
3374 .EXEC => comp.config.link_mode == .dynamic,3298 .EXEC => comp.config.link_mode == .dynamic,
3375 .DYN => true,3299 .DYN => true,
...@@ -3380,13 +3304,7 @@ fn initHeaders(...@@ -3380,13 +3304,7 @@ fn initHeaders(
3380 .@"64" => .@"8",3304 .@"64" => .@"8",
3381 };3305 };
33823306
3383 const init_plt_size: std.elf.Xword, const plt_align: std.mem.Alignment, const got_plt, const plt_sec =3307 const plt: PltInfo = .fromMachine(machine);
3384 switch (machine) {
3385 else => @panic(@tagName(machine)),
3386 .LOONGARCH => .{ 16 * 2, .@"4", true, false },
3387 .SPARCV9 => .{ 32 * 4, .fromByteUnits(256), false, false },
3388 .X86_64 => .{ 16, .@"16", true, true },
3389 };
33903308
3391 const shnum: u32 = shnum: {3309 const shnum: u32 = shnum: {
3392 var shnum: u32 = 1; // reserved ("null") shdr3310 var shnum: u32 = 1; // reserved ("null") shdr
...@@ -3408,9 +3326,9 @@ fn initHeaders(...@@ -3408,9 +3326,9 @@ fn initHeaders(
3408 }3326 }
3409 if (@"type" != .REL) {3327 if (@"type" != .REL) {
3410 shnum += 1; // .got3328 shnum += 1; // .got
3411 shnum += @intFromBool(got_plt); // .got.plt3329 shnum += @intFromBool(plt.got_plt != null); // .got.plt
3412 shnum += 1; // .plt3330 shnum += 1; // .plt
3413 shnum += @intFromBool(plt_sec); // .plt_sec3331 shnum += @intFromBool(plt.plt_sec != null); // .plt_sec
3414 }3332 }
3415 break :shnum shnum;3333 break :shnum shnum;
3416 };3334 };
...@@ -3427,7 +3345,6 @@ fn initHeaders(...@@ -3427,7 +3345,6 @@ fn initHeaders(
3427 gnu_stack: u32,3345 gnu_stack: u32,
3428 }, const phnum: u32 = ph: {3346 }, const phnum: u32 = ph: {
3429 switch (@"type") {3347 switch (@"type") {
3430 .NONE, .CORE, _ => unreachable,
3431 .REL => break :ph .{ undefined, 0 },3348 .REL => break :ph .{ undefined, 0 },
3432 .EXEC, .DYN => {},3349 .EXEC, .DYN => {},
3433 }3350 }
...@@ -3483,9 +3400,9 @@ fn initHeaders(...@@ -3483,9 +3400,9 @@ fn initHeaders(
3483 try elf.symtab.ensureTotalCapacity(gpa, 1);3400 try elf.symtab.ensureTotalCapacity(gpa, 1);
3484 elf.nodes.appendAssumeCapacity(.file);3401 elf.nodes.appendAssumeCapacity(.file);
34853402
3486 switch (class) {3403 const entsize: struct { ph: u32, sh: u32 } = switch (class) {
3487 .NONE, _ => unreachable,3404 .NONE, _ => unreachable,
3488 inline else => |ct_class| {3405 inline else => |ct_class| entsize: {
3489 const ElfN = ct_class.ElfN();3406 const ElfN = ct_class.ElfN();
3490 assert(elf.ni.ehdr == try elf.mf.addOnlyChildNode(gpa, elf.ni.file, .{3407 assert(elf.ni.ehdr == try elf.mf.addOnlyChildNode(gpa, elf.ni.file, .{
3491 .size = @sizeOf(ElfN.Ehdr),3408 .size = @sizeOf(ElfN.Ehdr),
...@@ -3502,42 +3419,35 @@ fn initHeaders(...@@ -3502,42 +3419,35 @@ fn initHeaders(
3502 .osabi = osabi,3419 .osabi = osabi,
3503 .abiversion = 0,3420 .abiversion = 0,
3504 };3421 };
3505 ehdr.type = @"type";3422 ehdr.type = @"type".toElf();
3506 ehdr.machine = machine;3423 ehdr.machine = machine.toElf();
3507 ehdr.version = 1;3424 ehdr.version = 1;
3508 ehdr.entry = 0;3425 ehdr.entry = 0;
3509 ehdr.phoff = 0;3426 ehdr.phoff = 0;
3510 ehdr.shoff = 0;3427 ehdr.shoff = 0;
3511 ehdr.flags = switch (machine) {3428 ehdr.flags = switch (machine) {
3512 .LOONGARCH => e_flags: {3429 .LOONGARCH => .{ .loongarch = .{
3513 const target_cpu = &elf.base.comp.getTarget().cpu;3430 .base_abi_modifier = mod: {
3514 const e_flags: std.elf.loongarch.EFlags = .{3431 const cpu = comp.getTarget().cpu;
3515 .base_abi_modifier = if (target_cpu.has(.loongarch, .d))3432 if (cpu.has(.loongarch, .d)) break :mod .d;
3516 .d3433 if (cpu.has(.loongarch, .f)) break :mod .f;
3517 else if (target_cpu.has(.loongarch, .f))3434 break :mod .s;
3518 .f3435 },
3519 else3436 .abi_extension = .base,
3520 .s,3437 .abi_version = 1,
3521 .abi_extension = .base,3438 } },
3522 .abi_version = 1,3439 .SPARCV9 => .{ .sparc = .{
3523 };3440 .mm = .rmo,
3524 break :e_flags @bitCast(e_flags);3441 .ext = .{
3525 },3442 .@"32plus" = false,
3526 .SPARCV9 => e_flags: {3443 .sun_us1 = false,
3527 const e_flags: std.elf.sparc.EFlags = .{3444 .hal_r1 = false,
3528 .mm = .rmo,3445 .sun_us3 = false,
3529 .ext = .{3446 .le_data = false,
3530 .@"32plus" = false,3447 },
3531 .sun_us1 = false,3448 } },
3532 .hal_r1 = false,3449 .X86_64 => .{ .int = 0 },
3533 .sun_us3 = false,3450 .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)),
3534 .le_data = false,
3535 },
3536 };
3537 break :e_flags @bitCast(e_flags);
3538 },
3539 .X86_64 => 0,
3540 else => @panic(@tagName(machine)),
3541 };3451 };
3542 ehdr.ehsize = @sizeOf(ElfN.Ehdr);3452 ehdr.ehsize = @sizeOf(ElfN.Ehdr);
3543 ehdr.phentsize = @sizeOf(ElfN.Phdr);3453 ehdr.phentsize = @sizeOf(ElfN.Phdr);
...@@ -3546,11 +3456,13 @@ fn initHeaders(...@@ -3546,11 +3456,13 @@ fn initHeaders(
3546 ehdr.shnum = 1; // Only the null shdr initially---will be incremented by `addSection`3456 ehdr.shnum = 1; // Only the null shdr initially---will be incremented by `addSection`
3547 ehdr.shstrndx = std.elf.SHN_UNDEF;3457 ehdr.shstrndx = std.elf.SHN_UNDEF;
3548 if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr);3458 if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr);
3459
3460 break :entsize .{ .ph = @sizeOf(ElfN.Phdr), .sh = @sizeOf(ElfN.Shdr) };
3549 },3461 },
3550 }3462 };
35513463
3552 assert(elf.ni.shdr == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{3464 assert(elf.ni.shdr == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{
3553 .size = @as(u64, elf.ehdrField(.shentsize)) * @as(u64, elf.ehdrField(.shnum)),3465 .size = 1 * entsize.sh, // as above, only the null shdr initially
3554 .alignment = elf.mf.flags.block_size,3466 .alignment = elf.mf.flags.block_size,
3555 .moved = true,3467 .moved = true,
3556 .resized = true,3468 .resized = true,
...@@ -3558,28 +3470,24 @@ fn initHeaders(...@@ -3558,28 +3470,24 @@ fn initHeaders(
3558 elf.nodes.appendAssumeCapacity(.shdr);3470 elf.nodes.appendAssumeCapacity(.shdr);
35593471
3560 const page_align: std.mem.Alignment = .fromByteUnits(switch (machine) {3472 const page_align: std.mem.Alignment = .fromByteUnits(switch (machine) {
3561 .BPF,3473 .AARCH64 => 0x10000,
3562 .SPARCV9,3474 .LOONGARCH => 0x4000,
3563 => 0x100000,3475 .PPC64 => 0x10000,
3564 .AARCH64,3476 .RISCV => 0x1000,
3565 .AMDGPU,3477 .SPARCV9 => 0x100000,
3566 .QDSP6,3478 .X86_64 => 0x1000,
3567 .MIPS,3479
3568 .PPC,3480 //.@"68K" => 0x2000,
3569 .PPC64,3481 //.AMDGPU => 0x10000,
3570 .SPARC,3482 //.ARC_COMPACT2 => 0x2000,
3571 .SPARC32PLUS,3483 //.AVR => 0x1,
3572 => 0x10000,3484 //.BPF => 0x100000,
3573 .LOONGARCH,3485 //.MIPS => 0x10000,
3574 => 0x4000,3486 //.MSP430 => 0x4,
3575 .ARC_COMPACT2,3487 //.PPC => 0x10000,
3576 .@"68K",3488 //.QDSP6 => 0x10000,
3577 => 0x2000,3489 //.SPARC => 0x10000,
3578 .MSP430,3490 //.SPARC32PLUS => 0x10000,
3579 => 0x4,
3580 .AVR,
3581 => 0x1,
3582 else => 0x1000,
3583 });3491 });
35843492
3585 var ph_vaddr: u32 = if (@"type" != .REL) ph_vaddr: {3493 var ph_vaddr: u32 = if (@"type" != .REL) ph_vaddr: {
...@@ -3592,7 +3500,7 @@ fn initHeaders(...@@ -3592,7 +3500,7 @@ fn initHeaders(
3592 elf.phdrs.items[phndx.rodata] = elf.ni.rodata;3500 elf.phdrs.items[phndx.rodata] = elf.ni.rodata;
35933501
3594 assert(elf.ni.phdr == try elf.mf.addOnlyChildNode(gpa, elf.ni.rodata, .{3502 assert(elf.ni.phdr == try elf.mf.addOnlyChildNode(gpa, elf.ni.rodata, .{
3595 .size = elf.ehdrField(.phentsize) * elf.ehdrField(.phnum),3503 .size = @as(u64, phnum) * entsize.ph,
3596 .alignment = addr_align,3504 .alignment = addr_align,
3597 .moved = true,3505 .moved = true,
3598 .resized = true,3506 .resized = true,
...@@ -3627,16 +3535,16 @@ fn initHeaders(...@@ -3627,16 +3535,16 @@ fn initHeaders(
36273535
3628 elf.phdrs.items[phndx.gnu_stack] = .none;3536 elf.phdrs.items[phndx.gnu_stack] = .none;
36293537
3630 break :ph_vaddr switch (elf.ehdrField(.type)) {3538 break :ph_vaddr switch (elf.ehdrType()) {
3631 .NONE, .CORE, _ => unreachable,
3632 .REL, .DYN => 0,3539 .REL, .DYN => 0,
3633 .EXEC => switch (machine) {3540 .EXEC => switch (machine) {
3634 .@"386" => 0x400000,3541 .AARCH64,
3635 .AARCH64, .X86_64 => 0x200000,3542 => 0x200000,
3636 .PPC, .PPC64 => 0x10000000,3543 .LOONGARCH => 0x10000,
3637 .S390 => 0x1000000,3544 .PPC64 => 0x10000000,
3545 .RISCV => 0x10000,
3638 .SPARCV9 => 0x100000,3546 .SPARCV9 => 0x100000,
3639 else => 0x10000,3547 .X86_64 => 0x200000,
3640 },3548 },
3641 };3549 };
3642 } else undefined;3550 } else undefined;
...@@ -3870,28 +3778,21 @@ fn initHeaders(...@@ -3870,28 +3778,21 @@ fn initHeaders(
3870 .type = .PROGBITS,3778 .type = .PROGBITS,
3871 // Reserve space for the reserved words, populated later.3779 // Reserve space for the reserved words, populated later.
3872 .size = switch (machine) {3780 .size = switch (machine) {
3873 else => @panic(@tagName(machine)),3781 .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)),
3874 .X86_64 => 3 * elf.targetPtrSize(),3782 .X86_64 => 3 * elf.targetPtrSize(),
3875 .LOONGARCH,3783 .LOONGARCH, .SPARCV9 => elf.targetPtrSize(),
3876 .SPARCV9,
3877 => elf.targetPtrSize(),
3878 },3784 },
3879 .flags = .{ .WRITE = true, .ALLOC = true },3785 .flags = .{ .WRITE = true, .ALLOC = true },
3880 .addralign = addr_align,3786 .addralign = addr_align,
3881 .entsize = @intCast(addr_align.toByteUnits()),3787 .entsize = @intCast(addr_align.toByteUnits()),
3882 });3788 });
3883 if (got_plt) elf.shndx.got_plt = try elf.addSection(3789 if (plt.got_plt) |got_plt| elf.shndx.got_plt = try elf.addSection(
3884 if (elf.options.z_now) elf.ni.data_rel_ro else elf.ni.data,3790 if (elf.options.z_now) elf.ni.data_rel_ro else elf.ni.data,
3885 .{3791 .{
3886 .name = ".got.plt",3792 .name = ".got.plt",
3887 .type = .PROGBITS,3793 .type = .PROGBITS,
3888 .flags = .{ .WRITE = true, .ALLOC = true },3794 .flags = .{ .WRITE = true, .ALLOC = true },
3889 .size = switch (machine) {3795 .size = got_plt.header_entries * elf.targetPtrSize(),
3890 else => @panic(@tagName(machine)),
3891 .@"386" => 3 * 4,
3892 .X86_64 => 3 * 8,
3893 .LOONGARCH => 2 * elf.targetPtrSize(),
3894 },
3895 .addralign = addr_align,3796 .addralign = addr_align,
3896 .entsize = @intCast(addr_align.toByteUnits()),3797 .entsize = @intCast(addr_align.toByteUnits()),
3897 },3798 },
...@@ -3902,19 +3803,16 @@ fn initHeaders(...@@ -3902,19 +3803,16 @@ fn initHeaders(
3902 .flags = .{3803 .flags = .{
3903 .ALLOC = true,3804 .ALLOC = true,
3904 .EXECINSTR = true,3805 .EXECINSTR = true,
3905 .WRITE = switch (machine) {3806 .WRITE = plt.got_plt == null,
3906 .SPARCV9 => true,
3907 else => false,
3908 },
3909 },3807 },
3910 .size = init_plt_size,3808 .size = plt.entry_size * plt.header_entries,
3911 .addralign = plt_align,3809 .addralign = plt.@"align",
3912 .node_align = elf.mf.flags.block_size,3810 .node_align = elf.mf.flags.block_size,
3913 });3811 });
3914 if (plt_sec) elf.shndx.plt_sec = try elf.addSection(elf.ni.text, .{3812 if (plt.plt_sec != null) elf.shndx.plt_sec = try elf.addSection(elf.ni.text, .{
3915 .name = ".plt.sec",3813 .name = ".plt.sec",
3916 .flags = .{ .ALLOC = true, .EXECINSTR = true },3814 .flags = .{ .ALLOC = true, .EXECINSTR = true },
3917 .addralign = plt_align,3815 .addralign = plt.@"align",
3918 .node_align = elf.mf.flags.block_size,3816 .node_align = elf.mf.flags.block_size,
3919 });3817 });
3920 if (maybe_interp) |interp| {3818 if (maybe_interp) |interp| {
...@@ -4005,7 +3903,7 @@ fn initHeaders(...@@ -4005,7 +3903,7 @@ fn initHeaders(
4005 .type = .RELA,3903 .type = .RELA,
4006 .flags = .{ .ALLOC = true, .INFO_LINK = true },3904 .flags = .{ .ALLOC = true, .INFO_LINK = true },
4007 .link = elf.shndx.dynsym.toSection().?,3905 .link = elf.shndx.dynsym.toSection().?,
4008 .info = (if (got_plt) elf.shndx.got_plt else elf.shndx.plt).toSection().?,3906 .info = (if (plt.got_plt != null) elf.shndx.got_plt else elf.shndx.plt).toSection().?,
4009 .addralign = addr_align,3907 .addralign = addr_align,
4010 .entsize = rela_size,3908 .entsize = rela_size,
4011 .node_align = elf.mf.flags.block_size,3909 .node_align = elf.mf.flags.block_size,
...@@ -4019,7 +3917,7 @@ fn initHeaders(...@@ -4019,7 +3917,7 @@ fn initHeaders(
4019 .node_align = addr_align,3917 .node_align = addr_align,
4020 });3918 });
4021 switch (machine) {3919 switch (machine) {
4022 else => @panic(@tagName(machine)),3920 .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)),
4023 .X86_64 => {3921 .X86_64 => {
4024 const plt_ni = elf.shndx.plt.get(elf).ni;3922 const plt_ni = elf.shndx.plt.get(elf).ni;
4025 const got_plt_sym: Symbol.Id = .local(elf.shndx.got_plt.get(elf).lsi);3923 const got_plt_sym: Symbol.Id = .local(elf.shndx.got_plt.get(elf).lsi);
...@@ -4035,14 +3933,14 @@ fn initHeaders(...@@ -4035,14 +3933,14 @@ fn initHeaders(
4035 2,3933 2,
4036 got_plt_sym,3934 got_plt_sym,
4037 8 * 1 - 4,3935 8 * 1 - 4,
4038 .rel32,3936 .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" }),
4039 );3937 );
4040 try elf.addSymbolRelocAssumeCapacity(3938 try elf.addSymbolRelocAssumeCapacity(
4041 plt_ni,3939 plt_ni,
4042 8,3940 8,
4043 got_plt_sym,3941 got_plt_sym,
4044 8 * 2 - 4,3942 8 * 2 - 4,
4045 .rel32,3943 .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" }),
4046 );3944 );
4047 },3945 },
4048 .LOONGARCH => {3946 .LOONGARCH => {
...@@ -4073,9 +3971,24 @@ fn initHeaders(...@@ -4073,9 +3971,24 @@ fn initHeaders(
4073 });3971 });
4074 elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);3972 elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);
4075 try elf.ensureUnusedRelocCapacity(plt_ni, 3);3973 try elf.ensureUnusedRelocCapacity(plt_ni, 3);
4076 try elf.addSymbolRelocAssumeCapacity(plt_ni, 0, got_plt_sym, 0, .larch_rel32_hi20);3974 elf.addRelocAssumeCapacity(plt_ni, 0, got_plt_sym, 0, .{ .LARCH = .PCALA_HI20 }) catch |err| switch (err) {
4077 try elf.addSymbolRelocAssumeCapacity(plt_ni, 8, got_plt_sym, 0, .larch_abs32_lo12);3975 error.UnknownRelocation => unreachable,
4078 try elf.addSymbolRelocAssumeCapacity(plt_ni, 16, got_plt_sym, 0, .larch_abs32_lo12);3976 error.NonStaticRelocation => unreachable,
3977 error.UnimplementedRelocation => unreachable,
3978 else => |e| return e,
3979 };
3980 elf.addRelocAssumeCapacity(plt_ni, 8, got_plt_sym, 0, .{ .LARCH = .PCALA_LO12 }) catch |err| switch (err) {
3981 error.UnknownRelocation => unreachable,
3982 error.NonStaticRelocation => unreachable,
3983 error.UnimplementedRelocation => unreachable,
3984 else => |e| return e,
3985 };
3986 elf.addRelocAssumeCapacity(plt_ni, 16, got_plt_sym, 0, .{ .LARCH = .PCALA_LO12 }) catch |err| switch (err) {
3987 error.UnknownRelocation => unreachable,
3988 error.NonStaticRelocation => unreachable,
3989 error.UnimplementedRelocation => unreachable,
3990 else => |e| return e,
3991 };
4079 },3992 },
4080 .SPARCV9 => {},3993 .SPARCV9 => {},
4081 }3994 }
...@@ -4092,7 +4005,7 @@ fn initHeaders(...@@ -4092,7 +4005,7 @@ fn initHeaders(
40924005
4093 // Populate reserved GOT words.4006 // Populate reserved GOT words.
4094 switch (machine) {4007 switch (machine) {
4095 else => @panic(@tagName(machine)),4008 .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)),
4096 .X86_64 => {4009 .X86_64 => {
4097 try elf.got.ensureUnusedCapacity(gpa, 3);4010 try elf.got.ensureUnusedCapacity(gpa, 3);
4098 elf.got.putAssumeCapacityNoClobber(switch (have_dynamic_section) {4011 elf.got.putAssumeCapacityNoClobber(switch (have_dynamic_section) {
...@@ -4102,9 +4015,7 @@ fn initHeaders(...@@ -4102,9 +4015,7 @@ fn initHeaders(
4102 elf.got.putAssumeCapacityNoClobber(.{ .reserved = 1 }, .none);4015 elf.got.putAssumeCapacityNoClobber(.{ .reserved = 1 }, .none);
4103 elf.got.putAssumeCapacityNoClobber(.{ .reserved = 2 }, .none);4016 elf.got.putAssumeCapacityNoClobber(.{ .reserved = 2 }, .none);
4104 },4017 },
4105 .LOONGARCH,4018 .LOONGARCH, .SPARCV9 => {
4106 .SPARCV9,
4107 => {
4108 try elf.got.ensureUnusedCapacity(gpa, 1);4019 try elf.got.ensureUnusedCapacity(gpa, 1);
4109 elf.got.putAssumeCapacityNoClobber(switch (have_dynamic_section) {4020 elf.got.putAssumeCapacityNoClobber(switch (have_dynamic_section) {
4110 true => .{ .symbol = .local(elf.shndx.dynamic.get(elf).lsi) },4021 true => .{ .symbol = .local(elf.shndx.dynamic.get(elf).lsi) },
...@@ -4153,8 +4064,17 @@ fn initHeaders(...@@ -4153,8 +4064,17 @@ fn initHeaders(
4153 .node = elf.shndx.got.get(elf).ni,4064 .node = elf.shndx.got.get(elf).ni,
4154 .name = try .string(elf, "_GLOBAL_OFFSET_TABLE_"),4065 .name = try .string(elf, "_GLOBAL_OFFSET_TABLE_"),
4155 .value = switch (machine) {4066 .value = switch (machine) {
4156 .QDSP6, .@"386", .X86_64 => elf.shndx.got_plt.vaddr(elf),4067 .AARCH64,
4157 else => elf.shndx.got.vaddr(elf),4068 .LOONGARCH,
4069 .PPC64,
4070 .RISCV,
4071 .SPARCV9,
4072 => elf.shndx.got.vaddr(elf),
4073
4074 //.QDSP6,
4075 //.@"386",
4076 .X86_64,
4077 => elf.shndx.got_plt.vaddr(elf),
4158 },4078 },
4159 .size = 0,4079 .size = 0,
4160 .type = .NOTYPE,4080 .type = .NOTYPE,
...@@ -4267,6 +4187,29 @@ fn initHeaders(...@@ -4267,6 +4187,29 @@ fn initHeaders(
4267 const shndx: Section.Index = @enumFromInt(shndx_raw);4187 const shndx: Section.Index = @enumFromInt(shndx_raw);
4268 elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {});4188 elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {});
4269 }4189 }
4190
4191 if (have_dynamic_section) elf.dynamic = .{
4192 .flags = if (elf.options.z_now) std.elf.DF_BIND_NOW else 0,
4193 .flags_1 = f: {
4194 var f: u32 = 0;
4195 if (elf.options.z_now) f |= std.elf.DF_1_NOW;
4196 if (comp.config.output_mode == .Exe and comp.config.pie) f |= std.elf.DF_1_PIE;
4197 break :f f;
4198 },
4199 .rpath = str: {
4200 var buf: std.ArrayList(u8) = .empty;
4201 defer buf.deinit(gpa);
4202 for (elf.options.rpath_list, 0..) |path, i| {
4203 if (i > 0) try buf.append(gpa, ':');
4204 try buf.appendSlice(gpa, path);
4205 }
4206 break :str try elf.string(.dynstr, buf.items);
4207 },
4208 .soname = str: {
4209 const slice = elf.options.soname orelse break :str .empty;
4210 break :str try elf.string(.dynstr, slice);
4211 },
4212 };
4270}4213}
42714214
4272pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void {4215pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void {
...@@ -4379,7 +4322,7 @@ fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {...@@ -4379,7 +4322,7 @@ fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {
4379 if (ptr.* != .none) {4322 if (ptr.* != .none) {
4380 for (elf.got_relocs.items[@intFromEnum(ptr.*)..]) |*reloc| {4323 for (elf.got_relocs.items[@intFromEnum(ptr.*)..]) |*reloc| {
4381 if (reloc.node != ni) break;4324 if (reloc.node != ni) break;
4382 reloc.* = .deleted;4325 reloc.delete(elf);
4383 }4326 }
4384 }4327 }
4385 ptr.* = @enumFromInt(elf.got_relocs.items.len);4328 ptr.* = @enumFromInt(elf.got_relocs.items.len);
...@@ -4418,15 +4361,124 @@ fn flushMovedNodeRelocs(...@@ -4418,15 +4361,124 @@ fn flushMovedNodeRelocs(
4418fn identClass(elf: *const Elf) std.elf.CLASS {4361fn identClass(elf: *const Elf) std.elf.CLASS {
4419 return @enumFromInt(elf.mf.memory_map.memory[std.elf.EI.CLASS]);4362 return @enumFromInt(elf.mf.memory_map.memory[std.elf.EI.CLASS]);
4420}4363}
4421fn identData(elf: *const Elf) std.elf.DATA {4364
4422 return @enumFromInt(elf.mf.memory_map.memory[std.elf.EI.DATA]);4365/// Like `std.elf.ET`, but only includes the ELF machine architectures we support, so that we can
4366/// use exhaustive `switch` statements in the linker implementation.
4367const EhdrMachine = enum(u16) {
4368 AARCH64 = @intFromEnum(std.elf.EM.AARCH64),
4369 LOONGARCH = @intFromEnum(std.elf.EM.LOONGARCH),
4370 PPC64 = @intFromEnum(std.elf.EM.PPC64),
4371 RISCV = @intFromEnum(std.elf.EM.RISCV),
4372 SPARCV9 = @intFromEnum(std.elf.EM.SPARCV9),
4373 X86_64 = @intFromEnum(std.elf.EM.X86_64),
4374
4375 fn toElf(m: EhdrMachine) std.elf.EM {
4376 return @bitCast(m);
4377 }
4378 /// Returns `null` if `m` is not a supported ELF machine architecture.
4379 fn fromElf(m: std.elf.EM) ?EhdrMachine {
4380 return std.enums.fromInt(EhdrMachine, @intFromEnum(m));
4381 }
4382};
4383/// Like `std.elf.ET`, but only includes the types of ELF file we can produce, so that we can use
4384/// exhaustive `switch` statements in the linker implementation.
4385const EhdrType = enum(u16) {
4386 REL = @intFromEnum(std.elf.ET.REL),
4387 EXEC = @intFromEnum(std.elf.ET.EXEC),
4388 DYN = @intFromEnum(std.elf.ET.DYN),
4389 fn toElf(t: EhdrType) std.elf.ET {
4390 return @bitCast(t);
4391 }
4392};
4393fn ehdrMachine(elf: *const Elf) EhdrMachine {
4394 const ehdr_slice = elf.ni.ehdr.sliceConst(&elf.mf);
4395 switch (elf.identClass()) {
4396 .NONE, _ => unreachable,
4397 inline else => |class| {
4398 const ehdr: *const class.ElfN().Ehdr = @ptrCast(@alignCast(ehdr_slice));
4399 return @bitCast(elf.targetLoad(&ehdr.machine));
4400 },
4401 }
4402}
4403fn ehdrType(elf: *const Elf) EhdrType {
4404 const ehdr_slice = elf.ni.ehdr.sliceConst(&elf.mf);
4405 switch (elf.identClass()) {
4406 .NONE, _ => unreachable,
4407 inline else => |class| {
4408 const ehdr: *const class.ElfN().Ehdr = @ptrCast(@alignCast(ehdr_slice));
4409 return @bitCast(elf.targetLoad(&ehdr.type));
4410 },
4411 }
4423}4412}
44244413
4425fn targetPtrSize(elf: *const Elf) u32 {4414fn targetPtrSize(elf: *const Elf) u8 {
4426 return elf.identClass().size();4415 return elf.identClass().size();
4427}4416}
4428fn targetEndian(elf: *const Elf) std.lang.Endian {4417fn targetEndian(elf: *const Elf) std.lang.Endian {
4429 return elf.identData().endian();4418 const ident_data: std.elf.DATA = @enumFromInt(elf.mf.memory_map.memory[std.elf.EI.DATA]);
4419 return ident_data.endian();
4420}
4421fn targetTlsVariant(elf: *const Elf) union(enum) {
4422 /// TP points to the start of the TCB, which immediately precedes the executable's TLS block.
4423 I_original: struct { tcb_size: u8 },
4424 /// TP points at a fixed offset from the start of the executable's TLS block.
4425 I_modified: struct { tp_off: u32 },
4426 /// TP points to the TCB, which immediately *succeeds* the executable's TLS block. (In other
4427 /// words, TP points to the *end* of the executable's TLS block.)
4428 II,
4429} {
4430 return switch (elf.ehdrMachine()) {
4431 .AARCH64 => .{ .I_original = .{ .tcb_size = 2 * elf.targetPtrSize() } },
4432 .LOONGARCH => .{ .I_original = .{ .tcb_size = elf.targetPtrSize() } },
4433 .PPC64 => .{ .I_modified = .{ .tp_off = 0x7000 } },
4434 .RISCV => .{ .I_modified = .{ .tp_off = 0 } },
4435 .SPARCV9 => .II,
4436 .X86_64 => .II,
4437 };
4438}
4439const PltInfo = struct {
4440 /// If not `null`, there is a `.got.plt` section containing the target addresses, and the PLT
4441 /// itself is immutable. If `false`, JUMP_SLOT relocations write directly to the `.plt` section,
4442 /// which must therefore be mutable.
4443 got_plt: ?struct { header_entries: u8 },
4444 /// If not `null`, there is a `.plt.sec` section, and every function in the PLT has both a
4445 /// `.plt` entry and a `.plt.sec` entry. Jumps targeting the PLT should jump to the `.plt.sec`
4446 /// entry, not the `.plt` entry. The `.plt.sec` section has no header entries, and is aligned to
4447 /// the same boundary as the `.plt` section.
4448 plt_sec: ?struct { entry_size: u8 },
4449 @"align": std.mem.Alignment,
4450 entry_size: u8,
4451 header_entries: u8,
4452
4453 fn fromMachine(machine: EhdrMachine) PltInfo {
4454 return switch (machine) {
4455 .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)),
4456 .LOONGARCH => .{
4457 .got_plt = .{ .header_entries = 2 },
4458 .plt_sec = null,
4459 .@"align" = .@"4",
4460 .entry_size = 16,
4461 .header_entries = 2,
4462 },
4463 .SPARCV9 => .{
4464 .got_plt = null,
4465 .plt_sec = null,
4466 .@"align" = .fromByteUnits(256),
4467 .entry_size = 32,
4468 .header_entries = 4,
4469 },
4470 .X86_64 => .{
4471 .got_plt = .{ .header_entries = 3 },
4472 .plt_sec = .{ .entry_size = 16 },
4473 .@"align" = .@"16",
4474 .entry_size = 16,
4475 .header_entries = 1,
4476 },
4477 };
4478 }
4479};
4480fn targetPltInfo(elf: *const Elf) PltInfo {
4481 return .fromMachine(elf.ehdrMachine());
4430}4482}
4431fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child {4483fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child {
4432 const pointer_ty = @typeInfo(@TypeOf(ptr)).pointer;4484 const pointer_ty = @typeInfo(@TypeOf(ptr)).pointer;
...@@ -4435,7 +4487,7 @@ fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.chi...@@ -4435,7 +4487,7 @@ fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.chi
4435 return switch (@typeInfo(Child)) {4487 return switch (@typeInfo(Child)) {
4436 else => @compileError(@typeName(Child)),4488 else => @compileError(@typeName(Child)),
4437 .int => std.mem.toNative(Child, ptr.*, elf.targetEndian()),4489 .int => std.mem.toNative(Child, ptr.*, elf.targetEndian()),
4438 .@"enum" => |@"enum"| @enumFromInt(elf.targetLoad(@as(*align(alignment) @"enum".tag_type, @ptrCast(ptr)))),4490 .@"enum" => |@"enum"| @enumFromInt(elf.targetLoad(@as(*align(alignment) const @"enum".tag_type, @ptrCast(ptr)))),
4439 .@"struct" => |@"struct"| @bitCast(4491 .@"struct" => |@"struct"| @bitCast(
4440 elf.targetLoad(@as(*align(alignment) @"struct".backing_integer.?, @ptrCast(ptr))),4492 elf.targetLoad(@as(*align(alignment) @"struct".backing_integer.?, @ptrCast(ptr))),
4441 ),4493 ),
...@@ -4475,14 +4527,6 @@ fn ehdrPtr(elf: *Elf) EhdrPtr {...@@ -4475,14 +4527,6 @@ fn ehdrPtr(elf: *Elf) EhdrPtr {
4475 ),4527 ),
4476 };4528 };
4477}4529}
4478fn ehdrField(
4479 elf: *Elf,
4480 comptime field: std.meta.FieldEnum(std.elf.Elf64.Ehdr),
4481) @FieldType(std.elf.Elf64.Ehdr, @tagName(field)) {
4482 return switch (elf.ehdrPtr()) {
4483 inline else => |ehdr| elf.targetLoad(&@field(ehdr, @tagName(field))),
4484 };
4485}
44864530
4487const PhdrSlice = union(std.elf.CLASS) {4531const PhdrSlice = union(std.elf.CLASS) {
4488 NONE: noreturn,4532 NONE: noreturn,
...@@ -4490,7 +4534,7 @@ const PhdrSlice = union(std.elf.CLASS) {...@@ -4490,7 +4534,7 @@ const PhdrSlice = union(std.elf.CLASS) {
4490 @"64": []std.elf.Elf64.Phdr,4534 @"64": []std.elf.Elf64.Phdr,
4491};4535};
4492fn phdrSlice(elf: *Elf) PhdrSlice {4536fn phdrSlice(elf: *Elf) PhdrSlice {
4493 assert(elf.ehdrField(.type) != .REL);4537 assert(elf.ehdrType() != .REL);
4494 const slice = elf.ni.phdr.slice(&elf.mf);4538 const slice = elf.ni.phdr.slice(&elf.mf);
4495 return switch (elf.identClass()) {4539 return switch (elf.identClass()) {
4496 .NONE, _ => unreachable,4540 .NONE, _ => unreachable,
...@@ -4587,8 +4631,7 @@ fn mapInputSection(elf: *Elf, opts: struct {...@@ -4587,8 +4631,7 @@ fn mapInputSection(elf: *Elf, opts: struct {
4587 return error.StripSection;4631 return error.StripSection;
4588 }4632 }
45894633
4590 const name: []const u8 = switch (elf.ehdrField(.type)) {4634 const name: []const u8 = switch (elf.ehdrType()) {
4591 .NONE, .CORE, _ => unreachable,
4592 .REL => opts.name,4635 .REL => opts.name,
4593 .EXEC, .DYN => name: {4636 .EXEC, .DYN => name: {
4594 if (std.mem.startsWith(u8, opts.name, ".text.")) break :name ".text";4637 if (std.mem.startsWith(u8, opts.name, ".text.")) break :name ".text";
...@@ -5050,7 +5093,7 @@ fn loadObject(...@@ -5050,7 +5093,7 @@ fn loadObject(
5050 const ElfN = class.ElfN();5093 const ElfN = class.ElfN();
5051 const ehdr = try r.peekStruct(ElfN.Ehdr, target_endian);5094 const ehdr = try r.peekStruct(ElfN.Ehdr, target_endian);
5052 if (ehdr.type != .REL) return diags.failParse(path, "unsupported object type", .{});5095 if (ehdr.type != .REL) return diags.failParse(path, "unsupported object type", .{});
5053 if (ehdr.machine != elf.ehdrField(.machine))5096 if (ehdr.machine != elf.ehdrMachine().toElf())
5054 return diags.failParse(path, "bad machine", .{});5097 return diags.failParse(path, "bad machine", .{});
5055 if (ehdr.shoff == 0 or ehdr.shnum <= 1) return;5098 if (ehdr.shoff == 0 or ehdr.shnum <= 1) return;
5056 if (ehdr.shoff + @as(u64, ehdr.shentsize) * @as(u64, ehdr.shnum) > fl.size)5099 if (ehdr.shoff + @as(u64, ehdr.shentsize) * @as(u64, ehdr.shnum) > fl.size)
...@@ -5383,23 +5426,43 @@ fn loadObject(...@@ -5383,23 +5426,43 @@ fn loadObject(
5383 );5426 );
5384 const target = symmap.items[rel.info.sym - 1];5427 const target = symmap.items[rel.info.sym - 1];
5385 if (target == Symbol.Id.null) {5428 if (target == Symbol.Id.null) {
5386 // If this is not an SHF_ALLOC section, then let's let this5429 // If this is not an SHF_ALLOC section, then let's not report
5387 // slide for now, because it probably doesn't affect the final5430 // this for now, because it probably doesn't affect the final
5388 // binary's functionality for this section to be a bit broken.5431 // binary's functionality for this section to be a bit broken.
5389 if (!loc_sec.shdr.flags.shf.ALLOC) continue;5432 if (loc_sec.shdr.flags.shf.ALLOC) {
5390 return diags.failParse(5433 diags.addParseError(
5391 path,5434 path,
5392 "unsupported symbol at index {d} required for relocation",5435 "unsupported symbol at index {d} required for relocation",
5393 .{rel.info.sym},5436 .{rel.info.sym},
5394 );5437 );
5438 }
5439 continue;
5395 }5440 }
5396 try elf.addRelocAssumeCapacity(5441 const rt: MachineRelocType = .wrap(rel.info.type, elf);
5442 elf.addRelocAssumeCapacity(
5397 loc_node,5443 loc_node,
5398 rel.offset - loc_sec.shdr.addr,5444 rel.offset - loc_sec.shdr.addr,
5399 target,5445 target,
5400 rel.addend,5446 rel.addend,
5401 .wrap(rel.info.type, elf),5447 rt,
5402 );5448 ) catch |err| switch (err) {
5449 error.UnknownRelocation => diags.addParseError(
5450 path,
5451 "unknown relocation type '{f}'",
5452 .{rt.fmt(elf)},
5453 ),
5454 error.NonStaticRelocation => diags.addParseError(
5455 path,
5456 "non-static relocation type '{f}'",
5457 .{rt.fmt(elf)},
5458 ),
5459 error.UnimplementedRelocation => diags.addParseError(
5460 path,
5461 "TODO(Elf2): unimplemented relocation type '{f}'",
5462 .{rt.fmt(elf)},
5463 ),
5464 else => |e| return e,
5465 };
5403 }5466 }
5404 },5467 },
5405 };5468 };
...@@ -5423,7 +5486,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadPars...@@ -5423,7 +5486,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadPars
5423 const ElfN = class.ElfN();5486 const ElfN = class.ElfN();
5424 const ehdr = try r.peekStruct(ElfN.Ehdr, target_endian);5487 const ehdr = try r.peekStruct(ElfN.Ehdr, target_endian);
5425 if (ehdr.type != .DYN) return diags.failParse(path, "unsupported dso type", .{});5488 if (ehdr.type != .DYN) return diags.failParse(path, "unsupported dso type", .{});
5426 if (ehdr.machine != elf.ehdrField(.machine))5489 if (ehdr.machine != elf.ehdrMachine().toElf())
5427 return diags.failParse(path, "bad machine", .{});5490 return diags.failParse(path, "bad machine", .{});
5428 if (ehdr.shnum > 0) try fr.seekTo(ehdr.shoff);5491 if (ehdr.shnum > 0) try fr.seekTo(ehdr.shoff);
5429 // We're going to need to know the alignment of every section later.5492 // We're going to need to know the alignment of every section later.
...@@ -5782,222 +5845,143 @@ fn prelinkInner(elf: *Elf) Error!void {...@@ -5782,222 +5845,143 @@ fn prelinkInner(elf: *Elf) Error!void {
5782 .file_symbol = zcu_file_symbol,5845 .file_symbol = zcu_file_symbol,
5783 };5846 };
5784 }5847 }
5848}
57855849
5786 const got_plt = switch (elf.ehdrField(.machine)) {5850fn prepareDynamic(elf: *Elf) Error!void {
5787 .SPARCV9 => false,5851 const comp = elf.base.comp;
5788 else => true,5852
5789 };5853 if (elf.shndx.dynamic == .UNDEF) return;
5854
5855 // Static PIEs don't need a PLT, so we shouldn't emit the associated dynamic entries.
5856 const use_plt = !(comp.config.output_mode == .Exe and
5857 comp.config.link_mode == .static and
5858 comp.config.pie);
5859
5860 const dynamic_len: u64 = elf.needed.count() + @intFromBool(elf.dynamic.soname != .empty) +
5861 @intFromBool(elf.dynamic.rpath != .empty) +
5862 @intFromBool(elf.dynamic.flags != 0) + @intFromBool(elf.dynamic.flags_1 != 0) +
5863 @as(usize, @intFromBool(elf.shndx.init_array != .UNDEF)) * 2 +
5864 @as(usize, @intFromBool(elf.shndx.fini_array != .UNDEF)) * 2 +
5865 @as(usize, @intFromBool(elf.shndx.preinit_array != .UNDEF)) * 2 +
5866 @as(usize, @intFromBool(use_plt)) * 4 +
5867 @intFromBool(comp.config.output_mode == .Exe) +
5868 @intFromBool(elf.textrel_count > 0) + 8;
5869
5870 const dynamic_size = dynamic_len * 2 * elf.targetPtrSize();
5871
5872 try elf.shndx.dynamic.get(elf).ni.resize(&elf.mf, comp.gpa, dynamic_size);
5873 switch (elf.shdrPtr(elf.shndx.dynamic)) {
5874 inline else => |shdr| elf.targetStore(&shdr.size, @intCast(dynamic_size)),
5875 }
5876}
5877
5878fn flushDynamic(elf: *Elf) void {
5879 const comp = elf.base.comp;
5880
5881 if (elf.shndx.dynamic == .UNDEF) return;
57905882
5791 if (elf.shndx.dynamic != .UNDEF) switch (elf.identClass()) {5883 switch (elf.identClass()) {
5792 .NONE, _ => unreachable,5884 .NONE, _ => unreachable,
5793 inline else => |ct_class| {5885 inline else => |class| {
5794 const ElfN = ct_class.ElfN();5886 const ElfN = class.ElfN();
5795 const flags: ElfN.Addr = if (elf.options.z_now) std.elf.DF_BIND_NOW else 0;5887
5796 const flags_1: ElfN.Addr = if (elf.options.z_now) std.elf.DF_1_NOW else 0;
5797 const rpath: String(.dynstr) = rpath: {
5798 var buf: std.ArrayList(u8) = .empty;
5799 defer buf.deinit(gpa);
5800 for (elf.options.rpath_list, 0..) |path, i| {
5801 if (i > 0) try buf.append(gpa, ':');
5802 try buf.appendSlice(gpa, path);
5803 }
5804 break :rpath try elf.string(.dynstr, buf.items);
5805 };
5806 // Static PIEs don't need a PLT, so we shouldn't emit the associated dynamic entries.5888 // Static PIEs don't need a PLT, so we shouldn't emit the associated dynamic entries.
5807 const use_plt = !(comp.config.output_mode == .Exe and5889 const use_plt = !(comp.config.output_mode == .Exe and
5808 comp.config.link_mode == .static and5890 comp.config.link_mode == .static and
5809 comp.config.pie);5891 comp.config.pie);
5810 const soname: ?String(.dynstr) = if (elf.options.soname) |soname_slice| str: {5892
5811 break :str try elf.string(.dynstr, soname_slice);5893 const dynamic_size = elf.targetLoad(&@field(elf.shdrPtr(elf.shndx.dynamic), @tagName(class)).size);
5812 } else null;5894 const dynamic_slice = elf.shndx.dynamic.get(elf).ni.slice(&elf.mf)[0..@intCast(dynamic_size)];
5813 const needed_len = elf.needed.count();5895 const dynamic_entries: [][2]ElfN.Addr = @ptrCast(@alignCast(dynamic_slice));
5814 const dynamic_len = needed_len + @intFromBool(elf.options.soname != null) +5896
5815 @intFromBool(rpath != .empty) +5897 var dynamic_index: usize = 0;
5816 @intFromBool(flags != 0) + @intFromBool(flags_1 != 0) +5898
5817 @as(usize, @intFromBool(elf.shndx.init_array != .UNDEF)) * 2 +5899 for (
5818 @as(usize, @intFromBool(elf.shndx.fini_array != .UNDEF)) * 2 +5900 dynamic_entries[dynamic_index..][0..elf.needed.count()],
5819 @as(usize, @intFromBool(elf.shndx.preinit_array != .UNDEF)) * 2 +5901 elf.needed.keys(),
5820 @as(usize, @intFromBool(use_plt)) * 4 +5902 ) |*dynamic_entry, needed| {
5821 @intFromBool(comp.config.output_mode == .Exe) + 8;5903 dynamic_entry.* = .{ std.elf.DT_NEEDED, @intFromEnum(needed) };
5822 const dynamic_size: u32 = @intCast(@sizeOf(ElfN.Addr) * 2 * dynamic_len);
5823 const dynamic_ni = elf.shndx.dynamic.get(elf).ni;
5824 try dynamic_ni.resize(&elf.mf, gpa, dynamic_size);
5825 switch (elf.shdrPtr(elf.shndx.dynamic)) {
5826 inline else => |shdr| elf.targetStore(&shdr.size, dynamic_size),
5827 }5904 }
5905 dynamic_index += elf.needed.count();
58285906
5829 const dynamic_indices: struct {5907 if (elf.dynamic.soname != .empty) {
5830 init_array: ?usize,5908 dynamic_entries[dynamic_index] = .{ std.elf.DT_SONAME, @intFromEnum(elf.dynamic.soname) };
5831 fini_array: ?usize,5909 dynamic_index += 1;
5832 preinit_array: ?usize,5910 }
5833 jmprel: ?usize,5911 if (elf.dynamic.rpath != .empty) {
5834 pltgot: ?usize,5912 dynamic_entries[dynamic_index] = .{ std.elf.DT_RUNPATH, @intFromEnum(elf.dynamic.rpath) };
5835 } = indices: {5913 dynamic_index += 1;
5836 const sec_dynamic = dynamic_ni.slice(&elf.mf);5914 }
5837 const dynamic_entries: [][2]ElfN.Addr = @ptrCast(@alignCast(sec_dynamic));5915 if (elf.dynamic.flags != 0) {
5838 errdefer comptime unreachable; // don't invalidate `dynamic_entries`5916 dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS, elf.dynamic.flags };
5839 var dynamic_index: usize = 0;5917 dynamic_index += 1;
5840 for (5918 }
5841 dynamic_entries[dynamic_index..][0..needed_len],5919 if (elf.dynamic.flags_1 != 0) {
5842 elf.needed.keys(),5920 dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS_1, elf.dynamic.flags_1 };
5843 ) |*dynamic_entry, needed| dynamic_entry.* = .{ std.elf.DT_NEEDED, @intFromEnum(needed) };5921 dynamic_index += 1;
5844 dynamic_index += needed_len;5922 }
5845 if (soname) |soname_dynstr| {5923 if (comp.config.output_mode == .Exe) {
5846 dynamic_entries[dynamic_index] = .{ std.elf.DT_SONAME, @intFromEnum(soname_dynstr) };5924 dynamic_entries[dynamic_index] = .{ std.elf.DT_DEBUG, 0 };
5847 dynamic_index += 1;5925 dynamic_index += 1;
5848 }5926 }
5849 if (rpath != .empty) {5927 if (elf.textrel_count > 0) {
5850 dynamic_entries[dynamic_index] = .{ std.elf.DT_RUNPATH, @intFromEnum(rpath) };5928 dynamic_entries[dynamic_index] = .{ std.elf.DT_TEXTREL, 0 };
5851 dynamic_index += 1;5929 dynamic_index += 1;
5852 }5930 }
5853 if (flags != 0) {5931 if (elf.shndx.init_array != .UNDEF) {
5854 dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS, flags };5932 dynamic_entries[dynamic_index..][0..2].* = .{
5855 dynamic_index += 1;5933 .{ std.elf.DT_INIT_ARRAY, @intCast(elf.shndx.init_array.vaddr(elf)) },
5856 }5934 .{ std.elf.DT_INIT_ARRAYSZ, @intCast(elf.shndx.init_array.size(elf)) },
5857 if (flags_1 != 0) {
5858 dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS_1, flags_1 };
5859 dynamic_index += 1;
5860 }
5861 if (comp.config.output_mode == .Exe) {
5862 dynamic_entries[dynamic_index] = .{ std.elf.DT_DEBUG, 0 };
5863 dynamic_index += 1;
5864 }
5865 const init_array_index: ?usize = if (elf.shndx.init_array != .UNDEF) i: {
5866 dynamic_entries[dynamic_index..][0..2].* = .{
5867 .{ std.elf.DT_INIT_ARRAY, 0 }, // reloc added below
5868 .{ std.elf.DT_INIT_ARRAYSZ, elf.targetLoad(
5869 &@field(elf.shdrPtr(elf.shndx.init_array), @tagName(ct_class)).size,
5870 ) },
5871 };
5872 defer dynamic_index += 2;
5873 break :i dynamic_index;
5874 } else null;
5875 const fini_array_index: ?usize = if (elf.shndx.fini_array != .UNDEF) i: {
5876 dynamic_entries[dynamic_index..][0..2].* = .{
5877 .{ std.elf.DT_FINI_ARRAY, 0 }, // reloc added below
5878 .{ std.elf.DT_FINI_ARRAYSZ, elf.targetLoad(
5879 &@field(elf.shdrPtr(elf.shndx.fini_array), @tagName(ct_class)).size,
5880 ) },
5881 };
5882 defer dynamic_index += 2;
5883 break :i dynamic_index;
5884 } else null;
5885 const preinit_array_index: ?usize = if (elf.shndx.preinit_array != .UNDEF) i: {
5886 dynamic_entries[dynamic_index..][0..2].* = .{
5887 .{ std.elf.DT_PREINIT_ARRAY, 0 }, // reloc added below
5888 .{ std.elf.DT_PREINIT_ARRAYSZ, elf.targetLoad(
5889 &@field(elf.shdrPtr(elf.shndx.preinit_array), @tagName(ct_class)).size,
5890 ) },
5891 };
5892 defer dynamic_index += 2;
5893 break :i dynamic_index;
5894 } else null;
5895 const jmprel_index: ?usize, const pltgot_index: ?usize = if (use_plt) i: {
5896 dynamic_entries[dynamic_index..][0..4].* = .{
5897 .{ std.elf.DT_JMPREL, 0 }, // reloc added below
5898 .{ std.elf.DT_PLTGOT, 0 }, // reloc added below
5899 .{ std.elf.DT_PLTRELSZ, elf.targetLoad(
5900 &@field(elf.shdrPtr(elf.shndx.rela_plt), @tagName(ct_class)).size,
5901 ) },
5902 .{ std.elf.DT_PLTREL, std.elf.DT_RELA },
5903 };
5904 defer dynamic_index += 4;
5905 break :i .{ dynamic_index, dynamic_index + 1 };
5906 } else .{ null, null };
5907 dynamic_entries[dynamic_index..][0..8].* = .{
5908 .{ std.elf.DT_RELA, 0 }, // reloc added below
5909 .{ std.elf.DT_RELASZ, elf.targetLoad(
5910 &@field(elf.shdrPtr(elf.shndx.rela_dyn), @tagName(ct_class)).size,
5911 ) },
5912 .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) },
5913 .{ std.elf.DT_SYMTAB, 0 }, // reloc added below
5914 .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) },
5915 .{ std.elf.DT_STRTAB, 0 }, // reloc added below
5916 .{ std.elf.DT_STRSZ, elf.targetLoad(
5917 &@field(elf.shdrPtr(elf.shndx.dynstr), @tagName(ct_class)).size,
5918 ) },
5919 .{ std.elf.DT_NULL, 0 },
5920 };5935 };
5921 dynamic_index += 8;5936 dynamic_index += 2;
5922 assert(dynamic_index == dynamic_len);5937 }
5923 if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry|5938 if (elf.shndx.fini_array != .UNDEF) {
5924 std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry);5939 dynamic_entries[dynamic_index..][0..2].* = .{
59255940 .{ std.elf.DT_FINI_ARRAY, @intCast(elf.shndx.fini_array.vaddr(elf)) },
5926 break :indices .{5941 .{ std.elf.DT_FINI_ARRAYSZ, @intCast(elf.shndx.fini_array.size(elf)) },
5927 .init_array = init_array_index,
5928 .fini_array = fini_array_index,
5929 .preinit_array = preinit_array_index,
5930 .jmprel = jmprel_index,
5931 .pltgot = pltgot_index,
5932 };5942 };
5933 };5943 dynamic_index += 2;
5944 }
5945 if (elf.shndx.preinit_array != .UNDEF) {
5946 dynamic_entries[dynamic_index..][0..2].* = .{
5947 .{ std.elf.DT_PREINIT_ARRAY, @intCast(elf.shndx.preinit_array.vaddr(elf)) },
5948 .{ std.elf.DT_PREINIT_ARRAYSZ, @intCast(elf.shndx.preinit_array.size(elf)) },
5949 };
5950 dynamic_index += 2;
5951 }
5952 if (use_plt) {
5953 // The `DT_PLTGOT` entry usually points to `.got.plt`, but on targets where that
5954 // section does not exist it instead points to `.plt`.
5955 const pltgot_shndx: Section.Index = switch (elf.targetPltInfo().got_plt != null) {
5956 true => elf.shndx.got_plt,
5957 false => elf.shndx.plt,
5958 };
5959 dynamic_entries[dynamic_index..][0..4].* = .{
5960 .{ std.elf.DT_JMPREL, @intCast(elf.shndx.rela_plt.vaddr(elf)) },
5961 .{ std.elf.DT_PLTGOT, @intCast(pltgot_shndx.vaddr(elf)) },
5962 .{ std.elf.DT_PLTRELSZ, @intCast(elf.shndx.rela_plt.size(elf)) },
5963 .{ std.elf.DT_PLTREL, std.elf.DT_RELA },
5964 };
5965 dynamic_index += 4;
5966 }
59345967
5935 const dsorel: SymbolReloc.Type = switch (ct_class) {5968 dynamic_entries[dynamic_index..][0..8].* = .{
5936 .NONE, _ => comptime unreachable,5969 .{ std.elf.DT_RELA, @intCast(elf.shndx.rela_dyn.vaddr(elf)) },
5937 .@"32" => .dsorel32,5970 .{ std.elf.DT_RELASZ, @intCast(elf.shndx.rela_dyn.size(elf)) },
5938 .@"64" => .dsorel64,5971 .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) },
5972 .{ std.elf.DT_SYMTAB, @intCast(elf.shndx.dynsym.vaddr(elf)) },
5973 .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) },
5974 .{ std.elf.DT_STRTAB, @intCast(elf.shndx.dynstr.vaddr(elf)) },
5975 .{ std.elf.DT_STRSZ, @intCast(elf.shndx.dynstr.size(elf)) },
5976 .{ std.elf.DT_NULL, 0 },
5939 };5977 };
5978 dynamic_index += 8;
59405979
5941 elf.dynamic_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);5980 assert(dynamic_index == dynamic_entries.len);
5942 try elf.ensureUnusedRelocCapacity(dynamic_ni, 8);5981 if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry|
5943 if (dynamic_indices.init_array) |index| try elf.addSymbolRelocAssumeCapacity(5982 std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry);
5944 dynamic_ni,
5945 @sizeOf(ElfN.Addr) * (2 * index + 1),
5946 .local(elf.shndx.init_array.get(elf).lsi),
5947 0,
5948 dsorel,
5949 );
5950 if (dynamic_indices.fini_array) |index| try elf.addSymbolRelocAssumeCapacity(
5951 dynamic_ni,
5952 @sizeOf(ElfN.Addr) * (2 * index + 1),
5953 .local(elf.shndx.fini_array.get(elf).lsi),
5954 0,
5955 dsorel,
5956 );
5957 if (dynamic_indices.preinit_array) |index| try elf.addSymbolRelocAssumeCapacity(
5958 dynamic_ni,
5959 @sizeOf(ElfN.Addr) * (2 * index + 1),
5960 .local(elf.shndx.preinit_array.get(elf).lsi),
5961 0,
5962 dsorel,
5963 );
5964 if (dynamic_indices.jmprel) |index| try elf.addSymbolRelocAssumeCapacity(
5965 dynamic_ni,
5966 @sizeOf(ElfN.Addr) * (2 * index + 1),
5967 .local(elf.shndx.rela_plt.get(elf).lsi),
5968 0,
5969 dsorel,
5970 );
5971 if (dynamic_indices.pltgot) |index| try elf.addSymbolRelocAssumeCapacity(
5972 dynamic_ni,
5973 @sizeOf(ElfN.Addr) * (2 * index + 1),
5974 .local((if (got_plt) elf.shndx.got_plt else elf.shndx.plt).get(elf).lsi),
5975 0,
5976 dsorel,
5977 );
5978 try elf.addSymbolRelocAssumeCapacity(
5979 dynamic_ni,
5980 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 8) + 1),
5981 .local(elf.shndx.rela_dyn.get(elf).lsi),
5982 0,
5983 dsorel,
5984 );
5985 try elf.addSymbolRelocAssumeCapacity(
5986 dynamic_ni,
5987 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 5) + 1),
5988 .local(elf.shndx.dynsym.get(elf).lsi),
5989 0,
5990 dsorel,
5991 );
5992 try elf.addSymbolRelocAssumeCapacity(
5993 dynamic_ni,
5994 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 3) + 1),
5995 .local(elf.shndx.dynstr.get(elf).lsi),
5996 0,
5997 dsorel,
5998 );
5999 },5983 },
6000 };5984 }
6001}5985}
60025986
6003fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {5987fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
...@@ -6017,7 +6001,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {...@@ -6017,7 +6001,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
6017 .PROGBITS => assert(opts.size > 0),6001 .PROGBITS => assert(opts.size > 0),
6018 else => {},6002 else => {},
6019 }6003 }
6020 if (opts.flags.ALLOC and elf.ehdrField(.type) != .REL) {6004 if (opts.flags.ALLOC and elf.ehdrType() != .REL) {
6021 assert(elf.getNode(segment_ni) == .segment);6005 assert(elf.getNode(segment_ni) == .segment);
6022 }6006 }
6023 const gpa = elf.base.comp.gpa;6007 const gpa = elf.base.comp.gpa;
...@@ -6054,8 +6038,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {...@@ -6054,8 +6038,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
6054 },6038 },
6055 };6039 };
6056 try elf.ensureNodeSize(elf.ni.shdr, new_shdr_size);6040 try elf.ensureNodeSize(elf.ni.shdr, new_shdr_size);
6057 const ni = try elf.mf.addLastChildNode(gpa, switch (elf.ehdrField(.type)) {6041 const ni = try elf.mf.addLastChildNode(gpa, switch (elf.ehdrType()) {
6058 .NONE, .CORE, _ => unreachable,
6059 .REL => elf.ni.file,6042 .REL => elf.ni.file,
6060 .EXEC, .DYN => segment_ni,6043 .EXEC, .DYN => segment_ni,
6061 }, .{6044 }, .{
...@@ -6106,8 +6089,7 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize)...@@ -6106,8 +6089,7 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize)
6106 try elf.symbol_relocs.ensureUnusedCapacity(gpa, len);6089 try elf.symbol_relocs.ensureUnusedCapacity(gpa, len);
6107 try elf.got_relocs.ensureUnusedCapacity(gpa, len);6090 try elf.got_relocs.ensureUnusedCapacity(gpa, len);
6108 const class = elf.identClass();6091 const class = elf.identClass();
6109 switch (elf.ehdrField(.type)) {6092 switch (elf.ehdrType()) {
6110 .NONE, .CORE, _ => unreachable,
6111 .REL => {6093 .REL => {
6112 const shndx = elf.getNodeShndx(node);6094 const shndx = elf.getNodeShndx(node);
6113 if (shndx.get(elf).rela.shndx == .UNDEF) {6095 if (shndx.get(elf).rela.shndx == .UNDEF) {
...@@ -6166,10 +6148,9 @@ fn addRelocAssumeCapacity(...@@ -6166,10 +6148,9 @@ fn addRelocAssumeCapacity(
6166 target: Symbol.Id,6148 target: Symbol.Id,
6167 addend: i64,6149 addend: i64,
6168 @"type": MachineRelocType,6150 @"type": MachineRelocType,
6169) Error!void {6151) (Error || error{ UnknownRelocation, NonStaticRelocation, UnimplementedRelocation })!void {
6170 assert(node != .none);6152 assert(node != .none);
6171 switch (elf.ehdrField(.type)) {6153 switch (elf.ehdrType()) {
6172 .NONE, .CORE, _ => unreachable,
6173 .REL => {6154 .REL => {
6174 const rela_shndx = elf.getNodeShndx(node).get(elf).rela.shndx;6155 const rela_shndx = elf.getNodeShndx(node).get(elf).rela.shndx;
6175 const rela_index = rela_shndx.relaAddOneAssumeCapacity(elf, .{6156 const rela_index = rela_shndx.relaAddOneAssumeCapacity(elf, .{
...@@ -6187,161 +6168,113 @@ fn addRelocAssumeCapacity(...@@ -6187,161 +6168,113 @@ fn addRelocAssumeCapacity(
6187 const target_ptr = target.index(elf).ptr(elf);6168 const target_ptr = target.index(elf).ptr(elf);
6188 const next = target_ptr.first_target_reloc;6169 const next = target_ptr.first_target_reloc;
6189 target_ptr.first_target_reloc = ri;6170 target_ptr.first_target_reloc = ri;
6190 break :next next;6171 break :next next;
6191 };6172 };
6192 if (next != .none) {6173 if (next != .none) {
6193 next.get(elf).prev = ri;6174 next.get(elf).prev = ri;
6194 }6175 }
6195 elf.symbol_relocs.appendAssumeCapacity(.{6176 elf.symbol_relocs.appendAssumeCapacity(.{
6196 .node = node,6177 .node = node,
6197 .offset = offset,6178 .offset = offset,
6198 .type = .write_rela,6179 .type = undefined,
6199 .target = target,6180 .target = target,
6200 .addend = addend,6181 .addend = addend,
6201 .next = next,6182 .next = next,
6202 .prev = .none,6183 .prev = .none,
6203 .rela_index = rela_index.toOptional(),6184 .rela_index = rela_index.toOptional(),
6204 });6185 .result = .ok,
6205 },6186 });
62066187 },
6207 .DYN, .EXEC => switch (elf.ehdrField(.machine)) {
6208 else => |machine| @panic(@tagName(machine)),
6209 .X86_64 => switch (@"type".X86_64) {
6210 _,
6211 .NONE,
6212 .COPY,
6213 .GLOB_DAT,
6214 .JUMP_SLOT,
6215 .RELATIVE64,
6216 .RELATIVE,
6217 .IRELATIVE,
6218 .@"16",
6219 .PC16,
6220 .@"8",
6221 .PC8,
6222 .DTPMOD64,
6223 .GOTPLT64,
6224 => @panic("TODO: error for illegal or unsupported input relocation"),
6225
6226 // TODO: the psABI links to https://www.fsfla.org/~lxoliva/writeups/TLS/RFC-TLSDESC-x86.txt
6227 .GOTPC32_TLSDESC => @panic("TODO: R_X86_64_GOTPC32_TLSDESC"),
6228 .TLSDESC_CALL => @panic("TODO: R_X86_64_TLSDESC_CALL"),
6229 .TLSDESC => @panic("TODO: R_X86_64_TLSDESC"),
6230
6231 // Relocations targeting a symbol
6232 .@"64" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs64),
6233 .@"32" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32),
6234 .@"32S" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32s),
6235 .PC64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel64),
6236 .PC32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel32),
6237 .PLT32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .pltrel32),
6238 .SIZE64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size64),
6239 .SIZE32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size32),
6240 .DTPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff64),
6241 .DTPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff32),
6242 .TPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff64),
6243 .TPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff32),
6244 .GOTPC64 => {
6245 const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi);
6246 try elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .rel64);
6247 },
6248 .GOTPC32 => {
6249 const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi);
6250 try elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .rel32);
6251 },
62526188
6253 // TODO: these are the address of an arbitrary symbol (or PLT entry) relative to the6189 .DYN, .EXEC => switch (elf.ehdrMachine()) {
6254 // base of the GOT, which is quite annoying. Luckily, they seem to be rare, so I'm6190 .AARCH64 => switch (@"type".AARCH64) {
6255 // probably just going to introduce a set (ArrayHashMap) of SymbolReloc.Index which6191 .NONE => {},
6256 // need to be re-applied whenever the GOT moves.6192 _ => return error.UnknownRelocation,
6257 .GOTOFF64 => @panic("TODO: R_X86_64_GOTOFF64"), // offset of symbol from GOT base6193 else => return error.UnimplementedRelocation,
6258 .PLTOFF64 => @panic("TODO: R_X86_64_PLTOFF64"), // offset of PLT entry from GOT base (yes, I know, the name is stupid)
6259
6260 // Relocations targeting a GOT entry
6261 .GOT64 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .offset64),
6262 .GOT32 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .offset32),
6263 .GOTPCREL64 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel64),
6264 .GOTPCREL => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel32),
6265 // TODO: the next two are relaxable to non-GOT relocations, but I haven't figured
6266 // out how to represent relaxations yet. If we want to remove a `GotReloc` and add a
6267 // `SymbolReloc` at some point, we can't do that in `GotReloc.apply`, because that
6268 // function must be idempotent to ensure reproducible binaries. I think we would
6269 // need to do that as soon as the operation is known to be relaxable (e.g. because
6270 // we found a defininition for a non-preemptible symbol).
6271 .GOTPCRELX => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel32),
6272 .REX_GOTPCRELX => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel32),
6273
6274 .TLSGD => elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .rel32),
6275 .TLSLD => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .rel32),
6276 .GOTTPOFF => elf.addGotRelocAssumeCapacity(node, offset, .{ .tpoff = target }, addend, .rel32),
6277 },6194 },
6278 .LOONGARCH => switch (@"type".LOONGARCH) {6195 .LOONGARCH => rel_type: switch (@"type".LARCH) {
6279 else => std.debug.panic("TODO: unsupported input relocation, {t}", .{@"type".LOONGARCH}),6196 .NONE => {},
6280 _,6197 _ => return error.UnknownRelocation,
6281 .NONE,6198
6282 .COPY,6199 .COPY,
6283 .JUMP_SLOT,6200 .JUMP_SLOT,
6284 .RELATIVE,6201 .RELATIVE,
6285 .IRELATIVE,6202 .IRELATIVE,
6286 => std.debug.panic("TODO: error for illegal or unsupported input relocation, {t}", .{@"type".LOONGARCH}),6203 => return error.NonStaticRelocation,
62876204
6288 .RELAX => {}, // TODO: relaxation is not yet implemented6205 else => return error.UnimplementedRelocation,
62896206
6290 // Relocations targeting a symbol6207 // These relocations signal that certain relaxations are legal, but this linker does
6291 .@"64" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs64),6208 // not yet implement relaxation, so these are ignored.
6292 .@"32" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32),6209 .RELAX, .TLS_LE_ADD_R => {},
6293 .@"64_PCREL" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel64),6210
6294 .@"32_PCREL" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel32),6211 // Relaxable versions of other relocations. Since we don't yet implement relaxation,
62956212 // just use the handling for the non-relaxable versions.
6296 .PCALA_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_abs32_lo12),6213 .TLS_LE_LO12_R => continue :rel_type .TLS_LE_LO12,
6297 .PCALA_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_rel32_hi20),6214 .TLS_LE_HI20_R => continue :rel_type .TLS_LE_HI20,
6298 .PCALA64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_rel64_hi12),6215
6299 .PCALA64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_rel64_lo20),6216 // zig fmt: off
63006217 .@"32" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
6301 .B16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_branch_rel18),6218 .@"64" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
6302 .B21 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_branch_rel23),6219 .@"32_PCREL" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
6303 .B26 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_branch_rel28),6220 .@"64_PCREL" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })),
6304 .CALL36 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_call_rel38),6221 .ABS_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })),
63056222 .ABS_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"12" })),
6306 // Relocations targeting a TLS symbol6223 .ABS64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"32" })),
6307 .TLS_LE_LO12, .TLS_LE_LO12_R => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_tpoff32_lo12),6224 .ABS64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .unsigned, .shift = .@"52" })),
6308 .TLS_LE_HI20, .TLS_LE_HI20_R => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_tpoff32_hi20),6225 .PCALA_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })),
6309 .TLS_LE64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_tpoff64_lo20),6226 .PCALA_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_pcala_hi20)),
6310 .TLS_LE64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_tpoff64_hi12),6227 .PCALA64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_pcala64_lo20)),
6311 .TLS_LE_ADD_R => {}, // TODO: relaxation is not yet implemented6228 .PCALA64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_pcala64_hi12)),
63126229
6313 // Relocations targeting a GOT entry6230 .B16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.pltrel, .{ .dest = .@"32[25:10]", .cast = .signed, .shift = .@"2_exact" })),
6314 .GOT_PC_LO12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_abs32_lo12),6231 .B21 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_b21)),
6315 .GOT_PC_HI20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_rel32_hi20),6232 .B26 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_b26)),
6316 .GOT64_PC_LO20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_rel64_lo20),6233 .CALL36 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_call36)),
6317 .GOT64_PC_HI12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_rel64_hi12),6234
63186235 .TLS_LE_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })),
6319 .GOT_LO12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_abs32_lo12),6236 .TLS_LE_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"12" })),
6320 .GOT_HI20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_abs32_hi20),6237 .TLS_LE64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"32" })),
6321 .GOT64_LO20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_abs64_lo20),6238 .TLS_LE64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[21:10]", .cast = .unsigned, .shift = .@"52" })),
6322 .GOT64_HI12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_abs64_hi12),6239
6240 .GOT_PC_LO12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })),
6241 .GOT_PC_HI20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.larch_pcala_hi20)),
6242 .GOT64_PC_LO20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.larch_pcala64_lo20)),
6243 .GOT64_PC_HI12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.larch_pcala64_hi12)),
6244 .GOT_LO12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })),
6245 .GOT_HI20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"12" })),
6246 .GOT64_LO20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"32" })),
6247 .GOT64_HI12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .unsigned, .shift = .@"52" })),
6248 // zig fmt: on
6249 },
6250 .PPC64 => switch (@"type".PPC64) {
6251 .NONE => {},
6252 _ => return error.UnknownRelocation,
6253 else => return error.UnimplementedRelocation,
6254 },
6255 .RISCV => switch (@"type".RISCV) {
6256 .NONE => {},
6257 _ => return error.UnknownRelocation,
6258 else => return error.UnimplementedRelocation,
6323 },6259 },
6324 .SPARCV9 => switch (@"type".SPARC) {6260 .SPARCV9 => switch (@"type".SPARC) {
6325 _,6261 .NONE => {},
6326 .NONE,6262 _ => return error.UnknownRelocation,
6263
6327 .COPY,6264 .COPY,
6328 .GLOB_DAT,6265 .GLOB_DAT,
6329 .JMP_SLOT,6266 .JMP_SLOT,
6330 .RELATIVE,6267 .RELATIVE,
6331 .IRELATIVE,6268 .IRELATIVE,
6332 => std.debug.panic("TODO: error for illegal or unsupported input relocation, {t}", .{@"type".SPARC}),6269 => return error.NonStaticRelocation,
63336270
6334 inline .WDISP22,6271 .WDISP22,
6335 .HI22,6272 .HI22,
6336 .@"22",
6337 .@"13",
6338 .LO10,6273 .LO10,
6339 .HIPLT22,6274 .HIPLT22,
6340 .LOPLT10,6275 .LOPLT10,
6341 .PCPLT22,6276 .PCPLT22,
6342 .PCPLT10,6277 .PCPLT10,
6343 .@"10",
6344 .@"11",
6345 .OLO10,6278 .OLO10,
6346 .HH22,6279 .HH22,
6347 .HM10,6280 .HM10,
...@@ -6351,62 +6284,24 @@ fn addRelocAssumeCapacity(...@@ -6351,62 +6284,24 @@ fn addRelocAssumeCapacity(
6351 .PC_LM22,6284 .PC_LM22,
6352 .WDISP16,6285 .WDISP16,
6353 .WDISP19,6286 .WDISP19,
6354 .@"7",
6355 .@"5",
6356 .@"6",
6357 .HIX22,6287 .HIX22,
6358 .LOX10,6288 .LOX10,
6359 .REGISTER,6289 .REGISTER,
6360 .TLS_GD_HI22,
6361 .TLS_GD_LO10,
6362 .TLS_IE_HI22,6290 .TLS_IE_HI22,
6363 .TLS_IE_LO10,6291 .TLS_IE_LO10,
6364 .TLS_DTPMOD32,6292 .TLS_DTPMOD32,
6365 .TLS_DTPMOD64,6293 .TLS_DTPMOD64,
6366 .H34,6294 .H34,
6367 .WDISP10,6295 .WDISP10,
6368 => |t| @panic("TODO: " ++ @tagName(t)),6296 => return error.UnimplementedRelocation,
63696297
6370 // Relocations targeting a symbol6298 // These need similar handling to `R_X86_64_GOTOFF64`. No compiler seems to emit them though.
6371 .@"8" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs8),6299 .GOTDATA_HIX22 => return error.UnimplementedRelocation,
6372 .@"16" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs16),6300 .GOTDATA_LOX10 => return error.UnimplementedRelocation,
6373 .@"32" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32),6301
6374 .DISP8 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel8),6302 // These relocations signal that certain relaxations are legal, but this linker does
6375 .DISP16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel16),6303 // not yet implement relaxation, so these are ignored.
6376 .DISP32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel32),6304 .GOTDATA_OP,
6377 .WDISP30 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .sparc_wdisp30),
6378 .PC10 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .sparc_pc10),
6379 .PC22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .sparc_pc22),
6380 .WPLT30 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .sparc_wplt30),
6381 .UA32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32),
6382 .PLT32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .pltabs32),
6383 .PCPLT32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .pltrel32),
6384 .@"64" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs64),
6385 .DISP64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel64),
6386 .PLT64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .pltabs64),
6387 .H44 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .sparc_h44),
6388 .M44 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .sparc_m44),
6389 .L44 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .sparc_l44),
6390 .UA64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs64),
6391 .UA16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs16),
6392 .TLS_GD_CALL, .TLS_LDM_CALL => try elf.addSymbolRelocAssumeCapacity(node, offset, try elf.externSymbolInner(.{
6393 .lib_name = null,
6394 .name = "__tls_get_addr",
6395 .type = .FUNC,
6396 }), addend, .sparc_wplt30),
6397 .SIZE32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size32),
6398 .SIZE64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size64),
6399
6400 // Relocations targeting a TLS symbol
6401 .TLS_LDO_HIX22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .sparc_ldo_hix22),
6402 .TLS_LDO_LOX10 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .sparc_ldo_lox10),
6403 .TLS_LE_HIX22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .sparc_le_hix22),
6404 .TLS_LE_LOX10 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .sparc_le_lox10),
6405 .TLS_DTPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff32),
6406 .TLS_DTPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff64),
6407 .TLS_TPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff32),
6408 .TLS_TPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff64),
6409 // We currently do no relaxation, so nothing to do for these.
6410 .TLS_GD_ADD,6305 .TLS_GD_ADD,
6411 .TLS_LDM_ADD,6306 .TLS_LDM_ADD,
6412 .TLS_LDO_ADD,6307 .TLS_LDO_ADD,
...@@ -6415,19 +6310,180 @@ fn addRelocAssumeCapacity(...@@ -6415,19 +6310,180 @@ fn addRelocAssumeCapacity(
6415 .TLS_IE_ADD,6310 .TLS_IE_ADD,
6416 => {},6311 => {},
64176312
6418 // Relocations targeting a GOT entry6313 // zig fmt: off
6419 .GOT10 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .sparc_10),6314 .@"8" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"8", .cast = .unsigned, .shift = .@"0" })),
6420 .GOT13 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .sparc_13),6315 .@"16", .UA16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"16", .cast = .unsigned, .shift = .@"0" })),
6421 .GOT22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .sparc_22),6316 .@"32", .UA32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
6422 .TLS_LDM_HI22 => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .sparc_ldm_hi22),6317 .@"64", .UA64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
6423 .TLS_LDM_LO10 => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .sparc_ldm_lo10),6318
6424 // These need similar handling to `R_X86_64_GOTOFF64`. No compiler seems to emit them though.6319 .@"5" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[4:0]", .cast = .unsigned, .shift = .@"0" })),
6425 .GOTDATA_HIX22 => @panic("TODO: R_SPARC_GOTDATA_HIX22"),6320 .@"6" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[5:0]", .cast = .unsigned, .shift = .@"0" })),
6426 .GOTDATA_LOX10 => @panic("TODO: R_SPARC_GOTDATA_LOX10"),6321 .@"7" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[6:0]", .cast = .unsigned, .shift = .@"0" })),
6427 .GOTDATA_OP_HIX22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .sparc_op_hix22),6322 .@"10" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[9:0]", .cast = .unsigned, .shift = .@"0" })),
6428 .GOTDATA_OP_LOX10 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .sparc_op_lox10),6323 .@"11" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[10:0]", .cast = .unsigned, .shift = .@"0" })),
6429 // We currently do no relaxation, so nothing to do for this one.6324 .@"13" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[12:0]", .cast = .unsigned, .shift = .@"0" })),
6430 .GOTDATA_OP => {},6325 .@"22" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:0]", .cast = .unsigned, .shift = .@"0" })),
6326
6327 .DISP8 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"8", .cast = .signed, .shift = .@"0" })),
6328 .DISP16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"16", .cast = .signed, .shift = .@"0" })),
6329 .DISP32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
6330 .DISP64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })),
6331
6332 .SIZE32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.size, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
6333 .SIZE64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.size, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
6334
6335 .PCPLT32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.pltrel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
6336 .PLT32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.pltabs, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
6337 .PLT64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.pltabs, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
6338
6339 .WDISP30 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32[29:0]", .cast = .signed, .shift = .@"2_exact" })),
6340 .WPLT30 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.pltrel, .{ .dest = .@"32[29:0]", .cast = .signed, .shift = .@"2_exact" })),
6341 .PC22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32[21:0]", .cast = .signed, .shift = .@"10" })),
6342 .H44 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:0]", .cast = .unsigned, .shift = .@"22" })),
6343 .M44 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"12" })),
6344
6345 .TLS_LDO_HIX22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })),
6346 .TLS_LE_HIX22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.sparc_le_hix22)),
6347 .TLS_DTPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
6348 .TLS_DTPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
6349 .TLS_TPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
6350 .TLS_TPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })),
6351
6352 .GOT13 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"32[12:0]", .cast = .unsigned, .shift = .@"0" })),
6353 .GOT22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })),
6354 .GOTDATA_OP_LOX10 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.sparc_op_lox10)),
6355 .GOTDATA_OP_HIX22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.sparc_op_hix22)),
6356 .TLS_GD_HI22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .simple(.offset, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })),
6357 .TLS_LDM_HI22 => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .simple(.offset, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })),
6358 // zig fmt: on
6359
6360 .TLS_GD_CALL, .TLS_LDM_CALL => {
6361 const callee_sym = try elf.externSymbolInner(.{
6362 .lib_name = null,
6363 .name = "__tls_get_addr",
6364 .type = .FUNC,
6365 });
6366 try elf.addSymbolRelocAssumeCapacity(node, offset, callee_sym, addend, .simple(.pltrel, .{ .dest = .@"32[29:0]", .cast = .signed, .shift = .@"2_exact" }));
6367 },
6368
6369 // The following relocations are all represented by the ABI as writing to a 13 bit
6370 // field (32[12:0]), but masking out some bits of the value. To simplify our logic
6371 // for applying relocations, we instead [un]set any fixed bits right now, then model
6372 // the relocation as only writing to a smaller 10--12 bit field.
6373 // TODO: because we flush input sections lazily, we can't actually write these bits
6374 // immediately---we'll instead have to queue the writes somehow.
6375 .PC10 => {
6376 // TODO: 32[12:10] = 0b000
6377 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
6378 },
6379 .L44 => {
6380 // TODO: 32[12:12] = 0b0
6381 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[11:0]", .cast = .trunc, .shift = .@"0" }));
6382 },
6383 .TLS_LDO_LOX10 => {
6384 // TODO: 32[12:10] = 0b000
6385 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
6386 },
6387 .TLS_LE_LOX10 => {
6388 // TODO: 32[12:10] = 0b111
6389 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
6390 },
6391 .GOT10 => {
6392 // TODO: 32[12:10] = 0b000
6393 elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
6394 },
6395 .TLS_GD_LO10 => {
6396 // TODO: 32[12:10] = 0b000
6397 elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
6398 },
6399 .TLS_LDM_LO10 => {
6400 // TODO: 32[12:10] = 0b000
6401 elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
6402 },
6403 },
6404 .X86_64 => rel_type: switch (@"type".X86_64) {
6405 .NONE => {},
6406 _ => return error.UnknownRelocation,
6407
6408 .COPY,
6409 .GLOB_DAT,
6410 .JUMP_SLOT,
6411 .RELATIVE64,
6412 .RELATIVE,
6413 .IRELATIVE,
6414 .DTPMOD64,
6415 => return error.NonStaticRelocation,
6416
6417 // TODO: the psABI links to https://www.fsfla.org/~lxoliva/writeups/TLS/RFC-TLSDESC-x86.txt
6418 .GOTPC32_TLSDESC => return error.UnimplementedRelocation,
6419 .TLSDESC_CALL => return error.UnimplementedRelocation,
6420 .TLSDESC => return error.UnimplementedRelocation,
6421
6422 // TODO: these are the address of an arbitrary symbol (or PLT entry) relative to the
6423 // base of the GOT, which is quite annoying. Luckily, they seem to be rare, so I'm
6424 // probably just going to introduce a set (ArrayHashMap) of SymbolReloc.Index which
6425 // need to be re-applied whenever the GOT moves.
6426 .GOTOFF64 => return error.UnimplementedRelocation, // offset of symbol from GOT base
6427 .PLTOFF64 => return error.UnimplementedRelocation, // offset of PLT entry from GOT base (yes, I know, the name is stupid)
6428
6429 // TODO: figure out how to do relaxations. Perhaps we want to remove a `GotReloc`
6430 // and replace it with a `SymbolReloc` when a relaxation becomes possible, but we'd
6431 // need to bear in mind whether incremental updates might make a relaxation
6432 // impossible again or something like that. Relaxations seem kind of hostile to
6433 // incremental compilation, so perhaps we just only support them in non-incremental
6434 // compilations and just apply them in flush or something.
6435
6436 // Relaxable versions of other relocations. Since we don't yet implement relaxation,
6437 // just use the handling for the non-relaxable versions.
6438 .GOTPCRELX, .REX_GOTPCRELX => continue :rel_type .GOTPCREL,
6439
6440 // This relocation was a historical attempt to help linkers optimize uses of symbols
6441 // which have both GOT entries and PLT entries, by encouraging the linker to create
6442 // a `.got.plt` entry instead of a `.got` entry. This makes no sense, because the
6443 // linker already has sufficient knowledge to do that optimization, while compilers
6444 // actually do *not* have sufficient knowledge (since the PLT and GOT relocations
6445 // may not be in the same compilation unit). This relocation has since been removed
6446 // from the psABI, but just in case it appears, we can easily support it by just
6447 // disregarding the PLT stuff and lowering to a normal GOT entry.
6448 //
6449 // More details: https://sourceware.org/pipermail/binutils/2014-November/086548.html
6450 .GOTPLT64 => continue :rel_type .GOT64,
6451
6452 // zig fmt: off
6453 .@"8" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"8", .cast = .unsigned, .shift = .@"0" })),
6454 .@"16" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"16", .cast = .unsigned, .shift = .@"0" })),
6455 .@"32" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
6456 .@"32S" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
6457 .@"64" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
6458 .PC8 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"8", .cast = .signed, .shift = .@"0" })),
6459 .PC16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"16", .cast = .signed, .shift = .@"0" })),
6460 .PC32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
6461 .PC64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })),
6462 .PLT32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.pltrel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
6463 .SIZE32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.size, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
6464 .SIZE64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.size, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
6465 .DTPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
6466 .DTPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
6467 .TPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
6468 .TPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })),
6469
6470 .GOT32 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
6471 .GOT64 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
6472 .GOTPCREL => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
6473 .GOTPCREL64 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.rel, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })),
6474 .TLSGD => elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
6475 .TLSLD => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
6476 .GOTTPOFF => elf.addGotRelocAssumeCapacity(node, offset, .{ .tpoff = target }, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
6477 // zig fmt: on
6478
6479 .GOTPC64 => {
6480 const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi);
6481 try elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .simple(.rel, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" }));
6482 },
6483 .GOTPC32 => {
6484 const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi);
6485 try elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" }));
6486 },
6431 },6487 },
6432 },6488 },
6433 }6489 }
...@@ -6440,10 +6496,12 @@ fn addSymbolRelocAssumeCapacity(...@@ -6440,10 +6496,12 @@ fn addSymbolRelocAssumeCapacity(
6440 addend: i64,6496 addend: i64,
6441 @"type": SymbolReloc.Type,6497 @"type": SymbolReloc.Type,
6442) Error!void {6498) Error!void {
6443 assert(elf.ehdrField(.type) != .REL);6499 assert(elf.ehdrType() != .REL);
6444 assert(node != .none);6500 assert(node != .none);
64456501
6446 const rela_index: Section.RelaIndex.Optional = r: {6502 const rela_index: Section.RelaIndex.Optional = r: {
6503 if (elf.shndx.dynamic == .UNDEF) break :r .none;
6504
6447 // If we emit a runtime relocation entry, its `offset` is a virtual address, so we need to6505 // If we emit a runtime relocation entry, its `offset` is a virtual address, so we need to
6448 // determine the vaddr of `node`.6506 // determine the vaddr of `node`.
6449 const node_vaddr: u64 = switch (elf.getNode(node)) {6507 const node_vaddr: u64 = switch (elf.getNode(node)) {
...@@ -6461,162 +6519,51 @@ fn addSymbolRelocAssumeCapacity(...@@ -6461,162 +6519,51 @@ fn addSymbolRelocAssumeCapacity(
6461 => |i| Symbol.Id.local(i.symbol(elf)).value(elf),6519 => |i| Symbol.Id.local(i.symbol(elf)).value(elf),
6462 };6520 };
64636521
6464 const rela_type: MachineRelocType = switch (elf.ehdrField(.machine)) {6522 // If this is `true`, we will try to create a copy relocation for the target symbol if it is
6465 else => |machine| @panic(@tagName(machine)),6523 // not locally defined. If the relocation value is always computed from the target symbol's
6466 .X86_64 => .{ .X86_64 = switch (@"type") {6524 // value (even for an external target symbol), and if the target symbol might be of type
6467 .write_rela => unreachable,6525 // STT_OBJECT, this should probably be `true`.
6468 .dsorel64, .dsorel32 => {6526 const try_copy_reloc: bool = switch (@"type".target) {
6469 assert(target.unwrap() == .local);6527 .rel, .abs => true,
6470 break :r .none;6528
6471 },6529 .pltrel,
6472 .abs64 => .@"64",6530 .pltabs,
6473 .abs32 => .@"32",6531 .dtpoff,
6474 .abs16 => unreachable,6532 .tpoff,
6475 .abs8 => unreachable,6533 .size,
6476 .abs32s => .@"32S",6534 => false,
6477 .rel64 => .PC64,6535
6478 .rel32 => .PC32,6536 .special => switch (@"type".action.special) {
6479 .rel16 => unreachable,6537 .larch_pcala_hi20,
6480 .rel8 => unreachable,6538 .larch_pcala64_lo20,
6481 .pltabs64, .pltabs32, .pltrel64, .pltrel32 => break :r .none,6539 .larch_pcala64_hi12,
6482 .dtpoff64 => .DTPOFF64,6540 => true,
6483 .dtpoff32 => .DTPOFF32,6541
6484 .tpoff64 => .TPOFF64,6542 .larch_b21,
6485 .tpoff32 => .TPOFF32,6543 .larch_b26,
6486 .size64 => .SIZE64,6544 .larch_call36,
6487 .size32 => .SIZE32,
6488
6489 .larch_abs32_lo12,
6490 .larch_rel32_hi20,
6491 .larch_rel64_lo20,
6492 .larch_rel64_hi12,
6493 .larch_branch_rel18,
6494 .larch_branch_rel23,
6495 .larch_branch_rel28,
6496 .larch_call_rel38,
6497 .larch_tpoff32_lo12,
6498 .larch_tpoff32_hi20,
6499 .larch_tpoff64_lo20,
6500 .larch_tpoff64_hi12,
6501 => unreachable,
6502
6503 .sparc_wdisp30,
6504 .sparc_pc10,
6505 .sparc_pc22,
6506 .sparc_wplt30,
6507 .sparc_h44,
6508 .sparc_m44,
6509 .sparc_l44,
6510 .sparc_ldo_hix22,
6511 .sparc_ldo_lox10,
6512 .sparc_le_hix22,
6513 .sparc_le_lox10,
6514 => unreachable,
6515 } },
6516 .LOONGARCH => .{ .LOONGARCH = switch (@"type") {
6517 .write_rela => unreachable,
6518 .dsorel64, .dsorel32 => {
6519 assert(target.unwrap() == .local);
6520 break :r .none;
6521 },
6522 .abs64 => .@"64",
6523 .abs32 => .@"32",
6524 .abs32s => unreachable,
6525 .abs16 => unreachable,
6526 .abs8 => unreachable,
6527 .rel64 => .@"64_PCREL",
6528 .rel32 => .@"32_PCREL",
6529 .rel16 => unreachable,
6530 .rel8 => unreachable,
6531 .pltabs64, .pltabs32, .pltrel64, .pltrel32 => break :r .none,
6532 .dtpoff64 => .TLS_DTPREL64,
6533 .dtpoff32 => .TLS_DTPREL32,
6534 .tpoff64 => .TLS_TPREL64,
6535 .tpoff32 => .TLS_TPREL32,
6536 .size64 => unreachable,
6537 .size32 => unreachable,
6538
6539 .larch_abs32_lo12 => .PCALA_LO12,
6540 .larch_rel32_hi20 => .PCALA_HI20,
6541 .larch_rel64_lo20 => .PCALA64_LO20,
6542 .larch_rel64_hi12 => .PCALA64_HI12,
6543 .larch_branch_rel18 => .B16,
6544 .larch_branch_rel23 => .B21,
6545 .larch_branch_rel28 => .B26,
6546 .larch_call_rel38 => .CALL36,
6547 .larch_tpoff32_lo12 => .TLS_LE_LO12,
6548 .larch_tpoff32_hi20 => .TLS_LE_HI20,
6549 .larch_tpoff64_lo20 => .TLS_LE64_LO20,
6550 .larch_tpoff64_hi12 => .TLS_LE64_HI12,
6551
6552 .sparc_wdisp30,
6553 .sparc_pc10,
6554 .sparc_pc22,
6555 .sparc_wplt30,
6556 .sparc_h44,
6557 .sparc_m44,
6558 .sparc_l44,
6559 .sparc_ldo_hix22,
6560 .sparc_ldo_lox10,
6561 .sparc_le_hix22,6545 .sparc_le_hix22,
6562 .sparc_le_lox10,6546 => false,
6563 => unreachable,6547 },
6564 } },
6565 .SPARCV9 => .{ .SPARC = switch (@"type") {
6566 .write_rela => unreachable,
6567 .dsorel64, .dsorel32 => {
6568 assert(target.unwrap() == .local);
6569 break :r .none;
6570 },
6571 .abs64 => .@"64",
6572 .abs32 => .@"32",
6573 .abs32s => unreachable,
6574 .abs16 => .@"16",
6575 .abs8 => .@"8",
6576 .rel64 => .DISP64,
6577 .rel32 => .DISP32,
6578 .rel16 => .DISP16,
6579 .rel8 => .DISP8,
6580 .pltabs64, .pltabs32, .pltrel64, .pltrel32 => break :r .none,
6581 .dtpoff64 => .TLS_DTPOFF64,
6582 .dtpoff32 => .TLS_DTPOFF32,
6583 .tpoff64 => .TLS_TPOFF64,
6584 .tpoff32 => .TLS_TPOFF32,
6585 .size64 => .SIZE64,
6586 .size32 => .SIZE32,
6587
6588 .larch_abs32_lo12,
6589 .larch_rel32_hi20,
6590 .larch_rel64_lo20,
6591 .larch_rel64_hi12,
6592 .larch_branch_rel18,
6593 .larch_branch_rel23,
6594 .larch_branch_rel28,
6595 .larch_call_rel38,
6596 .larch_tpoff32_lo12,
6597 .larch_tpoff32_hi20,
6598 .larch_tpoff64_lo20,
6599 .larch_tpoff64_hi12,
6600 => unreachable,
6601
6602 .sparc_wdisp30 => .WDISP30,
6603 .sparc_pc10 => .PC10,
6604 .sparc_pc22 => .PC22,
6605 .sparc_wplt30 => .WPLT30,
6606 .sparc_h44 => .H44,
6607 .sparc_m44 => .M44,
6608 .sparc_l44 => .L44,
6609 .sparc_ldo_hix22 => .TLS_LDO_HIX22,
6610 .sparc_ldo_lox10 => .TLS_LDO_LOX10,
6611 .sparc_le_hix22 => .TLS_LE_HIX22,
6612 .sparc_le_lox10 => .TLS_LE_LOX10,
6613 } },
6614 };6548 };
66156549
6616 class: switch (elf.classifySymbolValue(target)) {6550 classify: switch (elf.classifySymbolValue(target)) {
6617 .static => break :r .none,6551 .static => break :r .none,
6618 .static_relative => {6552 .static_relative => {
6619 if (!@"type".isAbsAddr(elf)) break :r .none;6553 switch (@"type".target) {
6554 // Only relocations which resolve to absolute addresses require runtime
6555 // `R_*_RELATIVE` relocations.
6556 .special,
6557 .pltrel,
6558 .rel,
6559 .dtpoff,
6560 .tpoff,
6561 .size,
6562 => break :r .none,
6563
6564 .abs, .pltabs => {},
6565 }
6566 if (!@"type".action.simple.dest.isAddr(elf)) break :r .none;
6620 switch (elf.nodeWantsDsoRelocation(node)) {6567 switch (elf.nodeWantsDsoRelocation(node)) {
6621 .no => break :r .none,6568 .no => break :r .none,
6622 .yes => {},6569 .yes => {},
...@@ -6629,30 +6576,47 @@ fn addSymbolRelocAssumeCapacity(...@@ -6629,30 +6576,47 @@ fn addSymbolRelocAssumeCapacity(
6629 .addend = 0,6576 .addend = 0,
6630 }).toOptional();6577 }).toOptional();
6631 },6578 },
6632 .dynamic => dso_reloc: switch (elf.nodeWantsDsoRelocation(node)) {6579 .dynamic => if (try_copy_reloc and try elf.maybeAddCopyRelocation(target.unwrap().global)) {
6633 .no => break :r .none,6580 switch (elf.classifySymbolValue(target)) {
6634 .yes_textrel => if (try elf.maybeAddCopyRelocation(target.unwrap().global)) {6581 .static => continue :classify .static,
6635 // We were able to use a copy relocation on this symbol to avoid a text relocation,6582 .static_relative => continue :classify .static_relative,
6636 // which is apparently considered a good thing despite copy relocations being an6583 .dynamic => unreachable, // we just added a copy relocation
6637 // abomination. (This is necessary for correctness in some cases, because e.g. a6584 }
6638 // 32-bit runtime relocation on a 64-bit target will often cause rtld errors due to6585 } else {
6639 // the DSOs being loaded too far apart.)6586 const dynamic_reloc_type: MachineRelocType = switch (@"type".target) {
6640 switch (elf.classifySymbolValue(target)) {6587 // PLT relocations targeting dynamic symbols actually target that symbol's PLT
6641 .dynamic => unreachable, // we just added a copy relocation6588 // entry, so we should emit an `R_*_RELATIVE` relocation instead.
6642 .static => continue :class .static,6589 .pltabs => continue :classify .static_relative,
6643 .static_relative => continue :class .static_relative,6590 // ...although PC-relative PLT relocations don't even need that!
6644 }6591 .pltrel => break :r .none,
6645 } else {6592 // Weird sizes or computations are not supported as runtime relocations.
6646 // At least for now, our only choice is a text relocation.6593 .special => break :r .none,
6647 elf.textrel_count += 1;6594 // Relative addresses are not supported as runtime relocations.
6648 continue :dso_reloc .yes;6595 .rel => break :r .none,
6649 },6596
6650 .yes => break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{6597 // On the few targets supporting size relocations, they are valid at runtime.
6651 .type = rela_type,6598 .size => switch (@"type".action.simple.dest) {
6599 .@"32" => MachineRelocType.size32(elf) orelse break :r .none,
6600 .@"64" => MachineRelocType.size64(elf) orelse break :r .none,
6601 else => break :r .none,
6602 },
6603 // Absolute addresses and TLS offsets can be lowered at runtime provided they
6604 // are address-sized.
6605 .dtpoff => if (@"type".action.simple.dest.isAddr(elf)) .dtpOff(elf) else break :r .none,
6606 .tpoff => if (@"type".action.simple.dest.isAddr(elf)) .tpOff(elf) else break :r .none,
6607 .abs => if (@"type".action.simple.dest.isAddr(elf)) .absAddr(elf) else break :r .none,
6608 };
6609 switch (elf.nodeWantsDsoRelocation(node)) {
6610 .no => break :r .none,
6611 .yes => {},
6612 .yes_textrel => elf.textrel_count += 1,
6613 }
6614 break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
6615 .type = dynamic_reloc_type,
6652 .offset = node_vaddr + offset,6616 .offset = node_vaddr + offset,
6653 .raw_sym_index = elf.globalByName(target.unwrap().global).?.dynsym_index,6617 .raw_sym_index = elf.globalByName(target.unwrap().global).?.dynsym_index,
6654 .addend = addend,6618 .addend = addend,
6655 }).toOptional(),6619 }).toOptional();
6656 },6620 },
6657 }6621 }
6658 };6622 };
...@@ -6673,8 +6637,9 @@ fn addSymbolRelocAssumeCapacity(...@@ -6673,8 +6637,9 @@ fn addSymbolRelocAssumeCapacity(
6673 .next = next,6637 .next = next,
6674 .prev = .none,6638 .prev = .none,
6675 .rela_index = rela_index,6639 .rela_index = rela_index,
6640 .result = .ok,
6676 });6641 });
6677 if (@"type".dependsOnTlsSize()) {6642 if (@"type".dependsOnTlsSize(elf)) {
6678 elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {});6643 elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {});
6679 }6644 }
66806645
...@@ -6689,7 +6654,7 @@ fn addGotRelocAssumeCapacity(...@@ -6689,7 +6654,7 @@ fn addGotRelocAssumeCapacity(
6689 addend: i64,6654 addend: i64,
6690 @"type": GotReloc.Type,6655 @"type": GotReloc.Type,
6691) void {6656) void {
6692 assert(elf.ehdrField(.type) != .REL);6657 assert(elf.ehdrType() != .REL);
6693 switch (elf.getNode(node)) {6658 switch (elf.getNode(node)) {
6694 .input_section,6659 .input_section,
6695 .nav,6660 .nav,
...@@ -6742,6 +6707,7 @@ fn addGotRelocAssumeCapacity(...@@ -6742,6 +6707,7 @@ fn addGotRelocAssumeCapacity(
6742 .target = target,6707 .target = target,
6743 .addend = addend,6708 .addend = addend,
6744 .type = @"type",6709 .type = @"type",
6710 .result = .ok,
6745 });6711 });
6746}6712}
6747fn updateGotEntry(elf: *Elf, got_index: usize) void {6713fn updateGotEntry(elf: *Elf, got_index: usize) void {
...@@ -6768,23 +6734,17 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -6768,23 +6734,17 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
6768 const sym_value = sym_id.value(elf);6734 const sym_value = sym_id.value(elf);
6769 break :val .{ .signed = @bitCast(sym_value -% tls_size) };6735 break :val .{ .signed = @bitCast(sym_value -% tls_size) };
6770 }6736 }
6771 const reloc_type: MachineRelocType = switch (elf.ehdrField(.machine)) {
6772 else => |machine| @panic(@tagName(machine)),
6773 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_TPREL64 else .TLS_TPREL32 },
6774 .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .TLS_TPOFF64 else .TLS_TPOFF32 },
6775 .X86_64 => .{ .X86_64 = .TPOFF64 },
6776 };
6777 break :val switch (sym_id.unwrap()) {6737 break :val switch (sym_id.unwrap()) {
6778 // For global symbols, just target the right dynsym with no addend.6738 // For global symbols, just target the right dynsym with no addend.
6779 .global => |name| .{ .reloc = .{6739 .global => |name| .{ .reloc = .{
6780 .type = reloc_type,6740 .type = .tpOff(elf),
6781 .dynsym_index = elf.globalByName(name).?.dynsym_index,6741 .dynsym_index = elf.globalByName(name).?.dynsym_index,
6782 .addend = 0,6742 .addend = 0,
6783 } },6743 } },
6784 // For local symbols, target the null symbol (index 0) so we get the offset to the6744 // For local symbols, target the null symbol (index 0) so we get the offset to the
6785 // base of our TLS block, and then use `addend` to offset to the right symbol.6745 // base of our TLS block, and then use `addend` to offset to the right symbol.
6786 .local => .{ .reloc = .{6746 .local => .{ .reloc = .{
6787 .type = reloc_type,6747 .type = .tpOff(elf),
6788 .dynsym_index = 0,6748 .dynsym_index = 0,
6789 .addend = @intCast(sym_id.value(elf)),6749 .addend = @intCast(sym_id.value(elf)),
6790 } },6750 } },
...@@ -6807,7 +6767,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -6807,7 +6767,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
6807 .static => .{ .unsigned = sym.value(elf) },6767 .static => .{ .unsigned = sym.value(elf) },
6808 .static_relative => unreachable, // TLS variables should be in TLS sections, which do not return `.static_relative`6768 .static_relative => unreachable, // TLS variables should be in TLS sections, which do not return `.static_relative`
6809 .dynamic => .{ .reloc = .{6769 .dynamic => .{ .reloc = .{
6810 .type = .dtpOffAddr(elf),6770 .type = .dtpOff(elf),
6811 .dynsym_index = elf.globalByName(sym.unwrap().global).?.dynsym_index,6771 .dynsym_index = elf.globalByName(sym.unwrap().global).?.dynsym_index,
6812 .addend = 0,6772 .addend = 0,
6813 } },6773 } },
...@@ -6818,12 +6778,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -6818,12 +6778,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
6818 break :val .{ .unsigned = 1 }; // TLS module ID for executable6778 break :val .{ .unsigned = 1 }; // TLS module ID for executable
6819 },6779 },
6820 .dynamic => .{ .reloc = .{6780 .dynamic => .{ .reloc = .{
6821 .type = switch (elf.ehdrField(.machine)) {6781 .type = .dtpMod(elf),
6822 else => |machine| @panic(@tagName(machine)),
6823 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 },
6824 .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 },
6825 .X86_64 => .{ .X86_64 = .DTPMOD64 },
6826 },
6827 .dynsym_index = switch (elf.classifySymbolValue(sym)) {6782 .dynsym_index = switch (elf.classifySymbolValue(sym)) {
6828 .static, .static_relative => 0,6783 .static, .static_relative => 0,
6829 .dynamic => elf.globalByName(sym.unwrap().global).?.dynsym_index,6784 .dynamic => elf.globalByName(sym.unwrap().global).?.dynsym_index,
...@@ -6837,12 +6792,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -6837,12 +6792,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
6837 break :val .{ .unsigned = 1 }; // TLS module ID for executable6792 break :val .{ .unsigned = 1 }; // TLS module ID for executable
6838 },6793 },
6839 .dynamic => .{ .reloc = .{6794 .dynamic => .{ .reloc = .{
6840 .type = switch (elf.ehdrField(.machine)) {6795 .type = .dtpMod(elf),
6841 else => |machine| @panic(@tagName(machine)),
6842 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 },
6843 .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 },
6844 .X86_64 => .{ .X86_64 = .DTPMOD64 },
6845 },
6846 .dynsym_index = 0,6796 .dynsym_index = 0,
6847 .addend = 0,6797 .addend = 0,
6848 } },6798 } },
...@@ -7113,18 +7063,30 @@ pub fn flush(...@@ -7113,18 +7063,30 @@ pub fn flush(
7113 for (elf.globals.strong_undef.keys()) |name| {7063 for (elf.globals.strong_undef.keys()) |name| {
7114 if (elf.dso_globals.contains(name)) continue;7064 if (elf.dso_globals.contains(name)) continue;
7115 any_undef = true;7065 any_undef = true;
7116 comp.link_diags.addError("undefined global symbol '{s}'", .{name.slice(elf)});7066 diags.addError("undefined global symbol '{s}'", .{name.slice(elf)});
7117 }7067 }
7118 if (any_undef) return error.AlreadyReported;7068 if (any_undef) return error.AlreadyReported;
7119 }7069 }
71207070
7121 elf.updateDynamicTextrel() catch |err| switch (err) {7071 elf.prepareDynamic() catch |err| switch (err) {
7122 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),7072 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
7123 else => |e| return e,7073 else => |e| return e,
7124 };7074 };
71257075
7126 while (try elf.idle(tid)) {}7076 while (try elf.idle(tid)) {}
71277077
7078 // We've done the final `idle` loop, so everything is at its final place in the file. We have a
7079 // few more things to check and write now that addresses and offsets are finalized.
7080
7081 if (elf.overflowed_reloc_count > 0) {
7082 diags.addError("failed to apply {d} relocations: overflow", .{elf.overflowed_reloc_count});
7083 }
7084 if (elf.misaligned_reloc_count > 0) {
7085 diags.addError("failed to apply {d} relocations: misaligned value", .{elf.misaligned_reloc_count});
7086 }
7087
7088 elf.flushDynamic();
7089
7128 const entry_addr: u64 = entry: {7090 const entry_addr: u64 = entry: {
7129 const sym_name_slice: []const u8 = name: switch (elf.options.entry) {7091 const sym_name_slice: []const u8 = name: switch (elf.options.entry) {
7130 .default => switch (comp.config.output_mode) {7092 .default => switch (comp.config.output_mode) {
...@@ -7151,44 +7113,6 @@ pub fn flush(...@@ -7151,44 +7113,6 @@ pub fn flush(
7151 else => |e| return e,7113 else => |e| return e,
7152 };7114 };
7153}7115}
7154fn updateDynamicTextrel(elf: *Elf) Error!void {
7155 if (elf.shndx.dynamic == .UNDEF) return;
7156 const dynamic_ni = elf.shndx.dynamic.get(elf).ni;
7157 switch (elf.shdrPtr(elf.shndx.dynamic)) {
7158 inline else => |shdr, class| if (elf.textrel_count > 0) {
7159 const cur_size = elf.targetLoad(&shdr.size);
7160 const cur_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast(
7161 dynamic_ni.slice(&elf.mf)[0..@intCast(cur_size)],
7162 ));
7163 const has_textrel: bool = for (cur_entries) |*entry| {
7164 if (elf.targetLoad(&entry[0]) == std.elf.DT_TEXTREL) {
7165 break true;
7166 }
7167 } else false;
7168 if (!has_textrel) {
7169 // Add a DT_TEXTREL entry before the final DT_NULL entry.
7170 const new_size = cur_size + @sizeOf([2]class.ElfN().Addr);
7171 try elf.ensureNodeSize(dynamic_ni, new_size);
7172 elf.targetStore(&shdr.size, new_size);
7173 const new_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast(
7174 dynamic_ni.slice(&elf.mf)[0..@intCast(new_size)],
7175 ));
7176 const write_entries = new_entries[new_entries.len - 2 ..][0..2];
7177 assert(elf.targetLoad(&write_entries[0][0]) == std.elf.DT_NULL);
7178 write_entries.* = .{
7179 .{ std.elf.DT_TEXTREL, 0 },
7180 .{ std.elf.DT_NULL, 0 },
7181 };
7182 if (elf.targetEndian() != native_endian) {
7183 std.mem.byteSwapAllElements([2]class.ElfN().Addr, write_entries);
7184 }
7185 }
7186 } else {
7187 // TODO: remove the DT_TEXTREL entry if there is one, because it's not necessary any
7188 // more. It won't cause any issues having it there, it's just inefficient.
7189 },
7190 }
7191}
71927116
7193pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {7117pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
7194 const comp = elf.base.comp;7118 const comp = elf.base.comp;
...@@ -7220,7 +7144,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {...@@ -7220,7 +7144,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
7220 const sym_id: Symbol.Id = .global(global_name);7144 const sym_id: Symbol.Id = .global(global_name);
7221 const sym = global.symtab_index.ptr(elf);7145 const sym = global.symtab_index.ptr(elf);
72227146
7223 switch (elf.ehdrField(.type)) {7147 switch (elf.ehdrType()) {
7224 .REL => {7148 .REL => {
7225 // Index in `.symtab` has changed. Relocatables are easy, we just need to update7149 // Index in `.symtab` has changed. Relocatables are easy, we just need to update
7226 // all of the output relocations.7150 // all of the output relocations.
...@@ -7238,7 +7162,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {...@@ -7238,7 +7162,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
7238 // For other `ET_*` values, the index in `.dynsym` has changed. There are a few7162 // For other `ET_*` values, the index in `.dynsym` has changed. There are a few
7239 // places we might have emitted output relocations, depending on whether or not the7163 // places we might have emitted output relocations, depending on whether or not the
7240 // symbol's value is statically known.7164 // symbol's value is statically known.
7241 else => switch (elf.classifySymbolValue(sym_id)) {7165 .EXEC, .DYN => switch (elf.classifySymbolValue(sym_id)) {
7242 .static, .static_relative => {7166 .static, .static_relative => {
7243 // Since the symbol value is statically known, we definitely aren't emitting7167 // Since the symbol value is statically known, we definitely aren't emitting
7244 // any relocation targeting it (we might have `R_*_RELATIVE` relocs but they7168 // any relocation targeting it (we might have `R_*_RELATIVE` relocs but they
...@@ -7807,10 +7731,202 @@ fn updateDynamicEntry(elf: *Elf, key: u32, new_val: u64) void {...@@ -7807,10 +7731,202 @@ fn updateDynamicEntry(elf: *Elf, key: u32, new_val: u64) void {
7807 },7731 },
7808 }7732 }
7809}7733}
7734fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void {
7735 const target_endian = elf.targetEndian();
7736
7737 // We use the existing free-list tracking of the `.rela.plt` section to also behave as a
7738 // free-list for the PLT itself---see `pltEntryIsDead` for details.
7739 const plt_index: u32 = @intFromEnum(elf.shndx.rela_plt.relaAddOneAssumeCapacity(elf, .{
7740 .type = .jumpSlot(elf),
7741 .offset = 0, // populated later
7742 .raw_sym_index = dynsym_index,
7743 .addend = 0,
7744 }));
7745
7746 // On architectures without `.got.plt` (e.g. SPARC) these values actually refer to `.plt`.
7747 const got_plt_section: Section.Index, const got_plt_offset: u64 = got_plt: {
7748 const plt = elf.targetPltInfo();
7749 break :got_plt if (plt.got_plt) |got_plt| .{
7750 elf.shndx.got_plt,
7751 elf.targetPtrSize() * (got_plt.header_entries + plt_index),
7752 } else .{
7753 elf.shndx.plt,
7754 plt.entry_size * (plt.header_entries + plt_index),
7755 };
7756 };
7757
7758 // Now that we know the index, we can set the relocation's offset.
7759 elf.shndx.rela_plt.relaSetOffset(elf, @enumFromInt(plt_index), got_plt_section.vaddr(elf) + got_plt_offset);
7760
7761 if (plt_index < elf.plt.count()) {
7762 // We reused a free entry, so we're already done!
7763 elf.plt.setKey(plt_index, global_name);
7764 return;
7765 }
7766
7767 // We added a new entry, so we now need to extend the PLT sections.
7768 assert(plt_index == elf.plt.count());
7769 elf.plt.putAssumeCapacityNoClobber(global_name, {});
7770
7771 switch (elf.ehdrMachine()) {
7772 .AARCH64, .PPC64, .RISCV => |machine| @panic(@tagName(machine)),
7773 .X86_64 => {
7774 const plt_ni = elf.shndx.plt.get(elf).ni;
7775 const plt_addr = plt_addr: switch (elf.shdrPtr(elf.shndx.plt)) {
7776 inline else => |shdr| {
7777 const old_size = 16 * (1 + plt_index);
7778 assert(elf.targetLoad(&shdr.size) == old_size);
7779 elf.targetStore(&shdr.size, old_size + 16);
7780 const plt_slice = plt_ni.slice(&elf.mf)[old_size..][0..16];
7781 @memcpy(plt_slice, &[16]u8{
7782 0xf3, 0x0f, 0x1e, 0xfa, // endbr64
7783 0x68, 0x00, 0x00, 0x00, 0x00, // push $0x0
7784 0xe9, 0x00, 0x00, 0x00, 0x00, // jmp 0
7785 0x66, 0x90, // xchg %ax,%ax
7786 });
7787 std.mem.writeInt(u32, plt_slice[5..][0..4], plt_index, target_endian);
7788 std.mem.writeInt(
7789 i32,
7790 plt_slice[10..][0..4],
7791 -@as(i32, @intCast(old_size + 14)),
7792 target_endian,
7793 );
7794 break :plt_addr elf.targetLoad(&shdr.addr) + old_size;
7795 },
7796 };
7797
7798 const got_plt_ni = elf.shndx.got_plt.get(elf).ni;
7799 switch (elf.shdrPtr(elf.shndx.got_plt)) {
7800 inline else => |shdr, class| {
7801 assert(elf.targetLoad(&shdr.size) == got_plt_offset);
7802 elf.targetStore(&shdr.size, @intCast(got_plt_offset + @sizeOf(class.ElfN().Addr)));
7803 std.mem.writeInt(
7804 class.ElfN().Addr,
7805 got_plt_ni.slice(&elf.mf)[@intCast(got_plt_offset)..][0..@sizeOf(class.ElfN().Addr)],
7806 @intCast(plt_addr),
7807 target_endian,
7808 );
7809 },
7810 }
7811
7812 const plt_sec_ni = elf.shndx.plt_sec.get(elf).ni;
7813 switch (elf.shdrPtr(elf.shndx.plt_sec)) {
7814 inline else => |shdr| {
7815 const old_size = 16 * plt_index;
7816 elf.targetStore(&shdr.size, old_size + 16);
7817 const plt_sec_slice = plt_sec_ni.slice(&elf.mf)[old_size..][0..16];
7818 @memcpy(plt_sec_slice, &[16]u8{
7819 0xf3, 0x0f, 0x1e, 0xfa, // endbr64
7820 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *0x0(%rip)
7821 0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00, // nopw 0x0(%rax,%rax,1)
7822 });
7823 std.mem.writeInt(
7824 i32,
7825 plt_sec_slice[6..][0..4],
7826 @intCast(@as(i64, @bitCast(
7827 (got_plt_section.vaddr(elf) + got_plt_offset) -% (elf.targetLoad(&shdr.addr) + old_size + 10),
7828 ))),
7829 target_endian,
7830 );
7831 },
7832 }
7833 },
7834 .LOONGARCH => {
7835 // add a .PLT entry, writing the template
7836 const plt_ni = elf.shndx.plt.get(elf).ni;
7837 const plt_addr, const plt_slice = plt_entry: switch (elf.shdrPtr(elf.shndx.plt)) {
7838 inline else => |shdr| {
7839 const old_size = 16 * (1 + plt_index);
7840 assert(elf.targetLoad(&shdr.size) == old_size);
7841 elf.targetStore(&shdr.size, old_size + 16);
7842 const plt_slice = plt_ni.slice(&elf.mf)[old_size..][0..16];
7843 @memcpy(plt_slice, source: switch (elf.identClass()) {
7844 .NONE, _ => unreachable,
7845 inline .@"32", .@"64" => |elf_class| {
7846 const ld_byte = if (elf_class == .@"64") 0xc0 else 0x80;
7847 break :source &[16]u8{
7848 0x1a, 0x00, 0x00, 0x0f, // pcalau12i $t3, %pc_hi20(func@.got.plt)
7849 0x28, ld_byte, 0x01, 0xef, // ld.w/d $t3, $t3, %lo12(func@.got.plt)
7850 0x4c, 0x00, 0x01, 0xed, // jirl $t1, $t3, 0
7851 0x00, 0x2a, 0x00, 0x00, // break
7852 };
7853 },
7854 });
7855 break :plt_entry .{ elf.targetLoad(&shdr.addr) + old_size, plt_slice };
7856 },
7857 };
7858
7859 // add a .GOT.PLT entry, writing the address of the corresponding .PLT entry
7860 const got_plt_ni = elf.shndx.got_plt.get(elf).ni;
7861 switch (elf.shdrPtr(elf.shndx.got_plt)) {
7862 inline else => |shdr, class| {
7863 assert(elf.targetLoad(&shdr.size) == got_plt_offset);
7864 elf.targetStore(&shdr.size, @intCast(got_plt_offset + @sizeOf(class.ElfN().Addr)));
7865 std.mem.writeInt(
7866 class.ElfN().Addr,
7867 got_plt_ni.slice(&elf.mf)[@intCast(got_plt_offset)..][0..@sizeOf(class.ElfN().Addr)],
7868 @intCast(plt_addr),
7869 target_endian,
7870 );
7871 },
7872 }
7873
7874 // relocate the PLT entry to point to the .GOT.PLT entry
7875 const got_plt_abs = got_plt_section.vaddr(elf) + got_plt_offset;
7876 // TODO: handle overflow gracefully
7877 const inst0: *align(1) link.loongarch.J20 = @ptrCast(plt_slice[0..4]);
7878 const inst1: *align(1) link.loongarch.K12 = @ptrCast(plt_slice[4..8]);
7879 elf.targetStore(inst0, .{
7880 .b0_4 = elf.targetLoad(inst0).b0_4,
7881 .j20 = link.loongarch.pcalaHi20(got_plt_abs, plt_addr),
7882 .b25_31 = elf.targetLoad(inst0).b25_31,
7883 });
7884 elf.targetStore(inst1, .{
7885 .b0_9 = elf.targetLoad(inst1).b0_9,
7886 .k12 = @truncate(got_plt_abs),
7887 .b22_31 = elf.targetLoad(inst1).b22_31,
7888 });
7889 },
7890 .SPARCV9 => {
7891 // add a .PLT entry, writing the template
7892 const plt_ni = elf.shndx.plt.get(elf).ni;
7893 switch (elf.shdrPtr(elf.shndx.plt)) {
7894 inline else => |shdr| {
7895 assert(elf.targetLoad(&shdr.size) == got_plt_offset);
7896 elf.targetStore(&shdr.size, @intCast(got_plt_offset + 32));
7897 const Inst = packed union(u32) {
7898 raw: u32,
7899 imm22: packed struct { imm: u22, op: u10 },
7900 disp19: packed struct { disp: u19, op: u13 },
7901 };
7902 const plt_slice: []Inst = @ptrCast(@alignCast(plt_ni.slice(&elf.mf)[@intCast(got_plt_offset)..][0..32]));
7903 @memcpy(plt_slice, &[8]Inst{
7904 // sethi (. - .plt[0]), %g1
7905 .{ .imm22 = .{ .imm = @truncate(got_plt_offset), .op = 0b0000000011 } },
7906 // ba,a %xcc, .plt[1]
7907 .{ .disp19 = .{ .disp = @truncate((got_plt_offset + 4 - 32) >> 2), .op = 0b1100001101000 } },
7908 // nop
7909 .{ .raw = 0x0100_0000 },
7910 // nop
7911 .{ .raw = 0x0100_0000 },
7912 // nop
7913 .{ .raw = 0x0100_0000 },
7914 // nop
7915 .{ .raw = 0x0100_0000 },
7916 // nop
7917 .{ .raw = 0x0100_0000 },
7918 // nop
7919 .{ .raw = 0x0100_0000 },
7920 });
7921 },
7922 }
7923 },
7924 }
7925}
7810fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_addr: u64, addr: u64) void {7926fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_addr: u64, addr: u64) void {
7811 const target_endian = elf.targetEndian();7927 const target_endian = elf.targetEndian();
7812 switch (elf.ehdrField(.machine)) {7928 switch (elf.ehdrMachine()) {
7813 else => |machine| @panic(@tagName(machine)),7929 .AARCH64, .PPC64, .RISCV => |machine| @panic(@tagName(machine)),
7814 .X86_64 => {7930 .X86_64 => {
7815 switch (which) {7931 switch (which) {
7816 .plt => return,7932 .plt => return,
...@@ -7912,8 +8028,20 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad...@@ -7912,8 +8028,20 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad
79128028
7913 const got_plt_abs: u64 = got_plt_addr + got_plt_offset;8029 const got_plt_abs: u64 = got_plt_addr + got_plt_offset;
7914 // TODO: handle overflow gracefully8030 // TODO: handle overflow gracefully
7915 link.loongarch.writeJ20(target_slice[0..4], link.loongarch.toPcalaHi20(got_plt_abs, plt_addr + plt_offset));8031 const inst0: *align(1) link.loongarch.J20 = @ptrCast(target_slice[0..4]);
7916 link.loongarch.writeK12(target_slice[4..8], @truncate(got_plt_abs));8032 const inst1: *align(1) link.loongarch.K12 = @ptrCast(target_slice[4..8]);
8033
8034 elf.targetStore(inst0, .{
8035 .b0_4 = elf.targetLoad(inst0).b0_4,
8036 .j20 = link.loongarch.pcalaHi20(got_plt_abs, plt_addr + plt_offset),
8037 .b25_31 = elf.targetLoad(inst0).b25_31,
8038 });
8039
8040 elf.targetStore(inst1, .{
8041 .b0_9 = elf.targetLoad(inst1).b0_9,
8042 .k12 = @truncate(got_plt_abs),
8043 .b22_31 = elf.targetLoad(inst1).b22_31,
8044 });
7917 }8045 }
7918 },8046 },
7919 }8047 }
...@@ -7986,10 +8114,10 @@ fn updateExportsInner(...@@ -7986,10 +8114,10 @@ fn updateExportsInner(
7986 .size = @intCast(size),8114 .size = @intCast(size),
7987 .type = @"type",8115 .type = @"type",
7988 .bind = switch (@"export".opts.linkage) {8116 .bind = switch (@"export".opts.linkage) {
7989 .internal => @panic("TODO internal linkage"),
7990 .strong => .strong,8117 .strong => .strong,
7991 .weak => .weak,8118 .weak => .weak,
7992 .link_once => return elf.base.comp.link_diags.fail("TODO(Elf2): link_once is not supported", .{}),8119 .internal => return elf.base.comp.link_diags.fail("TODO(Elf2): '.internal' linkage", .{}),
8120 .link_once => return elf.base.comp.link_diags.fail("TODO(Elf2): '.link_once' linkage", .{}),
7993 },8121 },
7994 .visibility = switch (@"export".opts.visibility) {8122 .visibility = switch (@"export".opts.visibility) {
7995 .default => .DEFAULT,8123 .default => .DEFAULT,
...@@ -8152,3 +8280,19 @@ fn ensureNodeSize(...@@ -8152,3 +8280,19 @@ fn ensureNodeSize(
8152 const new_size = need_size + need_size / MappedFile.growth_factor;8280 const new_size = need_size + need_size / MappedFile.growth_factor;
8153 try node.resize(&elf.mf, gpa, new_size);8281 try node.resize(&elf.mf, gpa, new_size);
8154}8282}
8283
8284/// If `sym` has a PLT entry, returns the address of that entry (specifically, the address which a
8285/// branch to the PLT should target). If `sym` does not have a PLT entry, returns `null`.
8286fn pltEntryTargetAddr(elf: *Elf, sym: Symbol.Id) ?u64 {
8287 const index = switch (sym.unwrap()) {
8288 .local => return null,
8289 .global => |name| elf.plt.getIndex(name) orelse return null,
8290 };
8291 if (elf.pltEntryIsDead(index)) return null;
8292 const plt = elf.targetPltInfo();
8293 if (plt.plt_sec) |plt_sec| {
8294 return elf.shndx.plt_sec.vaddr(elf) +% index * plt_sec.entry_size;
8295 } else {
8296 return elf.shndx.plt.vaddr(elf) +% (plt.header_entries + index) * plt.entry_size;
8297 }
8298}
src/link/loongarch.zig+8-42
...@@ -1,54 +1,20 @@...@@ -1,54 +1,20 @@
1const std = @import("std");1pub const J20 = packed struct(u32) { b0_4: u5, j20: u20, b25_31: u7 };
2const mem = std.mem;2pub const K12 = packed struct(u32) { b0_9: u10, k12: u12, b22_31: u10 };
3pub const K16 = packed struct(u32) { b0_9: u10, k16: u16, b26_31: u6 };
4pub const D5K16 = packed struct(u32) { d5: u5, b5_9: u5, k16: u16, b26_31: u6 };
5pub const D10K16 = packed struct(u32) { d10: u10, k16: u16, b26_31: u6 };
36
4pub fn writeK12(code: *[4]u8, target_value: u12) void {7pub fn pcalaHi20(target: u64, pc: u64) u20 {
5 var inst = std.mem.readInt(u32, code, .little);
6 inst &= 0b11111111110000000000001111111111;
7 inst |= (@as(u32, target_value) << 10);
8 std.mem.writeInt(u32, code, inst, .little);
9}
10
11pub fn writeK16(code: *[4]u8, target_value: u16) void {
12 var inst = std.mem.readInt(u32, code, .little);
13 inst &= 0b11111100000000000000001111111111;
14 inst |= (@as(u32, target_value) << 10);
15 std.mem.writeInt(u32, code, inst, .little);
16}
17
18pub fn writeJ20(code: *[4]u8, target_value: u20) void {
19 var inst = std.mem.readInt(u32, code, .little);
20 inst &= 0b11111110000000000000000000011111;
21 inst |= (@as(u32, target_value) << 5);
22 std.mem.writeInt(u32, code, inst, .little);
23}
24
25pub fn writeD5K16(code: *[4]u8, target_value: u21) void {
26 var inst = std.mem.readInt(u32, code, .little);
27 inst &= 0b11111100000000000000001111100000;
28 inst |= @as(u32, target_value >> 16);
29 inst |= (@as(u32, target_value << 5) << 5);
30 std.mem.writeInt(u32, code, inst, .little);
31}
32
33pub fn writeD10K16(code: *[4]u8, target_value: u26) void {
34 var inst = std.mem.readInt(u32, code, .little);
35 inst &= 0b11111100000000000000000000000000;
36 inst |= @as(u32, target_value >> 16);
37 inst |= @as(u32, target_value << 10);
38 std.mem.writeInt(u32, code, inst, .little);
39}
40
41pub fn toPcalaHi20(target: u64, pc: u64) u20 {
42 return @truncate(((target +% 0x800) >> 12) -% (pc >> 12));8 return @truncate(((target +% 0x800) >> 12) -% (pc >> 12));
43}9}
4410
45pub fn toPcala64Lo20(target: u64, pc: u64) u20 {11pub fn pcala64Lo20(target: u64, pc: u64) u20 {
46 const fixup = if (target & 0x800 != 0) (@as(u64, 0x1000) -% @as(u64, 0x100000000)) else 0;12 const fixup = if (target & 0x800 != 0) (@as(u64, 0x1000) -% @as(u64, 0x100000000)) else 0;
47 const hi32 = (((target +% 0x80000000 +% fixup) >> 12) -% ((pc -% 8) >> 12)) >> 20;13 const hi32 = (((target +% 0x80000000 +% fixup) >> 12) -% ((pc -% 8) >> 12)) >> 20;
48 return @truncate(hi32);14 return @truncate(hi32);
49}15}
5016
51pub fn toPcala64Hi12(target: u64, pc: u64) u12 {17pub fn pcala64Hi12(target: u64, pc: u64) u12 {
52 const fixup = if (target & 0x800 != 0) (@as(u64, 0x1000) -% @as(u64, 0x100000000)) else 0;18 const fixup = if (target & 0x800 != 0) (@as(u64, 0x1000) -% @as(u64, 0x100000000)) else 0;
53 const hi32 = (((target +% 0x80000000 +% fixup) >> 12) -% ((pc -% 12) >> 12)) >> 20;19 const hi32 = (((target +% 0x80000000 +% fixup) >> 12) -% ((pc -% 12) >> 12)) >> 20;
54 return @truncate(hi32 >> 20);20 return @truncate(hi32 >> 20);
src/link/sparc.zig deleted-197
...@@ -1,197 +0,0 @@
1const std = @import("std");
2
3/// Calculation operands:
4///
5/// * `A`: relocation addend
6/// * `G`: symbol GOT slot offset
7/// * `GOT`: GOT base address (`_GLOBAL_OFFSET_TABLE_` value)
8/// * `L`: symbol PLT slot address
9/// * `O`: secondary relocation addend
10/// * `P`: relocation address
11/// * `S`: symbol value
12/// * `Z`: symbol size
13///
14/// Field semantics:
15///
16/// * `T-*`: truncate (don't check for overflow)
17/// * `V-*`: verify (check for overflow)
18pub const reloc = struct {
19 /// R_SPARC_8 (V-byte8) = S + A
20 /// R_SPARC_DISP8 (V-byte8) = S + A - P
21 pub const Byte8 = packed struct(u8) {
22 byte8: u8,
23 };
24
25 /// R_SPARC_16 (V-half16) = S + A
26 /// R_SPARC_DISP16 (V-half16) = S + A - P
27 /// R_SPARC_UA16 (V-half16) = S + A
28 pub const Half16 = packed struct(u16) {
29 half16: u16,
30 };
31
32 /// R_SPARC_32 (V-word32) = S + A
33 /// R_SPARC_GLOB_DAT (V-word32) = S + A [32-bit only]
34 /// R_SPARC_UA32 (V-word32) = S + A
35 /// R_SPARC_PCPLT32 (V-word32) = L + A - P
36 /// R_SPARC_REGISTER (V-word32) = S + A [32-bit only]
37 /// R_SPARC_TLS_DTPMOD32 (V-word32) = @dtpmod(S + A)
38 /// R_SPARC_TLS_DTPOFF32 (V-word32) = @dtpoff(S + A)
39 /// R_SPARC_TLS_TPOFF32 (V-word32) = @tpoff(S + A)
40 /// R_SPARC_SIZE32 (V-word32) = Z + A
41 pub const Word32 = packed struct(u32) {
42 word32: u32,
43 };
44
45 /// R_SPARC_GLOB_DAT (V-word64) = S + A [64-bit only]
46 /// R_SPARC_64 (V-word64) = S + A
47 /// R_SPARC_DISP64 (V-word64) = S + A - P
48 /// R_SPARC_PLT64 (V-word64) = L + A
49 /// R_SPARC_REGISTER (V-word64) = S + A [64-bit only]
50 /// R_SPARC_UA64 (V-word64) = S + A
51 /// R_SPARC_TLS_DTPMOD64 (V-word64) = @dtpmod(S + A)
52 /// R_SPARC_TLS_DTPOFF64 (V-word64) = @dtpoff(S + A)
53 /// R_SPARC_TLS_TPOFF64 (V-word64) = @tpoff(S + A)
54 /// R_SPARC_SIZE64 (V-word64) = Z + A
55 pub const Word64 = packed struct(u64) {
56 word64: u64,
57 };
58
59 /// R_SPARC_5 (V-imm5) = S + A
60 pub const Imm5 = packed struct(u32) {
61 imm5: u5,
62 b5_31: u27,
63 };
64
65 /// R_SPARC_6 (V-imm6) = S + A
66 pub const Imm6 = packed struct(u32) {
67 imm6: u6,
68 b6_31: u26,
69 };
70
71 /// R_SPARC_7 (V-imm7) = S + A
72 pub const Imm7 = packed struct(u32) {
73 imm7: u7,
74 b7_31: u25,
75 };
76
77 /// R_SPARC_M44 (T-imm10) = ((S + A) >> 12) & 0x3ff
78 pub const Imm10 = packed struct(u32) {
79 imm10: u10,
80 b10_31: u22,
81 };
82
83 /// R_SPARC_10 (V-simm10) = S + A
84 pub const Simm10 = packed struct(u32) {
85 simm10: u10,
86 b10_31: u22,
87 };
88
89 /// R_SPARC_11 (V-simm11) = S + A
90 pub const Simm11 = packed struct(u32) {
91 simm11: u11,
92 b11_31: u21,
93 };
94
95 /// R_SPARC_L44 (T-imm13) = (S + A) & 0xfff
96 /// R_SPARC_GOTDATA_LOX10 (T-imm13) = ((S + A - GOT) & 0x3ff) | (((S + A - GOT) >> 31) & 0x1c00)
97 /// R_SPARC_GOTDATA_OP_LOX10 (T-imm13) = (G & 0x3ff) | ((G >> 31) & 0x1c00)
98 pub const Imm13 = packed struct(u32) {
99 imm13: u13,
100 b13_31: u19,
101 };
102
103 /// R_SPARC_13 (V-simm13) = S + A
104 /// R_SPARC_LO10 (T-simm13) = (S + A) & 0x3ff
105 /// R_SPARC_GOT10 (T-simm13) = G & 0x3ff
106 /// R_SPARC_GOT13 (V-simm13) = G
107 /// R_SPARC_PC10 (T-simm13) = (S + A - P) & 0x3ff
108 /// R_SPARC_LOPLT10 (T-simm13) = (L + A) & 0x3ff
109 /// R_SPARC_PCPLT10 (V-simm13) = (L + A - P) & 0x3ff
110 /// R_SPARC_OLO10 (V-simm13) = ((S + A) & 0x3ff) + O
111 /// R_SPARC_HM10 (T-simm13) = ((S + A) >> 32) & 0x3ff
112 /// R_SPARC_PC_HM10 (T-simm13) = ((S + A - P) >> 32) & 0x3ff
113 /// R_SPARC_LOX10 (T-simm13) = ((S + A) & 0x3ff) | 0x1c00
114 /// R_SPARC_TLS_GD_LO10 (T-simm13) = @dtlndx(S + A) & 0x3ff
115 /// R_SPARC_TLS_LDM_LO10 (T-simm13) = @tmndx(S + A) & 0x3ff
116 /// R_SPARC_TLS_LDO_LOX10 (T-simm13) = @dtpoff(S + A) & 0x3ff
117 /// R_SPARC_TLS_IE_LO10 (T-simm13) = @got(@tpoff(S + A)) & 0x3ff
118 /// R_SPARC_TLS_LE_LOX10 (T-simm13) = (@tpoff(S + A) & 0x3ff) | 0x1c00
119 pub const Simm13 = packed struct(u32) {
120 simm13: u13,
121 b13_31: u19,
122 };
123
124 /// R_SPARC_HI22 (T-imm22) = (S + A) >> 10 [32-bit only]
125 /// R_SPARC_HI22 (V-imm22) = (S + A) >> 10 [64-bit only]
126 /// R_SPARC_22 (V-imm22) = S + A
127 /// R_SPARC_HIPLT22 (T-imm22) = (L + A) >> 10
128 /// R_SPARC_HH22 (V-imm22) = (S + A) >> 42
129 /// R_SPARC_LM22 (T-imm22) = (S + A) >> 10
130 /// R_SPARC_PC_HH22 (V-imm22) = (S + A - P) >> 42
131 /// R_SPARC_PC_LM22 (T-imm22) = (S + A - P) >> 10
132 /// R_SPARC_HIX22 (V-imm22) = ((S + A) ^ 0xffffffffffffffff) >> 10
133 /// R_SPARC_H44 (V-imm22) = (S + A) >> 22
134 /// R_SPARC_TLS_LE_HIX22 (T-imm22) = (@tpoff(S + A) ^ 0xffffffffffffffff) >> 10
135 /// R_SPARC_GOTDATA_HIX22 (V-imm22) = ((S + A - GOT) >> 10) ^ ((S + A - GOT) >> 31)
136 /// R_SPARC_GOTDATA_OP_HIX22 (T-imm22) = (G >> 10) ^ (G >> 31)
137 /// R_SPARC_H34 (V-imm22) = (S + A) >> 12
138 pub const Imm22 = packed struct(u32) {
139 imm22: u22,
140 b22_31: u10,
141 };
142
143 /// R_SPARC_GOT22 (T-simm22) = G >> 10
144 /// R_SPARC_TLS_GD_HI22 (T-simm22) = @dtlndx(S + A) >> 10
145 /// R_SPARC_TLS_LDM_HI22 (T-simm22) = @tmndx(S + A) >> 10
146 /// R_SPARC_TLS_LDO_HIX22 (T-simm22) = @dtpoff(S + A) >> 10
147 /// R_SPARC_TLS_IE_HI22 (T-simm22) = @got(@tpoff(S + A)) >> 10
148 pub const Simm22 = packed struct(u32) {
149 simm22: u22,
150 b22_31: u10,
151 };
152
153 /// R_SPARC_WDISP19 (V-disp19) = (S + A - P) >> 2
154 pub const Disp19 = packed struct(u32) {
155 disp19: u19,
156 b19_31: u13,
157 };
158
159 /// R_SPARC_WDISP22 (V-disp22) = (S + A - P) >> 2
160 /// R_SPARC_PC22 (V-disp22) = (S + A - P) >> 10
161 /// R_SPARC_PCPLT22 (V-disp22) = (L + A - P) >> 10
162 pub const Disp22 = packed struct(u32) {
163 disp22: u22,
164 b22_31: u10,
165 };
166
167 /// R_SPARC_WDISP30 (V-disp30) = (S + A - P) >> 2
168 /// R_SPARC_WPLT30 (V-disp30) = (L + A - P) >> 2
169 /// R_SPARC_TLS_GD_CALL (V-disp30) = (L + A - P) >> 2
170 /// R_SPARC_TLS_LDM_CALL (V-disp30) = (L + A - P) >> 2
171 pub const Disp30 = packed struct(u32) {
172 disp30: u30,
173 b30_31: u2,
174 };
175
176 /// R_SPARC_DISP32 (V-disp32) = S + A - P
177 pub const Disp32 = packed struct(u32) {
178 disp32: u32,
179 };
180
181 /// R_SPARC_WDISP10 (V-d2/disp8) = (S + A - P) >> 2
182 pub const D2Disp8 = packed struct(u32) {
183 b0_3: u4,
184 disp8: u8,
185 b12_17: u6,
186 d2: u2,
187 b20_31: u12,
188 };
189
190 /// R_SPARC_WDISP16 (V-d2/disp14) = (S + A - P) >> 2
191 pub const D2Disp14 = packed struct(u32) {
192 disp14: u14,
193 b14_19: u6,
194 d2: u2,
195 b22_31: u10,
196 };
197};