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 {
10531053 entry: Elf32.Addr,
10541054 phoff: Elf32.Off,
10551055 shoff: Elf32.Off,
1056 flags: Word,
1056 flags: EhdrFlags,
10571057 ehsize: Half,
10581058 phentsize: Half,
10591059 phnum: Half,
......@@ -1143,7 +1143,7 @@ pub const Elf64 = struct {
11431143 entry: Elf64.Addr,
11441144 phoff: Elf64.Off,
11451145 shoff: Elf64.Off,
1146 flags: Word,
1146 flags: EhdrFlags,
11471147 ehsize: Half,
11481148 phentsize: Half,
11491149 phnum: Half,
......@@ -1644,7 +1644,7 @@ pub const CLASS = enum(u8) {
16441644
16451645 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 {
16481648 return switch (class) {
16491649 .NONE, _ => unreachable,
16501650 .@"32" => 4,
......@@ -3377,9 +3377,12 @@ pub const gnu_hash = struct {
33773377 }
33783378};
33793379
3380pub const loongarch = struct {
3381 /// Ehdr.e_flags bits of LoongArch
3382 pub const EFlags = packed struct(Word) {
3380pub const EhdrFlags = packed union(Word) {
3381 int: u32,
3382 loongarch: Loongarch,
3383 sparc: Sparc,
3384
3385 pub const Loongarch = packed struct(u32) {
33833386 base_abi_modifier: BaseAbiModifier,
33843387 abi_extension: AbiExtension,
33853388 abi_version: u2,
......@@ -3393,10 +3396,8 @@ pub const loongarch = struct {
33933396 };
33943397 pub const AbiExtension = enum(u3) { base = 0, _ };
33953398 };
3396};
33973399
3398pub const sparc = struct {
3399 pub const EFlags = packed struct(Word) {
3400 pub const Sparc = packed struct(u32) {
34003401 mm: MemoryModel,
34013402 _reserved1: u6 = 0,
34023403 ext: Extensions,
src/link.zig-1
......@@ -32,7 +32,6 @@ pub const ConstPool = @import("link/ConstPool.zig");
3232
3333pub const aarch64 = @import("link/aarch64.zig");
3434pub const loongarch = @import("link/loongarch.zig");
35pub const sparc = @import("link/sparc.zig");
3635
3736pub const Error = Allocator.Error || Io.Cancelable || error{
3837 /// 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 {
4444 fini_array: Section.Index,
4545 preinit_array: Section.Index,
4646},
47dynamic: struct {
48 flags: u32,
49 flags_1: u32,
50 rpath: String(.dynstr),
51 soname: String(.dynstr),
52},
4753symtab: std.ArrayList(Symbol),
4854globals: struct {
4955 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 {
5864 rela_index: Section.RelaIndex,
5965}),
6066/// 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.
6268///
6369/// Therefore, if, when scanning a DSO input, we discover a definition for one of these symbols, we
6470/// will remove it from this map and call `maybeAddCopyRelocation`.
......@@ -165,6 +171,9 @@ changed_symtab_index: std.array_hash_map.Auto(String(.strtab), void),
165171/// section in `flush` only when it is actually necessary. See also `nodeWantsDsoRelocation`.
166172textrel_count: u32,
167173
174overflowed_reloc_count: u32,
175misaligned_reloc_count: u32,
176
168177const_prog_node: std.Progress.Node,
169178synth_prog_node: std.Progress.Node,
170179input_prog_node: std.Progress.Node,
......@@ -486,6 +495,12 @@ const Section = struct {
486495 };
487496 }
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
489504 fn flags(s: Index, elf: *Elf) std.elf.SHF {
490505 return switch (elf.shdrPtr(s)) {
491506 inline else => |shdr| elf.targetLoad(&shdr.flags).shf,
......@@ -769,36 +784,111 @@ const GotReloc = struct {
769784 target: GotKey,
770785 addend: i64,
771786 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 = .{
774 .node = .none,
775 .offset = undefined,
776 .target = undefined,
777 .addend = undefined,
778 .type = undefined,
779 };
797 fn special(s: Special) GotReloc.Type {
798 return .{ .target = .special, .action = .{ .special = s } };
799 }
800
801 target: Target,
802 action: packed union {
803 simple: Simple,
804 special: Special,
805 },
780806
781 const Type = enum(u8) {
782 offset32,
783 offset64,
784 rel32,
785 rel64,
786
787 larch_rel32_hi20,
788 larch_rel64_lo20,
789 larch_rel64_hi12,
790 larch_abs32_lo12,
791 larch_abs32_hi20,
792 larch_abs64_lo20,
793 larch_abs64_hi12,
794
795 sparc_10,
796 sparc_13,
797 sparc_22,
798 sparc_ldm_hi22,
799 sparc_ldm_lo10,
800 sparc_op_hix22,
801 sparc_op_lox10,
807 /// Like `SymbolReloc.Target`, but for GOT relocations. There are fewer tags because there
808 /// are fewer different kinds of GOT relocation.
809 const Target = enum(u3) {
810 /// This is a "special" relocation whose specific type is in the `action.special` field.
811 special,
812
813 /// Absolute address of the GOT entry.
814 abs,
815 /// Offset from the relocation itself to the GOT entry ("PC-relative").
816 rel,
817 /// Offset from the base of the GOT to the GOT entry.
818 offset,
819 };
820
821 const Simple = SymbolReloc.Type.Simple;
822
823 /// Like `SymbolReloc.Special`, but for GOT relocations.
824 const Special = enum(u13) {
825 larch_pcala_hi20,
826 larch_pcala64_lo20,
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 };
802892 };
803893
804894 const Index = enum(u32) {
......@@ -810,14 +900,34 @@ const GotReloc = struct {
810900 }
811901 };
812902
813 fn apply(reloc: *const GotReloc, elf: *Elf) void {
814 assert(elf.ehdrField(.type) != .REL);
903 fn apply(reloc: *GotReloc, elf: *Elf) void {
904 assert(elf.ehdrType() != .REL);
815905 if (reloc.node == .none) return; // deleted
816906 if (reloc.node.hasMoved(&elf.mf) or elf.shndx.got.get(elf).ni.hasMoved(&elf.mf)) {
817907 // There's no point applying the relocation now, because it will be re-applied by
818908 // `flushMoved` at some point anyway.
819909 return;
820910 }
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 {
821931 const node_vaddr: u64 = switch (elf.getNode(reloc.node)) {
822932 .file => unreachable,
823933 .ehdr => unreachable,
......@@ -834,7 +944,7 @@ const GotReloc = struct {
834944 };
835945 const dest_vaddr = node_vaddr + reloc.offset;
836946 const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..];
837 const target_endian = elf.targetEndian();
947
838948 const got_vaddr = elf.shndx.got.vaddr(elf);
839949 const got_index: u64 = elf.got.getIndex(reloc.target).?;
840950 const got_offset: u64 = switch (elf.identClass()) {
......@@ -842,228 +952,201 @@ const GotReloc = struct {
842952 inline else => |class| @sizeOf(class.ElfN().Addr) * got_index,
843953 };
844954 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 => {
872 assert(elf.ehdrField(.machine) == .LOONGARCH);
873 const target_value = got_vaddr +% got_offset +% addend;
874 link.loongarch.writeJ20(dest_slice[0..4], link.loongarch.toPcalaHi20(target_value, dest_vaddr));
875 },
876 .larch_rel64_lo20 => {
877 assert(elf.ehdrField(.machine) == .LOONGARCH);
878 const target_value = got_vaddr +% got_offset +% addend;
879 link.loongarch.writeJ20(dest_slice[0..4], link.loongarch.toPcala64Lo20(target_value, dest_vaddr));
880 },
881 .larch_rel64_hi12 => {
882 assert(elf.ehdrField(.machine) == .LOONGARCH);
883 const target_value = got_vaddr +% got_offset +% addend;
884 link.loongarch.writeK12(dest_slice[0..4], link.loongarch.toPcala64Hi12(target_value, dest_vaddr));
885 },
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 },
956 const target_val: u64 = switch (reloc.type.target) {
957 .abs => got_vaddr +% got_offset +% addend,
958 .rel => got_vaddr +% got_offset +% addend -% dest_vaddr,
959 .offset => got_offset +% addend,
960 .special => return reloc.type.action.special.applyInner(
961 elf,
962 got_vaddr,
963 got_offset,
964 addend,
965 dest_vaddr,
966 dest_slice,
967 ),
968 };
969 try reloc.type.action.simple.write(target_val, dest_slice, elf.targetEndian());
970 }
906971
907 .sparc_10 => {
908 const dest_ptr: *link.sparc.reloc.Simm13 = @ptrCast(@alignCast(dest_slice));
909 var result = elf.targetLoad(dest_ptr);
910 result.simm13 = @as(u10, @truncate(got_offset));
911 elf.targetStore(dest_ptr, result);
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 },
972 fn delete(reloc: *GotReloc, elf: *Elf) void {
973 switch (reloc.result) {
974 .ok => {},
975 .overflowed => elf.overflowed_reloc_count -= 1,
976 .misaligned => elf.misaligned_reloc_count -= 1,
949977 }
978 reloc.* = .{
979 .node = .none,
980 .offset = undefined,
981 .target = undefined,
982 .addend = undefined,
983 .type = undefined,
984 .result = undefined,
985 };
950986 }
951987};
952988
953989pub const MachineRelocType = union {
954990 AARCH64: std.elf.R_AARCH64,
955 LOONGARCH: std.elf.R_LARCH,
991 LARCH: std.elf.R_LARCH,
956992 PPC64: std.elf.R_PPC64,
957993 RISCV: std.elf.R_RISCV,
958994 SPARC: std.elf.R_SPARC,
959995 X86_64: std.elf.R_X86_64,
960996
961 pub fn none(elf: *Elf) MachineRelocType {
962 return switch (elf.ehdrField(.machine)) {
963 else => unreachable,
997 pub const Format = struct {
998 rt: MachineRelocType,
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()) {
9641019 .AARCH64 => .{ .AARCH64 = .NONE },
965 .LOONGARCH => .{ .LOONGARCH = .NONE },
1020 .LOONGARCH => .{ .LARCH = .NONE },
9661021 .PPC64 => .{ .PPC64 = .NONE },
9671022 .RISCV => .{ .RISCV = .NONE },
9681023 .SPARCV9 => .{ .SPARC = .NONE },
9691024 .X86_64 => .{ .X86_64 = .NONE },
9701025 };
9711026 }
972 pub fn copy(elf: *Elf) MachineRelocType {
973 return switch (elf.ehdrField(.machine)) {
974 else => unreachable,
1027 pub fn copy(elf: *const Elf) MachineRelocType {
1028 return switch (elf.ehdrMachine()) {
9751029 .AARCH64 => .{ .AARCH64 = .COPY },
976 .LOONGARCH => .{ .LOONGARCH = .COPY },
1030 .LOONGARCH => .{ .LARCH = .COPY },
9771031 .PPC64 => .{ .PPC64 = .COPY },
9781032 .RISCV => .{ .RISCV = .COPY },
9791033 .SPARCV9 => .{ .SPARC = .COPY },
9801034 .X86_64 => .{ .X86_64 = .COPY },
9811035 };
9821036 }
983 pub fn relative(elf: *Elf) MachineRelocType {
984 return switch (elf.ehdrField(.machine)) {
985 else => unreachable,
1037 pub fn relative(elf: *const Elf) MachineRelocType {
1038 return switch (elf.ehdrMachine()) {
9861039 .AARCH64 => .{ .AARCH64 = .RELATIVE },
987 .LOONGARCH => .{ .LOONGARCH = .RELATIVE },
1040 .LOONGARCH => .{ .LARCH = .RELATIVE },
9881041 .PPC64 => .{ .PPC64 = .RELATIVE },
9891042 .RISCV => .{ .RISCV = .RELATIVE },
9901043 .SPARCV9 => .{ .SPARC = .RELATIVE },
9911044 .X86_64 => .{ .X86_64 = .RELATIVE },
9921045 };
9931046 }
994 pub fn jumpSlot(elf: *Elf) MachineRelocType {
995 return switch (elf.ehdrField(.machine)) {
996 else => unreachable,
1047 pub fn jumpSlot(elf: *const Elf) MachineRelocType {
1048 return switch (elf.ehdrMachine()) {
9971049 .AARCH64 => .{ .AARCH64 = .JUMP_SLOT },
998 .LOONGARCH => .{ .LOONGARCH = .JUMP_SLOT },
1050 .LOONGARCH => .{ .LARCH = .JUMP_SLOT },
9991051 .PPC64 => .{ .PPC64 = .JMP_SLOT },
10001052 .RISCV => .{ .RISCV = .JUMP_SLOT },
10011053 .SPARCV9 => .{ .SPARC = .JMP_SLOT },
10021054 .X86_64 => .{ .X86_64 = .JUMP_SLOT },
10031055 };
10041056 }
1005 pub fn globDat(elf: *Elf) MachineRelocType {
1006 return switch (elf.ehdrField(.machine)) {
1007 else => unreachable,
1057 pub fn globDat(elf: *const Elf) MachineRelocType {
1058 return switch (elf.ehdrMachine()) {
10081059 .AARCH64 => .{ .AARCH64 = .GLOB_DAT },
1009 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .@"64" else .@"32" },
1060 .LOONGARCH => .{ .LARCH = if (elf.identClass() == .@"64") .@"64" else .@"32" },
10101061 .PPC64 => .{ .PPC64 = .GLOB_DAT },
10111062 .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .@"64" else .@"32" },
10121063 .SPARCV9 => .{ .SPARC = .GLOB_DAT },
10131064 .X86_64 => .{ .X86_64 = .GLOB_DAT },
10141065 };
10151066 }
1016 pub fn dtpOffAddr(elf: *Elf) MachineRelocType {
1017 return switch (elf.ehdrField(.machine)) {
1018 else => unreachable,
1019 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_DTPREL64 else .TLS_DTPREL32 },
1067 pub fn dtpMod(elf: *const Elf) MachineRelocType {
1068 return switch (elf.ehdrMachine()) {
1069 .AARCH64 => .{ .AARCH64 = if (elf.identClass() == .@"64") .TLS_DTPMOD else .P32_TLS_DTPMOD },
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 },
10201081 .PPC64 => .{ .PPC64 = .DTPREL64 },
10211082 .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .TLS_DTPREL64 else .TLS_DTPREL32 },
10221083 .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 },
10241085 };
10251086 }
1026 pub fn absAddr(elf: *Elf) MachineRelocType {
1027 return switch (elf.ehdrField(.machine)) {
1028 else => unreachable,
1087 pub fn tpOff(elf: *const Elf) MachineRelocType {
1088 return switch (elf.ehdrMachine()) {
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()) {
10291099 .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" },
10311101 .PPC64 => .{ .PPC64 = .ADDR64 },
10321102 .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .@"64" else .@"32" },
10331103 .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .@"64" else .@"32" },
10341104 .X86_64 => .{ .X86_64 = if (elf.identClass() == .@"64") .@"64" else .@"32" },
10351105 };
10361106 }
1037 pub fn sizeAddr(elf: *Elf) MachineRelocType {
1038 return switch (elf.ehdrField(.machine)) {
1039 else => unreachable,
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,
1107 pub fn size32(elf: *const Elf) ?MachineRelocType {
1108 return switch (elf.ehdrMachine()) {
1109 .AARCH64,
10501110 .LOONGARCH,
10511111 .PPC64,
10521112 .RISCV,
1053 .X86_64,
1054 => |machine| @unionInit(MachineRelocType, @tagName(machine), @enumFromInt(int)),
1113 => null,
1114
1115 .SPARCV9 => .{ .SPARC = .SIZE32 },
1116 .X86_64 => .{ .X86_64 = .SIZE32 },
10551117 };
10561118 }
1057 pub fn unwrap(rt: MachineRelocType, elf: *Elf) u32 {
1058 return switch (elf.ehdrField(.machine)) {
1059 else => unreachable,
1060 .SPARCV9 => @intFromEnum(rt.SPARC),
1061 inline .AARCH64,
1119 pub fn size64(elf: *const Elf) ?MachineRelocType {
1120 return switch (elf.ehdrMachine()) {
1121 .AARCH64,
10621122 .LOONGARCH,
10631123 .PPC64,
10641124 .RISCV,
1065 .X86_64,
1066 => |machine| @intFromEnum(@field(rt, @tagName(machine))),
1125 => null,
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),
10671150 };
10681151 }
10691152};
......@@ -1082,6 +1165,8 @@ const SymbolReloc = struct {
10821165 /// A signed constant used to compute the relocated value. Precise meaning depends on `@"type"`.
10831166 addend: i64,
10841167 /// Specifies how to apply the relocation.
1168 ///
1169 /// When emitting a relocatable, this field is `undefined`.
10851170 type: SymbolReloc.Type,
10861171 /// Forms a linked list of all symbol relocations with the same `target`. This list exists so
10871172 /// that all relocations targeting a particular symbol can be re-applied if that symbol moves.
......@@ -1100,6 +1185,7 @@ const SymbolReloc = struct {
11001185 /// relocation entry. The entry will be removed if we discover a definition which allows us to
11011186 /// statically resolve the relocation.
11021187 rela_index: Section.RelaIndex.Optional,
1188 result: enum(u8) { ok, overflowed, misaligned },
11031189
11041190 /// Determines the section in which this relocation will be placed if it is outstanding.
11051191 ///
......@@ -1110,8 +1196,7 @@ const SymbolReloc = struct {
11101196 /// When producing a DSO, the relocation section is always `.rela.dyn`. It is not `.rela.plt`
11111197 /// because relocations in the GOTPLT are handled specially, without `SymbolReloc` entries.
11121198 fn relaSection(sr: *const SymbolReloc, elf: *Elf) Section.Index {
1113 const shndx = switch (elf.ehdrField(.type)) {
1114 .NONE, .CORE, _ => unreachable,
1199 const shndx = switch (elf.ehdrType()) {
11151200 .REL => elf.getNodeShndx(sr.node).get(elf).rela.shndx,
11161201 .EXEC, .DYN => elf.shndx.rela_dyn,
11171202 };
......@@ -1128,151 +1213,439 @@ const SymbolReloc = struct {
11281213 }
11291214 };
11301215
1131 const Type = enum {
1132 /// This input relocation is being directly forwarded to an `ElfN.Rela` entry in the output
1133 /// file. `rela_index` is guaranteed to be populated. The ELF relocation type is available
1134 /// in the `ElfN.Rela` entry.
1135 ///
1136 /// If we are emitting a relocatable (`ET_REL`), all symbol relocs use this type (since we
1137 /// do not apply any relocations ourselves). Otherwise, no symbol relocs use this type.
1138 write_rela,
1216 /// Instead of using the ELF relocation enums, we have our own internal representation for
1217 /// relocation types. This representation is more compact (requiring only 16 bits), and allows
1218 /// sharing a lot of relocation handling between multiple relocs and target architectures.
1219 ///
1220 /// A relocation type can be "simple" or "special".
1221 ///
1222 /// "Simple" relocations are designed to cover the majority of cases. They can represent most
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.
11411248 ///
1142 /// This is only used targeting local symbols so can always be statically resolved.
1143 dsorel64,
1144 /// Address relative to the DSO base. Like `.abs32` but does not emit `R_*_RELATIVE` relocs.
1249 /// Otherwise, the `.simple` field is used.
1250 action: packed union {
1251 simple: Simple,
1252 special: Special,
1253 },
1254
1255 /// If a relocation is "special", indicates that using the value `.@"special"`.
11451256 ///
1146 /// This is only used targeting local symbols so can always be statically resolved.
1147 dsorel32,
1148
1149 abs8,
1150 abs16,
1151 abs32,
1152 abs32s,
1153 abs64,
1154 rel8,
1155 rel16,
1156 rel32,
1157 rel64,
1158 pltabs32,
1159 pltabs64,
1160 pltrel32,
1161 pltrel64,
1162 dtpoff32,
1163 dtpoff64,
1164 tpoff32,
1165 tpoff64,
1166 size32,
1167 size64,
1168
1169 larch_abs32_lo12,
1170 larch_rel32_hi20,
1171 larch_rel64_lo20,
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,
1257 /// Otherwise (for "simple" relocations), `Target` indicates the first step in computing the
1258 /// relocation---whether we care about the target symbol's absolute address, its PC-relative
1259 /// address, its PLT entry, etc.
1260 const Target = enum(u3) {
1261 /// This is a "special" relocation whose specific type is in the `action.special` field.
1262 special,
1263
1264 /// Absolute value of the target symbol.
1265 abs,
1266 /// Offset from the relocation itself to the target symbol ("PC-relative").
1267 rel,
1268 /// Address of the target symbol's PLT entry.
1269 ///
1270 /// If the target symbol does not have a PLT entry, equivalent to `.abs`.
1271 pltabs,
1272 /// Offset from the relocation itself to the target symbol's PLT entry ("PC-relative").
1273 ///
1274 /// If the target symbol does not have a PLT entry, equivalent to `.rel`.
1275 pltrel,
1276 /// Offset of the target TLS symbol from the base of this DSO's own TLS region.
1277 dtpoff,
1278 /// Offset of the target TLS symbol from the raw thread pointer.
1279 tpoff,
1280 /// Size of the target symbol.
1281 size,
1282 };
11991283
1200 .larch_tpoff32_lo12,
1201 .larch_tpoff32_hi20,
1202 .larch_tpoff64_lo20,
1203 .larch_tpoff64_hi12,
1204 => true,
1284 /// For a "simple" relocation, after the initial value is computed according to `Target`, a
1285 /// `Simple` value communicates how to shift, truncate, and store that value into memory.
1286 const Simple = packed struct(u13) {
1287 /// The field being written to, represented as a sequence of bits in a backing integer
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,
1207 .sparc_le_lox10,
1208 => true,
1339 /// After the relocation value is shifted (see `shift`), it is truncated to the size of
1340 /// the bit field (see `dest`). This field specifies whether the linker will check for,
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,
1211 };
1212 }
1353 /// The relocation value (computed based on the `Target`) gets shifted to the right by
1354 /// this amount. By default, the shifted-out bits can be anything, but tags ending in
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 {
1215 return switch (elf.identClass()) {
1216 .NONE, _ => unreachable,
1217 .@"32" => switch (t) {
1218 .abs32,
1219 .pltabs32,
1220 => true,
1221 else => false,
1222 },
1223 .@"64" => switch (t) {
1224 .abs64,
1225 .pltabs64,
1226 => true,
1227 else => false,
1367 /// Given a value (computed based on the `Target`), applies the shift and truncation
1368 /// operations specified by `s`, then writes the result to the start of `dest_slice` as
1369 /// specified by `s.dest`.
1370 fn write(
1371 s: Simple,
1372 val: u64,
1373 dest_slice: []u8,
1374 target_endian: std.lang.Endian,
1375 ) error{ RelocationMisaligned, RelocationOverflow }!void {
1376 const shift: u6, const shift_exact: bool = switch (s.shift) {
1377 .@"0" => .{ 0, false },
1378 .@"2_exact" => .{ 2, true },
1379 .@"10" => .{ 10, true },
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 },
12281616 },
12291617 };
12301618 }
12311619 };
12321620
1233 fn apply(reloc: *const SymbolReloc, elf: *Elf) void {
1234 assert(elf.ehdrField(.type) != .REL);
1621 fn apply(reloc: *SymbolReloc, elf: *Elf) void {
1622 assert(elf.ehdrType() != .REL);
12351623 assert(reloc.node != .none);
1236
12371624 if (reloc.node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) {
12381625 // There's no point applying the relocation now, because it will be re-applied by
12391626 // `flushMoved` at some point anyway.
12401627 return;
12411628 }
1242
1243 if (reloc.rela_index.unwrap()) |rela_index| switch (elf.classifySymbolValue(reloc.target)) {
1244 .static => unreachable,
1245 .dynamic => return, // the relocation happens at runtime
1246 .static_relative => {
1247 // We have emitted an R_*_RELATIVE relocation to help lower an abs32/abs64 reloc.
1248 // This is a simplified version of the general relocation handling logic, where we
1249 // know we're using '.abs64' or '.abs32' (matching the ELF ident class).
1250 const value = type: switch (reloc.type) {
1251 .abs32,
1252 .abs64,
1253 => reloc.target.value(elf) +% @as(u64, @bitCast(reloc.addend)),
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;
1629 switch (reloc.result) {
1630 .ok => {},
1631 .overflowed => elf.overflowed_reloc_count -= 1,
1632 .misaligned => elf.misaligned_reloc_count -= 1,
1633 }
1634 if (reloc.applyInner(elf)) {
1635 @branchHint(.likely);
1636 reloc.result = .ok;
1637 } else |err| switch (err) {
1638 error.RelocationOverflow => {
1639 reloc.result = .overflowed;
1640 elf.overflowed_reloc_count += 1;
12741641 },
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 {
12761649 const node_vaddr: u64 = switch (elf.getNode(reloc.node)) {
12771650 .file => unreachable,
12781651 .ehdr => unreachable,
......@@ -1289,349 +1662,75 @@ const SymbolReloc = struct {
12891662 };
12901663 const dest_vaddr = node_vaddr + reloc.offset;
12911664 const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..];
1292 const target_endian = elf.targetEndian();
1293 const sym_value: u64 = reloc.target.value(elf);
1294 const sym_size: u64 = switch (elf.symPtr(reloc.target.index(elf))) {
1295 inline else => |target_sym| elf.targetLoad(&target_sym.size),
1296 };
1297 const target_value = sym_value +% @as(u64, @bitCast(reloc.addend));
1298 type: switch (reloc.type) {
1299 .write_rela => unreachable,
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 );
1665
1666 const addend: u64 = @bitCast(reloc.addend);
1667 const target_val: u64 = type: switch (reloc.type.target) {
1668 .abs => reloc.target.value(elf) +% addend,
1669 .rel => reloc.target.value(elf) +% addend -% dest_vaddr,
1670 .pltabs => {
1671 const plt_entry_addr = elf.pltEntryTargetAddr(reloc.target) orelse continue :type .abs;
1672 break :type plt_entry_addr +% addend;
13621673 },
1363 .pltabs32 => {
1364 const plt_index = switch (reloc.target.unwrap()) {
1365 .local => continue :type .abs32,
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 );
1674 .pltrel => {
1675 const plt_entry_addr = elf.pltEntryTargetAddr(reloc.target) orelse continue :type .rel;
1676 break :type plt_entry_addr +% addend -% dest_vaddr;
13831677 },
1384 .pltrel64 => {
1385 const plt_index = switch (reloc.target.unwrap()) {
1386 .local => continue :type .rel64,
1387 .global => |name| elf.plt.getIndex(name) orelse continue :type .rel64,
1388 };
1389 if (elf.pltEntryIsDead(plt_index)) continue :type .rel64;
1390 const plt_shndx: Section.Index, const plt_header_entries: u64, const plt_entry_size: u64 = switch (elf.ehdrField(.machine)) {
1391 else => |machine| @panic(@tagName(machine)),
1392 .SPARCV9 => .{ elf.shndx.plt, 4, 32 },
1393 .X86_64 => .{ elf.shndx.plt_sec, 0, 16 },
1394 };
1395 const plt_entry = plt_shndx.vaddr(elf) +% (plt_header_entries + plt_index) * plt_entry_size;
1396 std.mem.writeInt(
1397 i64,
1398 dest_slice[0..8],
1399 @bitCast(plt_entry +% @as(u64, @bitCast(reloc.addend)) -% dest_vaddr),
1400 target_endian,
1401 );
1678 .dtpoff => reloc.target.value(elf) +% addend,
1679 .tpoff => switch (elf.targetTlsVariant()) {
1680 .I_original => |tls| tls.tcb_size +% reloc.target.value(elf) +% addend,
1681 .I_modified => |tls| 0 -% tls.tp_off +% reloc.target.value(elf) +% addend,
1682 .II => {
1683 const tls_phndx = elf.getNode(elf.ni.tls).segment;
1684 const tls_size: u64 = switch (elf.phdrSlice()) {
1685 inline else => |phdr| tls_size: {
1686 assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS);
1687 break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz);
1688 },
1689 };
1690 break :type reloc.target.value(elf) +% addend -% tls_size;
1691 },
14021692 },
1403 .pltrel32 => {
1404 const plt_index = switch (reloc.target.unwrap()) {
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 );
1693 .size => switch (elf.symPtr(reloc.target.index(elf))) {
1694 inline else => |sym| elf.targetLoad(&sym.size),
14231695 },
1424 .size64 => std.mem.writeInt(
1425 u64,
1426 dest_slice[0..8],
1427 sym_size +% @as(u64, @bitCast(reloc.addend)),
1428 target_endian,
1429 ),
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,
1696 .special => return reloc.type.action.special.applyInner(
1697 elf,
1698 reloc.target,
1699 addend,
1700 dest_vaddr,
1701 dest_slice,
14471702 ),
1448 .tpoff64 => {
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 },
1703 };
14781704
1479 .larch_abs32_lo12 => {
1480 assert(elf.ehdrField(.machine) == .LOONGARCH);
1481 link.loongarch.writeK12(dest_slice[0..4], @truncate(target_value));
1482 },
1483 .larch_rel32_hi20 => {
1484 assert(elf.ehdrField(.machine) == .LOONGARCH);
1485 link.loongarch.writeJ20(dest_slice[0..4], link.loongarch.toPcalaHi20(target_value, dest_vaddr));
1486 },
1487 .larch_rel64_lo20 => {
1488 assert(elf.ehdrField(.machine) == .LOONGARCH);
1489 link.loongarch.writeJ20(dest_slice[0..4], link.loongarch.toPcala64Lo20(target_value, dest_vaddr));
1490 },
1491 .larch_rel64_hi12 => {
1492 assert(elf.ehdrField(.machine) == .LOONGARCH);
1493 link.loongarch.writeK12(dest_slice[0..4], link.loongarch.toPcala64Hi12(target_value, dest_vaddr));
1494 },
1495 // TODO: handle bad alignment and overflow gracefully
1496 .larch_branch_rel18 => {
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));
1705 // Check for the `R_*_RELATIVE` case now, because it is possible only when no shift or cast
1706 // is required, meaning we can handle it now and return early.
1707 if (reloc.rela_index.unwrap()) |rela_index| switch (elf.classifySymbolValue(reloc.target)) {
1708 .static => unreachable,
1709 .dynamic => return, // the relocation happens at runtime
1710 .static_relative => {
1711 // We have emitted an R_*_RELATIVE relocation to help lower an absolute-address
1712 // relocation. The value computed above is valid, but instead of writing it to the
1713 // destination slice, we actually want to write it to the runtime relocation entry.
1714 switch (elf.identClass()) {
1715 .NONE, _ => unreachable,
1716 .@"32" => assert(reloc.type.action.simple.dest == .@"32"),
1717 .@"64" => assert(reloc.type.action.simple.dest == .@"64"),
1718 }
1719 assert(reloc.type.action.simple.cast == .unsigned);
1720 assert(reloc.type.action.simple.shift == .@"0");
1721 elf.shndx.rela_dyn.relaSetRelativeOffset(elf, rela_index, target_val);
1722 return;
15391723 },
1724 };
15401725
1541 .sparc_wdisp30 => {
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 }
1726 try reloc.type.action.simple.write(target_val, dest_slice, elf.targetEndian());
16281727 }
16291728
16301729 fn delete(reloc: *SymbolReloc, elf: *Elf, index: SymbolReloc.Index) void {
16311730 assert(index.get(elf) == reloc);
16321731
16331732 reloc.deleteOutputRel(elf);
1634 if (reloc.type.dependsOnTlsSize()) {
1733 if (reloc.type.dependsOnTlsSize(elf)) {
16351734 assert(elf.tls_size_symbol_relocs.swapRemove(index));
16361735 }
16371736
......@@ -1647,6 +1746,11 @@ const SymbolReloc = struct {
16471746 .none => {},
16481747 else => |next| next.get(elf).prev = reloc.prev,
16491748 }
1749 switch (reloc.result) {
1750 .ok => {},
1751 .overflowed => elf.overflowed_reloc_count -= 1,
1752 .misaligned => elf.misaligned_reloc_count -= 1,
1753 }
16501754
16511755 reloc.* = undefined;
16521756 }
......@@ -1656,8 +1760,7 @@ const SymbolReloc = struct {
16561760 fn deleteOutputRel(reloc: *SymbolReloc, elf: *Elf) void {
16571761 const rela_index = reloc.rela_index.unwrap() orelse return;
16581762 reloc.relaSection(elf).relaDeleteOne(elf, rela_index);
1659 switch (elf.ehdrField(.type)) {
1660 .NONE, .CORE, _ => unreachable,
1763 switch (elf.ehdrType()) {
16611764 .REL => {},
16621765 .EXEC, .DYN => switch (elf.nodeWantsDsoRelocation(reloc.node)) {
16631766 .no => unreachable, // there *was* a dynamic relocation!
......@@ -1715,37 +1818,26 @@ fn ensureUnusedPltCapacity(elf: *Elf, len: u32) Error!void {
17151818 try elf.shndx.rela_plt.relaEnsureAdditionalCapacity(elf, len);
17161819
17171820 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)) {
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);
1823 const plt = elf.targetPltInfo();
17261824
1727 // Ensure the `.got.plt` section's node is big enough
1728 const got_plt_need_size: usize = elf.targetPtrSize() * (3 + need_plt_capacity);
1729 try elf.ensureNodeSize(elf.shndx.got_plt.get(elf).ni, got_plt_need_size);
1825 // Ensure the `.plt` section's node is big enough:
1826 {
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 enough
1732 const plt_sec_need_size: usize = 16 * need_plt_capacity;
1733 try elf.ensureNodeSize(elf.shndx.plt_sec.get(elf).ni, plt_sec_need_size);
1734 },
1735 .LOONGARCH => {
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);
1831 // If there is a `.got.plt` section, ensure its node is big enough
1832 if (plt.got_plt) |got_plt| {
1833 const need_size: usize = elf.targetPtrSize() * (got_plt.header_entries + need_plt_count);
1834 try elf.ensureNodeSize(elf.shndx.got_plt.get(elf).ni, need_size);
1835 }
17391836
1740 // Ensure the `.got.plt` section's node is big enough
1741 const got_plt_need_size: usize = elf.targetPtrSize() * (2 + need_plt_capacity);
1742 try elf.ensureNodeSize(elf.shndx.got_plt.get(elf).ni, got_plt_need_size);
1743 },
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 },
1837 // If there is a `.plt.sec` section, ensure its node is big enough
1838 if (plt.plt_sec) |plt_sec| {
1839 const need_size: usize = plt_sec.entry_size * need_plt_count;
1840 try elf.ensureNodeSize(elf.shndx.plt_sec.get(elf).ni, need_size);
17491841 }
17501842}
17511843/// 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
18081900 const global_name: String(.strtab) = @enumFromInt(elf.targetLoad(&new_sym.name));
18091901 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) {
18121904 // This symbol's index is changing, so queue an update of relocs targeting it.
18131905 elf.changed_symtab_index.putAssumeCapacity(global_name, {});
18141906 }
......@@ -1972,7 +2064,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
19722064 };
19732065
19742066 const force_local_bind: bool = switch (opts.visibility) {
1975 .HIDDEN, .INTERNAL => elf.ehdrField(.type) != .REL,
2067 .HIDDEN, .INTERNAL => elf.ehdrType() != .REL,
19762068 .PROTECTED, .DEFAULT => false,
19772069 };
19782070
......@@ -2071,7 +2163,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
20712163 }
20722164
20732165 switch (@"type") {
2074 .FUNC, .GNU_IFUNC => if (elf.ehdrField(.type) != .REL and
2166 .FUNC, .GNU_IFUNC => if (elf.ehdrType() != .REL and
20752167 elf.classifySymbolValue(.global(opts.name.strtab)) == .dynamic)
20762168 {
20772169 // 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
22402332 // object), then the symbol should have binding STB_LOCAL in the output. Therefore, if we are
22412333 // putting the global in this state for the first time---let's call it "demoting" the global to
22422334 // 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;
22442336 switch (elf.symPtr(global_ptr.symtab_index)) {
22452337 inline else => |sym, class| {
22462338 const old_info = elf.targetLoad(&sym.info);
......@@ -2274,7 +2366,7 @@ fn mergeGlobalSymbolVisibility(elf: *Elf, global_ptr: *Symbol.Global, other_visi
22742366/// the symbol must be moved from the "globals" part of the symtab to the "locals" part, because ELF
22752367/// requires that all STB_LOCAL symbols in a symbol table appear before any global symbols.
22762368fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {
2277 assert(elf.ehdrField(.type) != .REL); // demotion only happens when emitting an ELF module
2369 assert(elf.ehdrType() != .REL); // demotion only happens when emitting an ELF module
22782370 switch (elf.shdrPtr(.symtab)) {
22792371 inline else => |shdr, class| {
22802372 // `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 {
22922384 // The demoted global was not the first global in the symtab, so we need to swap it
22932385 // to its new location.
22942386
2295 const src_sym_ptr = @field(elf.symPtr(src_index), @tagName(class));
2296 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 };
2387 const src_sym_ptr = @field(elf.symPtr(src_index), @tagName(class));
2388 const dest_sym_ptr = @field(elf.symPtr(dest_index), @tagName(class));
24782389
2479 // add a .GOT.PLT entry, writing the address of the corresponding .PLT entry
2480 const got_plt_ni = elf.shndx.got_plt.get(elf).ni;
2481 switch (elf.shdrPtr(elf.shndx.got_plt)) {
2482 inline else => |shdr, class| {
2483 assert(elf.targetLoad(&shdr.size) == got_plt_offset);
2484 elf.targetStore(&shdr.size, got_plt_offset + @sizeOf(class.ElfN().Addr));
2485 std.mem.writeInt(
2486 class.ElfN().Addr,
2487 got_plt_ni.slice(&elf.mf)[got_plt_offset..][0..@sizeOf(class.ElfN().Addr)],
2488 @intCast(plt_addr),
2489 target_endian,
2490 );
2491 },
2390 const this_name: String(.strtab) = @enumFromInt(elf.targetLoad(&src_sym_ptr.name));
2391 assert(elf.globalByName(this_name).? == global_ptr);
2392
2393 const other_name: String(.strtab) = @enumFromInt(elf.targetLoad(&dest_sym_ptr.name));
2394 const other_global_ptr = elf.globalByName(other_name).?;
2395 assert(other_global_ptr.symtab_index == dest_index);
2396
2397 // First swap the symtab entries...
2398 std.mem.swap(class.ElfN().Sym, src_sym_ptr, dest_sym_ptr);
2399 // ...then the `elf.symtab` metadata...
2400 std.mem.swap(Symbol, src_index.ptr(elf), dest_index.ptr(elf));
2401 // ...then update the `elf.globals` tracking.
2402 global_ptr.symtab_index = dest_index;
2403 other_global_ptr.symtab_index = src_index;
24922404 }
24932405
2494 // relocate the PLT entry to point to the .GOT.PLT entry
2495 const got_plt_abs = got_plt_section.vaddr(elf) + got_plt_offset;
2496 // TODO: handle overflow gracefully
2497 link.loongarch.writeJ20(plt_slice[0..4], link.loongarch.toPcalaHi20(got_plt_abs, plt_addr));
2498 link.loongarch.writeK12(plt_slice[4..8], @truncate(got_plt_abs));
2499 },
2500 .SPARCV9 => {
2501 // add a .PLT entry, writing the template
2502 const plt_ni = elf.shndx.plt.get(elf).ni;
2503 switch (elf.shdrPtr(elf.shndx.plt)) {
2504 inline else => |shdr| {
2505 assert(elf.targetLoad(&shdr.size) == got_plt_offset);
2506 elf.targetStore(&shdr.size, got_plt_offset + 32);
2507 const plt_slice: []u32 = @ptrCast(@alignCast(plt_ni.slice(&elf.mf)[got_plt_offset..][0..32]));
2508 // sethi (. - .plt[0]), %g1
2509 // ba,a %xcc, .plt[1]
2510 // nop
2511 // nop
2512 // nop
2513 // nop
2514 // nop
2515 // nop
2516 @memcpy(plt_slice, &([2]u32{
2517 // TODO: handle overflow gracefully
2518 @bitCast(link.sparc.reloc.Imm22{
2519 .imm22 = @truncate(got_plt_offset),
2520 .b22_31 = 0b0000000011,
2521 }),
2522 @bitCast(link.sparc.reloc.Disp19{
2523 .disp19 = @truncate((got_plt_offset + 4 - 32) >> 2),
2524 .b19_31 = 0b1100001101000,
2525 }),
2526 } ++ @as([6]u32, @splat(0x01000000))));
2527 if (elf.targetEndian() != native_endian) std.mem.byteSwapAllElements(u32, plt_slice);
2528 },
2406 // We also need to get rid of the dynsym entry if there is one. To keep dynsym compact,
2407 // we'll move another symbol into its place just like we did above.
2408 if (global_ptr.dynsym_index != 0) {
2409 const dynsym_shdr = @field(elf.shdrPtr(elf.shndx.dynsym), @tagName(class));
2410
2411 const ent_size = @sizeOf(class.ElfN().Sym);
2412 assert(elf.targetLoad(&dynsym_shdr.entsize) == ent_size);
2413
2414 // We're going to decrease the size of `.dynsym`, thereby removing its last index.
2415 const old_size = elf.targetLoad(&dynsym_shdr.size);
2416 const new_size = old_size - ent_size;
2417 const remove_dynsym_index: u32 = @intCast(@divExact(new_size, ent_size));
2418
2419 const free_dynsym_index = global_ptr.dynsym_index;
2420 global_ptr.dynsym_index = 0;
2421
2422 if (free_dynsym_index != remove_dynsym_index) {
2423 // The demoted global wasn't the last entry, so move whatever entry we just
2424 // truncated out of dynsym into its place.
2425
2426 const src_dynsym_ptr = @field(elf.dynsymPtr(remove_dynsym_index), @tagName(class));
2427 const dest_dynsym_ptr = @field(elf.dynsymPtr(free_dynsym_index), @tagName(class));
2428
2429 const moved_name_dynstr: String(.dynstr) = @enumFromInt(elf.targetLoad(&src_dynsym_ptr.name));
2430 const moved_name = elf.stringExisting(.strtab, moved_name_dynstr.slice(elf));
2431 const moved_global_ptr = elf.globalByName(moved_name).?;
2432
2433 dest_dynsym_ptr.* = src_dynsym_ptr.*;
2434
2435 assert(moved_global_ptr.dynsym_index == remove_dynsym_index);
2436 moved_global_ptr.dynsym_index = free_dynsym_index;
2437
2438 // Since that symbol's dynsym index has changed, we'll have to update any
2439 // relocation entries targeting it.
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);
25292445 }
25302446 },
25312447 }
......@@ -2660,7 +2576,7 @@ const Symbol = struct {
26602576 }
26612577
26622578 // Re-apply relocations targeting this symbol
2663 if (elf.ehdrField(.type) != .REL) {
2579 if (elf.ehdrType() != .REL) {
26642580 sym_id.applyTargetRelocs(elf);
26652581 }
26662582
......@@ -2678,7 +2594,7 @@ const Symbol = struct {
26782594 }
26792595
26802596 fn applyTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void {
2681 assert(elf.ehdrField(.type) != .REL);
2597 assert(elf.ehdrType() != .REL);
26822598 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;
26832599 while (ri != .none) {
26842600 const reloc = ri.get(elf);
......@@ -2693,7 +2609,7 @@ const Symbol = struct {
26932609 ///
26942610 /// Asserts we are creating a DSO.
26952611 fn deleteDynamicTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void {
2696 assert(elf.ehdrField(.type) != .REL);
2612 assert(elf.ehdrType() != .REL);
26972613 assert(elf.shndx.dynamic != .UNDEF);
26982614 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;
26992615 while (ri != .none) {
......@@ -2713,7 +2629,20 @@ const Symbol = struct {
27132629 const reloc = ri.get(elf);
27142630 ri = reloc.next;
27152631 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;
27172646 switch (elf.nodeWantsDsoRelocation(reloc.node)) {
27182647 .no => continue,
27192648 .yes_textrel => elf.textrel_count += 1,
......@@ -2781,8 +2710,7 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {
27812710} {
27822711 const comp = elf.base.comp;
27832712
2784 const runtime_load_addr = switch (elf.ehdrField(.type)) {
2785 .NONE, .CORE, _ => unreachable,
2713 const runtime_load_addr = switch (elf.ehdrType()) {
27862714 .REL => unreachable,
27872715 .DYN => true,
27882716 .EXEC => false,
......@@ -2933,10 +2861,10 @@ fn externSymbolInner(elf: *Elf, opts: ExternSymbolOpts) Error!Symbol.Id {
29332861 .size = 0,
29342862 .type = opts.type,
29352863 .bind = switch (opts.linkage) {
2936 .internal => @panic("TODO internal extern symbol"),
29372864 .strong => .strong,
29382865 .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", .{}),
29402868 },
29412869 .visibility = switch (opts.visibility) {
29422870 .default => .DEFAULT,
......@@ -2965,6 +2893,9 @@ pub fn addReloc(
29652893 };
29662894 elf.addRelocAssumeCapacity(node, offset, .fromTypeErased(target), addend, @"type") catch |err| switch (err) {
29672895 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)
29682899 else => |e| return e,
29692900 };
29702901}
......@@ -3149,23 +3080,6 @@ const StringTable = struct {
31493080 }
31503081};
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
31693083pub fn open(
31703084 arena: std.mem.Allocator,
31713085 comp: *Compilation,
......@@ -3212,7 +3126,7 @@ fn create(
32123126 .amdpal => .AMDGPU_PAL,
32133127 .mesa3d => .AMDGPU_MESA3D,
32143128 };
3215 const @"type": std.elf.ET = switch (comp.config.output_mode) {
3129 const @"type": EhdrType = switch (comp.config.output_mode) {
32163130 .Exe => if (comp.config.pie or target.os.tag == .haiku) .DYN else .EXEC,
32173131 .Lib => switch (comp.config.link_mode) {
32183132 .static => .REL,
......@@ -3220,7 +3134,9 @@ fn create(
32203134 },
32213135 .Obj => .REL,
32223136 };
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 };
32243140 const maybe_interp = switch (comp.config.link_mode) {
32253141 .static => null,
32263142 .dynamic => switch (comp.config.output_mode) {
......@@ -3276,6 +3192,12 @@ fn create(
32763192 .fini_array = .UNDEF,
32773193 .preinit_array = .UNDEF,
32783194 },
3195 .dynamic = .{
3196 .flags = 0,
3197 .flags_1 = 0,
3198 .rpath = .empty,
3199 .soname = .empty,
3200 },
32793201 .symtab = .empty,
32803202 .globals = .{
32813203 .strong_def = .empty,
......@@ -3310,10 +3232,12 @@ fn create(
33103232 .tls_size_symbol_relocs = .empty,
33113233 .section_by_name = .empty,
33123234 .changed_symtab_index = .empty,
3235 .textrel_count = 0,
3236 .overflowed_reloc_count = 0,
3237 .misaligned_reloc_count = 0,
33133238 .const_prog_node = .none,
33143239 .synth_prog_node = .none,
33153240 .input_prog_node = .none,
3316 .textrel_count = 0,
33173241 };
33183242 errdefer elf.deinit();
33193243
......@@ -3362,14 +3286,14 @@ fn initHeaders(
33623286 class: std.elf.CLASS,
33633287 data: std.elf.DATA,
33643288 osabi: std.elf.OSABI,
3365 @"type": std.elf.ET,
3366 machine: std.elf.EM,
3289 @"type": EhdrType,
3290 machine: EhdrMachine,
33673291 maybe_interp: ?[]const u8,
3368) !void {
3292) Error!void {
33693293 const comp = elf.base.comp;
33703294 const gpa = comp.gpa;
3295
33713296 const have_dynamic_section = switch (@"type") {
3372 .NONE, .CORE, _ => unreachable,
33733297 .REL => false,
33743298 .EXEC => comp.config.link_mode == .dynamic,
33753299 .DYN => true,
......@@ -3380,13 +3304,7 @@ fn initHeaders(
33803304 .@"64" => .@"8",
33813305 };
33823306
3383 const init_plt_size: std.elf.Xword, const plt_align: std.mem.Alignment, const got_plt, const plt_sec =
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 };
3307 const plt: PltInfo = .fromMachine(machine);
33903308
33913309 const shnum: u32 = shnum: {
33923310 var shnum: u32 = 1; // reserved ("null") shdr
......@@ -3408,9 +3326,9 @@ fn initHeaders(
34083326 }
34093327 if (@"type" != .REL) {
34103328 shnum += 1; // .got
3411 shnum += @intFromBool(got_plt); // .got.plt
3329 shnum += @intFromBool(plt.got_plt != null); // .got.plt
34123330 shnum += 1; // .plt
3413 shnum += @intFromBool(plt_sec); // .plt_sec
3331 shnum += @intFromBool(plt.plt_sec != null); // .plt_sec
34143332 }
34153333 break :shnum shnum;
34163334 };
......@@ -3427,7 +3345,6 @@ fn initHeaders(
34273345 gnu_stack: u32,
34283346 }, const phnum: u32 = ph: {
34293347 switch (@"type") {
3430 .NONE, .CORE, _ => unreachable,
34313348 .REL => break :ph .{ undefined, 0 },
34323349 .EXEC, .DYN => {},
34333350 }
......@@ -3483,9 +3400,9 @@ fn initHeaders(
34833400 try elf.symtab.ensureTotalCapacity(gpa, 1);
34843401 elf.nodes.appendAssumeCapacity(.file);
34853402
3486 switch (class) {
3403 const entsize: struct { ph: u32, sh: u32 } = switch (class) {
34873404 .NONE, _ => unreachable,
3488 inline else => |ct_class| {
3405 inline else => |ct_class| entsize: {
34893406 const ElfN = ct_class.ElfN();
34903407 assert(elf.ni.ehdr == try elf.mf.addOnlyChildNode(gpa, elf.ni.file, .{
34913408 .size = @sizeOf(ElfN.Ehdr),
......@@ -3502,42 +3419,35 @@ fn initHeaders(
35023419 .osabi = osabi,
35033420 .abiversion = 0,
35043421 };
3505 ehdr.type = @"type";
3506 ehdr.machine = machine;
3422 ehdr.type = @"type".toElf();
3423 ehdr.machine = machine.toElf();
35073424 ehdr.version = 1;
35083425 ehdr.entry = 0;
35093426 ehdr.phoff = 0;
35103427 ehdr.shoff = 0;
35113428 ehdr.flags = switch (machine) {
3512 .LOONGARCH => e_flags: {
3513 const target_cpu = &elf.base.comp.getTarget().cpu;
3514 const e_flags: std.elf.loongarch.EFlags = .{
3515 .base_abi_modifier = if (target_cpu.has(.loongarch, .d))
3516 .d
3517 else if (target_cpu.has(.loongarch, .f))
3518 .f
3519 else
3520 .s,
3521 .abi_extension = .base,
3522 .abi_version = 1,
3523 };
3524 break :e_flags @bitCast(e_flags);
3525 },
3526 .SPARCV9 => e_flags: {
3527 const e_flags: std.elf.sparc.EFlags = .{
3528 .mm = .rmo,
3529 .ext = .{
3530 .@"32plus" = false,
3531 .sun_us1 = false,
3532 .hal_r1 = false,
3533 .sun_us3 = false,
3534 .le_data = false,
3535 },
3536 };
3537 break :e_flags @bitCast(e_flags);
3538 },
3539 .X86_64 => 0,
3540 else => @panic(@tagName(machine)),
3429 .LOONGARCH => .{ .loongarch = .{
3430 .base_abi_modifier = mod: {
3431 const cpu = comp.getTarget().cpu;
3432 if (cpu.has(.loongarch, .d)) break :mod .d;
3433 if (cpu.has(.loongarch, .f)) break :mod .f;
3434 break :mod .s;
3435 },
3436 .abi_extension = .base,
3437 .abi_version = 1,
3438 } },
3439 .SPARCV9 => .{ .sparc = .{
3440 .mm = .rmo,
3441 .ext = .{
3442 .@"32plus" = false,
3443 .sun_us1 = false,
3444 .hal_r1 = false,
3445 .sun_us3 = false,
3446 .le_data = false,
3447 },
3448 } },
3449 .X86_64 => .{ .int = 0 },
3450 .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)),
35413451 };
35423452 ehdr.ehsize = @sizeOf(ElfN.Ehdr);
35433453 ehdr.phentsize = @sizeOf(ElfN.Phdr);
......@@ -3546,11 +3456,13 @@ fn initHeaders(
35463456 ehdr.shnum = 1; // Only the null shdr initially---will be incremented by `addSection`
35473457 ehdr.shstrndx = std.elf.SHN_UNDEF;
35483458 if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr);
3459
3460 break :entsize .{ .ph = @sizeOf(ElfN.Phdr), .sh = @sizeOf(ElfN.Shdr) };
35493461 },
3550 }
3462 };
35513463
35523464 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
35543466 .alignment = elf.mf.flags.block_size,
35553467 .moved = true,
35563468 .resized = true,
......@@ -3558,28 +3470,24 @@ fn initHeaders(
35583470 elf.nodes.appendAssumeCapacity(.shdr);
35593471
35603472 const page_align: std.mem.Alignment = .fromByteUnits(switch (machine) {
3561 .BPF,
3562 .SPARCV9,
3563 => 0x100000,
3564 .AARCH64,
3565 .AMDGPU,
3566 .QDSP6,
3567 .MIPS,
3568 .PPC,
3569 .PPC64,
3570 .SPARC,
3571 .SPARC32PLUS,
3572 => 0x10000,
3573 .LOONGARCH,
3574 => 0x4000,
3575 .ARC_COMPACT2,
3576 .@"68K",
3577 => 0x2000,
3578 .MSP430,
3579 => 0x4,
3580 .AVR,
3581 => 0x1,
3582 else => 0x1000,
3473 .AARCH64 => 0x10000,
3474 .LOONGARCH => 0x4000,
3475 .PPC64 => 0x10000,
3476 .RISCV => 0x1000,
3477 .SPARCV9 => 0x100000,
3478 .X86_64 => 0x1000,
3479
3480 //.@"68K" => 0x2000,
3481 //.AMDGPU => 0x10000,
3482 //.ARC_COMPACT2 => 0x2000,
3483 //.AVR => 0x1,
3484 //.BPF => 0x100000,
3485 //.MIPS => 0x10000,
3486 //.MSP430 => 0x4,
3487 //.PPC => 0x10000,
3488 //.QDSP6 => 0x10000,
3489 //.SPARC => 0x10000,
3490 //.SPARC32PLUS => 0x10000,
35833491 });
35843492
35853493 var ph_vaddr: u32 = if (@"type" != .REL) ph_vaddr: {
......@@ -3592,7 +3500,7 @@ fn initHeaders(
35923500 elf.phdrs.items[phndx.rodata] = elf.ni.rodata;
35933501
35943502 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,
35963504 .alignment = addr_align,
35973505 .moved = true,
35983506 .resized = true,
......@@ -3627,16 +3535,16 @@ fn initHeaders(
36273535
36283536 elf.phdrs.items[phndx.gnu_stack] = .none;
36293537
3630 break :ph_vaddr switch (elf.ehdrField(.type)) {
3631 .NONE, .CORE, _ => unreachable,
3538 break :ph_vaddr switch (elf.ehdrType()) {
36323539 .REL, .DYN => 0,
36333540 .EXEC => switch (machine) {
3634 .@"386" => 0x400000,
3635 .AARCH64, .X86_64 => 0x200000,
3636 .PPC, .PPC64 => 0x10000000,
3637 .S390 => 0x1000000,
3541 .AARCH64,
3542 => 0x200000,
3543 .LOONGARCH => 0x10000,
3544 .PPC64 => 0x10000000,
3545 .RISCV => 0x10000,
36383546 .SPARCV9 => 0x100000,
3639 else => 0x10000,
3547 .X86_64 => 0x200000,
36403548 },
36413549 };
36423550 } else undefined;
......@@ -3870,28 +3778,21 @@ fn initHeaders(
38703778 .type = .PROGBITS,
38713779 // Reserve space for the reserved words, populated later.
38723780 .size = switch (machine) {
3873 else => @panic(@tagName(machine)),
3781 .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)),
38743782 .X86_64 => 3 * elf.targetPtrSize(),
3875 .LOONGARCH,
3876 .SPARCV9,
3877 => elf.targetPtrSize(),
3783 .LOONGARCH, .SPARCV9 => elf.targetPtrSize(),
38783784 },
38793785 .flags = .{ .WRITE = true, .ALLOC = true },
38803786 .addralign = addr_align,
38813787 .entsize = @intCast(addr_align.toByteUnits()),
38823788 });
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(
38843790 if (elf.options.z_now) elf.ni.data_rel_ro else elf.ni.data,
38853791 .{
38863792 .name = ".got.plt",
38873793 .type = .PROGBITS,
38883794 .flags = .{ .WRITE = true, .ALLOC = true },
3889 .size = switch (machine) {
3890 else => @panic(@tagName(machine)),
3891 .@"386" => 3 * 4,
3892 .X86_64 => 3 * 8,
3893 .LOONGARCH => 2 * elf.targetPtrSize(),
3894 },
3795 .size = got_plt.header_entries * elf.targetPtrSize(),
38953796 .addralign = addr_align,
38963797 .entsize = @intCast(addr_align.toByteUnits()),
38973798 },
......@@ -3902,19 +3803,16 @@ fn initHeaders(
39023803 .flags = .{
39033804 .ALLOC = true,
39043805 .EXECINSTR = true,
3905 .WRITE = switch (machine) {
3906 .SPARCV9 => true,
3907 else => false,
3908 },
3806 .WRITE = plt.got_plt == null,
39093807 },
3910 .size = init_plt_size,
3911 .addralign = plt_align,
3808 .size = plt.entry_size * plt.header_entries,
3809 .addralign = plt.@"align",
39123810 .node_align = elf.mf.flags.block_size,
39133811 });
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, .{
39153813 .name = ".plt.sec",
39163814 .flags = .{ .ALLOC = true, .EXECINSTR = true },
3917 .addralign = plt_align,
3815 .addralign = plt.@"align",
39183816 .node_align = elf.mf.flags.block_size,
39193817 });
39203818 if (maybe_interp) |interp| {
......@@ -4005,7 +3903,7 @@ fn initHeaders(
40053903 .type = .RELA,
40063904 .flags = .{ .ALLOC = true, .INFO_LINK = true },
40073905 .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().?,
40093907 .addralign = addr_align,
40103908 .entsize = rela_size,
40113909 .node_align = elf.mf.flags.block_size,
......@@ -4019,7 +3917,7 @@ fn initHeaders(
40193917 .node_align = addr_align,
40203918 });
40213919 switch (machine) {
4022 else => @panic(@tagName(machine)),
3920 .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)),
40233921 .X86_64 => {
40243922 const plt_ni = elf.shndx.plt.get(elf).ni;
40253923 const got_plt_sym: Symbol.Id = .local(elf.shndx.got_plt.get(elf).lsi);
......@@ -4035,14 +3933,14 @@ fn initHeaders(
40353933 2,
40363934 got_plt_sym,
40373935 8 * 1 - 4,
4038 .rel32,
3936 .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" }),
40393937 );
40403938 try elf.addSymbolRelocAssumeCapacity(
40413939 plt_ni,
40423940 8,
40433941 got_plt_sym,
40443942 8 * 2 - 4,
4045 .rel32,
3943 .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" }),
40463944 );
40473945 },
40483946 .LOONGARCH => {
......@@ -4073,9 +3971,24 @@ fn initHeaders(
40733971 });
40743972 elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);
40753973 try elf.ensureUnusedRelocCapacity(plt_ni, 3);
4076 try elf.addSymbolRelocAssumeCapacity(plt_ni, 0, got_plt_sym, 0, .larch_rel32_hi20);
4077 try elf.addSymbolRelocAssumeCapacity(plt_ni, 8, got_plt_sym, 0, .larch_abs32_lo12);
4078 try elf.addSymbolRelocAssumeCapacity(plt_ni, 16, got_plt_sym, 0, .larch_abs32_lo12);
3974 elf.addRelocAssumeCapacity(plt_ni, 0, got_plt_sym, 0, .{ .LARCH = .PCALA_HI20 }) catch |err| switch (err) {
3975 error.UnknownRelocation => unreachable,
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 };
40793992 },
40803993 .SPARCV9 => {},
40813994 }
......@@ -4092,7 +4005,7 @@ fn initHeaders(
40924005
40934006 // Populate reserved GOT words.
40944007 switch (machine) {
4095 else => @panic(@tagName(machine)),
4008 .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)),
40964009 .X86_64 => {
40974010 try elf.got.ensureUnusedCapacity(gpa, 3);
40984011 elf.got.putAssumeCapacityNoClobber(switch (have_dynamic_section) {
......@@ -4102,9 +4015,7 @@ fn initHeaders(
41024015 elf.got.putAssumeCapacityNoClobber(.{ .reserved = 1 }, .none);
41034016 elf.got.putAssumeCapacityNoClobber(.{ .reserved = 2 }, .none);
41044017 },
4105 .LOONGARCH,
4106 .SPARCV9,
4107 => {
4018 .LOONGARCH, .SPARCV9 => {
41084019 try elf.got.ensureUnusedCapacity(gpa, 1);
41094020 elf.got.putAssumeCapacityNoClobber(switch (have_dynamic_section) {
41104021 true => .{ .symbol = .local(elf.shndx.dynamic.get(elf).lsi) },
......@@ -4153,8 +4064,17 @@ fn initHeaders(
41534064 .node = elf.shndx.got.get(elf).ni,
41544065 .name = try .string(elf, "_GLOBAL_OFFSET_TABLE_"),
41554066 .value = switch (machine) {
4156 .QDSP6, .@"386", .X86_64 => elf.shndx.got_plt.vaddr(elf),
4157 else => elf.shndx.got.vaddr(elf),
4067 .AARCH64,
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),
41584078 },
41594079 .size = 0,
41604080 .type = .NOTYPE,
......@@ -4267,6 +4187,29 @@ fn initHeaders(
42674187 const shndx: Section.Index = @enumFromInt(shndx_raw);
42684188 elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {});
42694189 }
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 };
42704213}
42714214
42724215pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void {
......@@ -4379,7 +4322,7 @@ fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {
43794322 if (ptr.* != .none) {
43804323 for (elf.got_relocs.items[@intFromEnum(ptr.*)..]) |*reloc| {
43814324 if (reloc.node != ni) break;
4382 reloc.* = .deleted;
4325 reloc.delete(elf);
43834326 }
43844327 }
43854328 ptr.* = @enumFromInt(elf.got_relocs.items.len);
......@@ -4418,15 +4361,124 @@ fn flushMovedNodeRelocs(
44184361fn identClass(elf: *const Elf) std.elf.CLASS {
44194362 return @enumFromInt(elf.mf.memory_map.memory[std.elf.EI.CLASS]);
44204363}
4421fn identData(elf: *const Elf) std.elf.DATA {
4422 return @enumFromInt(elf.mf.memory_map.memory[std.elf.EI.DATA]);
4364
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 }
44234412}
44244413
4425fn targetPtrSize(elf: *const Elf) u32 {
4414fn targetPtrSize(elf: *const Elf) u8 {
44264415 return elf.identClass().size();
44274416}
44284417fn 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());
44304482}
44314483fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child {
44324484 const pointer_ty = @typeInfo(@TypeOf(ptr)).pointer;
......@@ -4435,7 +4487,7 @@ fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.chi
44354487 return switch (@typeInfo(Child)) {
44364488 else => @compileError(@typeName(Child)),
44374489 .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)))),
44394491 .@"struct" => |@"struct"| @bitCast(
44404492 elf.targetLoad(@as(*align(alignment) @"struct".backing_integer.?, @ptrCast(ptr))),
44414493 ),
......@@ -4475,14 +4527,6 @@ fn ehdrPtr(elf: *Elf) EhdrPtr {
44754527 ),
44764528 };
44774529}
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
44874531const PhdrSlice = union(std.elf.CLASS) {
44884532 NONE: noreturn,
......@@ -4490,7 +4534,7 @@ const PhdrSlice = union(std.elf.CLASS) {
44904534 @"64": []std.elf.Elf64.Phdr,
44914535};
44924536fn phdrSlice(elf: *Elf) PhdrSlice {
4493 assert(elf.ehdrField(.type) != .REL);
4537 assert(elf.ehdrType() != .REL);
44944538 const slice = elf.ni.phdr.slice(&elf.mf);
44954539 return switch (elf.identClass()) {
44964540 .NONE, _ => unreachable,
......@@ -4587,8 +4631,7 @@ fn mapInputSection(elf: *Elf, opts: struct {
45874631 return error.StripSection;
45884632 }
45894633
4590 const name: []const u8 = switch (elf.ehdrField(.type)) {
4591 .NONE, .CORE, _ => unreachable,
4634 const name: []const u8 = switch (elf.ehdrType()) {
45924635 .REL => opts.name,
45934636 .EXEC, .DYN => name: {
45944637 if (std.mem.startsWith(u8, opts.name, ".text.")) break :name ".text";
......@@ -5050,7 +5093,7 @@ fn loadObject(
50505093 const ElfN = class.ElfN();
50515094 const ehdr = try r.peekStruct(ElfN.Ehdr, target_endian);
50525095 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())
50545097 return diags.failParse(path, "bad machine", .{});
50555098 if (ehdr.shoff == 0 or ehdr.shnum <= 1) return;
50565099 if (ehdr.shoff + @as(u64, ehdr.shentsize) * @as(u64, ehdr.shnum) > fl.size)
......@@ -5383,23 +5426,43 @@ fn loadObject(
53835426 );
53845427 const target = symmap.items[rel.info.sym - 1];
53855428 if (target == Symbol.Id.null) {
5386 // If this is not an SHF_ALLOC section, then let's let this
5387 // slide for now, because it probably doesn't affect the final
5429 // If this is not an SHF_ALLOC section, then let's not report
5430 // this for now, because it probably doesn't affect the final
53885431 // binary's functionality for this section to be a bit broken.
5389 if (!loc_sec.shdr.flags.shf.ALLOC) continue;
5390 return diags.failParse(
5391 path,
5392 "unsupported symbol at index {d} required for relocation",
5393 .{rel.info.sym},
5394 );
5432 if (loc_sec.shdr.flags.shf.ALLOC) {
5433 diags.addParseError(
5434 path,
5435 "unsupported symbol at index {d} required for relocation",
5436 .{rel.info.sym},
5437 );
5438 }
5439 continue;
53955440 }
5396 try elf.addRelocAssumeCapacity(
5441 const rt: MachineRelocType = .wrap(rel.info.type, elf);
5442 elf.addRelocAssumeCapacity(
53975443 loc_node,
53985444 rel.offset - loc_sec.shdr.addr,
53995445 target,
54005446 rel.addend,
5401 .wrap(rel.info.type, elf),
5402 );
5447 rt,
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 };
54035466 }
54045467 },
54055468 };
......@@ -5423,7 +5486,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadPars
54235486 const ElfN = class.ElfN();
54245487 const ehdr = try r.peekStruct(ElfN.Ehdr, target_endian);
54255488 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())
54275490 return diags.failParse(path, "bad machine", .{});
54285491 if (ehdr.shnum > 0) try fr.seekTo(ehdr.shoff);
54295492 // We're going to need to know the alignment of every section later.
......@@ -5782,222 +5845,143 @@ fn prelinkInner(elf: *Elf) Error!void {
57825845 .file_symbol = zcu_file_symbol,
57835846 };
57845847 }
5848}
57855849
5786 const got_plt = switch (elf.ehdrField(.machine)) {
5787 .SPARCV9 => false,
5788 else => true,
5789 };
5850fn prepareDynamic(elf: *Elf) Error!void {
5851 const comp = elf.base.comp;
5852
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()) {
57925884 .NONE, _ => unreachable,
5793 inline else => |ct_class| {
5794 const ElfN = ct_class.ElfN();
5795 const flags: ElfN.Addr = if (elf.options.z_now) std.elf.DF_BIND_NOW else 0;
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 };
5885 inline else => |class| {
5886 const ElfN = class.ElfN();
5887
58065888 // Static PIEs don't need a PLT, so we shouldn't emit the associated dynamic entries.
58075889 const use_plt = !(comp.config.output_mode == .Exe and
58085890 comp.config.link_mode == .static and
58095891 comp.config.pie);
5810 const soname: ?String(.dynstr) = if (elf.options.soname) |soname_slice| str: {
5811 break :str try elf.string(.dynstr, soname_slice);
5812 } else null;
5813 const needed_len = elf.needed.count();
5814 const dynamic_len = needed_len + @intFromBool(elf.options.soname != null) +
5815 @intFromBool(rpath != .empty) +
5816 @intFromBool(flags != 0) + @intFromBool(flags_1 != 0) +
5817 @as(usize, @intFromBool(elf.shndx.init_array != .UNDEF)) * 2 +
5818 @as(usize, @intFromBool(elf.shndx.fini_array != .UNDEF)) * 2 +
5819 @as(usize, @intFromBool(elf.shndx.preinit_array != .UNDEF)) * 2 +
5820 @as(usize, @intFromBool(use_plt)) * 4 +
5821 @intFromBool(comp.config.output_mode == .Exe) + 8;
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),
5892
5893 const dynamic_size = elf.targetLoad(&@field(elf.shdrPtr(elf.shndx.dynamic), @tagName(class)).size);
5894 const dynamic_slice = elf.shndx.dynamic.get(elf).ni.slice(&elf.mf)[0..@intCast(dynamic_size)];
5895 const dynamic_entries: [][2]ElfN.Addr = @ptrCast(@alignCast(dynamic_slice));
5896
5897 var dynamic_index: usize = 0;
5898
5899 for (
5900 dynamic_entries[dynamic_index..][0..elf.needed.count()],
5901 elf.needed.keys(),
5902 ) |*dynamic_entry, needed| {
5903 dynamic_entry.* = .{ std.elf.DT_NEEDED, @intFromEnum(needed) };
58275904 }
5905 dynamic_index += elf.needed.count();
58285906
5829 const dynamic_indices: struct {
5830 init_array: ?usize,
5831 fini_array: ?usize,
5832 preinit_array: ?usize,
5833 jmprel: ?usize,
5834 pltgot: ?usize,
5835 } = indices: {
5836 const sec_dynamic = dynamic_ni.slice(&elf.mf);
5837 const dynamic_entries: [][2]ElfN.Addr = @ptrCast(@alignCast(sec_dynamic));
5838 errdefer comptime unreachable; // don't invalidate `dynamic_entries`
5839 var dynamic_index: usize = 0;
5840 for (
5841 dynamic_entries[dynamic_index..][0..needed_len],
5842 elf.needed.keys(),
5843 ) |*dynamic_entry, needed| dynamic_entry.* = .{ std.elf.DT_NEEDED, @intFromEnum(needed) };
5844 dynamic_index += needed_len;
5845 if (soname) |soname_dynstr| {
5846 dynamic_entries[dynamic_index] = .{ std.elf.DT_SONAME, @intFromEnum(soname_dynstr) };
5847 dynamic_index += 1;
5848 }
5849 if (rpath != .empty) {
5850 dynamic_entries[dynamic_index] = .{ std.elf.DT_RUNPATH, @intFromEnum(rpath) };
5851 dynamic_index += 1;
5852 }
5853 if (flags != 0) {
5854 dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS, flags };
5855 dynamic_index += 1;
5856 }
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 },
5907 if (elf.dynamic.soname != .empty) {
5908 dynamic_entries[dynamic_index] = .{ std.elf.DT_SONAME, @intFromEnum(elf.dynamic.soname) };
5909 dynamic_index += 1;
5910 }
5911 if (elf.dynamic.rpath != .empty) {
5912 dynamic_entries[dynamic_index] = .{ std.elf.DT_RUNPATH, @intFromEnum(elf.dynamic.rpath) };
5913 dynamic_index += 1;
5914 }
5915 if (elf.dynamic.flags != 0) {
5916 dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS, elf.dynamic.flags };
5917 dynamic_index += 1;
5918 }
5919 if (elf.dynamic.flags_1 != 0) {
5920 dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS_1, elf.dynamic.flags_1 };
5921 dynamic_index += 1;
5922 }
5923 if (comp.config.output_mode == .Exe) {
5924 dynamic_entries[dynamic_index] = .{ std.elf.DT_DEBUG, 0 };
5925 dynamic_index += 1;
5926 }
5927 if (elf.textrel_count > 0) {
5928 dynamic_entries[dynamic_index] = .{ std.elf.DT_TEXTREL, 0 };
5929 dynamic_index += 1;
5930 }
5931 if (elf.shndx.init_array != .UNDEF) {
5932 dynamic_entries[dynamic_index..][0..2].* = .{
5933 .{ std.elf.DT_INIT_ARRAY, @intCast(elf.shndx.init_array.vaddr(elf)) },
5934 .{ std.elf.DT_INIT_ARRAYSZ, @intCast(elf.shndx.init_array.size(elf)) },
59205935 };
5921 dynamic_index += 8;
5922 assert(dynamic_index == dynamic_len);
5923 if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry|
5924 std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry);
5925
5926 break :indices .{
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,
5936 dynamic_index += 2;
5937 }
5938 if (elf.shndx.fini_array != .UNDEF) {
5939 dynamic_entries[dynamic_index..][0..2].* = .{
5940 .{ std.elf.DT_FINI_ARRAY, @intCast(elf.shndx.fini_array.vaddr(elf)) },
5941 .{ std.elf.DT_FINI_ARRAYSZ, @intCast(elf.shndx.fini_array.size(elf)) },
59325942 };
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) {
5936 .NONE, _ => comptime unreachable,
5937 .@"32" => .dsorel32,
5938 .@"64" => .dsorel64,
5968 dynamic_entries[dynamic_index..][0..8].* = .{
5969 .{ std.elf.DT_RELA, @intCast(elf.shndx.rela_dyn.vaddr(elf)) },
5970 .{ std.elf.DT_RELASZ, @intCast(elf.shndx.rela_dyn.size(elf)) },
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 },
59395977 };
5978 dynamic_index += 8;
59405979
5941 elf.dynamic_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);
5942 try elf.ensureUnusedRelocCapacity(dynamic_ni, 8);
5943 if (dynamic_indices.init_array) |index| try elf.addSymbolRelocAssumeCapacity(
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 );
5980 assert(dynamic_index == dynamic_entries.len);
5981 if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry|
5982 std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry);
59995983 },
6000 };
5984 }
60015985}
60025986
60035987fn 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 {
60176001 .PROGBITS => assert(opts.size > 0),
60186002 else => {},
60196003 }
6020 if (opts.flags.ALLOC and elf.ehdrField(.type) != .REL) {
6004 if (opts.flags.ALLOC and elf.ehdrType() != .REL) {
60216005 assert(elf.getNode(segment_ni) == .segment);
60226006 }
60236007 const gpa = elf.base.comp.gpa;
......@@ -6054,8 +6038,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
60546038 },
60556039 };
60566040 try elf.ensureNodeSize(elf.ni.shdr, new_shdr_size);
6057 const ni = try elf.mf.addLastChildNode(gpa, switch (elf.ehdrField(.type)) {
6058 .NONE, .CORE, _ => unreachable,
6041 const ni = try elf.mf.addLastChildNode(gpa, switch (elf.ehdrType()) {
60596042 .REL => elf.ni.file,
60606043 .EXEC, .DYN => segment_ni,
60616044 }, .{
......@@ -6106,8 +6089,7 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize)
61066089 try elf.symbol_relocs.ensureUnusedCapacity(gpa, len);
61076090 try elf.got_relocs.ensureUnusedCapacity(gpa, len);
61086091 const class = elf.identClass();
6109 switch (elf.ehdrField(.type)) {
6110 .NONE, .CORE, _ => unreachable,
6092 switch (elf.ehdrType()) {
61116093 .REL => {
61126094 const shndx = elf.getNodeShndx(node);
61136095 if (shndx.get(elf).rela.shndx == .UNDEF) {
......@@ -6166,10 +6148,9 @@ fn addRelocAssumeCapacity(
61666148 target: Symbol.Id,
61676149 addend: i64,
61686150 @"type": MachineRelocType,
6169) Error!void {
6151) (Error || error{ UnknownRelocation, NonStaticRelocation, UnimplementedRelocation })!void {
61706152 assert(node != .none);
6171 switch (elf.ehdrField(.type)) {
6172 .NONE, .CORE, _ => unreachable,
6153 switch (elf.ehdrType()) {
61736154 .REL => {
61746155 const rela_shndx = elf.getNodeShndx(node).get(elf).rela.shndx;
61756156 const rela_index = rela_shndx.relaAddOneAssumeCapacity(elf, .{
......@@ -6187,161 +6168,113 @@ fn addRelocAssumeCapacity(
61876168 const target_ptr = target.index(elf).ptr(elf);
61886169 const next = target_ptr.first_target_reloc;
61896170 target_ptr.first_target_reloc = ri;
6190 break :next next;
6191 };
6192 if (next != .none) {
6193 next.get(elf).prev = ri;
6194 }
6195 elf.symbol_relocs.appendAssumeCapacity(.{
6196 .node = node,
6197 .offset = offset,
6198 .type = .write_rela,
6199 .target = target,
6200 .addend = addend,
6201 .next = next,
6202 .prev = .none,
6203 .rela_index = rela_index.toOptional(),
6204 });
6205 },
6206
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 },
6171 break :next next;
6172 };
6173 if (next != .none) {
6174 next.get(elf).prev = ri;
6175 }
6176 elf.symbol_relocs.appendAssumeCapacity(.{
6177 .node = node,
6178 .offset = offset,
6179 .type = undefined,
6180 .target = target,
6181 .addend = addend,
6182 .next = next,
6183 .prev = .none,
6184 .rela_index = rela_index.toOptional(),
6185 .result = .ok,
6186 });
6187 },
62526188
6253 // TODO: these are the address of an arbitrary symbol (or PLT entry) relative to the
6254 // base of the GOT, which is quite annoying. Luckily, they seem to be rare, so I'm
6255 // probably just going to introduce a set (ArrayHashMap) of SymbolReloc.Index which
6256 // need to be re-applied whenever the GOT moves.
6257 .GOTOFF64 => @panic("TODO: R_X86_64_GOTOFF64"), // offset of symbol from GOT base
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),
6189 .DYN, .EXEC => switch (elf.ehdrMachine()) {
6190 .AARCH64 => switch (@"type".AARCH64) {
6191 .NONE => {},
6192 _ => return error.UnknownRelocation,
6193 else => return error.UnimplementedRelocation,
62776194 },
6278 .LOONGARCH => switch (@"type".LOONGARCH) {
6279 else => std.debug.panic("TODO: unsupported input relocation, {t}", .{@"type".LOONGARCH}),
6280 _,
6281 .NONE,
6195 .LOONGARCH => rel_type: switch (@"type".LARCH) {
6196 .NONE => {},
6197 _ => return error.UnknownRelocation,
6198
62826199 .COPY,
62836200 .JUMP_SLOT,
62846201 .RELATIVE,
62856202 .IRELATIVE,
6286 => std.debug.panic("TODO: error for illegal or unsupported input relocation, {t}", .{@"type".LOONGARCH}),
6287
6288 .RELAX => {}, // TODO: relaxation is not yet implemented
6289
6290 // Relocations targeting a symbol
6291 .@"64" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs64),
6292 .@"32" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32),
6293 .@"64_PCREL" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel64),
6294 .@"32_PCREL" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel32),
6295
6296 .PCALA_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_abs32_lo12),
6297 .PCALA_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_rel32_hi20),
6298 .PCALA64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_rel64_hi12),
6299 .PCALA64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_rel64_lo20),
6300
6301 .B16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_branch_rel18),
6302 .B21 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_branch_rel23),
6303 .B26 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_branch_rel28),
6304 .CALL36 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_call_rel38),
6305
6306 // Relocations targeting a TLS symbol
6307 .TLS_LE_LO12, .TLS_LE_LO12_R => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_tpoff32_lo12),
6308 .TLS_LE_HI20, .TLS_LE_HI20_R => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_tpoff32_hi20),
6309 .TLS_LE64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_tpoff64_lo20),
6310 .TLS_LE64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .larch_tpoff64_hi12),
6311 .TLS_LE_ADD_R => {}, // TODO: relaxation is not yet implemented
6312
6313 // Relocations targeting a GOT entry
6314 .GOT_PC_LO12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_abs32_lo12),
6315 .GOT_PC_HI20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_rel32_hi20),
6316 .GOT64_PC_LO20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_rel64_lo20),
6317 .GOT64_PC_HI12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_rel64_hi12),
6318
6319 .GOT_LO12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_abs32_lo12),
6320 .GOT_HI20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_abs32_hi20),
6321 .GOT64_LO20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_abs64_lo20),
6322 .GOT64_HI12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .larch_abs64_hi12),
6203 => return error.NonStaticRelocation,
6204
6205 else => return error.UnimplementedRelocation,
6206
6207 // These relocations signal that certain relaxations are legal, but this linker does
6208 // not yet implement relaxation, so these are ignored.
6209 .RELAX, .TLS_LE_ADD_R => {},
6210
6211 // Relaxable versions of other relocations. Since we don't yet implement relaxation,
6212 // just use the handling for the non-relaxable versions.
6213 .TLS_LE_LO12_R => continue :rel_type .TLS_LE_LO12,
6214 .TLS_LE_HI20_R => continue :rel_type .TLS_LE_HI20,
6215
6216 // zig fmt: off
6217 .@"32" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
6218 .@"64" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
6219 .@"32_PCREL" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
6220 .@"64_PCREL" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })),
6221 .ABS_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })),
6222 .ABS_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"12" })),
6223 .ABS64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"32" })),
6224 .ABS64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .unsigned, .shift = .@"52" })),
6225 .PCALA_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })),
6226 .PCALA_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_pcala_hi20)),
6227 .PCALA64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_pcala64_lo20)),
6228 .PCALA64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_pcala64_hi12)),
6229
6230 .B16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.pltrel, .{ .dest = .@"32[25:10]", .cast = .signed, .shift = .@"2_exact" })),
6231 .B21 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_b21)),
6232 .B26 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_b26)),
6233 .CALL36 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_call36)),
6234
6235 .TLS_LE_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })),
6236 .TLS_LE_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"12" })),
6237 .TLS_LE64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"32" })),
6238 .TLS_LE64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[21:10]", .cast = .unsigned, .shift = .@"52" })),
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,
63236259 },
63246260 .SPARCV9 => switch (@"type".SPARC) {
6325 _,
6326 .NONE,
6261 .NONE => {},
6262 _ => return error.UnknownRelocation,
6263
63276264 .COPY,
63286265 .GLOB_DAT,
63296266 .JMP_SLOT,
63306267 .RELATIVE,
63316268 .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,
63356272 .HI22,
6336 .@"22",
6337 .@"13",
63386273 .LO10,
63396274 .HIPLT22,
63406275 .LOPLT10,
63416276 .PCPLT22,
63426277 .PCPLT10,
6343 .@"10",
6344 .@"11",
63456278 .OLO10,
63466279 .HH22,
63476280 .HM10,
......@@ -6351,62 +6284,24 @@ fn addRelocAssumeCapacity(
63516284 .PC_LM22,
63526285 .WDISP16,
63536286 .WDISP19,
6354 .@"7",
6355 .@"5",
6356 .@"6",
63576287 .HIX22,
63586288 .LOX10,
63596289 .REGISTER,
6360 .TLS_GD_HI22,
6361 .TLS_GD_LO10,
63626290 .TLS_IE_HI22,
63636291 .TLS_IE_LO10,
63646292 .TLS_DTPMOD32,
63656293 .TLS_DTPMOD64,
63666294 .H34,
63676295 .WDISP10,
6368 => |t| @panic("TODO: " ++ @tagName(t)),
6369
6370 // Relocations targeting a symbol
6371 .@"8" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs8),
6372 .@"16" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs16),
6373 .@"32" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32),
6374 .DISP8 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel8),
6375 .DISP16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel16),
6376 .DISP32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel32),
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.
6296 => return error.UnimplementedRelocation,
6297
6298 // These need similar handling to `R_X86_64_GOTOFF64`. No compiler seems to emit them though.
6299 .GOTDATA_HIX22 => return error.UnimplementedRelocation,
6300 .GOTDATA_LOX10 => return error.UnimplementedRelocation,
6301
6302 // These relocations signal that certain relaxations are legal, but this linker does
6303 // not yet implement relaxation, so these are ignored.
6304 .GOTDATA_OP,
64106305 .TLS_GD_ADD,
64116306 .TLS_LDM_ADD,
64126307 .TLS_LDO_ADD,
......@@ -6415,19 +6310,180 @@ fn addRelocAssumeCapacity(
64156310 .TLS_IE_ADD,
64166311 => {},
64176312
6418 // Relocations targeting a GOT entry
6419 .GOT10 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .sparc_10),
6420 .GOT13 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .sparc_13),
6421 .GOT22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .sparc_22),
6422 .TLS_LDM_HI22 => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .sparc_ldm_hi22),
6423 .TLS_LDM_LO10 => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .sparc_ldm_lo10),
6424 // These need similar handling to `R_X86_64_GOTOFF64`. No compiler seems to emit them though.
6425 .GOTDATA_HIX22 => @panic("TODO: R_SPARC_GOTDATA_HIX22"),
6426 .GOTDATA_LOX10 => @panic("TODO: R_SPARC_GOTDATA_LOX10"),
6427 .GOTDATA_OP_HIX22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .sparc_op_hix22),
6428 .GOTDATA_OP_LOX10 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .sparc_op_lox10),
6429 // We currently do no relaxation, so nothing to do for this one.
6430 .GOTDATA_OP => {},
6313 // zig fmt: off
6314 .@"8" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"8", .cast = .unsigned, .shift = .@"0" })),
6315 .@"16", .UA16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"16", .cast = .unsigned, .shift = .@"0" })),
6316 .@"32", .UA32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
6317 .@"64", .UA64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
6318
6319 .@"5" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[4:0]", .cast = .unsigned, .shift = .@"0" })),
6320 .@"6" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[5:0]", .cast = .unsigned, .shift = .@"0" })),
6321 .@"7" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[6:0]", .cast = .unsigned, .shift = .@"0" })),
6322 .@"10" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[9:0]", .cast = .unsigned, .shift = .@"0" })),
6323 .@"11" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[10:0]", .cast = .unsigned, .shift = .@"0" })),
6324 .@"13" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[12:0]", .cast = .unsigned, .shift = .@"0" })),
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 },
64316487 },
64326488 },
64336489 }
......@@ -6440,10 +6496,12 @@ fn addSymbolRelocAssumeCapacity(
64406496 addend: i64,
64416497 @"type": SymbolReloc.Type,
64426498) Error!void {
6443 assert(elf.ehdrField(.type) != .REL);
6499 assert(elf.ehdrType() != .REL);
64446500 assert(node != .none);
64456501
64466502 const rela_index: Section.RelaIndex.Optional = r: {
6503 if (elf.shndx.dynamic == .UNDEF) break :r .none;
6504
64476505 // If we emit a runtime relocation entry, its `offset` is a virtual address, so we need to
64486506 // determine the vaddr of `node`.
64496507 const node_vaddr: u64 = switch (elf.getNode(node)) {
......@@ -6461,162 +6519,51 @@ fn addSymbolRelocAssumeCapacity(
64616519 => |i| Symbol.Id.local(i.symbol(elf)).value(elf),
64626520 };
64636521
6464 const rela_type: MachineRelocType = switch (elf.ehdrField(.machine)) {
6465 else => |machine| @panic(@tagName(machine)),
6466 .X86_64 => .{ .X86_64 = switch (@"type") {
6467 .write_rela => unreachable,
6468 .dsorel64, .dsorel32 => {
6469 assert(target.unwrap() == .local);
6470 break :r .none;
6471 },
6472 .abs64 => .@"64",
6473 .abs32 => .@"32",
6474 .abs16 => unreachable,
6475 .abs8 => unreachable,
6476 .abs32s => .@"32S",
6477 .rel64 => .PC64,
6478 .rel32 => .PC32,
6479 .rel16 => unreachable,
6480 .rel8 => unreachable,
6481 .pltabs64, .pltabs32, .pltrel64, .pltrel32 => break :r .none,
6482 .dtpoff64 => .DTPOFF64,
6483 .dtpoff32 => .DTPOFF32,
6484 .tpoff64 => .TPOFF64,
6485 .tpoff32 => .TPOFF32,
6486 .size64 => .SIZE64,
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,
6522 // If this is `true`, we will try to create a copy relocation for the target symbol if it is
6523 // not locally defined. If the relocation value is always computed from the target symbol's
6524 // value (even for an external target symbol), and if the target symbol might be of type
6525 // STT_OBJECT, this should probably be `true`.
6526 const try_copy_reloc: bool = switch (@"type".target) {
6527 .rel, .abs => true,
6528
6529 .pltrel,
6530 .pltabs,
6531 .dtpoff,
6532 .tpoff,
6533 .size,
6534 => false,
6535
6536 .special => switch (@"type".action.special) {
6537 .larch_pcala_hi20,
6538 .larch_pcala64_lo20,
6539 .larch_pcala64_hi12,
6540 => true,
6541
6542 .larch_b21,
6543 .larch_b26,
6544 .larch_call36,
65616545 .sparc_le_hix22,
6562 .sparc_le_lox10,
6563 => unreachable,
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 } },
6546 => false,
6547 },
66146548 };
66156549
6616 class: switch (elf.classifySymbolValue(target)) {
6550 classify: switch (elf.classifySymbolValue(target)) {
66176551 .static => break :r .none,
66186552 .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;
66206567 switch (elf.nodeWantsDsoRelocation(node)) {
66216568 .no => break :r .none,
66226569 .yes => {},
......@@ -6629,30 +6576,47 @@ fn addSymbolRelocAssumeCapacity(
66296576 .addend = 0,
66306577 }).toOptional();
66316578 },
6632 .dynamic => dso_reloc: switch (elf.nodeWantsDsoRelocation(node)) {
6633 .no => break :r .none,
6634 .yes_textrel => if (try elf.maybeAddCopyRelocation(target.unwrap().global)) {
6635 // We were able to use a copy relocation on this symbol to avoid a text relocation,
6636 // which is apparently considered a good thing despite copy relocations being an
6637 // abomination. (This is necessary for correctness in some cases, because e.g. a
6638 // 32-bit runtime relocation on a 64-bit target will often cause rtld errors due to
6639 // the DSOs being loaded too far apart.)
6640 switch (elf.classifySymbolValue(target)) {
6641 .dynamic => unreachable, // we just added a copy relocation
6642 .static => continue :class .static,
6643 .static_relative => continue :class .static_relative,
6644 }
6645 } else {
6646 // At least for now, our only choice is a text relocation.
6647 elf.textrel_count += 1;
6648 continue :dso_reloc .yes;
6649 },
6650 .yes => break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
6651 .type = rela_type,
6579 .dynamic => if (try_copy_reloc and try elf.maybeAddCopyRelocation(target.unwrap().global)) {
6580 switch (elf.classifySymbolValue(target)) {
6581 .static => continue :classify .static,
6582 .static_relative => continue :classify .static_relative,
6583 .dynamic => unreachable, // we just added a copy relocation
6584 }
6585 } else {
6586 const dynamic_reloc_type: MachineRelocType = switch (@"type".target) {
6587 // PLT relocations targeting dynamic symbols actually target that symbol's PLT
6588 // entry, so we should emit an `R_*_RELATIVE` relocation instead.
6589 .pltabs => continue :classify .static_relative,
6590 // ...although PC-relative PLT relocations don't even need that!
6591 .pltrel => break :r .none,
6592 // Weird sizes or computations are not supported as runtime relocations.
6593 .special => break :r .none,
6594 // Relative addresses are not supported as runtime relocations.
6595 .rel => break :r .none,
6596
6597 // On the few targets supporting size relocations, they are valid at runtime.
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,
66526616 .offset = node_vaddr + offset,
66536617 .raw_sym_index = elf.globalByName(target.unwrap().global).?.dynsym_index,
66546618 .addend = addend,
6655 }).toOptional(),
6619 }).toOptional();
66566620 },
66576621 }
66586622 };
......@@ -6673,8 +6637,9 @@ fn addSymbolRelocAssumeCapacity(
66736637 .next = next,
66746638 .prev = .none,
66756639 .rela_index = rela_index,
6640 .result = .ok,
66766641 });
6677 if (@"type".dependsOnTlsSize()) {
6642 if (@"type".dependsOnTlsSize(elf)) {
66786643 elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {});
66796644 }
66806645
......@@ -6689,7 +6654,7 @@ fn addGotRelocAssumeCapacity(
66896654 addend: i64,
66906655 @"type": GotReloc.Type,
66916656) void {
6692 assert(elf.ehdrField(.type) != .REL);
6657 assert(elf.ehdrType() != .REL);
66936658 switch (elf.getNode(node)) {
66946659 .input_section,
66956660 .nav,
......@@ -6742,6 +6707,7 @@ fn addGotRelocAssumeCapacity(
67426707 .target = target,
67436708 .addend = addend,
67446709 .type = @"type",
6710 .result = .ok,
67456711 });
67466712}
67476713fn updateGotEntry(elf: *Elf, got_index: usize) void {
......@@ -6768,23 +6734,17 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
67686734 const sym_value = sym_id.value(elf);
67696735 break :val .{ .signed = @bitCast(sym_value -% tls_size) };
67706736 }
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 };
67776737 break :val switch (sym_id.unwrap()) {
67786738 // For global symbols, just target the right dynsym with no addend.
67796739 .global => |name| .{ .reloc = .{
6780 .type = reloc_type,
6740 .type = .tpOff(elf),
67816741 .dynsym_index = elf.globalByName(name).?.dynsym_index,
67826742 .addend = 0,
67836743 } },
67846744 // For local symbols, target the null symbol (index 0) so we get the offset to the
67856745 // base of our TLS block, and then use `addend` to offset to the right symbol.
67866746 .local => .{ .reloc = .{
6787 .type = reloc_type,
6747 .type = .tpOff(elf),
67886748 .dynsym_index = 0,
67896749 .addend = @intCast(sym_id.value(elf)),
67906750 } },
......@@ -6807,7 +6767,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
68076767 .static => .{ .unsigned = sym.value(elf) },
68086768 .static_relative => unreachable, // TLS variables should be in TLS sections, which do not return `.static_relative`
68096769 .dynamic => .{ .reloc = .{
6810 .type = .dtpOffAddr(elf),
6770 .type = .dtpOff(elf),
68116771 .dynsym_index = elf.globalByName(sym.unwrap().global).?.dynsym_index,
68126772 .addend = 0,
68136773 } },
......@@ -6818,12 +6778,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
68186778 break :val .{ .unsigned = 1 }; // TLS module ID for executable
68196779 },
68206780 .dynamic => .{ .reloc = .{
6821 .type = switch (elf.ehdrField(.machine)) {
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 },
6781 .type = .dtpMod(elf),
68276782 .dynsym_index = switch (elf.classifySymbolValue(sym)) {
68286783 .static, .static_relative => 0,
68296784 .dynamic => elf.globalByName(sym.unwrap().global).?.dynsym_index,
......@@ -6837,12 +6792,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
68376792 break :val .{ .unsigned = 1 }; // TLS module ID for executable
68386793 },
68396794 .dynamic => .{ .reloc = .{
6840 .type = switch (elf.ehdrField(.machine)) {
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 },
6795 .type = .dtpMod(elf),
68466796 .dynsym_index = 0,
68476797 .addend = 0,
68486798 } },
......@@ -7113,18 +7063,30 @@ pub fn flush(
71137063 for (elf.globals.strong_undef.keys()) |name| {
71147064 if (elf.dso_globals.contains(name)) continue;
71157065 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)});
71177067 }
71187068 if (any_undef) return error.AlreadyReported;
71197069 }
71207070
7121 elf.updateDynamicTextrel() catch |err| switch (err) {
7071 elf.prepareDynamic() catch |err| switch (err) {
71227072 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
71237073 else => |e| return e,
71247074 };
71257075
71267076 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
71287090 const entry_addr: u64 = entry: {
71297091 const sym_name_slice: []const u8 = name: switch (elf.options.entry) {
71307092 .default => switch (comp.config.output_mode) {
......@@ -7151,44 +7113,6 @@ pub fn flush(
71517113 else => |e| return e,
71527114 };
71537115}
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
71937117pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
71947118 const comp = elf.base.comp;
......@@ -7220,7 +7144,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
72207144 const sym_id: Symbol.Id = .global(global_name);
72217145 const sym = global.symtab_index.ptr(elf);
72227146
7223 switch (elf.ehdrField(.type)) {
7147 switch (elf.ehdrType()) {
72247148 .REL => {
72257149 // Index in `.symtab` has changed. Relocatables are easy, we just need to update
72267150 // all of the output relocations.
......@@ -7238,7 +7162,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
72387162 // For other `ET_*` values, the index in `.dynsym` has changed. There are a few
72397163 // places we might have emitted output relocations, depending on whether or not the
72407164 // symbol's value is statically known.
7241 else => switch (elf.classifySymbolValue(sym_id)) {
7165 .EXEC, .DYN => switch (elf.classifySymbolValue(sym_id)) {
72427166 .static, .static_relative => {
72437167 // Since the symbol value is statically known, we definitely aren't emitting
72447168 // 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 {
78077731 },
78087732 }
78097733}
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}
78107926fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_addr: u64, addr: u64) void {
78117927 const target_endian = elf.targetEndian();
7812 switch (elf.ehdrField(.machine)) {
7813 else => |machine| @panic(@tagName(machine)),
7928 switch (elf.ehdrMachine()) {
7929 .AARCH64, .PPC64, .RISCV => |machine| @panic(@tagName(machine)),
78147930 .X86_64 => {
78157931 switch (which) {
78167932 .plt => return,
......@@ -7912,8 +8028,20 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad
79128028
79138029 const got_plt_abs: u64 = got_plt_addr + got_plt_offset;
79148030 // TODO: handle overflow gracefully
7915 link.loongarch.writeJ20(target_slice[0..4], link.loongarch.toPcalaHi20(got_plt_abs, plt_addr + plt_offset));
7916 link.loongarch.writeK12(target_slice[4..8], @truncate(got_plt_abs));
8031 const inst0: *align(1) link.loongarch.J20 = @ptrCast(target_slice[0..4]);
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 });
79178045 }
79188046 },
79198047 }
......@@ -7986,10 +8114,10 @@ fn updateExportsInner(
79868114 .size = @intCast(size),
79878115 .type = @"type",
79888116 .bind = switch (@"export".opts.linkage) {
7989 .internal => @panic("TODO internal linkage"),
79908117 .strong => .strong,
79918118 .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", .{}),
79938121 },
79948122 .visibility = switch (@"export".opts.visibility) {
79958123 .default => .DEFAULT,
......@@ -8152,3 +8280,19 @@ fn ensureNodeSize(
81528280 const new_size = need_size + need_size / MappedFile.growth_factor;
81538281 try node.resize(&elf.mf, gpa, new_size);
81548282}
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 @@
1const std = @import("std");
2const mem = std.mem;
1pub const J20 = packed struct(u32) { b0_4: u5, j20: u20, b25_31: u7 };
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 {
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 {
7pub fn pcalaHi20(target: u64, pc: u64) u20 {
428 return @truncate(((target +% 0x800) >> 12) -% (pc >> 12));
439}
4410
45pub fn toPcala64Lo20(target: u64, pc: u64) u20 {
11pub fn pcala64Lo20(target: u64, pc: u64) u20 {
4612 const fixup = if (target & 0x800 != 0) (@as(u64, 0x1000) -% @as(u64, 0x100000000)) else 0;
4713 const hi32 = (((target +% 0x80000000 +% fixup) >> 12) -% ((pc -% 8) >> 12)) >> 20;
4814 return @truncate(hi32);
4915}
5016
51pub fn toPcala64Hi12(target: u64, pc: u64) u12 {
17pub fn pcala64Hi12(target: u64, pc: u64) u12 {
5218 const fixup = if (target & 0x800 != 0) (@as(u64, 0x1000) -% @as(u64, 0x100000000)) else 0;
5319 const hi32 = (((target +% 0x80000000 +% fixup) >> 12) -% ((pc -% 12) >> 12)) >> 20;
5420 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};