authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-11-05 23:20:05+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-11-06 08:41:43+01:00
log1aeef297337985c5fd8647462cc7c78ea1a4df43
treecca12f12bd3eaff958c441c9fad1c9d232d4f0ba
parent5ef33e7c7ea58f3777b0acac2335bd940b8a1fa6

coff: move relocation in its own module


3 files changed, 247 insertions(+), 216 deletions(-)

src/link/Coff.zig+3-214
......@@ -9,11 +9,9 @@ const fmt = std.fmt;
99const log = std.log.scoped(.link);
1010const math = std.math;
1111const mem = std.mem;
12const meta = std.meta;
1312
1413const Allocator = std.mem.Allocator;
1514
16const aarch64 = @import("../arch/aarch64/bits.zig");
1715const codegen = @import("../codegen.zig");
1816const link = @import("../link.zig");
1917const lld = @import("Coff/lld.zig");
......@@ -26,6 +24,7 @@ const Liveness = @import("../Liveness.zig");
2624const LlvmObject = @import("../codegen/llvm.zig").Object;
2725const Module = @import("../Module.zig");
2826const Object = @import("Coff/Object.zig");
27const Relocation = @import("Coff/Relocation.zig");
2928const StringTable = @import("strtab.zig").StringTable;
3029const TypedValue = @import("../TypedValue.zig");
3130
......@@ -125,61 +124,7 @@ const Entry = struct {
125124 sym_index: u32,
126125};
127126
128pub const Reloc = struct {
129 @"type": enum {
130 // x86, x86_64
131 /// RIP-relative displacement to a GOT pointer
132 got,
133 /// RIP-relative displacement to an import pointer
134 import,
135
136 // aarch64
137 /// PC-relative distance to target page in GOT section
138 got_page,
139 /// Offset to a GOT pointer relative to the start of a page in GOT section
140 got_pageoff,
141 /// PC-relative distance to target page in a section (e.g., .rdata)
142 page,
143 /// Offset to a pointer relative to the start of a page in a section (e.g., .rdata)
144 pageoff,
145 /// PC-relative distance to target page in a import section
146 import_page,
147 /// Offset to a pointer relative to the start of a page in an import section (e.g., .rdata)
148 import_pageoff,
149
150 // common
151 /// Absolute pointer value
152 direct,
153 },
154 target: SymbolWithLoc,
155 offset: u32,
156 addend: u32,
157 pcrel: bool,
158 length: u2,
159 dirty: bool = true,
160
161 /// Returns an Atom which is the target node of this relocation edge (if any).
162 fn getTargetAtom(self: Reloc, coff_file: *Coff) ?*Atom {
163 switch (self.@"type") {
164 .got,
165 .got_page,
166 .got_pageoff,
167 => return coff_file.getGotAtomForSymbol(self.target),
168
169 .direct,
170 .page,
171 .pageoff,
172 => return coff_file.getAtomForSymbol(self.target),
173
174 .import,
175 .import_page,
176 .import_pageoff,
177 => return coff_file.getImportAtomForSymbol(self.target),
178 }
179 }
180};
181
182const RelocTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Reloc));
127const RelocTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Relocation));
183128const BaseRelocationTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(u32));
184129const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*Atom));
185130
......@@ -888,163 +833,12 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void {
888833
889834fn resolveRelocs(self: *Coff, atom: *Atom) !void {
890835 const relocs = self.relocs.get(atom) orelse return;
891 const source_sym = atom.getSymbol(self);
892 const source_section = self.sections.get(@enumToInt(source_sym.section_number) - 1).header;
893 const file_offset = source_section.pointer_to_raw_data + source_sym.value - source_section.virtual_address;
894836
895837 log.debug("relocating '{s}'", .{atom.getName(self)});
896838
897839 for (relocs.items) |*reloc| {
898840 if (!reloc.dirty) continue;
899
900 const source_vaddr = source_sym.value + reloc.offset;
901 const target_atom = reloc.getTargetAtom(self) orelse continue;
902 const target_vaddr = target_atom.getSymbol(self).value;
903 const target_vaddr_with_addend = target_vaddr + reloc.addend;
904 const image_base = self.getImageBase();
905
906 log.debug(" ({x}: [() => 0x{x} ({s})) ({s}) (in file at 0x{x})", .{
907 source_vaddr,
908 target_vaddr_with_addend,
909 self.getSymbolName(reloc.target),
910 @tagName(reloc.@"type"),
911 file_offset + reloc.offset,
912 });
913
914 reloc.dirty = false;
915
916 switch (self.base.options.target.cpu.arch) {
917 .aarch64 => {
918 var buffer: [@sizeOf(u64)]u8 = undefined;
919 switch (reloc.length) {
920 2 => {
921 const amt = try self.base.file.?.preadAll(buffer[0..4], file_offset + reloc.offset);
922 if (amt != 4) return error.InputOutput;
923 },
924 3 => {
925 const amt = try self.base.file.?.preadAll(&buffer, file_offset + reloc.offset);
926 if (amt != 8) return error.InputOutput;
927 },
928 else => unreachable,
929 }
930
931 switch (reloc.@"type") {
932 .got_page, .import_page, .page => {
933 const source_page = @intCast(i32, source_vaddr >> 12);
934 const target_page = @intCast(i32, target_vaddr_with_addend >> 12);
935 const pages = @bitCast(u21, @intCast(i21, target_page - source_page));
936 var inst = aarch64.Instruction{
937 .pc_relative_address = mem.bytesToValue(meta.TagPayload(
938 aarch64.Instruction,
939 aarch64.Instruction.pc_relative_address,
940 ), buffer[0..4]),
941 };
942 inst.pc_relative_address.immhi = @truncate(u19, pages >> 2);
943 inst.pc_relative_address.immlo = @truncate(u2, pages);
944 mem.writeIntLittle(u32, buffer[0..4], inst.toU32());
945 },
946 .got_pageoff, .import_pageoff, .pageoff => {
947 assert(!reloc.pcrel);
948
949 const narrowed = @truncate(u12, @intCast(u64, target_vaddr_with_addend));
950 if (isArithmeticOp(buffer[0..4])) {
951 var inst = aarch64.Instruction{
952 .add_subtract_immediate = mem.bytesToValue(meta.TagPayload(
953 aarch64.Instruction,
954 aarch64.Instruction.add_subtract_immediate,
955 ), buffer[0..4]),
956 };
957 inst.add_subtract_immediate.imm12 = narrowed;
958 mem.writeIntLittle(u32, buffer[0..4], inst.toU32());
959 } else {
960 var inst = aarch64.Instruction{
961 .load_store_register = mem.bytesToValue(meta.TagPayload(
962 aarch64.Instruction,
963 aarch64.Instruction.load_store_register,
964 ), buffer[0..4]),
965 };
966 const offset: u12 = blk: {
967 if (inst.load_store_register.size == 0) {
968 if (inst.load_store_register.v == 1) {
969 // 128-bit SIMD is scaled by 16.
970 break :blk @divExact(narrowed, 16);
971 }
972 // Otherwise, 8-bit SIMD or ldrb.
973 break :blk narrowed;
974 } else {
975 const denom: u4 = math.powi(u4, 2, inst.load_store_register.size) catch unreachable;
976 break :blk @divExact(narrowed, denom);
977 }
978 };
979 inst.load_store_register.offset = offset;
980 mem.writeIntLittle(u32, buffer[0..4], inst.toU32());
981 }
982 },
983 .direct => {
984 assert(!reloc.pcrel);
985 switch (reloc.length) {
986 2 => mem.writeIntLittle(
987 u32,
988 buffer[0..4],
989 @truncate(u32, target_vaddr_with_addend + image_base),
990 ),
991 3 => mem.writeIntLittle(u64, &buffer, target_vaddr_with_addend + image_base),
992 else => unreachable,
993 }
994 },
995
996 .got => unreachable,
997 .import => unreachable,
998 }
999
1000 switch (reloc.length) {
1001 2 => try self.base.file.?.pwriteAll(buffer[0..4], file_offset + reloc.offset),
1002 3 => try self.base.file.?.pwriteAll(&buffer, file_offset + reloc.offset),
1003 else => unreachable,
1004 }
1005 },
1006
1007 .x86_64, .x86 => {
1008 switch (reloc.@"type") {
1009 .got_page => unreachable,
1010 .got_pageoff => unreachable,
1011 .page => unreachable,
1012 .pageoff => unreachable,
1013 .import_page => unreachable,
1014 .import_pageoff => unreachable,
1015
1016 .got, .import => {
1017 assert(reloc.pcrel);
1018 const disp = @intCast(i32, target_vaddr_with_addend) - @intCast(i32, source_vaddr) - 4;
1019 try self.base.file.?.pwriteAll(mem.asBytes(&disp), file_offset + reloc.offset);
1020 },
1021 .direct => {
1022 if (reloc.pcrel) {
1023 const disp = @intCast(i32, target_vaddr_with_addend) - @intCast(i32, source_vaddr) - 4;
1024 try self.base.file.?.pwriteAll(mem.asBytes(&disp), file_offset + reloc.offset);
1025 } else switch (self.ptr_width) {
1026 .p32 => try self.base.file.?.pwriteAll(
1027 mem.asBytes(&@intCast(u32, target_vaddr_with_addend + image_base)),
1028 file_offset + reloc.offset,
1029 ),
1030 .p64 => switch (reloc.length) {
1031 2 => try self.base.file.?.pwriteAll(
1032 mem.asBytes(&@truncate(u32, target_vaddr_with_addend + image_base)),
1033 file_offset + reloc.offset,
1034 ),
1035 3 => try self.base.file.?.pwriteAll(
1036 mem.asBytes(&(target_vaddr_with_addend + image_base)),
1037 file_offset + reloc.offset,
1038 ),
1039 else => unreachable,
1040 },
1041 }
1042 },
1043 }
1044 },
1045
1046 else => unreachable, // unhandled target architecture
1047 }
841 try reloc.resolve(atom, self);
1048842 }
1049843}
1050844
......@@ -2397,8 +2191,3 @@ fn logSections(self: *Coff) void {
23972191 });
23982192 }
23992193}
2400
2401inline fn isArithmeticOp(inst: *const [4]u8) bool {
2402 const group_decode = @truncate(u5, inst[3]);
2403 return ((group_decode >> 2) == 4);
2404}
src/link/Coff/Atom.zig+2-2
......@@ -5,7 +5,7 @@ const coff = std.coff;
55const log = std.log.scoped(.link);
66
77const Coff = @import("../Coff.zig");
8const Reloc = Coff.Reloc;
8const Relocation = @import("Relocation.zig");
99const SymbolWithLoc = Coff.SymbolWithLoc;
1010
1111/// Each decl always gets a local symbol with the fully qualified name.
......@@ -92,7 +92,7 @@ pub fn freeListEligible(self: Atom, coff_file: *const Coff) bool {
9292 return surplus >= Coff.min_text_capacity;
9393}
9494
95pub fn addRelocation(self: *Atom, coff_file: *Coff, reloc: Reloc) !void {
95pub fn addRelocation(self: *Atom, coff_file: *Coff, reloc: Relocation) !void {
9696 const gpa = coff_file.base.allocator;
9797 log.debug(" (adding reloc of type {s} to target %{d})", .{ @tagName(reloc.@"type"), reloc.target.sym_index });
9898 const gop = try coff_file.relocs.getOrPut(gpa, self);
src/link/Coff/Relocation.zig created+242
......@@ -0,0 +1,242 @@
1const Relocation = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const log = std.log.scoped(.link);
6const math = std.math;
7const mem = std.mem;
8const meta = std.meta;
9
10const aarch64 = @import("../../arch/aarch64/bits.zig");
11
12const Atom = @import("Atom.zig");
13const Coff = @import("../Coff.zig");
14const SymbolWithLoc = Coff.SymbolWithLoc;
15
16@"type": enum {
17 // x86, x86_64
18 /// RIP-relative displacement to a GOT pointer
19 got,
20 /// RIP-relative displacement to an import pointer
21 import,
22
23 // aarch64
24 /// PC-relative distance to target page in GOT section
25 got_page,
26 /// Offset to a GOT pointer relative to the start of a page in GOT section
27 got_pageoff,
28 /// PC-relative distance to target page in a section (e.g., .rdata)
29 page,
30 /// Offset to a pointer relative to the start of a page in a section (e.g., .rdata)
31 pageoff,
32 /// PC-relative distance to target page in a import section
33 import_page,
34 /// Offset to a pointer relative to the start of a page in an import section (e.g., .rdata)
35 import_pageoff,
36
37 // common
38 /// Absolute pointer value
39 direct,
40},
41target: SymbolWithLoc,
42offset: u32,
43addend: u32,
44pcrel: bool,
45length: u2,
46dirty: bool = true,
47
48/// Returns an Atom which is the target node of this relocation edge (if any).
49pub fn getTargetAtom(self: Relocation, coff_file: *Coff) ?*Atom {
50 switch (self.@"type") {
51 .got,
52 .got_page,
53 .got_pageoff,
54 => return coff_file.getGotAtomForSymbol(self.target),
55
56 .direct,
57 .page,
58 .pageoff,
59 => return coff_file.getAtomForSymbol(self.target),
60
61 .import,
62 .import_page,
63 .import_pageoff,
64 => return coff_file.getImportAtomForSymbol(self.target),
65 }
66}
67
68pub fn resolve(self: *Relocation, atom: *Atom, coff_file: *Coff) !void {
69 const source_sym = atom.getSymbol(coff_file);
70 const source_section = coff_file.sections.get(@enumToInt(source_sym.section_number) - 1).header;
71 const source_vaddr = source_sym.value + self.offset;
72
73 const file_offset = source_section.pointer_to_raw_data + source_sym.value - source_section.virtual_address;
74
75 const target_atom = self.getTargetAtom(coff_file) orelse return;
76 const target_vaddr = target_atom.getSymbol(coff_file).value;
77 const target_vaddr_with_addend = target_vaddr + self.addend;
78
79 log.debug(" ({x}: [() => 0x{x} ({s})) ({s}) (in file at 0x{x})", .{
80 source_vaddr,
81 target_vaddr_with_addend,
82 coff_file.getSymbolName(self.target),
83 @tagName(self.@"type"),
84 file_offset + self.offset,
85 });
86
87 const ctx: Context = .{
88 .source_vaddr = source_vaddr,
89 .target_vaddr = target_vaddr_with_addend,
90 .file_offset = file_offset,
91 .image_base = coff_file.getImageBase(),
92 };
93
94 switch (coff_file.base.options.target.cpu.arch) {
95 .aarch64 => try self.resolveAarch64(ctx, coff_file),
96 .x86, .x86_64 => try self.resolveX86(ctx, coff_file),
97 else => unreachable, // unhandled target architecture
98 }
99
100 self.dirty = false;
101}
102
103const Context = struct {
104 source_vaddr: u32,
105 target_vaddr: u32,
106 file_offset: u32,
107 image_base: u64,
108};
109
110fn resolveAarch64(self: *Relocation, ctx: Context, coff_file: *Coff) !void {
111 var buffer: [@sizeOf(u64)]u8 = undefined;
112 switch (self.length) {
113 2 => {
114 const amt = try coff_file.base.file.?.preadAll(buffer[0..4], ctx.file_offset + self.offset);
115 if (amt != 4) return error.InputOutput;
116 },
117 3 => {
118 const amt = try coff_file.base.file.?.preadAll(&buffer, ctx.file_offset + self.offset);
119 if (amt != 8) return error.InputOutput;
120 },
121 else => unreachable,
122 }
123
124 switch (self.@"type") {
125 .got_page, .import_page, .page => {
126 const source_page = @intCast(i32, ctx.source_vaddr >> 12);
127 const target_page = @intCast(i32, ctx.target_vaddr >> 12);
128 const pages = @bitCast(u21, @intCast(i21, target_page - source_page));
129 var inst = aarch64.Instruction{
130 .pc_relative_address = mem.bytesToValue(meta.TagPayload(
131 aarch64.Instruction,
132 aarch64.Instruction.pc_relative_address,
133 ), buffer[0..4]),
134 };
135 inst.pc_relative_address.immhi = @truncate(u19, pages >> 2);
136 inst.pc_relative_address.immlo = @truncate(u2, pages);
137 mem.writeIntLittle(u32, buffer[0..4], inst.toU32());
138 },
139 .got_pageoff, .import_pageoff, .pageoff => {
140 assert(!self.pcrel);
141
142 const narrowed = @truncate(u12, @intCast(u64, ctx.target_vaddr));
143 if (isArithmeticOp(buffer[0..4])) {
144 var inst = aarch64.Instruction{
145 .add_subtract_immediate = mem.bytesToValue(meta.TagPayload(
146 aarch64.Instruction,
147 aarch64.Instruction.add_subtract_immediate,
148 ), buffer[0..4]),
149 };
150 inst.add_subtract_immediate.imm12 = narrowed;
151 mem.writeIntLittle(u32, buffer[0..4], inst.toU32());
152 } else {
153 var inst = aarch64.Instruction{
154 .load_store_register = mem.bytesToValue(meta.TagPayload(
155 aarch64.Instruction,
156 aarch64.Instruction.load_store_register,
157 ), buffer[0..4]),
158 };
159 const offset: u12 = blk: {
160 if (inst.load_store_register.size == 0) {
161 if (inst.load_store_register.v == 1) {
162 // 128-bit SIMD is scaled by 16.
163 break :blk @divExact(narrowed, 16);
164 }
165 // Otherwise, 8-bit SIMD or ldrb.
166 break :blk narrowed;
167 } else {
168 const denom: u4 = math.powi(u4, 2, inst.load_store_register.size) catch unreachable;
169 break :blk @divExact(narrowed, denom);
170 }
171 };
172 inst.load_store_register.offset = offset;
173 mem.writeIntLittle(u32, buffer[0..4], inst.toU32());
174 }
175 },
176 .direct => {
177 assert(!self.pcrel);
178 switch (self.length) {
179 2 => mem.writeIntLittle(
180 u32,
181 buffer[0..4],
182 @truncate(u32, ctx.target_vaddr + ctx.image_base),
183 ),
184 3 => mem.writeIntLittle(u64, &buffer, ctx.target_vaddr + ctx.image_base),
185 else => unreachable,
186 }
187 },
188
189 .got => unreachable,
190 .import => unreachable,
191 }
192
193 switch (self.length) {
194 2 => try coff_file.base.file.?.pwriteAll(buffer[0..4], ctx.file_offset + self.offset),
195 3 => try coff_file.base.file.?.pwriteAll(&buffer, ctx.file_offset + self.offset),
196 else => unreachable,
197 }
198}
199
200fn resolveX86(self: *Relocation, ctx: Context, coff_file: *Coff) !void {
201 switch (self.@"type") {
202 .got_page => unreachable,
203 .got_pageoff => unreachable,
204 .page => unreachable,
205 .pageoff => unreachable,
206 .import_page => unreachable,
207 .import_pageoff => unreachable,
208
209 .got, .import => {
210 assert(self.pcrel);
211 const disp = @intCast(i32, ctx.target_vaddr) - @intCast(i32, ctx.source_vaddr) - 4;
212 try coff_file.base.file.?.pwriteAll(mem.asBytes(&disp), ctx.file_offset + self.offset);
213 },
214 .direct => {
215 if (self.pcrel) {
216 const disp = @intCast(i32, ctx.target_vaddr) - @intCast(i32, ctx.source_vaddr) - 4;
217 try coff_file.base.file.?.pwriteAll(mem.asBytes(&disp), ctx.file_offset + self.offset);
218 } else switch (coff_file.ptr_width) {
219 .p32 => try coff_file.base.file.?.pwriteAll(
220 mem.asBytes(&@intCast(u32, ctx.target_vaddr + ctx.image_base)),
221 ctx.file_offset + self.offset,
222 ),
223 .p64 => switch (self.length) {
224 2 => try coff_file.base.file.?.pwriteAll(
225 mem.asBytes(&@truncate(u32, ctx.target_vaddr + ctx.image_base)),
226 ctx.file_offset + self.offset,
227 ),
228 3 => try coff_file.base.file.?.pwriteAll(
229 mem.asBytes(&(ctx.target_vaddr + ctx.image_base)),
230 ctx.file_offset + self.offset,
231 ),
232 else => unreachable,
233 },
234 }
235 },
236 }
237}
238
239inline fn isArithmeticOp(inst: *const [4]u8) bool {
240 const group_decode = @truncate(u5, inst[3]);
241 return ((group_decode >> 2) == 4);
242}