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) {
30013001 _,
30023002};
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
30043139pub const ar_hdr = extern struct {
30053140 /// Member file name, sometimes / terminated.
30063141 ar_name: [16]u8,
......@@ -3128,3 +3263,21 @@ pub const gnu_hash = struct {
31283263 try std.testing.expectEqual(0x8ae9f18e, calculate("flapenguin.me"));
31293264 }
31303265};
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");
3232pub const Queue = @import("link/Queue.zig");
3333pub const ConstPool = @import("link/ConstPool.zig");
3434
35pub const loongarch = @import("link/loongarch.zig");
36
3537pub const Error = Allocator.Error || Io.Cancelable || error{
3638 /// An error message has already been stored in persistent state on `Compilation` or `Zcu`, for
3739 /// instance in `Compilation.link_diags`.
src/link/Elf2.zig+410-12
......@@ -749,6 +749,14 @@ const GotReloc = struct {
749749 offset32,
750750 rel64,
751751 rel32,
752
753 rel32_hi20,
754 rel64_lo20,
755 rel64_hi12,
756 abs32_lo12,
757 abs32_hi20,
758 abs64_lo20,
759 abs64_hi12,
752760 };
753761
754762 const Index = enum(u32) {
......@@ -817,6 +825,41 @@ const GotReloc = struct {
817825 @intCast(@as(i64, @bitCast(got_vaddr +% got_offset +% addend -% dest_vaddr))),
818826 target_endian,
819827 ),
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 },
820863 }
821864 }
822865};
......@@ -824,6 +867,7 @@ const GotReloc = struct {
824867pub const MachineRelocType = union {
825868 X86_64: std.elf.R_X86_64,
826869 AARCH64: std.elf.R_AARCH64,
870 LOONGARCH: std.elf.R_LARCH,
827871 RISCV: std.elf.R_RISCV,
828872 PPC64: std.elf.R_PPC64,
829873
......@@ -831,6 +875,7 @@ pub const MachineRelocType = union {
831875 return switch (elf.ehdrField(.machine)) {
832876 else => unreachable,
833877 .AARCH64 => .{ .AARCH64 = .NONE },
878 .LOONGARCH => .{ .LOONGARCH = .NONE },
834879 .PPC64 => .{ .PPC64 = .NONE },
835880 .RISCV => .{ .RISCV = .NONE },
836881 .X86_64 => .{ .X86_64 = .NONE },
......@@ -840,6 +885,7 @@ pub const MachineRelocType = union {
840885 return switch (elf.ehdrField(.machine)) {
841886 else => unreachable,
842887 .AARCH64 => .{ .AARCH64 = .COPY },
888 .LOONGARCH => .{ .LOONGARCH = .COPY },
843889 .PPC64 => .{ .PPC64 = .COPY },
844890 .RISCV => .{ .RISCV = .COPY },
845891 .X86_64 => .{ .X86_64 = .COPY },
......@@ -849,24 +895,28 @@ pub const MachineRelocType = union {
849895 return switch (elf.ehdrField(.machine)) {
850896 else => unreachable,
851897 .X86_64 => .{ .X86_64 = .JUMP_SLOT },
898 .LOONGARCH => .{ .LOONGARCH = .JUMP_SLOT },
852899 };
853900 }
854901 pub fn globDat(elf: *Elf) MachineRelocType {
855902 return switch (elf.ehdrField(.machine)) {
856903 else => unreachable,
857904 .X86_64 => .{ .X86_64 = .GLOB_DAT },
905 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .@"64" else .@"32" },
858906 };
859907 }
860908 pub fn dtpOffAddr(elf: *Elf) MachineRelocType {
861909 return switch (elf.ehdrField(.machine)) {
862910 else => unreachable,
863911 .X86_64 => .{ .X86_64 = .DTPOFF64 },
912 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_DTPREL64 else .TLS_DTPREL32 },
864913 };
865914 }
866915 pub fn absAddr(elf: *Elf) MachineRelocType {
867916 return switch (elf.ehdrField(.machine)) {
868917 else => unreachable,
869918 .AARCH64 => .{ .AARCH64 = .ABS64 },
919 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .@"64" else .@"32" },
870920 .PPC64 => .{ .PPC64 = .ADDR64 },
871921 .RISCV => .{ .RISCV = .@"64" },
872922 .X86_64 => .{ .X86_64 = .@"64" },
......@@ -883,6 +933,7 @@ pub const MachineRelocType = union {
883933 return switch (elf.ehdrField(.machine)) {
884934 else => unreachable,
885935 inline .AARCH64,
936 .LOONGARCH,
886937 .PPC64,
887938 .RISCV,
888939 .X86_64,
......@@ -893,6 +944,7 @@ pub const MachineRelocType = union {
893944 return switch (elf.ehdrField(.machine)) {
894945 else => unreachable,
895946 inline .AARCH64,
947 .LOONGARCH,
896948 .PPC64,
897949 .RISCV,
898950 .X86_64,
......@@ -984,9 +1036,23 @@ const SymbolReloc = struct {
9841036 size64,
9851037 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
9871052 fn dependsOnTlsSize(t: SymbolReloc.Type) bool {
9881053 return switch (t) {
9891054 .tpoff32, .tpoff64 => true,
1055 .tpoff32_lo12, .tpoff32_hi20, .tpoff64_lo20, .tpoff64_hi12 => true,
9901056 else => false,
9911057 };
9921058 }
......@@ -1145,6 +1211,67 @@ const SymbolReloc = struct {
11451211 target_endian,
11461212 );
11471213 },
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 },
11481275 }
11491276 }
11501277
......@@ -1259,6 +1386,18 @@ fn ensureUnusedPltCapacity(elf: *Elf, len: u32) Error!void {
12591386 const plt_sec_need_size: usize = 16 * need_plt_capacity;
12601387 try elf.ensureNodeSize(elf.shndx.plt_sec.get(elf).ni, plt_sec_need_size);
12611388 },
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 },
12621401 }
12631402}
12641403/// 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
18742013 .addend = 0,
18752014 }));
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
18772022 // Now that we know the index, we can set the relocation's offset.
18782023 const got_plt_addr = switch (elf.shdrPtr(elf.shndx.got_plt)) {
18792024 inline else => |shdr, class| got_plt_addr: {
18802025 const ent_size = @sizeOf(class.ElfN().Addr);
18812026 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);
18832028 assert(offset <= elf.targetLoad(&shdr.size));
18842029 break :got_plt_addr elf.targetLoad(&shdr.addr) + offset;
18852030 },
......@@ -1961,6 +2106,55 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
19612106 },
19622107 }
19632108 },
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 },
19642158 }
19652159}
19662160
......@@ -2715,6 +2909,12 @@ fn initHeaders(
27152909 const relro_phndx = phnum;
27162910 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 };
27182918 const expected_nodes_len = expected_nodes_len: switch (@"type") {
27192919 .NONE, .CORE, _ => unreachable,
27202920 .REL => {
......@@ -2722,9 +2922,10 @@ fn initHeaders(
27222922 defer phnum = 0;
27232923 break :expected_nodes_len 5 + phnum;
27242924 },
2725 .EXEC, .DYN => break :expected_nodes_len 10 +
2925 .EXEC, .DYN => break :expected_nodes_len 9 +
27262926 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.plt
2927 @as(usize, 4) * @intFromBool(have_dynamic_section) + // .dynstr, .dynsym, .rela.dyn, .rela.plt
2928 @intFromBool(plt_sec),
27282929 };
27292930 try elf.nodes.ensureTotalCapacity(gpa, expected_nodes_len);
27302931 try elf.shdrs.ensureTotalCapacity(gpa, shnum);
......@@ -2757,7 +2958,24 @@ fn initHeaders(
27572958 ehdr.entry = 0;
27582959 ehdr.phoff = 0;
27592960 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 };
27612979 ehdr.ehsize = @sizeOf(ElfN.Ehdr);
27622980 ehdr.phentsize = @sizeOf(ElfN.Phdr);
27632981 ehdr.phnum = @min(phnum, std.elf.PN_XNUM);
......@@ -3051,6 +3269,7 @@ fn initHeaders(
30513269 .size = switch (machine) {
30523270 else => @panic(@tagName(machine)),
30533271 .X86_64 => 3 * 8,
3272 .LOONGARCH => if (elf.identClass() == .@"64") 8 else 4,
30543273 },
30553274 .flags = .{ .WRITE = true, .ALLOC = true },
30563275 .addralign = addr_align,
......@@ -3066,21 +3285,17 @@ fn initHeaders(
30663285 else => @panic(@tagName(machine)),
30673286 .@"386" => 3 * 4,
30683287 .X86_64 => 3 * 8,
3288 .LOONGARCH => if (elf.identClass() == .@"64") 2 * 8 else 2 * 4,
30693289 },
30703290 .addralign = addr_align,
30713291 .entsize = @intCast(addr_align.toByteUnits()),
30723292 },
30733293 );
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 };
30793294 elf.shndx.plt = try elf.addSection(elf.ni.text, .{
30803295 .name = ".plt",
30813296 .type = .PROGBITS,
30823297 .flags = .{ .ALLOC = true, .EXECINSTR = true },
3083 .size = plt_size,
3298 .size = init_plt_size,
30843299 .addralign = plt_align,
30853300 .node_align = elf.mf.flags.block_size,
30863301 });
......@@ -3218,6 +3433,38 @@ fn initHeaders(
32183433 .{ .X86_64 = .PC32 },
32193434 );
32203435 },
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 },
32213468 }
32223469 }
32233470 if (comp.config.any_non_single_threaded) {
......@@ -3242,6 +3489,13 @@ fn initHeaders(
32423489 elf.got.putAssumeCapacityNoClobber(.{ .reserved = 1 }, .none);
32433490 elf.got.putAssumeCapacityNoClobber(.{ .reserved = 2 }, .none);
32443491 },
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 },
32453499 }
32463500 switch (elf.shdrPtr(elf.shndx.got)) {
32473501 inline else => |shdr, ct_class| {
......@@ -5375,6 +5629,52 @@ fn addRelocAssumeCapacity(
53755629 .TLSLD => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .rel32),
53765630 .GOTTPOFF => elf.addGotRelocAssumeCapacity(node, offset, .{ .tpoff = target }, addend, .rel32),
53775631 },
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 },
53785678 },
53795679 }
53805680}
......@@ -5412,7 +5712,47 @@ fn addSymbolRelocAssumeCapacity(
54125712 .tpoff32 => .TPOFF32,
54135713 .size64 => .SIZE64,
54145714 .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,
54155728 } },
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 },
54165756 };
54175757 // TODO: even if the symbol is locally defined, preemption/interposition is a
54185758 // possibility, which this condition does not currently consider!
......@@ -5583,6 +5923,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
55835923 .type = switch (elf.ehdrField(.machine)) {
55845924 else => |machine| @panic(@tagName(machine)),
55855925 .X86_64 => .{ .X86_64 = .TPOFF64 },
5926 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_TPREL64 else .TLS_TPREL32 },
55865927 },
55875928 .dynsym_index = switch (sym_id.unwrap()) {
55885929 .global => |name| elf.globalByName(name).?.dynsym_index,
......@@ -5639,7 +5980,11 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
56395980 .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable
56405981 else => .{
56415982 .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 },
56435988 .dynsym_index = switch (sym.unwrap()) {
56445989 .local => 0,
56455990 .global => |name| dsi: {
......@@ -5671,7 +6016,11 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
56716016 .tlsld0 => switch (elf.shndx.dynamic) {
56726017 .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable
56736018 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 },
56756024 .dynsym_index = 0,
56766025 } },
56776026 },
......@@ -6618,6 +6967,55 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad
66186967 },
66196968 }
66206969 },
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 },
66217019 }
66227020}
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}