| ... | @@ -9,9 +9,11 @@ const fmt = std.fmt; | ... | @@ -9,9 +9,11 @@ const fmt = std.fmt; |
| 9 | const log = std.log.scoped(.link); | 9 | const log = std.log.scoped(.link); |
| 10 | const math = std.math; | 10 | const math = std.math; |
| 11 | const mem = std.mem; | 11 | const mem = std.mem; |
| | 12 | const meta = std.meta; |
| 12 | | 13 | |
| 13 | const Allocator = std.mem.Allocator; | 14 | const Allocator = std.mem.Allocator; |
| 14 | | 15 | |
| | 16 | const aarch64 = @import("../arch/aarch64/bits.zig"); |
| 15 | const codegen = @import("../codegen.zig"); | 17 | const codegen = @import("../codegen.zig"); |
| 16 | const link = @import("../link.zig"); | 18 | const link = @import("../link.zig"); |
| 17 | const lld = @import("Coff/lld.zig"); | 19 | const lld = @import("Coff/lld.zig"); |
| ... | @@ -885,12 +887,13 @@ fn resolveRelocs(self: *Coff, atom: *Atom) !void { | ... | @@ -885,12 +887,13 @@ fn resolveRelocs(self: *Coff, atom: *Atom) !void { |
| 885 | for (relocs.items) |*reloc| { | 887 | for (relocs.items) |*reloc| { |
| 886 | if (!reloc.dirty) continue; | 888 | if (!reloc.dirty) continue; |
| 887 | | 889 | |
| | 890 | const source_vaddr = source_sym.value + reloc.offset; |
| 888 | const target_atom = reloc.getTargetAtom(self) orelse continue; | 891 | const target_atom = reloc.getTargetAtom(self) orelse continue; |
| 889 | const target_vaddr = target_atom.getSymbol(self).value; | 892 | const target_vaddr = target_atom.getSymbol(self).value; |
| 890 | const target_vaddr_with_addend = target_vaddr + reloc.addend; | 893 | const target_vaddr_with_addend = target_vaddr + reloc.addend; |
| 891 | | 894 | |
| 892 | log.debug(" ({x}: [() => 0x{x} ({s})) ({s}) (in file at 0x{x})", .{ | 895 | log.debug(" ({x}: [() => 0x{x} ({s})) ({s}) (in file at 0x{x})", .{ |
| 893 | source_sym.value + reloc.offset, | 896 | source_vaddr, |
| 894 | target_vaddr_with_addend, | 897 | target_vaddr_with_addend, |
| 895 | self.getSymbolName(reloc.target), | 898 | self.getSymbolName(reloc.target), |
| 896 | @tagName(reloc.@"type"), | 899 | @tagName(reloc.@"type"), |
| ... | @@ -899,25 +902,108 @@ fn resolveRelocs(self: *Coff, atom: *Atom) !void { | ... | @@ -899,25 +902,108 @@ fn resolveRelocs(self: *Coff, atom: *Atom) !void { |
| 899 | | 902 | |
| 900 | reloc.dirty = false; | 903 | reloc.dirty = false; |
| 901 | | 904 | |
| | 905 | var buffer: [@sizeOf(u32)]u8 = undefined; |
| 902 | switch (reloc.@"type") { | 906 | switch (reloc.@"type") { |
| 903 | .branch_26 => @panic("TODO branch26"), | 907 | .branch_26, |
| 904 | .got_page => @panic("TODO got_page"), | 908 | .got_page, |
| 905 | .got_pageoff => @panic("TODO got_pageoff"), | 909 | .got_pageoff, |
| 906 | .page => @panic("TODO page"), | 910 | .page, |
| 907 | .pageoff => @panic("TODO pageoff"), | 911 | .pageoff, |
| | 912 | => { |
| | 913 | const amt = try self.base.file.?.preadAll(&buffer, file_offset + reloc.offset); |
| | 914 | if (amt != buffer.len) return error.InputOutput; |
| | 915 | |
| | 916 | switch (reloc.@"type") { |
| | 917 | .branch_26 => { |
| | 918 | const displacement = math.cast(i28, @intCast(i64, target_vaddr_with_addend) - @intCast(i64, source_vaddr)) orelse |
| | 919 | unreachable; // TODO generate thunks |
| | 920 | var inst = aarch64.Instruction{ |
| | 921 | .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload( |
| | 922 | aarch64.Instruction, |
| | 923 | aarch64.Instruction.unconditional_branch_immediate, |
| | 924 | ), &buffer), |
| | 925 | }; |
| | 926 | inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2)); |
| | 927 | mem.writeIntLittle(u32, &buffer, inst.toU32()); |
| | 928 | }, |
| | 929 | .got_page, .page => { |
| | 930 | const source_page = @intCast(i32, source_vaddr >> 12); |
| | 931 | const target_page = @intCast(i32, target_vaddr_with_addend >> 12); |
| | 932 | const pages = @bitCast(u21, @intCast(i21, target_page - source_page)); |
| | 933 | var inst = aarch64.Instruction{ |
| | 934 | .pc_relative_address = mem.bytesToValue(meta.TagPayload( |
| | 935 | aarch64.Instruction, |
| | 936 | aarch64.Instruction.pc_relative_address, |
| | 937 | ), &buffer), |
| | 938 | }; |
| | 939 | inst.pc_relative_address.immhi = @truncate(u19, pages >> 2); |
| | 940 | inst.pc_relative_address.immlo = @truncate(u2, pages); |
| | 941 | mem.writeIntLittle(u32, &buffer, inst.toU32()); |
| | 942 | }, |
| | 943 | .got_pageoff, .pageoff => { |
| | 944 | assert(!reloc.pcrel); |
| | 945 | |
| | 946 | const narrowed = @truncate(u12, @intCast(u64, target_vaddr_with_addend)); |
| | 947 | if (isArithmeticOp(&buffer)) { |
| | 948 | var inst = aarch64.Instruction{ |
| | 949 | .add_subtract_immediate = mem.bytesToValue(meta.TagPayload( |
| | 950 | aarch64.Instruction, |
| | 951 | aarch64.Instruction.add_subtract_immediate, |
| | 952 | ), &buffer), |
| | 953 | }; |
| | 954 | inst.add_subtract_immediate.imm12 = narrowed; |
| | 955 | mem.writeIntLittle(u32, &buffer, inst.toU32()); |
| | 956 | } else { |
| | 957 | var inst = aarch64.Instruction{ |
| | 958 | .load_store_register = mem.bytesToValue(meta.TagPayload( |
| | 959 | aarch64.Instruction, |
| | 960 | aarch64.Instruction.load_store_register, |
| | 961 | ), &buffer), |
| | 962 | }; |
| | 963 | const offset: u12 = blk: { |
| | 964 | if (inst.load_store_register.size == 0) { |
| | 965 | if (inst.load_store_register.v == 1) { |
| | 966 | // 128-bit SIMD is scaled by 16. |
| | 967 | break :blk @divExact(narrowed, 16); |
| | 968 | } |
| | 969 | // Otherwise, 8-bit SIMD or ldrb. |
| | 970 | break :blk narrowed; |
| | 971 | } else { |
| | 972 | const denom: u4 = math.powi(u4, 2, inst.load_store_register.size) catch unreachable; |
| | 973 | break :blk @divExact(narrowed, denom); |
| | 974 | } |
| | 975 | }; |
| | 976 | inst.load_store_register.offset = offset; |
| | 977 | mem.writeIntLittle(u32, &buffer, inst.toU32()); |
| | 978 | } |
| | 979 | }, |
| | 980 | |
| | 981 | else => unreachable, |
| | 982 | } |
| | 983 | |
| | 984 | try self.base.file.?.pwriteAll(&buffer, file_offset + reloc.offset); |
| | 985 | |
| | 986 | return; |
| | 987 | }, |
| | 988 | |
| | 989 | else => {}, |
| | 990 | } |
| | 991 | |
| | 992 | switch (reloc.@"type") { |
| | 993 | .branch_26 => unreachable, |
| | 994 | .got_page => unreachable, |
| | 995 | .got_pageoff => unreachable, |
| | 996 | .page => unreachable, |
| | 997 | .pageoff => unreachable, |
| 908 | | 998 | |
| 909 | .got, .import => { | 999 | .got, .import => { |
| 910 | assert(reloc.pcrel); | 1000 | assert(reloc.pcrel); |
| 911 | const source_vaddr = source_sym.value + reloc.offset; | 1001 | const disp = @intCast(i32, target_vaddr_with_addend) - @intCast(i32, source_vaddr) - 4; |
| 912 | const disp = | | |
| 913 | @intCast(i32, target_vaddr_with_addend) - @intCast(i32, source_vaddr) - 4; | | |
| 914 | try self.base.file.?.pwriteAll(mem.asBytes(&disp), file_offset + reloc.offset); | 1002 | try self.base.file.?.pwriteAll(mem.asBytes(&disp), file_offset + reloc.offset); |
| 915 | }, | 1003 | }, |
| 916 | .direct => { | 1004 | .direct => { |
| 917 | if (reloc.pcrel) { | 1005 | if (reloc.pcrel) { |
| 918 | const source_vaddr = source_sym.value + reloc.offset; | 1006 | const disp = @intCast(i32, target_vaddr_with_addend) - @intCast(i32, source_vaddr) - 4; |
| 919 | const disp = | | |
| 920 | @intCast(i32, target_vaddr_with_addend) - @intCast(i32, source_vaddr) - 4; | | |
| 921 | try self.base.file.?.pwriteAll(mem.asBytes(&disp), file_offset + reloc.offset); | 1007 | try self.base.file.?.pwriteAll(mem.asBytes(&disp), file_offset + reloc.offset); |
| 922 | } else switch (self.ptr_width) { | 1008 | } else switch (self.ptr_width) { |
| 923 | .p32 => try self.base.file.?.pwriteAll( | 1009 | .p32 => try self.base.file.?.pwriteAll( |
| ... | @@ -2281,3 +2367,8 @@ fn logSections(self: *Coff) void { | ... | @@ -2281,3 +2367,8 @@ fn logSections(self: *Coff) void { |
| 2281 | }); | 2367 | }); |
| 2282 | } | 2368 | } |
| 2283 | } | 2369 | } |
| | 2370 | |
| | 2371 | inline fn isArithmeticOp(inst: *const [4]u8) bool { |
| | 2372 | const group_decode = @truncate(u5, inst[3]); |
| | 2373 | return ((group_decode >> 2) == 4); |
| | 2374 | } |