authorgravatar for xtex@astrafall.orgxtex <xtex@astrafall.org> 2026-06-21 21:55:05+08:00
committergravatar for xtex@astrafall.orgxtex <xtex@astrafall.org> 2026-06-25 22:12:55+08:00
log75b232ab2b47220d3ca01e66cfbf64ccb06cf101
tree734a7423d198fbc7bde349a723d7d747f8df70fc
parent360deedcf15c9e8cc0179f9e18b8562aadc8ba09
signaturebadge-check Signed by SSH key SHA256:IEYEjkZlkUTr5U9GiDAmZU/4eZus2t2RsxusyhQqwao

link.Elf2: add support for LoongArch


3 files changed, 449 insertions(+), 11 deletions(-)

src/link.zig+2
......@@ -31,6 +31,8 @@ pub const LdScript = @import("link/LdScript.zig");
3131pub const Queue = @import("link/Queue.zig");
3232pub const ConstPool = @import("link/ConstPool.zig");
3333
34pub const loongarch = @import("link/loongarch.zig");
35
3436pub const Error = Allocator.Error || Io.Cancelable || error{
3537 /// An error message has already been stored in persistent state on `Compilation` or `Zcu`, for
3638 /// instance in `Compilation.link_diags`.
src/link/Elf2.zig+392-11
......@@ -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);
......@@ -3051,6 +3252,7 @@ fn initHeaders(
30513252 .size = switch (machine) {
30523253 else => @panic(@tagName(machine)),
30533254 .X86_64 => 3 * 8,
3255 .LOONGARCH => if (elf.identClass() == .@"64") 8 else 4,
30543256 },
30553257 .flags = .{ .WRITE = true, .ALLOC = true },
30563258 .addralign = addr_align,
......@@ -3066,21 +3268,17 @@ fn initHeaders(
30663268 else => @panic(@tagName(machine)),
30673269 .@"386" => 3 * 4,
30683270 .X86_64 => 3 * 8,
3271 .LOONGARCH => if (elf.identClass() == .@"64") 2 * 8 else 2 * 4,
30693272 },
30703273 .addralign = addr_align,
30713274 .entsize = @intCast(addr_align.toByteUnits()),
30723275 },
30733276 );
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 };
30793277 elf.shndx.plt = try elf.addSection(elf.ni.text, .{
30803278 .name = ".plt",
30813279 .type = .PROGBITS,
30823280 .flags = .{ .ALLOC = true, .EXECINSTR = true },
3083 .size = plt_size,
3281 .size = init_plt_size,
30843282 .addralign = plt_align,
30853283 .node_align = elf.mf.flags.block_size,
30863284 });
......@@ -3218,6 +3416,38 @@ fn initHeaders(
32183416 .{ .X86_64 = .PC32 },
32193417 );
32203418 },
3419 .LOONGARCH => {
3420 const plt_ni = elf.shndx.plt.get(elf).ni;
3421 const got_plt_sym: Symbol.Id = .local(elf.shndx.got_plt.get(elf).lsi);
3422 @memcpy(plt_ni.slice(&elf.mf)[0..32], switch (class) {
3423 .NONE, _ => unreachable,
3424 .@"32" => &[32]u8{
3425 0x1a, 0x00, 0x00, 0x0e, // pcalau12i $t2, %pc_hi20(.got.plt)
3426 0x00, 0x11, 0x3d, 0xad, // sub.w $t1, $t1, $t3
3427 0x28, 0x80, 0x01, 0xcf, // ld.w $t3, $t2, %lo12(.got.plt) # _dl_runtime_resolve
3428 0x02, 0xbf, 0x51, 0xad, // addi.w $t1, $t1, -44 # .plt entry
3429 0x02, 0x80, 0x01, 0xcc, // addi.w $t0, $t2, %lo12(.got.plt) # &.got.plt
3430 0x00, 0x44, 0x89, 0xad, // srli.w $t1, $t1, 2 # .plt entry offset
3431 0x28, 0x80, 0x11, 0x8c, // ld.w $t0, $t0, 4 # link map
3432 0x4c, 0x00, 0x01, 0xe0, // jr $t3
3433 },
3434 .@"64" => &[32]u8{
3435 0x1a, 0x00, 0x00, 0x0e, // pcalau12i $t2, %pc_hi20(.got.plt)
3436 0x00, 0x11, 0xbd, 0xad, // sub.d $t1, $t1, $t3
3437 0x28, 0xc0, 0x01, 0xcf, // ld.d $t3, $t2, %lo12(.got.plt) # _dl_runtime_resolve
3438 0x02, 0xff, 0x51, 0xad, // addi.d $t1, $t1, -44 # .plt entry
3439 0x02, 0xc0, 0x01, 0xcc, // addi.d $t0, $t2, %lo12(.got.plt) # &.got.plt
3440 0x00, 0x45, 0x05, 0xad, // srli.d $t1, $t1, 1 # .plt entry offset
3441 0x28, 0xc0, 0x21, 0x8c, // ld.d $t0, $t0, 8 # link map
3442 0x4c, 0x00, 0x01, 0xe0, // jr $t3
3443 },
3444 });
3445 elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);
3446 try elf.ensureUnusedRelocCapacity(plt_ni, 3);
3447 try elf.addRelocAssumeCapacity(plt_ni, 0, got_plt_sym, 0, .{ .LOONGARCH = .PCALA_HI20 });
3448 try elf.addRelocAssumeCapacity(plt_ni, 8, got_plt_sym, 0, .{ .LOONGARCH = .PCALA_LO12 });
3449 try elf.addRelocAssumeCapacity(plt_ni, 16, got_plt_sym, 0, .{ .LOONGARCH = .PCALA_LO12 });
3450 },
32213451 }
32223452 }
32233453 if (comp.config.any_non_single_threaded) {
......@@ -3242,6 +3472,13 @@ fn initHeaders(
32423472 elf.got.putAssumeCapacityNoClobber(.{ .reserved = 1 }, .none);
32433473 elf.got.putAssumeCapacityNoClobber(.{ .reserved = 2 }, .none);
32443474 },
3475 .LOONGARCH => {
3476 try elf.got.ensureUnusedCapacity(gpa, 1);
3477 elf.got.putAssumeCapacityNoClobber(switch (have_dynamic_section) {
3478 true => .{ .symbol = .local(elf.shndx.dynamic.get(elf).lsi) },
3479 false => .{ .reserved = 0 },
3480 }, .none);
3481 },
32453482 }
32463483 switch (elf.shdrPtr(elf.shndx.got)) {
32473484 inline else => |shdr, ct_class| {
......@@ -5375,6 +5612,52 @@ fn addRelocAssumeCapacity(
53755612 .TLSLD => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .rel32),
53765613 .GOTTPOFF => elf.addGotRelocAssumeCapacity(node, offset, .{ .tpoff = target }, addend, .rel32),
53775614 },
5615 .LOONGARCH => switch (@"type".LOONGARCH) {
5616 else => std.debug.panic("TODO: unsupported input relocation, {t}", .{@"type".LOONGARCH}),
5617 _,
5618 .NONE,
5619 .COPY,
5620 .JUMP_SLOT,
5621 .RELATIVE,
5622 .IRELATIVE,
5623 => std.debug.panic("TODO: error for illegal or unsupported input relocation, {t}", .{@"type".LOONGARCH}),
5624
5625 .RELAX => {}, // TODO: relaxation is not yet implemented
5626
5627 // Relocations targeting a symbol
5628 .@"64" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs64),
5629 .@"32" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32),
5630 .@"64_PCREL" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel64),
5631 .@"32_PCREL" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel32),
5632
5633 .PCALA_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32_lo12),
5634 .PCALA_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel32_hi20),
5635 .PCALA64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel64_hi12),
5636 .PCALA64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel64_lo20),
5637
5638 .B16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .branch_rel18),
5639 .B21 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .branch_rel23),
5640 .B26 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .branch_rel28),
5641 .CALL36 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .call_rel38),
5642
5643 // Relocations targeting a TLS symbol
5644 .TLS_LE_LO12, .TLS_LE_LO12_R => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff32_lo12),
5645 .TLS_LE_HI20, .TLS_LE_HI20_R => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff32_hi20),
5646 .TLS_LE64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff64_lo20),
5647 .TLS_LE64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff64_hi12),
5648 .TLS_LE_ADD_R => {}, // TODO: relaxation is not yet implemented
5649
5650 // Relocations targeting a GOT entry
5651 .GOT_PC_LO12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .abs32_lo12),
5652 .GOT_PC_HI20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel32_hi20),
5653 .GOT64_PC_LO20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel64_lo20),
5654 .GOT64_PC_HI12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel64_hi12),
5655
5656 .GOT_LO12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .abs32_lo12),
5657 .GOT_HI20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .abs32_hi20),
5658 .GOT64_LO20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .abs64_lo20),
5659 .GOT64_HI12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .abs64_hi12),
5660 },
53785661 },
53795662 }
53805663}
......@@ -5412,7 +5695,47 @@ fn addSymbolRelocAssumeCapacity(
54125695 .tpoff32 => .TPOFF32,
54135696 .size64 => .SIZE64,
54145697 .size32 => .SIZE32,
5698 .abs32_lo12,
5699 .rel32_hi20,
5700 .rel64_lo20,
5701 .rel64_hi12,
5702 .branch_rel18,
5703 .branch_rel23,
5704 .branch_rel28,
5705 .call_rel38,
5706 .tpoff32_lo12,
5707 .tpoff32_hi20,
5708 .tpoff64_lo20,
5709 .tpoff64_hi12,
5710 => unreachable,
54155711 } },
5712 .LOONGARCH => .{
5713 .LOONGARCH = switch (@"type") {
5714 .write_rela => unreachable,
5715 .abs64 => .@"64",
5716 .abs32 => .@"32",
5717 .abs32s, .size64, .size32 => unreachable,
5718 .rel64 => .@"64_PCREL",
5719 .rel32 => .@"32_PCREL",
5720 .pltrel64, .pltrel32 => break :r .none,
5721 .dtpoff64 => .TLS_DTPREL64,
5722 .dtpoff32 => .TLS_DTPREL32,
5723 .tpoff64 => .TLS_TPREL64,
5724 .tpoff32 => .TLS_TPREL32,
5725 .abs32_lo12 => .PCALA_LO12,
5726 .rel32_hi20 => .PCALA_HI20,
5727 .rel64_lo20 => .PCALA64_LO20,
5728 .rel64_hi12 => .PCALA64_HI12,
5729 .branch_rel18 => .B16,
5730 .branch_rel23 => .B21,
5731 .branch_rel28 => .B26,
5732 .call_rel38 => .CALL36,
5733 .tpoff32_lo12 => .TLS_LE_LO12,
5734 .tpoff32_hi20 => .TLS_LE_HI20,
5735 .tpoff64_lo20 => .TLS_LE64_LO20,
5736 .tpoff64_hi12 => .TLS_LE64_HI12,
5737 },
5738 },
54165739 };
54175740 // TODO: even if the symbol is locally defined, preemption/interposition is a
54185741 // possibility, which this condition does not currently consider!
......@@ -5583,6 +5906,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
55835906 .type = switch (elf.ehdrField(.machine)) {
55845907 else => |machine| @panic(@tagName(machine)),
55855908 .X86_64 => .{ .X86_64 = .TPOFF64 },
5909 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_TPREL64 else .TLS_TPREL32 },
55865910 },
55875911 .dynsym_index = switch (sym_id.unwrap()) {
55885912 .global => |name| elf.globalByName(name).?.dynsym_index,
......@@ -5639,7 +5963,11 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
56395963 .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable
56405964 else => .{
56415965 .reloc = .{
5642 .type = .{ .X86_64 = .DTPMOD64 },
5966 .type = switch (elf.ehdrField(.machine)) {
5967 else => |machine| @panic(@tagName(machine)),
5968 .X86_64 => .{ .X86_64 = .DTPMOD64 },
5969 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 },
5970 },
56435971 .dynsym_index = switch (sym.unwrap()) {
56445972 .local => 0,
56455973 .global => |name| dsi: {
......@@ -5671,7 +5999,11 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
56715999 .tlsld0 => switch (elf.shndx.dynamic) {
56726000 .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable
56736001 else => .{ .reloc = .{
5674 .type = .{ .X86_64 = .DTPMOD64 },
6002 .type = switch (elf.ehdrField(.machine)) {
6003 else => |machine| @panic(@tagName(machine)),
6004 .X86_64 => .{ .X86_64 = .DTPMOD64 },
6005 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 },
6006 },
56756007 .dynsym_index = 0,
56766008 } },
56776009 },
......@@ -6618,6 +6950,55 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad
66186950 },
66196951 }
66206952 },
6953 .LOONGARCH => {
6954 switch (which) {
6955 .plt => {
6956 // We also need to update all of the references from `.plt` to `.got.plt`.
6957 // However, if there's also a flush pending for `.got.plt`, don't bother doing
6958 // this now, because we'll do it when `.got.plt` is flushed anyway.
6959 if (elf.shndx.got_plt.get(elf).ni.hasMoved(&elf.mf)) {
6960 return;
6961 }
6962 // Exit this `switch` to update those references.
6963 },
6964 .plt_sec => unreachable,
6965 .got_plt => {
6966 // Update the offsets of the relocation entries in `.rela.plt`.
6967 const rela_plt_shndx = elf.shndx.rela_plt;
6968 for (0..elf.plt.count()) |plt_index| {
6969 if (elf.pltEntryIsDead(plt_index)) continue;
6970 rela_plt_shndx.relaAdjustOffset(elf, @enumFromInt(plt_index), old_addr, addr);
6971 }
6972 // We also need to update all of the references from `.plt` to `.got.plt`.
6973 // However, if there's also a flush pending for `.plt`, don't bother doing
6974 // this now, because we'll do it when `.plt` is flushed anyway.
6975 if (elf.shndx.plt.get(elf).ni.hasMoved(&elf.mf)) {
6976 return;
6977 }
6978 // Exit this `switch` to update those references.
6979 },
6980 }
6981 // We are updating the references from `.plt` to `.got.plt`.
6982 const got_plt_addr = elf.shndx.got_plt.vaddr(elf);
6983 const plt_addr = elf.shndx.plt.vaddr(elf);
6984 const plt_slice = elf.shndx.plt.get(elf).ni.slice(&elf.mf);
6985 switch (elf.identClass()) {
6986 .NONE, _ => unreachable,
6987 inline else => |class| {
6988 const Addr = class.ElfN().Addr;
6989 for (0..elf.plt.count()) |plt_index| {
6990 const plt_offset = 16 * plt_index;
6991 const got_plt_offset = @sizeOf(Addr) * (2 + plt_index);
6992 const target_slice = plt_slice[plt_offset..];
6993
6994 const got_plt_abs: u64 = got_plt_addr + got_plt_offset;
6995 // TODO: handle overflow gracefully
6996 link.loongarch.writeJ20(target_slice[0..4], link.loongarch.toPcalaHi20(got_plt_abs, plt_addr + plt_offset));
6997 link.loongarch.writeK12(target_slice[4..8], @truncate(got_plt_abs));
6998 }
6999 },
7000 }
7001 },
66217002 }
66227003}
66237004
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}