Commit c47ce31071

Jakub Konka <kubkon@jakubkonka.com>
2021-07-15 18:25:10
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.
1 parent f8678c4
Changed files (2)
src
link
src/link/MachO/reloc.zig
@@ -20,9 +20,6 @@ pub const Relocation = struct {
     /// Note relocation size can be inferred by relocation's kind.
     offset: u32,
 
-    /// Parent block containing this relocation.
-    block: *TextBlock,
-
     /// Target symbol: either a regular or a proxy.
     target: *Symbol,
 
@@ -36,6 +33,13 @@ pub const Relocation = struct {
         load: Load,
     },
 
+    const ResolveArgs = struct {
+        block: *TextBlock,
+        offset: u32,
+        source_addr: u64,
+        target_addr: u64,
+    };
+
     pub const Unsigned = struct {
         subtractor: ?*Symbol = null,
 
@@ -48,16 +52,16 @@ pub const Relocation = struct {
         /// => * is unreachable
         is_64bit: bool,
 
-        pub fn resolve(self: Unsigned, base: Relocation, _: u64, target_addr: u64) !void {
+        pub fn resolve(self: Unsigned, args: ResolveArgs) !void {
             const result = if (self.subtractor) |subtractor|
-                @intCast(i64, target_addr) - @intCast(i64, subtractor.payload.regular.address) + self.addend
+                @intCast(i64, args.target_addr) - @intCast(i64, subtractor.payload.regular.address) + self.addend
             else
-                @intCast(i64, target_addr) + self.addend;
+                @intCast(i64, args.target_addr) + self.addend;
 
             if (self.is_64bit) {
-                mem.writeIntLittle(u64, base.block.code[base.offset..][0..8], @bitCast(u64, result));
+                mem.writeIntLittle(u64, args.block.code[args.offset..][0..8], @bitCast(u64, result));
             } else {
-                mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], @truncate(u32, @bitCast(u64, result)));
+                mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @truncate(u32, @bitCast(u64, result)));
             }
         }
 
@@ -78,25 +82,29 @@ pub const Relocation = struct {
     pub const Branch = struct {
         arch: Arch,
 
-        pub fn resolve(self: Branch, base: Relocation, source_addr: u64, target_addr: u64) !void {
+        pub fn resolve(self: Branch, args: ResolveArgs) !void {
             switch (self.arch) {
                 .aarch64 => {
-                    const displacement = try math.cast(i28, @intCast(i64, target_addr) - @intCast(i64, source_addr));
+                    const displacement = try math.cast(
+                        i28,
+                        @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr),
+                    );
+                    const code = args.block.code[args.offset..][0..4];
                     var inst = aarch64.Instruction{
-                        .unconditional_branch_immediate = mem.bytesToValue(
-                            meta.TagPayload(
-                                aarch64.Instruction,
-                                aarch64.Instruction.unconditional_branch_immediate,
-                            ),
-                            base.block.code[base.offset..][0..4],
-                        ),
+                        .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload(
+                            aarch64.Instruction,
+                            aarch64.Instruction.unconditional_branch_immediate,
+                        ), code),
                     };
                     inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2));
-                    mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], inst.toU32());
+                    mem.writeIntLittle(u32, code, inst.toU32());
                 },
                 .x86_64 => {
-                    const displacement = try math.cast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4);
-                    mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], @bitCast(u32, displacement));
+                    const displacement = try math.cast(
+                        i32,
+                        @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4,
+                    );
+                    mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @bitCast(u32, displacement));
                 },
                 else => return error.UnsupportedCpuArchitecture,
             }
@@ -118,25 +126,23 @@ pub const Relocation = struct {
         },
         addend: ?u32 = null,
 
-        pub fn resolve(self: Page, base: Relocation, source_addr: u64, target_addr: u64) !void {
-            const actual_target_addr = if (self.addend) |addend| target_addr + addend else target_addr;
-            const source_page = @intCast(i32, source_addr >> 12);
-            const target_page = @intCast(i32, actual_target_addr >> 12);
+        pub fn resolve(self: Page, args: ResolveArgs) !void {
+            const target_addr = if (self.addend) |addend| args.target_addr + addend else args.target_addr;
+            const source_page = @intCast(i32, args.source_addr >> 12);
+            const target_page = @intCast(i32, target_addr >> 12);
             const pages = @bitCast(u21, @intCast(i21, target_page - source_page));
 
+            const code = args.block.code[args.offset..][0..4];
             var inst = aarch64.Instruction{
-                .pc_relative_address = mem.bytesToValue(
-                    meta.TagPayload(
-                        aarch64.Instruction,
-                        aarch64.Instruction.pc_relative_address,
-                    ),
-                    base.block.code[base.offset..][0..4],
-                ),
+                .pc_relative_address = mem.bytesToValue(meta.TagPayload(
+                    aarch64.Instruction,
+                    aarch64.Instruction.pc_relative_address,
+                ), code),
             };
             inst.pc_relative_address.immhi = @truncate(u19, pages >> 2);
             inst.pc_relative_address.immlo = @truncate(u2, pages);
 
-            mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], inst.toU32());
+            mem.writeIntLittle(u32, code, inst.toU32());
         }
 
         pub fn format(self: Page, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
@@ -173,35 +179,31 @@ pub const Relocation = struct {
             load,
         };
 
-        pub fn resolve(self: PageOff, base: Relocation, _: u64, target_addr: u64) !void {
+        pub fn resolve(self: PageOff, args: ResolveArgs) !void {
+            const code = args.block.code[args.offset..][0..4];
+
             switch (self.kind) {
                 .page => {
-                    const actual_target_addr = if (self.addend) |addend| target_addr + addend else target_addr;
-                    const narrowed = @truncate(u12, actual_target_addr);
+                    const target_addr = if (self.addend) |addend| args.target_addr + addend else args.target_addr;
+                    const narrowed = @truncate(u12, target_addr);
 
                     const op_kind = self.op_kind orelse unreachable;
                     var inst: aarch64.Instruction = blk: {
                         switch (op_kind) {
                             .arithmetic => {
                                 break :blk .{
-                                    .add_subtract_immediate = mem.bytesToValue(
-                                        meta.TagPayload(
-                                            aarch64.Instruction,
-                                            aarch64.Instruction.add_subtract_immediate,
-                                        ),
-                                        base.block.code[base.offset..][0..4],
-                                    ),
+                                    .add_subtract_immediate = mem.bytesToValue(meta.TagPayload(
+                                        aarch64.Instruction,
+                                        aarch64.Instruction.add_subtract_immediate,
+                                    ), code),
                                 };
                             },
                             .load => {
                                 break :blk .{
-                                    .load_store_register = mem.bytesToValue(
-                                        meta.TagPayload(
-                                            aarch64.Instruction,
-                                            aarch64.Instruction.load_store_register,
-                                        ),
-                                        base.block.code[base.offset..][0..4],
-                                    ),
+                                    .load_store_register = mem.bytesToValue(meta.TagPayload(
+                                        aarch64.Instruction,
+                                        aarch64.Instruction.load_store_register,
+                                    ), code),
                                 };
                             },
                         }
@@ -226,22 +228,19 @@ pub const Relocation = struct {
                         inst.load_store_register.offset = offset;
                     }
 
-                    mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], inst.toU32());
+                    mem.writeIntLittle(u32, code, inst.toU32());
                 },
                 .got => {
-                    const narrowed = @truncate(u12, target_addr);
+                    const narrowed = @truncate(u12, args.target_addr);
                     var inst: aarch64.Instruction = .{
-                        .load_store_register = mem.bytesToValue(
-                            meta.TagPayload(
-                                aarch64.Instruction,
-                                aarch64.Instruction.load_store_register,
-                            ),
-                            base.block.code[base.offset..][0..4],
-                        ),
+                        .load_store_register = mem.bytesToValue(meta.TagPayload(
+                            aarch64.Instruction,
+                            aarch64.Instruction.load_store_register,
+                        ), code),
                     };
                     const offset = try math.divExact(u12, narrowed, 8);
                     inst.load_store_register.offset = offset;
-                    mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], inst.toU32());
+                    mem.writeIntLittle(u32, code, inst.toU32());
                 },
                 .tlvp => {
                     const RegInfo = struct {
@@ -250,27 +249,21 @@ pub const Relocation = struct {
                         size: u1,
                     };
                     const reg_info: RegInfo = blk: {
-                        if (isArithmeticOp(base.block.code[base.offset..][0..4])) {
-                            const inst = mem.bytesToValue(
-                                meta.TagPayload(
-                                    aarch64.Instruction,
-                                    aarch64.Instruction.add_subtract_immediate,
-                                ),
-                                base.block.code[base.offset..][0..4],
-                            );
+                        if (isArithmeticOp(code)) {
+                            const inst = mem.bytesToValue(meta.TagPayload(
+                                aarch64.Instruction,
+                                aarch64.Instruction.add_subtract_immediate,
+                            ), code);
                             break :blk .{
                                 .rd = inst.rd,
                                 .rn = inst.rn,
                                 .size = inst.sf,
                             };
                         } else {
-                            const inst = mem.bytesToValue(
-                                meta.TagPayload(
-                                    aarch64.Instruction,
-                                    aarch64.Instruction.load_store_register,
-                                ),
-                                base.block.code[base.offset..][0..4],
-                            );
+                            const inst = mem.bytesToValue(meta.TagPayload(
+                                aarch64.Instruction,
+                                aarch64.Instruction.load_store_register,
+                            ), code);
                             break :blk .{
                                 .rd = inst.rt,
                                 .rn = inst.rn,
@@ -278,7 +271,7 @@ pub const Relocation = struct {
                             };
                         }
                     };
-                    const narrowed = @truncate(u12, target_addr);
+                    const narrowed = @truncate(u12, args.target_addr);
                     var inst = aarch64.Instruction{
                         .add_subtract_immediate = .{
                             .rd = reg_info.rd,
@@ -290,7 +283,7 @@ pub const Relocation = struct {
                             .sf = reg_info.size,
                         },
                     };
-                    mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], inst.toU32());
+                    mem.writeIntLittle(u32, code, inst.toU32());
                 },
             }
         }
@@ -319,9 +312,9 @@ pub const Relocation = struct {
     };
 
     pub const PointerToGot = struct {
-        pub fn resolve(_: PointerToGot, base: Relocation, source_addr: u64, target_addr: u64) !void {
-            const result = try math.cast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr));
-            mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], @bitCast(u32, result));
+        pub fn resolve(_: PointerToGot, args: ResolveArgs) !void {
+            const result = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr));
+            mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @bitCast(u32, result));
         }
 
         pub fn format(self: PointerToGot, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
@@ -336,13 +329,13 @@ pub const Relocation = struct {
         addend: i64,
         correction: i4,
 
-        pub fn resolve(self: Signed, base: Relocation, source_addr: u64, target_addr: u64) !void {
-            const actual_target_addr = @intCast(i64, target_addr) + self.addend;
+        pub fn resolve(self: Signed, args: ResolveArgs) !void {
+            const target_addr = @intCast(i64, args.target_addr) + self.addend;
             const displacement = try math.cast(
                 i32,
-                actual_target_addr - @intCast(i64, source_addr) - self.correction - 4,
+                target_addr - @intCast(i64, args.source_addr) - self.correction - 4,
             );
-            mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], @bitCast(u32, displacement));
+            mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @bitCast(u32, displacement));
         }
 
         pub fn format(self: Signed, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
@@ -362,17 +355,17 @@ pub const Relocation = struct {
         },
         addend: ?i32 = null,
 
-        pub fn resolve(self: Load, base: Relocation, source_addr: u64, target_addr: u64) !void {
+        pub fn resolve(self: Load, args: ResolveArgs) !void {
             if (self.kind == .tlvp) {
                 // We need to rewrite the opcode from movq to leaq.
-                base.block.code[base.offset - 2] = 0x8d;
+                args.block.code[args.offset - 2] = 0x8d;
             }
             const addend = if (self.addend) |addend| addend else 0;
             const displacement = try math.cast(
                 i32,
-                @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4 + addend,
+                @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4 + addend,
             );
-            mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], @bitCast(u32, displacement));
+            mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @bitCast(u32, displacement));
         }
 
         pub fn format(self: Load, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
@@ -387,106 +380,27 @@ pub const Relocation = struct {
         }
     };
 
-    pub fn resolve(self: Relocation, zld: *Zld) !void {
-        log.debug("relocating {}", .{self});
-
-        const source_addr = blk: {
-            const sym = zld.locals.items[self.block.local_sym_index];
-            break :blk sym.payload.regular.address + self.offset;
-        };
-        const target_addr = blk: {
-            const is_via_got = switch (self.payload) {
-                .pointer_to_got => true,
-                .page => |page| page.kind == .got,
-                .page_off => |page_off| page_off.kind == .got,
-                .load => |load| load.kind == .got,
-                else => false,
-            };
-
-            if (is_via_got) {
-                const dc_seg = zld.load_commands.items[zld.data_const_segment_cmd_index.?].Segment;
-                const got = dc_seg.sections.items[zld.got_section_index.?];
-                const got_index = self.target.got_index orelse {
-                    log.err("expected GOT entry for symbol '{s}'", .{zld.getString(self.target.strx)});
-                    log.err("  this is an internal linker error", .{});
-                    return error.FailedToResolveRelocationTarget;
-                };
-                break :blk got.addr + got_index * @sizeOf(u64);
-            }
-
-            switch (self.target.payload) {
-                .regular => |reg| {
-                    const is_tlv = is_tlv: {
-                        const sym = zld.locals.items[self.block.local_sym_index];
-                        const seg = zld.load_commands.items[sym.payload.regular.segment_id].Segment;
-                        const sect = seg.sections.items[sym.payload.regular.section_id];
-                        break :is_tlv commands.sectionType(sect) == macho.S_THREAD_LOCAL_VARIABLES;
-                    };
-                    if (is_tlv) {
-                        // For TLV relocations, the value specified as a relocation is the displacement from the
-                        // TLV initializer (either value in __thread_data or zero-init in __thread_bss) to the first
-                        // defined TLV template init section in the following order:
-                        // * wrt to __thread_data if defined, then
-                        // * wrt to __thread_bss
-                        const seg = zld.load_commands.items[zld.data_segment_cmd_index.?].Segment;
-                        const base_address = inner: {
-                            if (zld.tlv_data_section_index) |i| {
-                                break :inner seg.sections.items[i].addr;
-                            } else if (zld.tlv_bss_section_index) |i| {
-                                break :inner seg.sections.items[i].addr;
-                            } else {
-                                log.err("threadlocal variables present but no initializer sections found", .{});
-                                log.err("  __thread_data not found", .{});
-                                log.err("  __thread_bss not found", .{});
-                                return error.FailedToResolveRelocationTarget;
-                            }
-                        };
-                        break :blk reg.address - base_address;
-                    }
-
-                    break :blk reg.address;
-                },
-                .proxy => {
-                    if (mem.eql(u8, zld.getString(self.target.strx), "__tlv_bootstrap")) {
-                        break :blk 0; // Dynamically bound by dyld.
-                    }
-
-                    const segment = zld.load_commands.items[zld.text_segment_cmd_index.?].Segment;
-                    const stubs = segment.sections.items[zld.stubs_section_index.?];
-                    const stubs_index = self.target.stubs_index orelse {
-                        // TODO verify in TextBlock that the symbol is indeed dynamically bound.
-                        break :blk 0; // Dynamically bound by dyld.
-                    };
-                    break :blk stubs.addr + stubs_index * stubs.reserved2;
-                },
-                else => {
-                    log.err("failed to resolve symbol '{s}' as a relocation target", .{
-                        zld.getString(self.target.strx),
-                    });
-                    log.err("  this is an internal linker error", .{});
-                    return error.FailedToResolveRelocationTarget;
-                },
-            }
+    pub fn resolve(self: Relocation, block: *TextBlock, source_addr: u64, target_addr: u64) !void {
+        const args = ResolveArgs{
+            .block = block,
+            .offset = self.offset,
+            .source_addr = source_addr,
+            .target_addr = target_addr,
         };
-
-        log.debug("  | source_addr = 0x{x}", .{source_addr});
-        log.debug("  | target_addr = 0x{x}", .{target_addr});
-
         switch (self.payload) {
-            .unsigned => |unsigned| try unsigned.resolve(self, source_addr, target_addr),
-            .branch => |branch| try branch.resolve(self, source_addr, target_addr),
-            .page => |page| try page.resolve(self, source_addr, target_addr),
-            .page_off => |page_off| try page_off.resolve(self, source_addr, target_addr),
-            .pointer_to_got => |pointer_to_got| try pointer_to_got.resolve(self, source_addr, target_addr),
-            .signed => |signed| try signed.resolve(self, source_addr, target_addr),
-            .load => |load| try load.resolve(self, source_addr, target_addr),
+            .unsigned => |unsigned| try unsigned.resolve(args),
+            .branch => |branch| try branch.resolve(args),
+            .page => |page| try page.resolve(args),
+            .page_off => |page_off| try page_off.resolve(args),
+            .pointer_to_got => |pointer_to_got| try pointer_to_got.resolve(args),
+            .signed => |signed| try signed.resolve(args),
+            .load => |load| try load.resolve(args),
         }
     }
 
     pub fn format(self: Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
         try std.fmt.format(writer, "Relocation {{ ", .{});
         try std.fmt.format(writer, ".offset = {}, ", .{self.offset});
-        try std.fmt.format(writer, ".block = {}", .{self.block.local_sym_index});
         try std.fmt.format(writer, ".target = {}, ", .{self.target});
 
         switch (self.payload) {
@@ -713,7 +627,6 @@ pub const Parser = struct {
         return Relocation{
             .offset = offset,
             .target = target,
-            .block = self.block,
             .payload = undefined,
         };
     }
src/link/MachO/Zld.zig
@@ -246,7 +246,91 @@ pub const TextBlock = struct {
 
     pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
         for (self.relocs.items) |rel| {
-            try rel.resolve(zld);
+            log.debug("relocating {}", .{rel});
+
+            const source_addr = blk: {
+                const sym = zld.locals.items[self.local_sym_index];
+                break :blk sym.payload.regular.address + rel.offset;
+            };
+            const target_addr = blk: {
+                const is_via_got = switch (rel.payload) {
+                    .pointer_to_got => true,
+                    .page => |page| page.kind == .got,
+                    .page_off => |page_off| page_off.kind == .got,
+                    .load => |load| load.kind == .got,
+                    else => false,
+                };
+
+                if (is_via_got) {
+                    const dc_seg = zld.load_commands.items[zld.data_const_segment_cmd_index.?].Segment;
+                    const got = dc_seg.sections.items[zld.got_section_index.?];
+                    const got_index = rel.target.got_index orelse {
+                        log.err("expected GOT entry for symbol '{s}'", .{zld.getString(rel.target.strx)});
+                        log.err("  this is an internal linker error", .{});
+                        return error.FailedToResolveRelocationTarget;
+                    };
+                    break :blk got.addr + got_index * @sizeOf(u64);
+                }
+
+                switch (rel.target.payload) {
+                    .regular => |reg| {
+                        const is_tlv = is_tlv: {
+                            const sym = zld.locals.items[self.local_sym_index];
+                            const seg = zld.load_commands.items[sym.payload.regular.segment_id].Segment;
+                            const sect = seg.sections.items[sym.payload.regular.section_id];
+                            break :is_tlv sectionType(sect) == macho.S_THREAD_LOCAL_VARIABLES;
+                        };
+                        if (is_tlv) {
+                            // For TLV relocations, the value specified as a relocation is the displacement from the
+                            // TLV initializer (either value in __thread_data or zero-init in __thread_bss) to the first
+                            // defined TLV template init section in the following order:
+                            // * wrt to __thread_data if defined, then
+                            // * wrt to __thread_bss
+                            const seg = zld.load_commands.items[zld.data_segment_cmd_index.?].Segment;
+                            const base_address = inner: {
+                                if (zld.tlv_data_section_index) |i| {
+                                    break :inner seg.sections.items[i].addr;
+                                } else if (zld.tlv_bss_section_index) |i| {
+                                    break :inner seg.sections.items[i].addr;
+                                } else {
+                                    log.err("threadlocal variables present but no initializer sections found", .{});
+                                    log.err("  __thread_data not found", .{});
+                                    log.err("  __thread_bss not found", .{});
+                                    return error.FailedToResolveRelocationTarget;
+                                }
+                            };
+                            break :blk reg.address - base_address;
+                        }
+
+                        break :blk reg.address;
+                    },
+                    .proxy => {
+                        if (mem.eql(u8, zld.getString(rel.target.strx), "__tlv_bootstrap")) {
+                            break :blk 0; // Dynamically bound by dyld.
+                        }
+
+                        const segment = zld.load_commands.items[zld.text_segment_cmd_index.?].Segment;
+                        const stubs = segment.sections.items[zld.stubs_section_index.?];
+                        const stubs_index = rel.target.stubs_index orelse {
+                            // TODO verify in TextBlock that the symbol is indeed dynamically bound.
+                            break :blk 0; // Dynamically bound by dyld.
+                        };
+                        break :blk stubs.addr + stubs_index * stubs.reserved2;
+                    },
+                    else => {
+                        log.err("failed to resolve symbol '{s}' as a relocation target", .{
+                            zld.getString(rel.target.strx),
+                        });
+                        log.err("  this is an internal linker error", .{});
+                        return error.FailedToResolveRelocationTarget;
+                    },
+                }
+            };
+
+            log.debug("  | source_addr = 0x{x}", .{source_addr});
+            log.debug("  | target_addr = 0x{x}", .{target_addr});
+
+            try rel.resolve(self, source_addr, target_addr);
         }
     }