authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-06-26 02:08:14+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-06-26 02:08:14+02:00
log1999d80d6e9fc28fde7e50cc35d879e2467756ae
tree285153f0ae3fe723894956abf4e11a4eb396044d
parent1526ae4e5112b388209f0a0d7352c27da28cec74
parentf1e3b67b1fae868fed3f13a18642607c777fc087

Merge pull request 'Elf2: add initial LoongArch support' (#35875) from AstraFall/zig:loongarch/elf2/init into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/35875 Reviewed-by: mlugg <mlugg@noreply.codeberg.org>

4 files changed, 620 insertions(+), 12 deletions(-)

lib/std/elf.zig+153
...@@ -3001,6 +3001,141 @@ pub const R_PPC64 = enum(u32) {...@@ -3001,6 +3001,141 @@ pub const R_PPC64 = enum(u32) {
3001 _,3001 _,
3002};3002};
30033003
3004/// LoongArch relocations, as of v2.50 of the ABI specs.
3005pub const R_LARCH = enum(u32) {
3006 NONE = 0,
3007 @"32" = 1,
3008 @"64" = 2,
3009 RELATIVE = 3,
3010 COPY = 4,
3011 JUMP_SLOT = 5,
3012 TLS_DTPMOD32 = 6,
3013 TLS_DTPMOD64 = 7,
3014 TLS_DTPREL32 = 8,
3015 TLS_DTPREL64 = 9,
3016 TLS_TPREL32 = 10,
3017 TLS_TPREL64 = 11,
3018 IRELATIVE = 12,
3019 TLS_DESC32 = 13,
3020 TLS_DESC64 = 14,
3021 MARK_LA = 20,
3022 MARK_PCREL = 21,
3023 SOP_PUSH_PCREL = 22,
3024 SOP_PUSH_ABSOLUTE = 23,
3025 SOP_PUSH_DUP = 24,
3026 SOP_PUSH_GPREL = 25,
3027 SOP_PUSH_TLS_TPREL = 26,
3028 SOP_PUSH_TLS_GOT = 27,
3029 SOP_PUSH_TLS_GD = 28,
3030 SOP_PUSH_PLT_PCREL = 29,
3031 SOP_ASSERT = 30,
3032 SOP_NOT = 31,
3033 SOP_SUB = 32,
3034 SOP_SL = 33,
3035 SOP_SR = 34,
3036 SOP_ADD = 35,
3037 SOP_AND = 36,
3038 SOP_IF_ELSE = 37,
3039 SOP_POP_32_S_10_5 = 38,
3040 SOP_POP_32_U_10_12 = 39,
3041 SOP_POP_32_S_10_12 = 40,
3042 SOP_POP_32_S_10_16 = 41,
3043 SOP_POP_32_S_10_16_S2 = 42,
3044 SOP_POP_32_S_5_20 = 43,
3045 SOP_POP_32_S_0_5_10_16_S2 = 44,
3046 SOP_POP_32_S_0_10_10_16_S2 = 45,
3047 SOP_POP_32_U = 46,
3048 ADD8 = 47,
3049 ADD16 = 48,
3050 ADD24 = 49,
3051 ADD32 = 50,
3052 ADD64 = 51,
3053 SUB8 = 52,
3054 SUB16 = 53,
3055 SUB24 = 54,
3056 SUB32 = 55,
3057 SUB64 = 56,
3058 GNU_VTINHERIT = 57,
3059 GNU_VTENTRY = 58,
3060 B16 = 64,
3061 B21 = 65,
3062 B26 = 66,
3063 ABS_HI20 = 67,
3064 ABS_LO12 = 68,
3065 ABS64_LO20 = 69,
3066 ABS64_HI12 = 70,
3067 PCALA_HI20 = 71,
3068 PCALA_LO12 = 72,
3069 PCALA64_LO20 = 73,
3070 PCALA64_HI12 = 74,
3071 GOT_PC_HI20 = 75,
3072 GOT_PC_LO12 = 76,
3073 GOT64_PC_LO20 = 77,
3074 GOT64_PC_HI12 = 78,
3075 GOT_HI20 = 79,
3076 GOT_LO12 = 80,
3077 GOT64_LO20 = 81,
3078 GOT64_HI12 = 82,
3079 TLS_LE_HI20 = 83,
3080 TLS_LE_LO12 = 84,
3081 TLS_LE64_LO20 = 85,
3082 TLS_LE64_HI12 = 86,
3083 TLS_IE_PC_HI20 = 87,
3084 TLS_IE_PC_LO12 = 88,
3085 TLS_IE64_PC_LO20 = 89,
3086 TLS_IE64_PC_HI12 = 90,
3087 TLS_IE_HI20 = 91,
3088 TLS_IE_LO12 = 92,
3089 TLS_IE64_LO20 = 93,
3090 TLS_IE64_HI12 = 94,
3091 TLS_LD_PC_HI20 = 95,
3092 TLS_LD_HI20 = 96,
3093 TLS_GD_PC_HI20 = 97,
3094 TLS_GD_HI20 = 98,
3095 @"32_PCREL" = 99,
3096 RELAX = 100,
3097 DELETE = 101,
3098 ALIGN = 102,
3099 PCREL20_S2 = 103,
3100 CFA = 104,
3101 ADD6 = 105,
3102 SUB6 = 106,
3103 ADD_ULEB128 = 107,
3104 SUB_ULEB128 = 108,
3105 @"64_PCREL" = 109,
3106 CALL36 = 110,
3107 TLS_DESC_PC_HI20 = 111,
3108 TLS_DESC_PC_LO12 = 112,
3109 TLS_DESC64_PC_LO20 = 113,
3110 TLS_DESC64_PC_HI12 = 114,
3111 TLS_DESC_HI20 = 115,
3112 TLS_DESC_LO12 = 116,
3113 TLS_DESC64_LO20 = 117,
3114 TLS_DESC64_HI12 = 118,
3115 TLS_DESC_LD = 119,
3116 TLS_DESC_CALL = 120,
3117 TLS_LE_HI20_R = 121,
3118 TLS_LE_ADD_R = 122,
3119 TLS_LE_LO12_R = 123,
3120 TLS_LD_PCREL20_S2 = 124,
3121 TLS_GD_PCREL20_S2 = 125,
3122 TLS_DESC_PCREL20_S2 = 126,
3123 CALL30 = 127,
3124 PCADD_HI20 = 128,
3125 PCADD_LO12 = 129,
3126 GOT_PCADD_HI20 = 130,
3127 GOT_PCADD_LO12 = 131,
3128 TLS_IE_PCADD_HI20 = 132,
3129 TLS_IE_PCADD_LO12 = 133,
3130 TLS_LD_PCADD_HI20 = 134,
3131 TLS_LD_PCADD_LO12 = 135,
3132 TLS_GD_PCADD_HI20 = 136,
3133 TLS_GD_PCADD_LO12 = 137,
3134 TLS_DESC_PCADD_HI20 = 138,
3135 TLS_DESC_PCADD_LO12 = 139,
3136 _,
3137};
3138
3004pub const ar_hdr = extern struct {3139pub const ar_hdr = extern struct {
3005 /// Member file name, sometimes / terminated.3140 /// Member file name, sometimes / terminated.
3006 ar_name: [16]u8,3141 ar_name: [16]u8,
...@@ -3128,3 +3263,21 @@ pub const gnu_hash = struct {...@@ -3128,3 +3263,21 @@ pub const gnu_hash = struct {
3128 try std.testing.expectEqual(0x8ae9f18e, calculate("flapenguin.me"));3263 try std.testing.expectEqual(0x8ae9f18e, calculate("flapenguin.me"));
3129 }3264 }
3130};3265};
3266
3267pub const loongarch = struct {
3268 /// Ehdr.e_flags bits of LoongArch
3269 pub const EFlags = packed struct(Word) {
3270 base_abi_modifier: BaseAbiModifier,
3271 abi_extension: AbiExtension,
3272 abi_version: u2,
3273 reserved: u24 = 0,
3274
3275 pub const BaseAbiModifier = enum(u3) {
3276 s = 1,
3277 f = 2,
3278 d = 3,
3279 _,
3280 };
3281 pub const AbiExtension = enum(u3) { base = 0, _ };
3282 };
3283};
src/link.zig+2
...@@ -32,6 +32,8 @@ pub const LdScript = @import("link/LdScript.zig");...@@ -32,6 +32,8 @@ pub const LdScript = @import("link/LdScript.zig");
32pub const Queue = @import("link/Queue.zig");32pub const Queue = @import("link/Queue.zig");
33pub const ConstPool = @import("link/ConstPool.zig");33pub const ConstPool = @import("link/ConstPool.zig");
3434
35pub const loongarch = @import("link/loongarch.zig");
36
35pub const Error = Allocator.Error || Io.Cancelable || error{37pub const Error = Allocator.Error || Io.Cancelable || error{
36 /// An error message has already been stored in persistent state on `Compilation` or `Zcu`, for38 /// An error message has already been stored in persistent state on `Compilation` or `Zcu`, for
37 /// instance in `Compilation.link_diags`.39 /// instance in `Compilation.link_diags`.
src/link/Elf2.zig+410-12
...@@ -749,6 +749,14 @@ const GotReloc = struct {...@@ -749,6 +749,14 @@ const GotReloc = struct {
749 offset32,749 offset32,
750 rel64,750 rel64,
751 rel32,751 rel32,
752
753 rel32_hi20,
754 rel64_lo20,
755 rel64_hi12,
756 abs32_lo12,
757 abs32_hi20,
758 abs64_lo20,
759 abs64_hi12,
752 };760 };
753761
754 const Index = enum(u32) {762 const Index = enum(u32) {
...@@ -817,6 +825,41 @@ const GotReloc = struct {...@@ -817,6 +825,41 @@ const GotReloc = struct {
817 @intCast(@as(i64, @bitCast(got_vaddr +% got_offset +% addend -% dest_vaddr))),825 @intCast(@as(i64, @bitCast(got_vaddr +% got_offset +% addend -% dest_vaddr))),
818 target_endian,826 target_endian,
819 ),827 ),
828 .rel32_hi20 => {
829 assert(elf.ehdrField(.machine) == .LOONGARCH);
830 const target_value = got_vaddr +% got_offset +% addend;
831 link.loongarch.writeJ20(dest_slice[0..4], link.loongarch.toPcalaHi20(target_value, dest_vaddr));
832 },
833 .rel64_lo20 => {
834 assert(elf.ehdrField(.machine) == .LOONGARCH);
835 const target_value = got_vaddr +% got_offset +% addend;
836 link.loongarch.writeJ20(dest_slice[0..4], link.loongarch.toPcala64Lo20(target_value, dest_vaddr));
837 },
838 .rel64_hi12 => {
839 assert(elf.ehdrField(.machine) == .LOONGARCH);
840 const target_value = got_vaddr +% got_offset +% addend;
841 link.loongarch.writeK12(dest_slice[0..4], link.loongarch.toPcala64Hi12(target_value, dest_vaddr));
842 },
843 .abs32_lo12 => {
844 assert(elf.ehdrField(.machine) == .LOONGARCH);
845 const target_value = got_vaddr +% got_offset +% addend;
846 link.loongarch.writeK12(dest_slice[0..4], @truncate(target_value));
847 },
848 .abs32_hi20 => {
849 assert(elf.ehdrField(.machine) == .LOONGARCH);
850 const target_value = got_vaddr +% got_offset +% addend;
851 link.loongarch.writeJ20(dest_slice[0..4], @truncate(target_value >> 12));
852 },
853 .abs64_lo20 => {
854 assert(elf.ehdrField(.machine) == .LOONGARCH);
855 const target_value = got_vaddr +% got_offset +% addend;
856 link.loongarch.writeJ20(dest_slice[0..4], @truncate(target_value >> 32));
857 },
858 .abs64_hi12 => {
859 assert(elf.ehdrField(.machine) == .LOONGARCH);
860 const target_value = got_vaddr +% got_offset +% addend;
861 link.loongarch.writeK12(dest_slice[0..4], @truncate(target_value >> 52));
862 },
820 }863 }
821 }864 }
822};865};
...@@ -824,6 +867,7 @@ const GotReloc = struct {...@@ -824,6 +867,7 @@ const GotReloc = struct {
824pub const MachineRelocType = union {867pub const MachineRelocType = union {
825 X86_64: std.elf.R_X86_64,868 X86_64: std.elf.R_X86_64,
826 AARCH64: std.elf.R_AARCH64,869 AARCH64: std.elf.R_AARCH64,
870 LOONGARCH: std.elf.R_LARCH,
827 RISCV: std.elf.R_RISCV,871 RISCV: std.elf.R_RISCV,
828 PPC64: std.elf.R_PPC64,872 PPC64: std.elf.R_PPC64,
829873
...@@ -831,6 +875,7 @@ pub const MachineRelocType = union {...@@ -831,6 +875,7 @@ pub const MachineRelocType = union {
831 return switch (elf.ehdrField(.machine)) {875 return switch (elf.ehdrField(.machine)) {
832 else => unreachable,876 else => unreachable,
833 .AARCH64 => .{ .AARCH64 = .NONE },877 .AARCH64 => .{ .AARCH64 = .NONE },
878 .LOONGARCH => .{ .LOONGARCH = .NONE },
834 .PPC64 => .{ .PPC64 = .NONE },879 .PPC64 => .{ .PPC64 = .NONE },
835 .RISCV => .{ .RISCV = .NONE },880 .RISCV => .{ .RISCV = .NONE },
836 .X86_64 => .{ .X86_64 = .NONE },881 .X86_64 => .{ .X86_64 = .NONE },
...@@ -840,6 +885,7 @@ pub const MachineRelocType = union {...@@ -840,6 +885,7 @@ pub const MachineRelocType = union {
840 return switch (elf.ehdrField(.machine)) {885 return switch (elf.ehdrField(.machine)) {
841 else => unreachable,886 else => unreachable,
842 .AARCH64 => .{ .AARCH64 = .COPY },887 .AARCH64 => .{ .AARCH64 = .COPY },
888 .LOONGARCH => .{ .LOONGARCH = .COPY },
843 .PPC64 => .{ .PPC64 = .COPY },889 .PPC64 => .{ .PPC64 = .COPY },
844 .RISCV => .{ .RISCV = .COPY },890 .RISCV => .{ .RISCV = .COPY },
845 .X86_64 => .{ .X86_64 = .COPY },891 .X86_64 => .{ .X86_64 = .COPY },
...@@ -849,24 +895,28 @@ pub const MachineRelocType = union {...@@ -849,24 +895,28 @@ pub const MachineRelocType = union {
849 return switch (elf.ehdrField(.machine)) {895 return switch (elf.ehdrField(.machine)) {
850 else => unreachable,896 else => unreachable,
851 .X86_64 => .{ .X86_64 = .JUMP_SLOT },897 .X86_64 => .{ .X86_64 = .JUMP_SLOT },
898 .LOONGARCH => .{ .LOONGARCH = .JUMP_SLOT },
852 };899 };
853 }900 }
854 pub fn globDat(elf: *Elf) MachineRelocType {901 pub fn globDat(elf: *Elf) MachineRelocType {
855 return switch (elf.ehdrField(.machine)) {902 return switch (elf.ehdrField(.machine)) {
856 else => unreachable,903 else => unreachable,
857 .X86_64 => .{ .X86_64 = .GLOB_DAT },904 .X86_64 => .{ .X86_64 = .GLOB_DAT },
905 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .@"64" else .@"32" },
858 };906 };
859 }907 }
860 pub fn dtpOffAddr(elf: *Elf) MachineRelocType {908 pub fn dtpOffAddr(elf: *Elf) MachineRelocType {
861 return switch (elf.ehdrField(.machine)) {909 return switch (elf.ehdrField(.machine)) {
862 else => unreachable,910 else => unreachable,
863 .X86_64 => .{ .X86_64 = .DTPOFF64 },911 .X86_64 => .{ .X86_64 = .DTPOFF64 },
912 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_DTPREL64 else .TLS_DTPREL32 },
864 };913 };
865 }914 }
866 pub fn absAddr(elf: *Elf) MachineRelocType {915 pub fn absAddr(elf: *Elf) MachineRelocType {
867 return switch (elf.ehdrField(.machine)) {916 return switch (elf.ehdrField(.machine)) {
868 else => unreachable,917 else => unreachable,
869 .AARCH64 => .{ .AARCH64 = .ABS64 },918 .AARCH64 => .{ .AARCH64 = .ABS64 },
919 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .@"64" else .@"32" },
870 .PPC64 => .{ .PPC64 = .ADDR64 },920 .PPC64 => .{ .PPC64 = .ADDR64 },
871 .RISCV => .{ .RISCV = .@"64" },921 .RISCV => .{ .RISCV = .@"64" },
872 .X86_64 => .{ .X86_64 = .@"64" },922 .X86_64 => .{ .X86_64 = .@"64" },
...@@ -883,6 +933,7 @@ pub const MachineRelocType = union {...@@ -883,6 +933,7 @@ pub const MachineRelocType = union {
883 return switch (elf.ehdrField(.machine)) {933 return switch (elf.ehdrField(.machine)) {
884 else => unreachable,934 else => unreachable,
885 inline .AARCH64,935 inline .AARCH64,
936 .LOONGARCH,
886 .PPC64,937 .PPC64,
887 .RISCV,938 .RISCV,
888 .X86_64,939 .X86_64,
...@@ -893,6 +944,7 @@ pub const MachineRelocType = union {...@@ -893,6 +944,7 @@ pub const MachineRelocType = union {
893 return switch (elf.ehdrField(.machine)) {944 return switch (elf.ehdrField(.machine)) {
894 else => unreachable,945 else => unreachable,
895 inline .AARCH64,946 inline .AARCH64,
947 .LOONGARCH,
896 .PPC64,948 .PPC64,
897 .RISCV,949 .RISCV,
898 .X86_64,950 .X86_64,
...@@ -984,9 +1036,23 @@ const SymbolReloc = struct {...@@ -984,9 +1036,23 @@ const SymbolReloc = struct {
984 size64,1036 size64,
985 size32,1037 size32,
9861038
1039 abs32_lo12,
1040 rel32_hi20,
1041 rel64_lo20,
1042 rel64_hi12,
1043 branch_rel18,
1044 branch_rel23,
1045 branch_rel28,
1046 call_rel38,
1047 tpoff32_lo12,
1048 tpoff32_hi20,
1049 tpoff64_lo20,
1050 tpoff64_hi12,
1051
987 fn dependsOnTlsSize(t: SymbolReloc.Type) bool {1052 fn dependsOnTlsSize(t: SymbolReloc.Type) bool {
988 return switch (t) {1053 return switch (t) {
989 .tpoff32, .tpoff64 => true,1054 .tpoff32, .tpoff64 => true,
1055 .tpoff32_lo12, .tpoff32_hi20, .tpoff64_lo20, .tpoff64_hi12 => true,
990 else => false,1056 else => false,
991 };1057 };
992 }1058 }
...@@ -1145,6 +1211,67 @@ const SymbolReloc = struct {...@@ -1145,6 +1211,67 @@ const SymbolReloc = struct {
1145 target_endian,1211 target_endian,
1146 );1212 );
1147 },1213 },
1214 .abs32_lo12 => {
1215 assert(elf.ehdrField(.machine) == .LOONGARCH);
1216 link.loongarch.writeK12(dest_slice[0..4], @truncate(target_value));
1217 },
1218 .rel32_hi20 => {
1219 assert(elf.ehdrField(.machine) == .LOONGARCH);
1220 link.loongarch.writeJ20(dest_slice[0..4], link.loongarch.toPcalaHi20(target_value, dest_vaddr));
1221 },
1222 .rel64_lo20 => {
1223 assert(elf.ehdrField(.machine) == .LOONGARCH);
1224 link.loongarch.writeJ20(dest_slice[0..4], link.loongarch.toPcala64Lo20(target_value, dest_vaddr));
1225 },
1226 .rel64_hi12 => {
1227 assert(elf.ehdrField(.machine) == .LOONGARCH);
1228 link.loongarch.writeK12(dest_slice[0..4], link.loongarch.toPcala64Hi12(target_value, dest_vaddr));
1229 },
1230 // TODO: handle bad alignment and overflow gracefully
1231 .branch_rel18 => {
1232 assert(elf.ehdrField(.machine) == .LOONGARCH);
1233 const target_rel: i64 = @bitCast(target_value -% dest_vaddr);
1234 const slot_target: i16 = @intCast(@shrExact(target_rel, 2));
1235 link.loongarch.writeK16(dest_slice[0..4], @bitCast(slot_target));
1236 },
1237 .branch_rel23 => {
1238 assert(elf.ehdrField(.machine) == .LOONGARCH);
1239 const target_rel: i64 = @bitCast(target_value -% dest_vaddr);
1240 const slot_target: i21 = @intCast(@shrExact(target_rel, 2));
1241 link.loongarch.writeD5K16(dest_slice[0..4], @bitCast(slot_target));
1242 },
1243 .branch_rel28 => {
1244 assert(elf.ehdrField(.machine) == .LOONGARCH);
1245 const target_rel: i64 = @bitCast(target_value -% dest_vaddr);
1246 const slot_target: i26 = @intCast(@shrExact(target_rel, 2));
1247 link.loongarch.writeD10K16(dest_slice[0..4], @bitCast(slot_target));
1248 },
1249 .call_rel38 => {
1250 assert(elf.ehdrField(.machine) == .LOONGARCH);
1251 const target_rel: i64 = @bitCast(target_value -% dest_vaddr);
1252 // We use i64 instead of i36 here because the allowed range is
1253 // [PC - 128 GiB - 0x20000, PC + 128GiB - 0x20000 - 4].
1254 // The intCast in writeJ20 will do the final check.
1255 const slot_target: i64 = @intCast(@shrExact(target_rel, 2));
1256 link.loongarch.writeJ20(dest_slice[0..4], @bitCast(@as(i20, @intCast((slot_target +% 0x8000) >> 16))));
1257 link.loongarch.writeK16(dest_slice[4..8], @bitCast(@as(i16, @truncate(slot_target))));
1258 },
1259 .tpoff32_lo12 => {
1260 assert(elf.ehdrField(.machine) == .LOONGARCH);
1261 link.loongarch.writeK12(dest_slice[0..4], @truncate(target_value));
1262 },
1263 .tpoff32_hi20 => {
1264 assert(elf.ehdrField(.machine) == .LOONGARCH);
1265 link.loongarch.writeJ20(dest_slice[0..4], @truncate(target_value >> 12));
1266 },
1267 .tpoff64_lo20 => {
1268 assert(elf.ehdrField(.machine) == .LOONGARCH);
1269 link.loongarch.writeJ20(dest_slice[0..4], @truncate(target_value >> 32));
1270 },
1271 .tpoff64_hi12 => {
1272 assert(elf.ehdrField(.machine) == .LOONGARCH);
1273 link.loongarch.writeK12(dest_slice[0..4], @truncate(target_value >> 52));
1274 },
1148 }1275 }
1149 }1276 }
11501277
...@@ -1259,6 +1386,18 @@ fn ensureUnusedPltCapacity(elf: *Elf, len: u32) Error!void {...@@ -1259,6 +1386,18 @@ fn ensureUnusedPltCapacity(elf: *Elf, len: u32) Error!void {
1259 const plt_sec_need_size: usize = 16 * need_plt_capacity;1386 const plt_sec_need_size: usize = 16 * need_plt_capacity;
1260 try elf.ensureNodeSize(elf.shndx.plt_sec.get(elf).ni, plt_sec_need_size);1387 try elf.ensureNodeSize(elf.shndx.plt_sec.get(elf).ni, plt_sec_need_size);
1261 },1388 },
1389 .LOONGARCH => {
1390 // Ensure the `.plt` section's node is big enough
1391 const plt_need_size: usize = 32 + 16 * need_plt_capacity;
1392 try elf.ensureNodeSize(elf.shndx.plt.get(elf).ni, plt_need_size);
1393
1394 // Ensure the `.got.plt` section's node is big enough
1395 const got_plt_need_size: usize = switch (elf.identClass()) {
1396 .NONE, _ => unreachable,
1397 inline else => |class| @sizeOf(class.ElfN().Addr) * (2 + need_plt_capacity),
1398 };
1399 try elf.ensureNodeSize(elf.shndx.got_plt.get(elf).ni, got_plt_need_size);
1400 },
1262 }1401 }
1263}1402}
1264/// Given an index into the PLT, returns whether that PLT entry is dead, meaning it may be reused at1403/// Given an index into the PLT, returns whether that PLT entry is dead, meaning it may be reused at
...@@ -1874,12 +2013,18 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void...@@ -1874,12 +2013,18 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
1874 .addend = 0,2013 .addend = 0,
1875 }));2014 }));
18762015
2016 const reserved_got_plt_entries: u32 = switch (elf.ehdrField(.machine)) {
2017 else => |machine| @panic(@tagName(machine)),
2018 .X86_64 => 3,
2019 .LOONGARCH => 2,
2020 };
2021
1877 // Now that we know the index, we can set the relocation's offset.2022 // Now that we know the index, we can set the relocation's offset.
1878 const got_plt_addr = switch (elf.shdrPtr(elf.shndx.got_plt)) {2023 const got_plt_addr = switch (elf.shdrPtr(elf.shndx.got_plt)) {
1879 inline else => |shdr, class| got_plt_addr: {2024 inline else => |shdr, class| got_plt_addr: {
1880 const ent_size = @sizeOf(class.ElfN().Addr);2025 const ent_size = @sizeOf(class.ElfN().Addr);
1881 assert(elf.targetLoad(&shdr.entsize) == ent_size);2026 assert(elf.targetLoad(&shdr.entsize) == ent_size);
1882 const offset = ent_size * @as(u64, 3 + plt_index);2027 const offset = ent_size * @as(u64, reserved_got_plt_entries + plt_index);
1883 assert(offset <= elf.targetLoad(&shdr.size));2028 assert(offset <= elf.targetLoad(&shdr.size));
1884 break :got_plt_addr elf.targetLoad(&shdr.addr) + offset;2029 break :got_plt_addr elf.targetLoad(&shdr.addr) + offset;
1885 },2030 },
...@@ -1961,6 +2106,55 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void...@@ -1961,6 +2106,55 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
1961 },2106 },
1962 }2107 }
1963 },2108 },
2109 .LOONGARCH => {
2110 // add a .PLT entry, writing the template
2111 const plt_ni = elf.shndx.plt.get(elf).ni;
2112 const plt_addr, const plt_slice = plt_entry: switch (elf.shdrPtr(elf.shndx.plt)) {
2113 inline else => |shdr| {
2114 const old_size = 16 * (1 + plt_index);
2115 assert(elf.targetLoad(&shdr.size) == old_size);
2116 elf.targetStore(&shdr.size, old_size + 16);
2117 const plt_slice = plt_ni.slice(&elf.mf)[old_size..][0..16];
2118 @memcpy(plt_slice, source: switch (elf.identClass()) {
2119 .NONE, _ => unreachable,
2120 inline .@"32", .@"64" => |elf_class| {
2121 const ld_byte = if (elf_class == .@"64") 0xc0 else 0x80;
2122 break :source &[16]u8{
2123 0x1a, 0x00, 0x00, 0x0f, // pcalau12i $t3, %pc_hi20(func@.got.plt)
2124 0x28, ld_byte, 0x01, 0xef, // ld.w/d $t3, $t3, %lo12(func@.got.plt)
2125 0x4c, 0x00, 0x01, 0xed, // jirl $t1, $t3, 0
2126 0x00, 0x2a, 0x00, 0x00, // break
2127 };
2128 },
2129 });
2130 break :plt_entry .{ elf.targetLoad(&shdr.addr) + old_size, plt_slice };
2131 },
2132 };
2133
2134 // add a .GOT.PLT entry, writing the address of the corresponding .PLT entry
2135 const got_plt_ni = elf.shndx.got_plt.get(elf).ni;
2136 switch (elf.shdrPtr(elf.shndx.got_plt)) {
2137 inline else => |shdr, class| {
2138 const ent_size = @sizeOf(class.ElfN().Addr);
2139 const old_size = ent_size * (2 + plt_index);
2140 assert(elf.targetLoad(&shdr.size) == old_size);
2141 elf.targetStore(&shdr.size, old_size + ent_size);
2142 std.mem.writeInt(
2143 class.ElfN().Addr,
2144 got_plt_ni.slice(&elf.mf)[old_size..][0..ent_size],
2145 @intCast(plt_addr),
2146 target_endian,
2147 );
2148 assert(got_plt_addr == (elf.targetLoad(&shdr.addr) + old_size));
2149 },
2150 }
2151
2152 // relocate the PLT entry to point to the .GOT.PLT entry
2153 const got_plt_abs: u64 = @as(u64, got_plt_addr);
2154 // TODO: handle overflow gracefully
2155 link.loongarch.writeJ20(plt_slice[0..4], link.loongarch.toPcalaHi20(got_plt_abs, plt_addr));
2156 link.loongarch.writeK12(plt_slice[4..8], @truncate(got_plt_abs));
2157 },
1964 }2158 }
1965}2159}
19662160
...@@ -2715,6 +2909,12 @@ fn initHeaders(...@@ -2715,6 +2909,12 @@ fn initHeaders(
2715 const relro_phndx = phnum;2909 const relro_phndx = phnum;
2716 phnum += 1;2910 phnum += 1;
27172911
2912 const init_plt_size: std.elf.Xword, const plt_align: std.mem.Alignment, const plt_sec =
2913 switch (machine) {
2914 else => @panic(@tagName(machine)),
2915 .X86_64 => .{ 16, .@"16", true },
2916 .LOONGARCH => .{ 32, .@"4", false },
2917 };
2718 const expected_nodes_len = expected_nodes_len: switch (@"type") {2918 const expected_nodes_len = expected_nodes_len: switch (@"type") {
2719 .NONE, .CORE, _ => unreachable,2919 .NONE, .CORE, _ => unreachable,
2720 .REL => {2920 .REL => {
...@@ -2722,9 +2922,10 @@ fn initHeaders(...@@ -2722,9 +2922,10 @@ fn initHeaders(
2722 defer phnum = 0;2922 defer phnum = 0;
2723 break :expected_nodes_len 5 + phnum;2923 break :expected_nodes_len 5 + phnum;
2724 },2924 },
2725 .EXEC, .DYN => break :expected_nodes_len 10 +2925 .EXEC, .DYN => break :expected_nodes_len 9 +
2726 phnum * 2 - 1 + // each phdr also has a matching shdr, except for the PT_PHDR phdr2926 phnum * 2 - 1 + // each phdr also has a matching shdr, except for the PT_PHDR phdr
2727 @as(usize, 4) * @intFromBool(have_dynamic_section), // .dynstr, .dynsym, .rela.dyn, .rela.plt2927 @as(usize, 4) * @intFromBool(have_dynamic_section) + // .dynstr, .dynsym, .rela.dyn, .rela.plt
2928 @intFromBool(plt_sec),
2728 };2929 };
2729 try elf.nodes.ensureTotalCapacity(gpa, expected_nodes_len);2930 try elf.nodes.ensureTotalCapacity(gpa, expected_nodes_len);
2730 try elf.shdrs.ensureTotalCapacity(gpa, shnum);2931 try elf.shdrs.ensureTotalCapacity(gpa, shnum);
...@@ -2757,7 +2958,24 @@ fn initHeaders(...@@ -2757,7 +2958,24 @@ fn initHeaders(
2757 ehdr.entry = 0;2958 ehdr.entry = 0;
2758 ehdr.phoff = 0;2959 ehdr.phoff = 0;
2759 ehdr.shoff = 0;2960 ehdr.shoff = 0;
2760 ehdr.flags = 0;2961 ehdr.flags = switch (machine) {
2962 .X86_64 => 0,
2963 .LOONGARCH => e_flags: {
2964 const target_cpu = &elf.base.comp.getTarget().cpu;
2965 const e_flags: std.elf.loongarch.EFlags = .{
2966 .base_abi_modifier = if (target_cpu.has(.loongarch, .d))
2967 .d
2968 else if (target_cpu.has(.loongarch, .f))
2969 .f
2970 else
2971 .s,
2972 .abi_extension = .base,
2973 .abi_version = 1,
2974 };
2975 break :e_flags @bitCast(e_flags);
2976 },
2977 else => @panic(@tagName(machine)),
2978 };
2761 ehdr.ehsize = @sizeOf(ElfN.Ehdr);2979 ehdr.ehsize = @sizeOf(ElfN.Ehdr);
2762 ehdr.phentsize = @sizeOf(ElfN.Phdr);2980 ehdr.phentsize = @sizeOf(ElfN.Phdr);
2763 ehdr.phnum = @min(phnum, std.elf.PN_XNUM);2981 ehdr.phnum = @min(phnum, std.elf.PN_XNUM);
...@@ -3051,6 +3269,7 @@ fn initHeaders(...@@ -3051,6 +3269,7 @@ fn initHeaders(
3051 .size = switch (machine) {3269 .size = switch (machine) {
3052 else => @panic(@tagName(machine)),3270 else => @panic(@tagName(machine)),
3053 .X86_64 => 3 * 8,3271 .X86_64 => 3 * 8,
3272 .LOONGARCH => if (elf.identClass() == .@"64") 8 else 4,
3054 },3273 },
3055 .flags = .{ .WRITE = true, .ALLOC = true },3274 .flags = .{ .WRITE = true, .ALLOC = true },
3056 .addralign = addr_align,3275 .addralign = addr_align,
...@@ -3066,21 +3285,17 @@ fn initHeaders(...@@ -3066,21 +3285,17 @@ fn initHeaders(
3066 else => @panic(@tagName(machine)),3285 else => @panic(@tagName(machine)),
3067 .@"386" => 3 * 4,3286 .@"386" => 3 * 4,
3068 .X86_64 => 3 * 8,3287 .X86_64 => 3 * 8,
3288 .LOONGARCH => if (elf.identClass() == .@"64") 2 * 8 else 2 * 4,
3069 },3289 },
3070 .addralign = addr_align,3290 .addralign = addr_align,
3071 .entsize = @intCast(addr_align.toByteUnits()),3291 .entsize = @intCast(addr_align.toByteUnits()),
3072 },3292 },
3073 );3293 );
3074 const plt_size: std.elf.Xword, const plt_align: std.mem.Alignment, const plt_sec =
3075 switch (machine) {
3076 else => @panic(@tagName(machine)),
3077 .X86_64 => .{ 16, .@"16", true },
3078 };
3079 elf.shndx.plt = try elf.addSection(elf.ni.text, .{3294 elf.shndx.plt = try elf.addSection(elf.ni.text, .{
3080 .name = ".plt",3295 .name = ".plt",
3081 .type = .PROGBITS,3296 .type = .PROGBITS,
3082 .flags = .{ .ALLOC = true, .EXECINSTR = true },3297 .flags = .{ .ALLOC = true, .EXECINSTR = true },
3083 .size = plt_size,3298 .size = init_plt_size,
3084 .addralign = plt_align,3299 .addralign = plt_align,
3085 .node_align = elf.mf.flags.block_size,3300 .node_align = elf.mf.flags.block_size,
3086 });3301 });
...@@ -3218,6 +3433,38 @@ fn initHeaders(...@@ -3218,6 +3433,38 @@ fn initHeaders(
3218 .{ .X86_64 = .PC32 },3433 .{ .X86_64 = .PC32 },
3219 );3434 );
3220 },3435 },
3436 .LOONGARCH => {
3437 const plt_ni = elf.shndx.plt.get(elf).ni;
3438 const got_plt_sym: Symbol.Id = .local(elf.shndx.got_plt.get(elf).lsi);
3439 @memcpy(plt_ni.slice(&elf.mf)[0..32], switch (class) {
3440 .NONE, _ => unreachable,
3441 .@"32" => &[32]u8{
3442 0x1a, 0x00, 0x00, 0x0e, // pcalau12i $t2, %pc_hi20(.got.plt)
3443 0x00, 0x11, 0x3d, 0xad, // sub.w $t1, $t1, $t3
3444 0x28, 0x80, 0x01, 0xcf, // ld.w $t3, $t2, %lo12(.got.plt) # _dl_runtime_resolve
3445 0x02, 0xbf, 0x51, 0xad, // addi.w $t1, $t1, -44 # .plt entry
3446 0x02, 0x80, 0x01, 0xcc, // addi.w $t0, $t2, %lo12(.got.plt) # &.got.plt
3447 0x00, 0x44, 0x89, 0xad, // srli.w $t1, $t1, 2 # .plt entry offset
3448 0x28, 0x80, 0x11, 0x8c, // ld.w $t0, $t0, 4 # link map
3449 0x4c, 0x00, 0x01, 0xe0, // jr $t3
3450 },
3451 .@"64" => &[32]u8{
3452 0x1a, 0x00, 0x00, 0x0e, // pcalau12i $t2, %pc_hi20(.got.plt)
3453 0x00, 0x11, 0xbd, 0xad, // sub.d $t1, $t1, $t3
3454 0x28, 0xc0, 0x01, 0xcf, // ld.d $t3, $t2, %lo12(.got.plt) # _dl_runtime_resolve
3455 0x02, 0xff, 0x51, 0xad, // addi.d $t1, $t1, -44 # .plt entry
3456 0x02, 0xc0, 0x01, 0xcc, // addi.d $t0, $t2, %lo12(.got.plt) # &.got.plt
3457 0x00, 0x45, 0x05, 0xad, // srli.d $t1, $t1, 1 # .plt entry offset
3458 0x28, 0xc0, 0x21, 0x8c, // ld.d $t0, $t0, 8 # link map
3459 0x4c, 0x00, 0x01, 0xe0, // jr $t3
3460 },
3461 });
3462 elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);
3463 try elf.ensureUnusedRelocCapacity(plt_ni, 3);
3464 try elf.addRelocAssumeCapacity(plt_ni, 0, got_plt_sym, 0, .{ .LOONGARCH = .PCALA_HI20 });
3465 try elf.addRelocAssumeCapacity(plt_ni, 8, got_plt_sym, 0, .{ .LOONGARCH = .PCALA_LO12 });
3466 try elf.addRelocAssumeCapacity(plt_ni, 16, got_plt_sym, 0, .{ .LOONGARCH = .PCALA_LO12 });
3467 },
3221 }3468 }
3222 }3469 }
3223 if (comp.config.any_non_single_threaded) {3470 if (comp.config.any_non_single_threaded) {
...@@ -3242,6 +3489,13 @@ fn initHeaders(...@@ -3242,6 +3489,13 @@ fn initHeaders(
3242 elf.got.putAssumeCapacityNoClobber(.{ .reserved = 1 }, .none);3489 elf.got.putAssumeCapacityNoClobber(.{ .reserved = 1 }, .none);
3243 elf.got.putAssumeCapacityNoClobber(.{ .reserved = 2 }, .none);3490 elf.got.putAssumeCapacityNoClobber(.{ .reserved = 2 }, .none);
3244 },3491 },
3492 .LOONGARCH => {
3493 try elf.got.ensureUnusedCapacity(gpa, 1);
3494 elf.got.putAssumeCapacityNoClobber(switch (have_dynamic_section) {
3495 true => .{ .symbol = .local(elf.shndx.dynamic.get(elf).lsi) },
3496 false => .{ .reserved = 0 },
3497 }, .none);
3498 },
3245 }3499 }
3246 switch (elf.shdrPtr(elf.shndx.got)) {3500 switch (elf.shdrPtr(elf.shndx.got)) {
3247 inline else => |shdr, ct_class| {3501 inline else => |shdr, ct_class| {
...@@ -5375,6 +5629,52 @@ fn addRelocAssumeCapacity(...@@ -5375,6 +5629,52 @@ fn addRelocAssumeCapacity(
5375 .TLSLD => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .rel32),5629 .TLSLD => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .rel32),
5376 .GOTTPOFF => elf.addGotRelocAssumeCapacity(node, offset, .{ .tpoff = target }, addend, .rel32),5630 .GOTTPOFF => elf.addGotRelocAssumeCapacity(node, offset, .{ .tpoff = target }, addend, .rel32),
5377 },5631 },
5632 .LOONGARCH => switch (@"type".LOONGARCH) {
5633 else => std.debug.panic("TODO: unsupported input relocation, {t}", .{@"type".LOONGARCH}),
5634 _,
5635 .NONE,
5636 .COPY,
5637 .JUMP_SLOT,
5638 .RELATIVE,
5639 .IRELATIVE,
5640 => std.debug.panic("TODO: error for illegal or unsupported input relocation, {t}", .{@"type".LOONGARCH}),
5641
5642 .RELAX => {}, // TODO: relaxation is not yet implemented
5643
5644 // Relocations targeting a symbol
5645 .@"64" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs64),
5646 .@"32" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32),
5647 .@"64_PCREL" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel64),
5648 .@"32_PCREL" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel32),
5649
5650 .PCALA_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32_lo12),
5651 .PCALA_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel32_hi20),
5652 .PCALA64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel64_hi12),
5653 .PCALA64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel64_lo20),
5654
5655 .B16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .branch_rel18),
5656 .B21 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .branch_rel23),
5657 .B26 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .branch_rel28),
5658 .CALL36 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .call_rel38),
5659
5660 // Relocations targeting a TLS symbol
5661 .TLS_LE_LO12, .TLS_LE_LO12_R => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff32_lo12),
5662 .TLS_LE_HI20, .TLS_LE_HI20_R => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff32_hi20),
5663 .TLS_LE64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff64_lo20),
5664 .TLS_LE64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff64_hi12),
5665 .TLS_LE_ADD_R => {}, // TODO: relaxation is not yet implemented
5666
5667 // Relocations targeting a GOT entry
5668 .GOT_PC_LO12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .abs32_lo12),
5669 .GOT_PC_HI20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel32_hi20),
5670 .GOT64_PC_LO20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel64_lo20),
5671 .GOT64_PC_HI12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel64_hi12),
5672
5673 .GOT_LO12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .abs32_lo12),
5674 .GOT_HI20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .abs32_hi20),
5675 .GOT64_LO20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .abs64_lo20),
5676 .GOT64_HI12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .abs64_hi12),
5677 },
5378 },5678 },
5379 }5679 }
5380}5680}
...@@ -5412,7 +5712,47 @@ fn addSymbolRelocAssumeCapacity(...@@ -5412,7 +5712,47 @@ fn addSymbolRelocAssumeCapacity(
5412 .tpoff32 => .TPOFF32,5712 .tpoff32 => .TPOFF32,
5413 .size64 => .SIZE64,5713 .size64 => .SIZE64,
5414 .size32 => .SIZE32,5714 .size32 => .SIZE32,
5715 .abs32_lo12,
5716 .rel32_hi20,
5717 .rel64_lo20,
5718 .rel64_hi12,
5719 .branch_rel18,
5720 .branch_rel23,
5721 .branch_rel28,
5722 .call_rel38,
5723 .tpoff32_lo12,
5724 .tpoff32_hi20,
5725 .tpoff64_lo20,
5726 .tpoff64_hi12,
5727 => unreachable,
5415 } },5728 } },
5729 .LOONGARCH => .{
5730 .LOONGARCH = switch (@"type") {
5731 .write_rela => unreachable,
5732 .abs64 => .@"64",
5733 .abs32 => .@"32",
5734 .abs32s, .size64, .size32 => unreachable,
5735 .rel64 => .@"64_PCREL",
5736 .rel32 => .@"32_PCREL",
5737 .pltrel64, .pltrel32 => break :r .none,
5738 .dtpoff64 => .TLS_DTPREL64,
5739 .dtpoff32 => .TLS_DTPREL32,
5740 .tpoff64 => .TLS_TPREL64,
5741 .tpoff32 => .TLS_TPREL32,
5742 .abs32_lo12 => .PCALA_LO12,
5743 .rel32_hi20 => .PCALA_HI20,
5744 .rel64_lo20 => .PCALA64_LO20,
5745 .rel64_hi12 => .PCALA64_HI12,
5746 .branch_rel18 => .B16,
5747 .branch_rel23 => .B21,
5748 .branch_rel28 => .B26,
5749 .call_rel38 => .CALL36,
5750 .tpoff32_lo12 => .TLS_LE_LO12,
5751 .tpoff32_hi20 => .TLS_LE_HI20,
5752 .tpoff64_lo20 => .TLS_LE64_LO20,
5753 .tpoff64_hi12 => .TLS_LE64_HI12,
5754 },
5755 },
5416 };5756 };
5417 // TODO: even if the symbol is locally defined, preemption/interposition is a5757 // TODO: even if the symbol is locally defined, preemption/interposition is a
5418 // possibility, which this condition does not currently consider!5758 // possibility, which this condition does not currently consider!
...@@ -5583,6 +5923,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -5583,6 +5923,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
5583 .type = switch (elf.ehdrField(.machine)) {5923 .type = switch (elf.ehdrField(.machine)) {
5584 else => |machine| @panic(@tagName(machine)),5924 else => |machine| @panic(@tagName(machine)),
5585 .X86_64 => .{ .X86_64 = .TPOFF64 },5925 .X86_64 => .{ .X86_64 = .TPOFF64 },
5926 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_TPREL64 else .TLS_TPREL32 },
5586 },5927 },
5587 .dynsym_index = switch (sym_id.unwrap()) {5928 .dynsym_index = switch (sym_id.unwrap()) {
5588 .global => |name| elf.globalByName(name).?.dynsym_index,5929 .global => |name| elf.globalByName(name).?.dynsym_index,
...@@ -5639,7 +5980,11 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -5639,7 +5980,11 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
5639 .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable5980 .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable
5640 else => .{5981 else => .{
5641 .reloc = .{5982 .reloc = .{
5642 .type = .{ .X86_64 = .DTPMOD64 },5983 .type = switch (elf.ehdrField(.machine)) {
5984 else => |machine| @panic(@tagName(machine)),
5985 .X86_64 => .{ .X86_64 = .DTPMOD64 },
5986 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 },
5987 },
5643 .dynsym_index = switch (sym.unwrap()) {5988 .dynsym_index = switch (sym.unwrap()) {
5644 .local => 0,5989 .local => 0,
5645 .global => |name| dsi: {5990 .global => |name| dsi: {
...@@ -5671,7 +6016,11 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -5671,7 +6016,11 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
5671 .tlsld0 => switch (elf.shndx.dynamic) {6016 .tlsld0 => switch (elf.shndx.dynamic) {
5672 .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable6017 .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable
5673 else => .{ .reloc = .{6018 else => .{ .reloc = .{
5674 .type = .{ .X86_64 = .DTPMOD64 },6019 .type = switch (elf.ehdrField(.machine)) {
6020 else => |machine| @panic(@tagName(machine)),
6021 .X86_64 => .{ .X86_64 = .DTPMOD64 },
6022 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 },
6023 },
5675 .dynsym_index = 0,6024 .dynsym_index = 0,
5676 } },6025 } },
5677 },6026 },
...@@ -6618,6 +6967,55 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad...@@ -6618,6 +6967,55 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad
6618 },6967 },
6619 }6968 }
6620 },6969 },
6970 .LOONGARCH => {
6971 switch (which) {
6972 .plt => {
6973 // We also need to update all of the references from `.plt` to `.got.plt`.
6974 // However, if there's also a flush pending for `.got.plt`, don't bother doing
6975 // this now, because we'll do it when `.got.plt` is flushed anyway.
6976 if (elf.shndx.got_plt.get(elf).ni.hasMoved(&elf.mf)) {
6977 return;
6978 }
6979 // Exit this `switch` to update those references.
6980 },
6981 .plt_sec => unreachable,
6982 .got_plt => {
6983 // Update the offsets of the relocation entries in `.rela.plt`.
6984 const rela_plt_shndx = elf.shndx.rela_plt;
6985 for (0..elf.plt.count()) |plt_index| {
6986 if (elf.pltEntryIsDead(plt_index)) continue;
6987 rela_plt_shndx.relaAdjustOffset(elf, @enumFromInt(plt_index), old_addr, addr);
6988 }
6989 // We also need to update all of the references from `.plt` to `.got.plt`.
6990 // However, if there's also a flush pending for `.plt`, don't bother doing
6991 // this now, because we'll do it when `.plt` is flushed anyway.
6992 if (elf.shndx.plt.get(elf).ni.hasMoved(&elf.mf)) {
6993 return;
6994 }
6995 // Exit this `switch` to update those references.
6996 },
6997 }
6998 // We are updating the references from `.plt` to `.got.plt`.
6999 const got_plt_addr = elf.shndx.got_plt.vaddr(elf);
7000 const plt_addr = elf.shndx.plt.vaddr(elf);
7001 const plt_slice = elf.shndx.plt.get(elf).ni.slice(&elf.mf);
7002 switch (elf.identClass()) {
7003 .NONE, _ => unreachable,
7004 inline else => |class| {
7005 const Addr = class.ElfN().Addr;
7006 for (0..elf.plt.count()) |plt_index| {
7007 const plt_offset = 16 * plt_index;
7008 const got_plt_offset = @sizeOf(Addr) * (2 + plt_index);
7009 const target_slice = plt_slice[plt_offset..];
7010
7011 const got_plt_abs: u64 = got_plt_addr + got_plt_offset;
7012 // TODO: handle overflow gracefully
7013 link.loongarch.writeJ20(target_slice[0..4], link.loongarch.toPcalaHi20(got_plt_abs, plt_addr + plt_offset));
7014 link.loongarch.writeK12(target_slice[4..8], @truncate(got_plt_abs));
7015 }
7016 },
7017 }
7018 },
6621 }7019 }
6622}7020}
66237021
src/link/loongarch.zig created+55
...@@ -0,0 +1,55 @@
1const std = @import("std");
2const mem = std.mem;
3
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 {
42 return @truncate(((target +% 0x800) >> 12) -% (pc >> 12));
43}
44
45pub fn toPcala64Lo20(target: u64, pc: u64) u20 {
46 const fixup = if (target & 0x800 != 0) (@as(u64, 0x1000) -% @as(u64, 0x100000000)) else 0;
47 const hi32 = (((target +% 0x80000000 +% fixup) >> 12) -% ((pc -% 8) >> 12)) >> 20;
48 return @truncate(hi32);
49}
50
51pub fn toPcala64Hi12(target: u64, pc: u64) u12 {
52 const fixup = if (target & 0x800 != 0) (@as(u64, 0x1000) -% @as(u64, 0x100000000)) else 0;
53 const hi32 = (((target +% 0x80000000 +% fixup) >> 12) -% ((pc -% 12) >> 12)) >> 20;
54 return @truncate(hi32 >> 20);
55}