authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-02-27 01:03:36+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-03-17 19:59:13+01:00
logd2008db6235f2544a1aa3dbd419db911bc858cbd
tree62d7b7dab965f67c68a5a0460862cc26f3b41e37
parent14590795b18585582c79d77afa65ce0cb9bf4744

zld: bullet-proof stubs for long jumps


1 files changed, 95 insertions(+), 44 deletions(-)

src/link/MachO/Zld.zig+95-44
...@@ -252,9 +252,9 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void {...@@ -252,9 +252,9 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void {
252 try self.populateMetadata();252 try self.populateMetadata();
253 try self.parseInputFiles(files);253 try self.parseInputFiles(files);
254 try self.resolveImports();254 try self.resolveImports();
255 try self.allocateTextSegment();255 self.allocateTextSegment();
256 try self.allocateDataSegment();256 self.allocateDataSegment();
257 try self.allocateLinkeditSegment();257 self.allocateLinkeditSegment();
258 try self.writeStubHelperCommon();258 try self.writeStubHelperCommon();
259 try self.resolveSymbols();259 try self.resolveSymbols();
260 try self.doRelocs();260 try self.doRelocs();
...@@ -317,20 +317,13 @@ fn parseObjectFile(self: *Zld, object: *const Object) !void {...@@ -317,20 +317,13 @@ fn parseObjectFile(self: *Zld, object: *const Object) !void {
317 if (mem.eql(u8, sectname, "__thread_vars")) {317 if (mem.eql(u8, sectname, "__thread_vars")) {
318 self.tlv_section_index = sect_index;318 self.tlv_section_index = sect_index;
319 }319 }
320 log.warn("{s} align 0x{x}", .{ sectname, sect.@"align" });
321 const alignment = switch (sect.flags) {
322 macho.S_4BYTE_LITERALS => 2,
323 macho.S_8BYTE_LITERALS => 3,
324 macho.S_16BYTE_LITERALS => 4,
325 else => sect.@"align",
326 };
327 try seg.append(self.allocator, .{320 try seg.append(self.allocator, .{
328 .sectname = makeStaticString(&sect.sectname),321 .sectname = makeStaticString(&sect.sectname),
329 .segname = makeStaticString(&sect.segname),322 .segname = makeStaticString(&sect.segname),
330 .addr = 0,323 .addr = 0,
331 .size = 0,324 .size = 0,
332 .offset = 0,325 .offset = 0,
333 .@"align" = alignment,326 .@"align" = sect.@"align",
334 .reloff = 0,327 .reloff = 0,
335 .nreloc = 0,328 .nreloc = 0,
336 .flags = sect.flags,329 .flags = sect.flags,
...@@ -436,7 +429,7 @@ fn resolveImports(self: *Zld) !void {...@@ -436,7 +429,7 @@ fn resolveImports(self: *Zld) !void {
436 });429 });
437}430}
438431
439fn allocateTextSegment(self: *Zld) !void {432fn allocateTextSegment(self: *Zld) void {
440 const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;433 const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
441 const nexterns = @intCast(u32, self.lazy_imports.items().len);434 const nexterns = @intCast(u32, self.lazy_imports.items().len);
442435
...@@ -457,7 +450,7 @@ fn allocateTextSegment(self: *Zld) !void {...@@ -457,7 +450,7 @@ fn allocateTextSegment(self: *Zld) !void {
457 sizeofcmds += lc.cmdsize();450 sizeofcmds += lc.cmdsize();
458 }451 }
459452
460 try self.allocateSegment(453 self.allocateSegment(
461 self.text_segment_cmd_index.?,454 self.text_segment_cmd_index.?,
462 0,455 0,
463 sizeofcmds,456 sizeofcmds,
...@@ -465,7 +458,7 @@ fn allocateTextSegment(self: *Zld) !void {...@@ -465,7 +458,7 @@ fn allocateTextSegment(self: *Zld) !void {
465 );458 );
466}459}
467460
468fn allocateDataSegment(self: *Zld) !void {461fn allocateDataSegment(self: *Zld) void {
469 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;462 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
470 const nonlazy = @intCast(u32, self.nonlazy_imports.items().len);463 const nonlazy = @intCast(u32, self.nonlazy_imports.items().len);
471 const lazy = @intCast(u32, self.lazy_imports.items().len);464 const lazy = @intCast(u32, self.lazy_imports.items().len);
...@@ -482,16 +475,16 @@ fn allocateDataSegment(self: *Zld) !void {...@@ -482,16 +475,16 @@ fn allocateDataSegment(self: *Zld) !void {
482475
483 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;476 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
484 const offset = text_seg.inner.fileoff + text_seg.inner.filesize;477 const offset = text_seg.inner.fileoff + text_seg.inner.filesize;
485 try self.allocateSegment(self.data_segment_cmd_index.?, offset, 0, false);478 self.allocateSegment(self.data_segment_cmd_index.?, offset, 0, false);
486}479}
487480
488fn allocateLinkeditSegment(self: *Zld) !void {481fn allocateLinkeditSegment(self: *Zld) void {
489 const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;482 const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
490 const offset = data_seg.inner.fileoff + data_seg.inner.filesize;483 const offset = data_seg.inner.fileoff + data_seg.inner.filesize;
491 try self.allocateSegment(self.linkedit_segment_cmd_index.?, offset, 0, false);484 self.allocateSegment(self.linkedit_segment_cmd_index.?, offset, 0, false);
492}485}
493486
494fn allocateSegment(self: *Zld, index: u16, offset: u64, start: u64, reverse: bool) !void {487fn allocateSegment(self: *Zld, index: u16, offset: u64, start: u64, reverse: bool) void {
495 const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].Segment.inner.vmsize;488 const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].Segment.inner.vmsize;
496 const seg = &self.load_commands.items[index].Segment;489 const seg = &self.load_commands.items[index].Segment;
497490
...@@ -565,39 +558,72 @@ fn writeStubHelperCommon(self: *Zld) !void {...@@ -565,39 +558,72 @@ fn writeStubHelperCommon(self: *Zld) !void {
565 },558 },
566 .aarch64 => {559 .aarch64 => {
567 var code: [6 * @sizeOf(u32)]u8 = undefined;560 var code: [6 * @sizeOf(u32)]u8 = undefined;
568 {561 data_blk_outer: {
562 const this_addr = stub_helper.addr;
569 const target_addr = data.addr + data.size - @sizeOf(u64);563 const target_addr = data.addr + data.size - @sizeOf(u64);
570 const displacement = @bitCast(u21, try math.cast(i21, target_addr - stub_helper.addr));564 data_blk: {
571 // adr x17, disp565 const displacement = math.cast(i21, target_addr - this_addr) catch |_| break :data_blk;
572 mem.writeIntLittle(u32, code[0..4], Arm64.adr(17, displacement).toU32());566 // adr x17, disp
573 // TODO check if adr is enough and expand into adrp + add if not.567 mem.writeIntLittle(u32, code[0..4], Arm64.adr(17, @bitCast(u21, displacement)).toU32());
574 // nop in case we need to expand adr for adrp followed by add.568 // nop
575 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32());569 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32());
570 break :data_blk_outer;
571 }
572 data_blk: {
573 const new_this_addr = this_addr + @sizeOf(u32);
574 const displacement = math.cast(i21, target_addr - new_this_addr) catch |_| break :data_blk;
575 // nop
576 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32());
577 // adr x17, disp
578 mem.writeIntLittle(u32, code[4..8], Arm64.adr(17, @bitCast(u21, displacement)).toU32());
579 break :data_blk_outer;
580 }
581 // Jump is too big, replace adr with adrp and add.
582 const this_page = @intCast(i32, this_addr >> 12);
583 const target_page = @intCast(i32, target_addr >> 12);
584 const pages = @bitCast(u21, @intCast(i21, target_page - this_page));
585 mem.writeIntLittle(u32, code[0..4], Arm64.adrp(17, pages).toU32());
586 const narrowed = @truncate(u12, target_addr);
587 mem.writeIntLittle(u32, code[4..8], Arm64.add(17, 17, narrowed, 1).toU32());
576 }588 }
577 // stp x16, x17, [sp, #-16]!589 // stp x16, x17, [sp, #-16]!
578 code[8] = 0xf0;590 code[8] = 0xf0;
579 code[9] = 0x47;591 code[9] = 0x47;
580 code[10] = 0xbf;592 code[10] = 0xbf;
581 code[11] = 0xa9;593 code[11] = 0xa9;
582 binder: {594 binder_blk_outer: {
583 const dyld_stub_binder = self.nonlazy_imports.get("dyld_stub_binder").?;595 const dyld_stub_binder = self.nonlazy_imports.get("dyld_stub_binder").?;
584 const addr = (got.addr + dyld_stub_binder.index * @sizeOf(u64));596 const this_addr = stub_helper.addr + 3 * @sizeOf(u32);
585 const displacement = math.divExact(u64, addr - stub_helper.addr - 3 * @sizeOf(u32), 4) catch |_| {597 const target_addr = (got.addr + dyld_stub_binder.index * @sizeOf(u64));
586 log.warn("0x{x}", .{addr - stub_helper.addr - 3 * @sizeOf(u32)});598 binder_blk: {
599 const displacement = math.divExact(u64, target_addr - this_addr, 4) catch |_| break :binder_blk;
600 const literal = math.cast(u18, displacement) catch |_| break :binder_blk;
601 // ldr x16, label
602 mem.writeIntLittle(u32, code[12..16], Arm64.ldr(16, literal, 1).toU32());
603 // nop
604 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.nop().toU32());
605 break :binder_blk_outer;
606 }
607 binder_blk: {
608 const new_this_addr = this_addr + @sizeOf(u32);
609 const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch |_| break :binder_blk;
610 const literal = math.cast(u18, displacement) catch |_| break :binder_blk;
611 log.warn("2: disp=0x{x}, literal=0x{x}", .{ displacement, literal });
587 // Pad with nop to please division.612 // Pad with nop to please division.
588 // nop613 // nop
589 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.nop().toU32());614 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.nop().toU32());
590 // ldr x16, label615 // ldr x16, label
591 const disp = try math.divExact(u64, addr - stub_helper.addr - 4 * @sizeOf(u32), 4);
592 const literal = try math.cast(u19, disp); // TODO use adrp + add if we exceed the range.
593 mem.writeIntLittle(u32, code[16..20], Arm64.ldr(16, literal, 1).toU32());616 mem.writeIntLittle(u32, code[16..20], Arm64.ldr(16, literal, 1).toU32());
594 break :binder;617 break :binder_blk_outer;
595 };618 }
596 const literal = try math.cast(u19, displacement); // TODO use adrp + add if we exceed the range.619 // Use adrp followed by ldr(immediate).
597 // ldr x16, label620 const this_page = @intCast(i32, this_addr >> 12);
598 mem.writeIntLittle(u32, code[12..16], Arm64.ldr(16, literal, 1).toU32());621 const target_page = @intCast(i32, target_addr >> 12);
599 // nop622 const pages = @bitCast(u21, @intCast(i21, target_page - this_page));
600 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.nop().toU32());623 mem.writeIntLittle(u32, code[12..16], Arm64.adrp(16, pages).toU32());
624 const narrowed = @truncate(u12, target_addr);
625 const offset = try math.divExact(u12, narrowed, 8);
626 mem.writeIntLittle(u32, code[16..20], Arm64.ldrq(16, 16, offset).toU32());
601 }627 }
602 // br x16628 // br x16
603 code[20] = 0x00;629 code[20] = 0x00;
...@@ -662,12 +688,37 @@ fn writeStub(self: *Zld, index: u32) !void {...@@ -662,12 +688,37 @@ fn writeStub(self: *Zld, index: u32) !void {
662 },688 },
663 .aarch64 => {689 .aarch64 => {
664 assert(la_ptr_addr >= stub_addr);690 assert(la_ptr_addr >= stub_addr);
665 const displacement = try math.divExact(u64, la_ptr_addr - stub_addr - @sizeOf(u32), 4);691 outer: {
666 const literal = try math.cast(u19, displacement);692 const this_addr = stub_addr;
667 // nop693 const target_addr = la_ptr_addr;
668 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32());694 inner: {
669 // ldr x16, literal695 const displacement = math.divExact(u64, target_addr - this_addr, 4) catch |_| break :inner;
670 mem.writeIntLittle(u32, code[4..8], Arm64.ldr(16, literal, 1).toU32());696 const literal = math.cast(u18, displacement) catch |_| break :inner;
697 // ldr x16, literal
698 mem.writeIntLittle(u32, code[0..4], Arm64.ldr(16, literal, 1).toU32());
699 // nop
700 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32());
701 break :outer;
702 }
703 inner: {
704 const new_this_addr = this_addr + @sizeOf(u32);
705 const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch |_| break :inner;
706 const literal = math.cast(u18, displacement) catch |_| break :inner;
707 // nop
708 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32());
709 // ldr x16, literal
710 mem.writeIntLittle(u32, code[4..8], Arm64.ldr(16, literal, 1).toU32());
711 break :outer;
712 }
713 // Use adrp followed by ldr(immediate).
714 const this_page = @intCast(i32, this_addr >> 12);
715 const target_page = @intCast(i32, target_addr >> 12);
716 const pages = @bitCast(u21, @intCast(i21, target_page - this_page));
717 mem.writeIntLittle(u32, code[0..4], Arm64.adrp(16, pages).toU32());
718 const narrowed = @truncate(u12, target_addr);
719 const offset = try math.divExact(u12, narrowed, 8);
720 mem.writeIntLittle(u32, code[4..8], Arm64.ldrq(16, 16, offset).toU32());
721 }
671 // br x16722 // br x16
672 mem.writeIntLittle(u32, code[8..12], Arm64.br(16).toU32());723 mem.writeIntLittle(u32, code[8..12], Arm64.br(16).toU32());
673 },724 },