authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-15 18:25:10+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-15 18:49:47+02:00
logc47ce310716de806b3d95c328a9c2f9735221806
treed671bf86a46084c5068933fd9fbe8b10d9b173ae
parentf8678c48ff43879d043b626031f7a5c92303fdea

zld: thin out Relocation by not storing *TextBlock

this way we shave off 8 bytes per Relocation structure, and instead we can pass the *TextBlock as args to resolve function.

2 files changed, 178 insertions(+), 181 deletions(-)

src/link/MachO/Zld.zig+85-1
......@@ -246,7 +246,91 @@ pub const TextBlock = struct {
246246
247247 pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
248248 for (self.relocs.items) |rel| {
249 try rel.resolve(zld);
249 log.debug("relocating {}", .{rel});
250
251 const source_addr = blk: {
252 const sym = zld.locals.items[self.local_sym_index];
253 break :blk sym.payload.regular.address + rel.offset;
254 };
255 const target_addr = blk: {
256 const is_via_got = switch (rel.payload) {
257 .pointer_to_got => true,
258 .page => |page| page.kind == .got,
259 .page_off => |page_off| page_off.kind == .got,
260 .load => |load| load.kind == .got,
261 else => false,
262 };
263
264 if (is_via_got) {
265 const dc_seg = zld.load_commands.items[zld.data_const_segment_cmd_index.?].Segment;
266 const got = dc_seg.sections.items[zld.got_section_index.?];
267 const got_index = rel.target.got_index orelse {
268 log.err("expected GOT entry for symbol '{s}'", .{zld.getString(rel.target.strx)});
269 log.err(" this is an internal linker error", .{});
270 return error.FailedToResolveRelocationTarget;
271 };
272 break :blk got.addr + got_index * @sizeOf(u64);
273 }
274
275 switch (rel.target.payload) {
276 .regular => |reg| {
277 const is_tlv = is_tlv: {
278 const sym = zld.locals.items[self.local_sym_index];
279 const seg = zld.load_commands.items[sym.payload.regular.segment_id].Segment;
280 const sect = seg.sections.items[sym.payload.regular.section_id];
281 break :is_tlv sectionType(sect) == macho.S_THREAD_LOCAL_VARIABLES;
282 };
283 if (is_tlv) {
284 // For TLV relocations, the value specified as a relocation is the displacement from the
285 // TLV initializer (either value in __thread_data or zero-init in __thread_bss) to the first
286 // defined TLV template init section in the following order:
287 // * wrt to __thread_data if defined, then
288 // * wrt to __thread_bss
289 const seg = zld.load_commands.items[zld.data_segment_cmd_index.?].Segment;
290 const base_address = inner: {
291 if (zld.tlv_data_section_index) |i| {
292 break :inner seg.sections.items[i].addr;
293 } else if (zld.tlv_bss_section_index) |i| {
294 break :inner seg.sections.items[i].addr;
295 } else {
296 log.err("threadlocal variables present but no initializer sections found", .{});
297 log.err(" __thread_data not found", .{});
298 log.err(" __thread_bss not found", .{});
299 return error.FailedToResolveRelocationTarget;
300 }
301 };
302 break :blk reg.address - base_address;
303 }
304
305 break :blk reg.address;
306 },
307 .proxy => {
308 if (mem.eql(u8, zld.getString(rel.target.strx), "__tlv_bootstrap")) {
309 break :blk 0; // Dynamically bound by dyld.
310 }
311
312 const segment = zld.load_commands.items[zld.text_segment_cmd_index.?].Segment;
313 const stubs = segment.sections.items[zld.stubs_section_index.?];
314 const stubs_index = rel.target.stubs_index orelse {
315 // TODO verify in TextBlock that the symbol is indeed dynamically bound.
316 break :blk 0; // Dynamically bound by dyld.
317 };
318 break :blk stubs.addr + stubs_index * stubs.reserved2;
319 },
320 else => {
321 log.err("failed to resolve symbol '{s}' as a relocation target", .{
322 zld.getString(rel.target.strx),
323 });
324 log.err(" this is an internal linker error", .{});
325 return error.FailedToResolveRelocationTarget;
326 },
327 }
328 };
329
330 log.debug(" | source_addr = 0x{x}", .{source_addr});
331 log.debug(" | target_addr = 0x{x}", .{target_addr});
332
333 try rel.resolve(self, source_addr, target_addr);
250334 }
251335 }
252336
src/link/MachO/reloc.zig+93-180
......@@ -20,9 +20,6 @@ pub const Relocation = struct {
2020 /// Note relocation size can be inferred by relocation's kind.
2121 offset: u32,
2222
23 /// Parent block containing this relocation.
24 block: *TextBlock,
25
2623 /// Target symbol: either a regular or a proxy.
2724 target: *Symbol,
2825
......@@ -36,6 +33,13 @@ pub const Relocation = struct {
3633 load: Load,
3734 },
3835
36 const ResolveArgs = struct {
37 block: *TextBlock,
38 offset: u32,
39 source_addr: u64,
40 target_addr: u64,
41 };
42
3943 pub const Unsigned = struct {
4044 subtractor: ?*Symbol = null,
4145
......@@ -48,16 +52,16 @@ pub const Relocation = struct {
4852 /// => * is unreachable
4953 is_64bit: bool,
5054
51 pub fn resolve(self: Unsigned, base: Relocation, _: u64, target_addr: u64) !void {
55 pub fn resolve(self: Unsigned, args: ResolveArgs) !void {
5256 const result = if (self.subtractor) |subtractor|
53 @intCast(i64, target_addr) - @intCast(i64, subtractor.payload.regular.address) + self.addend
57 @intCast(i64, args.target_addr) - @intCast(i64, subtractor.payload.regular.address) + self.addend
5458 else
55 @intCast(i64, target_addr) + self.addend;
59 @intCast(i64, args.target_addr) + self.addend;
5660
5761 if (self.is_64bit) {
58 mem.writeIntLittle(u64, base.block.code[base.offset..][0..8], @bitCast(u64, result));
62 mem.writeIntLittle(u64, args.block.code[args.offset..][0..8], @bitCast(u64, result));
5963 } else {
60 mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], @truncate(u32, @bitCast(u64, result)));
64 mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @truncate(u32, @bitCast(u64, result)));
6165 }
6266 }
6367
......@@ -78,25 +82,29 @@ pub const Relocation = struct {
7882 pub const Branch = struct {
7983 arch: Arch,
8084
81 pub fn resolve(self: Branch, base: Relocation, source_addr: u64, target_addr: u64) !void {
85 pub fn resolve(self: Branch, args: ResolveArgs) !void {
8286 switch (self.arch) {
8387 .aarch64 => {
84 const displacement = try math.cast(i28, @intCast(i64, target_addr) - @intCast(i64, source_addr));
88 const displacement = try math.cast(
89 i28,
90 @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr),
91 );
92 const code = args.block.code[args.offset..][0..4];
8593 var inst = aarch64.Instruction{
86 .unconditional_branch_immediate = mem.bytesToValue(
87 meta.TagPayload(
88 aarch64.Instruction,
89 aarch64.Instruction.unconditional_branch_immediate,
90 ),
91 base.block.code[base.offset..][0..4],
92 ),
94 .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload(
95 aarch64.Instruction,
96 aarch64.Instruction.unconditional_branch_immediate,
97 ), code),
9398 };
9499 inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2));
95 mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], inst.toU32());
100 mem.writeIntLittle(u32, code, inst.toU32());
96101 },
97102 .x86_64 => {
98 const displacement = try math.cast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4);
99 mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], @bitCast(u32, displacement));
103 const displacement = try math.cast(
104 i32,
105 @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4,
106 );
107 mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @bitCast(u32, displacement));
100108 },
101109 else => return error.UnsupportedCpuArchitecture,
102110 }
......@@ -118,25 +126,23 @@ pub const Relocation = struct {
118126 },
119127 addend: ?u32 = null,
120128
121 pub fn resolve(self: Page, base: Relocation, source_addr: u64, target_addr: u64) !void {
122 const actual_target_addr = if (self.addend) |addend| target_addr + addend else target_addr;
123 const source_page = @intCast(i32, source_addr >> 12);
124 const target_page = @intCast(i32, actual_target_addr >> 12);
129 pub fn resolve(self: Page, args: ResolveArgs) !void {
130 const target_addr = if (self.addend) |addend| args.target_addr + addend else args.target_addr;
131 const source_page = @intCast(i32, args.source_addr >> 12);
132 const target_page = @intCast(i32, target_addr >> 12);
125133 const pages = @bitCast(u21, @intCast(i21, target_page - source_page));
126134
135 const code = args.block.code[args.offset..][0..4];
127136 var inst = aarch64.Instruction{
128 .pc_relative_address = mem.bytesToValue(
129 meta.TagPayload(
130 aarch64.Instruction,
131 aarch64.Instruction.pc_relative_address,
132 ),
133 base.block.code[base.offset..][0..4],
134 ),
137 .pc_relative_address = mem.bytesToValue(meta.TagPayload(
138 aarch64.Instruction,
139 aarch64.Instruction.pc_relative_address,
140 ), code),
135141 };
136142 inst.pc_relative_address.immhi = @truncate(u19, pages >> 2);
137143 inst.pc_relative_address.immlo = @truncate(u2, pages);
138144
139 mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], inst.toU32());
145 mem.writeIntLittle(u32, code, inst.toU32());
140146 }
141147
142148 pub fn format(self: Page, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
......@@ -173,35 +179,31 @@ pub const Relocation = struct {
173179 load,
174180 };
175181
176 pub fn resolve(self: PageOff, base: Relocation, _: u64, target_addr: u64) !void {
182 pub fn resolve(self: PageOff, args: ResolveArgs) !void {
183 const code = args.block.code[args.offset..][0..4];
184
177185 switch (self.kind) {
178186 .page => {
179 const actual_target_addr = if (self.addend) |addend| target_addr + addend else target_addr;
180 const narrowed = @truncate(u12, actual_target_addr);
187 const target_addr = if (self.addend) |addend| args.target_addr + addend else args.target_addr;
188 const narrowed = @truncate(u12, target_addr);
181189
182190 const op_kind = self.op_kind orelse unreachable;
183191 var inst: aarch64.Instruction = blk: {
184192 switch (op_kind) {
185193 .arithmetic => {
186194 break :blk .{
187 .add_subtract_immediate = mem.bytesToValue(
188 meta.TagPayload(
189 aarch64.Instruction,
190 aarch64.Instruction.add_subtract_immediate,
191 ),
192 base.block.code[base.offset..][0..4],
193 ),
195 .add_subtract_immediate = mem.bytesToValue(meta.TagPayload(
196 aarch64.Instruction,
197 aarch64.Instruction.add_subtract_immediate,
198 ), code),
194199 };
195200 },
196201 .load => {
197202 break :blk .{
198 .load_store_register = mem.bytesToValue(
199 meta.TagPayload(
200 aarch64.Instruction,
201 aarch64.Instruction.load_store_register,
202 ),
203 base.block.code[base.offset..][0..4],
204 ),
203 .load_store_register = mem.bytesToValue(meta.TagPayload(
204 aarch64.Instruction,
205 aarch64.Instruction.load_store_register,
206 ), code),
205207 };
206208 },
207209 }
......@@ -226,22 +228,19 @@ pub const Relocation = struct {
226228 inst.load_store_register.offset = offset;
227229 }
228230
229 mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], inst.toU32());
231 mem.writeIntLittle(u32, code, inst.toU32());
230232 },
231233 .got => {
232 const narrowed = @truncate(u12, target_addr);
234 const narrowed = @truncate(u12, args.target_addr);
233235 var inst: aarch64.Instruction = .{
234 .load_store_register = mem.bytesToValue(
235 meta.TagPayload(
236 aarch64.Instruction,
237 aarch64.Instruction.load_store_register,
238 ),
239 base.block.code[base.offset..][0..4],
240 ),
236 .load_store_register = mem.bytesToValue(meta.TagPayload(
237 aarch64.Instruction,
238 aarch64.Instruction.load_store_register,
239 ), code),
241240 };
242241 const offset = try math.divExact(u12, narrowed, 8);
243242 inst.load_store_register.offset = offset;
244 mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], inst.toU32());
243 mem.writeIntLittle(u32, code, inst.toU32());
245244 },
246245 .tlvp => {
247246 const RegInfo = struct {
......@@ -250,27 +249,21 @@ pub const Relocation = struct {
250249 size: u1,
251250 };
252251 const reg_info: RegInfo = blk: {
253 if (isArithmeticOp(base.block.code[base.offset..][0..4])) {
254 const inst = mem.bytesToValue(
255 meta.TagPayload(
256 aarch64.Instruction,
257 aarch64.Instruction.add_subtract_immediate,
258 ),
259 base.block.code[base.offset..][0..4],
260 );
252 if (isArithmeticOp(code)) {
253 const inst = mem.bytesToValue(meta.TagPayload(
254 aarch64.Instruction,
255 aarch64.Instruction.add_subtract_immediate,
256 ), code);
261257 break :blk .{
262258 .rd = inst.rd,
263259 .rn = inst.rn,
264260 .size = inst.sf,
265261 };
266262 } else {
267 const inst = mem.bytesToValue(
268 meta.TagPayload(
269 aarch64.Instruction,
270 aarch64.Instruction.load_store_register,
271 ),
272 base.block.code[base.offset..][0..4],
273 );
263 const inst = mem.bytesToValue(meta.TagPayload(
264 aarch64.Instruction,
265 aarch64.Instruction.load_store_register,
266 ), code);
274267 break :blk .{
275268 .rd = inst.rt,
276269 .rn = inst.rn,
......@@ -278,7 +271,7 @@ pub const Relocation = struct {
278271 };
279272 }
280273 };
281 const narrowed = @truncate(u12, target_addr);
274 const narrowed = @truncate(u12, args.target_addr);
282275 var inst = aarch64.Instruction{
283276 .add_subtract_immediate = .{
284277 .rd = reg_info.rd,
......@@ -290,7 +283,7 @@ pub const Relocation = struct {
290283 .sf = reg_info.size,
291284 },
292285 };
293 mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], inst.toU32());
286 mem.writeIntLittle(u32, code, inst.toU32());
294287 },
295288 }
296289 }
......@@ -319,9 +312,9 @@ pub const Relocation = struct {
319312 };
320313
321314 pub const PointerToGot = struct {
322 pub fn resolve(_: PointerToGot, base: Relocation, source_addr: u64, target_addr: u64) !void {
323 const result = try math.cast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr));
324 mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], @bitCast(u32, result));
315 pub fn resolve(_: PointerToGot, args: ResolveArgs) !void {
316 const result = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr));
317 mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @bitCast(u32, result));
325318 }
326319
327320 pub fn format(self: PointerToGot, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
......@@ -336,13 +329,13 @@ pub const Relocation = struct {
336329 addend: i64,
337330 correction: i4,
338331
339 pub fn resolve(self: Signed, base: Relocation, source_addr: u64, target_addr: u64) !void {
340 const actual_target_addr = @intCast(i64, target_addr) + self.addend;
332 pub fn resolve(self: Signed, args: ResolveArgs) !void {
333 const target_addr = @intCast(i64, args.target_addr) + self.addend;
341334 const displacement = try math.cast(
342335 i32,
343 actual_target_addr - @intCast(i64, source_addr) - self.correction - 4,
336 target_addr - @intCast(i64, args.source_addr) - self.correction - 4,
344337 );
345 mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], @bitCast(u32, displacement));
338 mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @bitCast(u32, displacement));
346339 }
347340
348341 pub fn format(self: Signed, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
......@@ -362,17 +355,17 @@ pub const Relocation = struct {
362355 },
363356 addend: ?i32 = null,
364357
365 pub fn resolve(self: Load, base: Relocation, source_addr: u64, target_addr: u64) !void {
358 pub fn resolve(self: Load, args: ResolveArgs) !void {
366359 if (self.kind == .tlvp) {
367360 // We need to rewrite the opcode from movq to leaq.
368 base.block.code[base.offset - 2] = 0x8d;
361 args.block.code[args.offset - 2] = 0x8d;
369362 }
370363 const addend = if (self.addend) |addend| addend else 0;
371364 const displacement = try math.cast(
372365 i32,
373 @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4 + addend,
366 @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4 + addend,
374367 );
375 mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], @bitCast(u32, displacement));
368 mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @bitCast(u32, displacement));
376369 }
377370
378371 pub fn format(self: Load, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
......@@ -387,106 +380,27 @@ pub const Relocation = struct {
387380 }
388381 };
389382
390 pub fn resolve(self: Relocation, zld: *Zld) !void {
391 log.debug("relocating {}", .{self});
392
393 const source_addr = blk: {
394 const sym = zld.locals.items[self.block.local_sym_index];
395 break :blk sym.payload.regular.address + self.offset;
396 };
397 const target_addr = blk: {
398 const is_via_got = switch (self.payload) {
399 .pointer_to_got => true,
400 .page => |page| page.kind == .got,
401 .page_off => |page_off| page_off.kind == .got,
402 .load => |load| load.kind == .got,
403 else => false,
404 };
405
406 if (is_via_got) {
407 const dc_seg = zld.load_commands.items[zld.data_const_segment_cmd_index.?].Segment;
408 const got = dc_seg.sections.items[zld.got_section_index.?];
409 const got_index = self.target.got_index orelse {
410 log.err("expected GOT entry for symbol '{s}'", .{zld.getString(self.target.strx)});
411 log.err(" this is an internal linker error", .{});
412 return error.FailedToResolveRelocationTarget;
413 };
414 break :blk got.addr + got_index * @sizeOf(u64);
415 }
416
417 switch (self.target.payload) {
418 .regular => |reg| {
419 const is_tlv = is_tlv: {
420 const sym = zld.locals.items[self.block.local_sym_index];
421 const seg = zld.load_commands.items[sym.payload.regular.segment_id].Segment;
422 const sect = seg.sections.items[sym.payload.regular.section_id];
423 break :is_tlv commands.sectionType(sect) == macho.S_THREAD_LOCAL_VARIABLES;
424 };
425 if (is_tlv) {
426 // For TLV relocations, the value specified as a relocation is the displacement from the
427 // TLV initializer (either value in __thread_data or zero-init in __thread_bss) to the first
428 // defined TLV template init section in the following order:
429 // * wrt to __thread_data if defined, then
430 // * wrt to __thread_bss
431 const seg = zld.load_commands.items[zld.data_segment_cmd_index.?].Segment;
432 const base_address = inner: {
433 if (zld.tlv_data_section_index) |i| {
434 break :inner seg.sections.items[i].addr;
435 } else if (zld.tlv_bss_section_index) |i| {
436 break :inner seg.sections.items[i].addr;
437 } else {
438 log.err("threadlocal variables present but no initializer sections found", .{});
439 log.err(" __thread_data not found", .{});
440 log.err(" __thread_bss not found", .{});
441 return error.FailedToResolveRelocationTarget;
442 }
443 };
444 break :blk reg.address - base_address;
445 }
446
447 break :blk reg.address;
448 },
449 .proxy => {
450 if (mem.eql(u8, zld.getString(self.target.strx), "__tlv_bootstrap")) {
451 break :blk 0; // Dynamically bound by dyld.
452 }
453
454 const segment = zld.load_commands.items[zld.text_segment_cmd_index.?].Segment;
455 const stubs = segment.sections.items[zld.stubs_section_index.?];
456 const stubs_index = self.target.stubs_index orelse {
457 // TODO verify in TextBlock that the symbol is indeed dynamically bound.
458 break :blk 0; // Dynamically bound by dyld.
459 };
460 break :blk stubs.addr + stubs_index * stubs.reserved2;
461 },
462 else => {
463 log.err("failed to resolve symbol '{s}' as a relocation target", .{
464 zld.getString(self.target.strx),
465 });
466 log.err(" this is an internal linker error", .{});
467 return error.FailedToResolveRelocationTarget;
468 },
469 }
383 pub fn resolve(self: Relocation, block: *TextBlock, source_addr: u64, target_addr: u64) !void {
384 const args = ResolveArgs{
385 .block = block,
386 .offset = self.offset,
387 .source_addr = source_addr,
388 .target_addr = target_addr,
470389 };
471
472 log.debug(" | source_addr = 0x{x}", .{source_addr});
473 log.debug(" | target_addr = 0x{x}", .{target_addr});
474
475390 switch (self.payload) {
476 .unsigned => |unsigned| try unsigned.resolve(self, source_addr, target_addr),
477 .branch => |branch| try branch.resolve(self, source_addr, target_addr),
478 .page => |page| try page.resolve(self, source_addr, target_addr),
479 .page_off => |page_off| try page_off.resolve(self, source_addr, target_addr),
480 .pointer_to_got => |pointer_to_got| try pointer_to_got.resolve(self, source_addr, target_addr),
481 .signed => |signed| try signed.resolve(self, source_addr, target_addr),
482 .load => |load| try load.resolve(self, source_addr, target_addr),
391 .unsigned => |unsigned| try unsigned.resolve(args),
392 .branch => |branch| try branch.resolve(args),
393 .page => |page| try page.resolve(args),
394 .page_off => |page_off| try page_off.resolve(args),
395 .pointer_to_got => |pointer_to_got| try pointer_to_got.resolve(args),
396 .signed => |signed| try signed.resolve(args),
397 .load => |load| try load.resolve(args),
483398 }
484399 }
485400
486401 pub fn format(self: Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
487402 try std.fmt.format(writer, "Relocation {{ ", .{});
488403 try std.fmt.format(writer, ".offset = {}, ", .{self.offset});
489 try std.fmt.format(writer, ".block = {}", .{self.block.local_sym_index});
490404 try std.fmt.format(writer, ".target = {}, ", .{self.target});
491405
492406 switch (self.payload) {
......@@ -713,7 +627,6 @@ pub const Parser = struct {
713627 return Relocation{
714628 .offset = offset,
715629 .target = target,
716 .block = self.block,
717630 .payload = undefined,
718631 };
719632 }