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");...@@ -31,6 +31,8 @@ pub const LdScript = @import("link/LdScript.zig");
31pub const Queue = @import("link/Queue.zig");31pub const Queue = @import("link/Queue.zig");
32pub const ConstPool = @import("link/ConstPool.zig");32pub const ConstPool = @import("link/ConstPool.zig");
3333
34pub const loongarch = @import("link/loongarch.zig");
35
34pub const Error = Allocator.Error || Io.Cancelable || error{36pub const Error = Allocator.Error || Io.Cancelable || error{
35 /// An error message has already been stored in persistent state on `Compilation` or `Zcu`, for37 /// An error message has already been stored in persistent state on `Compilation` or `Zcu`, for
36 /// instance in `Compilation.link_diags`.38 /// instance in `Compilation.link_diags`.
src/link/Elf2.zig+392-11
...@@ -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);
...@@ -3051,6 +3252,7 @@ fn initHeaders(...@@ -3051,6 +3252,7 @@ fn initHeaders(
3051 .size = switch (machine) {3252 .size = switch (machine) {
3052 else => @panic(@tagName(machine)),3253 else => @panic(@tagName(machine)),
3053 .X86_64 => 3 * 8,3254 .X86_64 => 3 * 8,
3255 .LOONGARCH => if (elf.identClass() == .@"64") 8 else 4,
3054 },3256 },
3055 .flags = .{ .WRITE = true, .ALLOC = true },3257 .flags = .{ .WRITE = true, .ALLOC = true },
3056 .addralign = addr_align,3258 .addralign = addr_align,
...@@ -3066,21 +3268,17 @@ fn initHeaders(...@@ -3066,21 +3268,17 @@ fn initHeaders(
3066 else => @panic(@tagName(machine)),3268 else => @panic(@tagName(machine)),
3067 .@"386" => 3 * 4,3269 .@"386" => 3 * 4,
3068 .X86_64 => 3 * 8,3270 .X86_64 => 3 * 8,
3271 .LOONGARCH => if (elf.identClass() == .@"64") 2 * 8 else 2 * 4,
3069 },3272 },
3070 .addralign = addr_align,3273 .addralign = addr_align,
3071 .entsize = @intCast(addr_align.toByteUnits()),3274 .entsize = @intCast(addr_align.toByteUnits()),
3072 },3275 },
3073 );3276 );
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, .{3277 elf.shndx.plt = try elf.addSection(elf.ni.text, .{
3080 .name = ".plt",3278 .name = ".plt",
3081 .type = .PROGBITS,3279 .type = .PROGBITS,
3082 .flags = .{ .ALLOC = true, .EXECINSTR = true },3280 .flags = .{ .ALLOC = true, .EXECINSTR = true },
3083 .size = plt_size,3281 .size = init_plt_size,
3084 .addralign = plt_align,3282 .addralign = plt_align,
3085 .node_align = elf.mf.flags.block_size,3283 .node_align = elf.mf.flags.block_size,
3086 });3284 });
...@@ -3218,6 +3416,38 @@ fn initHeaders(...@@ -3218,6 +3416,38 @@ fn initHeaders(
3218 .{ .X86_64 = .PC32 },3416 .{ .X86_64 = .PC32 },
3219 );3417 );
3220 },3418 },
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 },
3221 }3451 }
3222 }3452 }
3223 if (comp.config.any_non_single_threaded) {3453 if (comp.config.any_non_single_threaded) {
...@@ -3242,6 +3472,13 @@ fn initHeaders(...@@ -3242,6 +3472,13 @@ fn initHeaders(
3242 elf.got.putAssumeCapacityNoClobber(.{ .reserved = 1 }, .none);3472 elf.got.putAssumeCapacityNoClobber(.{ .reserved = 1 }, .none);
3243 elf.got.putAssumeCapacityNoClobber(.{ .reserved = 2 }, .none);3473 elf.got.putAssumeCapacityNoClobber(.{ .reserved = 2 }, .none);
3244 },3474 },
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 },
3245 }3482 }
3246 switch (elf.shdrPtr(elf.shndx.got)) {3483 switch (elf.shdrPtr(elf.shndx.got)) {
3247 inline else => |shdr, ct_class| {3484 inline else => |shdr, ct_class| {
...@@ -5375,6 +5612,52 @@ fn addRelocAssumeCapacity(...@@ -5375,6 +5612,52 @@ fn addRelocAssumeCapacity(
5375 .TLSLD => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .rel32),5612 .TLSLD => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .rel32),
5376 .GOTTPOFF => elf.addGotRelocAssumeCapacity(node, offset, .{ .tpoff = target }, addend, .rel32),5613 .GOTTPOFF => elf.addGotRelocAssumeCapacity(node, offset, .{ .tpoff = target }, addend, .rel32),
5377 },5614 },
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 },
5378 },5661 },
5379 }5662 }
5380}5663}
...@@ -5412,7 +5695,47 @@ fn addSymbolRelocAssumeCapacity(...@@ -5412,7 +5695,47 @@ fn addSymbolRelocAssumeCapacity(
5412 .tpoff32 => .TPOFF32,5695 .tpoff32 => .TPOFF32,
5413 .size64 => .SIZE64,5696 .size64 => .SIZE64,
5414 .size32 => .SIZE32,5697 .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,
5415 } },5711 } },
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 },
5416 };5739 };
5417 // TODO: even if the symbol is locally defined, preemption/interposition is a5740 // TODO: even if the symbol is locally defined, preemption/interposition is a
5418 // possibility, which this condition does not currently consider!5741 // possibility, which this condition does not currently consider!
...@@ -5583,6 +5906,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -5583,6 +5906,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
5583 .type = switch (elf.ehdrField(.machine)) {5906 .type = switch (elf.ehdrField(.machine)) {
5584 else => |machine| @panic(@tagName(machine)),5907 else => |machine| @panic(@tagName(machine)),
5585 .X86_64 => .{ .X86_64 = .TPOFF64 },5908 .X86_64 => .{ .X86_64 = .TPOFF64 },
5909 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_TPREL64 else .TLS_TPREL32 },
5586 },5910 },
5587 .dynsym_index = switch (sym_id.unwrap()) {5911 .dynsym_index = switch (sym_id.unwrap()) {
5588 .global => |name| elf.globalByName(name).?.dynsym_index,5912 .global => |name| elf.globalByName(name).?.dynsym_index,
...@@ -5639,7 +5963,11 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -5639,7 +5963,11 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
5639 .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable5963 .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable
5640 else => .{5964 else => .{
5641 .reloc = .{5965 .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 },
5643 .dynsym_index = switch (sym.unwrap()) {5971 .dynsym_index = switch (sym.unwrap()) {
5644 .local => 0,5972 .local => 0,
5645 .global => |name| dsi: {5973 .global => |name| dsi: {
...@@ -5671,7 +5999,11 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -5671,7 +5999,11 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
5671 .tlsld0 => switch (elf.shndx.dynamic) {5999 .tlsld0 => switch (elf.shndx.dynamic) {
5672 .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable6000 .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable
5673 else => .{ .reloc = .{6001 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 },
5675 .dynsym_index = 0,6007 .dynsym_index = 0,
5676 } },6008 } },
5677 },6009 },
...@@ -6618,6 +6950,55 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad...@@ -6618,6 +6950,55 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad
6618 },6950 },
6619 }6951 }
6620 },6952 },
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 },
6621 }7002 }
6622}7003}
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}