authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-22 14:05:12+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-22 14:05:12+02:00
logca90efe88e3b354884a82d341936e5a0724d74c8
tree274523502a8d255c9648b066d5162779b0705f49
parentdef135918740846d9b206c3188563cb77333f3a9

macho: fix memory leaks when emptying TextBlocks

This happens on every call to `TextBlock.empty` by the `Module`.

3 files changed, 42 insertions(+), 29 deletions(-)

src/link/MachO.zig+22-9
...@@ -1828,7 +1828,7 @@ fn writeTextBlocks(self: *MachO) !void {...@@ -1828,7 +1828,7 @@ fn writeTextBlocks(self: *MachO) !void {
1828 });1828 });
18291829
1830 try block.resolveRelocs(self);1830 try block.resolveRelocs(self);
1831 mem.copy(u8, code[aligned_base_off..][0..block.size], block.code);1831 mem.copy(u8, code[aligned_base_off..][0..block.size], block.code.items);
18321832
1833 // TODO NOP for machine code instead of just zeroing out1833 // TODO NOP for machine code instead of just zeroing out
1834 const padding_len = aligned_base_off - base_off;1834 const padding_len = aligned_base_off - base_off;
...@@ -2262,6 +2262,7 @@ fn resolveSymbols(self: *MachO) !void {...@@ -2262,6 +2262,7 @@ fn resolveSymbols(self: *MachO) !void {
22622262
2263 const size = sym.n_value;2263 const size = sym.n_value;
2264 const code = try self.base.allocator.alloc(u8, size);2264 const code = try self.base.allocator.alloc(u8, size);
2265 defer self.base.allocator.free(code);
2265 mem.set(u8, code, 0);2266 mem.set(u8, code, 0);
2266 const alignment = (sym.n_desc >> 8) & 0x0f;2267 const alignment = (sym.n_desc >> 8) & 0x0f;
22672268
...@@ -2287,11 +2288,12 @@ fn resolveSymbols(self: *MachO) !void {...@@ -2287,11 +2288,12 @@ fn resolveSymbols(self: *MachO) !void {
2287 const block = try self.base.allocator.create(TextBlock);2288 const block = try self.base.allocator.create(TextBlock);
2288 block.* = TextBlock.empty;2289 block.* = TextBlock.empty;
2289 block.local_sym_index = local_sym_index;2290 block.local_sym_index = local_sym_index;
2290 block.code = code;
2291 block.size = size;2291 block.size = size;
2292 block.alignment = alignment;2292 block.alignment = alignment;
2293 try self.managed_blocks.append(self.base.allocator, block);2293 try self.managed_blocks.append(self.base.allocator, block);
22942294
2295 try block.code.appendSlice(self.base.allocator, code);
2296
2295 // Update target section's metadata2297 // Update target section's metadata
2296 // TODO should we update segment's size here too?2298 // TODO should we update segment's size here too?
2297 // How does it tie with incremental space allocs?2299 // How does it tie with incremental space allocs?
...@@ -2428,7 +2430,6 @@ fn resolveSymbols(self: *MachO) !void {...@@ -2428,7 +2430,6 @@ fn resolveSymbols(self: *MachO) !void {
2428 const block = try self.base.allocator.create(TextBlock);2430 const block = try self.base.allocator.create(TextBlock);
2429 block.* = TextBlock.empty;2431 block.* = TextBlock.empty;
2430 block.local_sym_index = local_sym_index;2432 block.local_sym_index = local_sym_index;
2431 block.code = try self.base.allocator.alloc(u8, 0);
2432 block.size = 0;2433 block.size = 0;
2433 block.alignment = 0;2434 block.alignment = 0;
2434 try self.managed_blocks.append(self.base.allocator, block);2435 try self.managed_blocks.append(self.base.allocator, block);
...@@ -3527,7 +3528,13 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv...@@ -3527,7 +3528,13 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
3527 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);3528 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);
3528 switch (res) {3529 switch (res) {
3529 .appended => {3530 .appended => {
3530 decl.link.macho.code = code_buffer.toOwnedSlice();3531 // TODO clearing the code and relocs buffer should probably be orchestrated
3532 // in a different, smarter, more automatic way somewhere else, in a more centralised
3533 // way than this.
3534 // If we don't clear the buffers here, we are up for some nasty surprises when
3535 // this TextBlock is reused later on and was not freed by freeTextBlock().
3536 decl.link.macho.code.clearAndFree(self.base.allocator);
3537 try decl.link.macho.code.appendSlice(self.base.allocator, code_buffer.items);
3531 },3538 },
3532 .fail => |em| {3539 .fail => |em| {
3533 decl.analysis = .codegen_failure;3540 decl.analysis = .codegen_failure;
...@@ -3536,9 +3543,9 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv...@@ -3536,9 +3543,9 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
3536 },3543 },
3537 }3544 }
35383545
3539 const symbol = try self.placeDecl(decl, decl.link.macho.code.len);3546 const symbol = try self.placeDecl(decl, decl.link.macho.code.items.len);
35403547
3541 try self.writeCode(symbol, decl.link.macho.code);3548 try self.writeCode(symbol, decl.link.macho.code.items);
35423549
3543 if (debug_buffers) |db| {3550 if (debug_buffers) |db| {
3544 try self.d_sym.?.commitDeclDebugInfo(3551 try self.d_sym.?.commitDeclDebugInfo(
...@@ -3613,8 +3620,14 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -3613,8 +3620,14 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
3613 switch (res) {3620 switch (res) {
3614 .externally_managed => |x| break :blk x,3621 .externally_managed => |x| break :blk x,
3615 .appended => {3622 .appended => {
3616 decl.link.macho.code = code_buffer.toOwnedSlice();3623 // TODO clearing the code and relocs buffer should probably be orchestrated
3617 break :blk decl.link.macho.code;3624 // in a different, smarter, more automatic way somewhere else, in a more centralised
3625 // way than this.
3626 // If we don't clear the buffers here, we are up for some nasty surprises when
3627 // this TextBlock is reused later on and was not freed by freeTextBlock().
3628 decl.link.macho.code.clearAndFree(self.base.allocator);
3629 try decl.link.macho.code.appendSlice(self.base.allocator, code_buffer.items);
3630 break :blk decl.link.macho.code.items;
3618 },3631 },
3619 .fail => |em| {3632 .fail => |em| {
3620 decl.analysis = .codegen_failure;3633 decl.analysis = .codegen_failure;
...@@ -3705,7 +3718,7 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64...@@ -3705,7 +3718,7 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64
3705 try decl.link.macho.resolveRelocs(self);3718 try decl.link.macho.resolveRelocs(self);
3706 // TODO this requires further investigation: should we dispose of resolved relocs, or keep them3719 // TODO this requires further investigation: should we dispose of resolved relocs, or keep them
3707 // so that we can reapply them when moving/growing sections?3720 // so that we can reapply them when moving/growing sections?
3708 decl.link.macho.relocs.clearRetainingCapacity();3721 decl.link.macho.relocs.clearAndFree(self.base.allocator);
37093722
3710 // Apply pending updates3723 // Apply pending updates
3711 while (self.pending_updates.popOrNull()) |update| {3724 while (self.pending_updates.popOrNull()) |update| {
src/link/MachO/Object.zig+2-1
...@@ -726,11 +726,12 @@ pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void {...@@ -726,11 +726,12 @@ pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void {
726 const block = try macho_file.base.allocator.create(TextBlock);726 const block = try macho_file.base.allocator.create(TextBlock);
727 block.* = TextBlock.empty;727 block.* = TextBlock.empty;
728 block.local_sym_index = block_local_sym_index;728 block.local_sym_index = block_local_sym_index;
729 block.code = try self.allocator.dupe(u8, code);
730 block.size = sect.size;729 block.size = sect.size;
731 block.alignment = sect.@"align";730 block.alignment = sect.@"align";
732 try macho_file.managed_blocks.append(macho_file.base.allocator, block);731 try macho_file.managed_blocks.append(macho_file.base.allocator, block);
733732
733 try block.code.appendSlice(macho_file.base.allocator, code);
734
734 try block.parseRelocsFromObject(self.allocator, relocs, self, .{735 try block.parseRelocsFromObject(self.allocator, relocs, self, .{
735 .base_addr = 0,736 .base_addr = 0,
736 .macho_file = macho_file,737 .macho_file = macho_file,
src/link/MachO/TextBlock.zig+18-19
...@@ -30,7 +30,7 @@ aliases: std.ArrayListUnmanaged(u32) = .{},...@@ -30,7 +30,7 @@ aliases: std.ArrayListUnmanaged(u32) = .{},
30contained: std.ArrayListUnmanaged(SymbolAtOffset) = .{},30contained: std.ArrayListUnmanaged(SymbolAtOffset) = .{},
3131
32/// Code (may be non-relocated) this block represents32/// Code (may be non-relocated) this block represents
33code: []u8,33code: std.ArrayListUnmanaged(u8) = .{},
3434
35/// Size and alignment of this text block35/// Size and alignment of this text block
36/// Unlike in Elf, we need to store the size of this symbol as part of36/// Unlike in Elf, we need to store the size of this symbol as part of
...@@ -196,9 +196,9 @@ pub const Relocation = struct {...@@ -196,9 +196,9 @@ pub const Relocation = struct {
196 };196 };
197197
198 if (self.is_64bit) {198 if (self.is_64bit) {
199 mem.writeIntLittle(u64, args.block.code[args.offset..][0..8], @bitCast(u64, result));199 mem.writeIntLittle(u64, args.block.code.items[args.offset..][0..8], @bitCast(u64, result));
200 } else {200 } else {
201 mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @truncate(u32, @bitCast(u64, result)));201 mem.writeIntLittle(u32, args.block.code.items[args.offset..][0..4], @truncate(u32, @bitCast(u64, result)));
202 }202 }
203 }203 }
204204
...@@ -226,7 +226,7 @@ pub const Relocation = struct {...@@ -226,7 +226,7 @@ pub const Relocation = struct {
226 i28,226 i28,
227 @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr),227 @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr),
228 );228 );
229 const code = args.block.code[args.offset..][0..4];229 const code = args.block.code.items[args.offset..][0..4];
230 var inst = aarch64.Instruction{230 var inst = aarch64.Instruction{
231 .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload(231 .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload(
232 aarch64.Instruction,232 aarch64.Instruction,
...@@ -241,7 +241,7 @@ pub const Relocation = struct {...@@ -241,7 +241,7 @@ pub const Relocation = struct {
241 i32,241 i32,
242 @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4,242 @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4,
243 );243 );
244 mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @bitCast(u32, displacement));244 mem.writeIntLittle(u32, args.block.code.items[args.offset..][0..4], @bitCast(u32, displacement));
245 },245 },
246 else => return error.UnsupportedCpuArchitecture,246 else => return error.UnsupportedCpuArchitecture,
247 }247 }
...@@ -269,7 +269,7 @@ pub const Relocation = struct {...@@ -269,7 +269,7 @@ pub const Relocation = struct {
269 const target_page = @intCast(i32, target_addr >> 12);269 const target_page = @intCast(i32, target_addr >> 12);
270 const pages = @bitCast(u21, @intCast(i21, target_page - source_page));270 const pages = @bitCast(u21, @intCast(i21, target_page - source_page));
271271
272 const code = args.block.code[args.offset..][0..4];272 const code = args.block.code.items[args.offset..][0..4];
273 var inst = aarch64.Instruction{273 var inst = aarch64.Instruction{
274 .pc_relative_address = mem.bytesToValue(meta.TagPayload(274 .pc_relative_address = mem.bytesToValue(meta.TagPayload(
275 aarch64.Instruction,275 aarch64.Instruction,
...@@ -315,7 +315,7 @@ pub const Relocation = struct {...@@ -315,7 +315,7 @@ pub const Relocation = struct {
315 };315 };
316316
317 pub fn resolve(self: PageOff, args: ResolveArgs) !void {317 pub fn resolve(self: PageOff, args: ResolveArgs) !void {
318 const code = args.block.code[args.offset..][0..4];318 const code = args.block.code.items[args.offset..][0..4];
319319
320 switch (self.kind) {320 switch (self.kind) {
321 .page => {321 .page => {
...@@ -445,7 +445,7 @@ pub const Relocation = struct {...@@ -445,7 +445,7 @@ pub const Relocation = struct {
445 pub const PointerToGot = struct {445 pub const PointerToGot = struct {
446 pub fn resolve(_: PointerToGot, args: ResolveArgs) !void {446 pub fn resolve(_: PointerToGot, args: ResolveArgs) !void {
447 const result = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr));447 const result = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr));
448 mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @bitCast(u32, result));448 mem.writeIntLittle(u32, args.block.code.items[args.offset..][0..4], @bitCast(u32, result));
449 }449 }
450450
451 pub fn format(self: PointerToGot, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {451 pub fn format(self: PointerToGot, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
...@@ -466,7 +466,7 @@ pub const Relocation = struct {...@@ -466,7 +466,7 @@ pub const Relocation = struct {
466 i32,466 i32,
467 target_addr - @intCast(i64, args.source_addr) - self.correction - 4,467 target_addr - @intCast(i64, args.source_addr) - self.correction - 4,
468 );468 );
469 mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @bitCast(u32, displacement));469 mem.writeIntLittle(u32, args.block.code.items[args.offset..][0..4], @bitCast(u32, displacement));
470 }470 }
471471
472 pub fn format(self: Signed, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {472 pub fn format(self: Signed, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
...@@ -489,13 +489,13 @@ pub const Relocation = struct {...@@ -489,13 +489,13 @@ pub const Relocation = struct {
489 pub fn resolve(self: Load, args: ResolveArgs) !void {489 pub fn resolve(self: Load, args: ResolveArgs) !void {
490 if (self.kind == .tlvp) {490 if (self.kind == .tlvp) {
491 // We need to rewrite the opcode from movq to leaq.491 // We need to rewrite the opcode from movq to leaq.
492 args.block.code[args.offset - 2] = 0x8d;492 args.block.code.items[args.offset - 2] = 0x8d;
493 }493 }
494 const displacement = try math.cast(494 const displacement = try math.cast(
495 i32,495 i32,
496 @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4 + self.addend,496 @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4 + self.addend,
497 );497 );
498 mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @bitCast(u32, displacement));498 mem.writeIntLittle(u32, args.block.code.items[args.offset..][0..4], @bitCast(u32, displacement));
499 }499 }
500500
501 pub fn format(self: Load, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {501 pub fn format(self: Load, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
...@@ -542,7 +542,6 @@ pub const Relocation = struct {...@@ -542,7 +542,6 @@ pub const Relocation = struct {
542542
543pub const empty = TextBlock{543pub const empty = TextBlock{
544 .local_sym_index = 0,544 .local_sym_index = 0,
545 .code = undefined,
546 .size = 0,545 .size = 0,
547 .alignment = 0,546 .alignment = 0,
548 .prev = null,547 .prev = null,
...@@ -560,7 +559,7 @@ pub fn deinit(self: *TextBlock, allocator: *Allocator) void {...@@ -560,7 +559,7 @@ pub fn deinit(self: *TextBlock, allocator: *Allocator) void {
560 self.relocs.deinit(allocator);559 self.relocs.deinit(allocator);
561 self.contained.deinit(allocator);560 self.contained.deinit(allocator);
562 self.aliases.deinit(allocator);561 self.aliases.deinit(allocator);
563 allocator.free(self.code);562 self.code.deinit(allocator);
564}563}
565564
566/// Returns how much room there is to grow in virtual address space.565/// Returns how much room there is to grow in virtual address space.
...@@ -914,9 +913,9 @@ fn parseUnsigned(...@@ -914,9 +913,9 @@ fn parseUnsigned(
914 };913 };
915914
916 var addend: i64 = if (is_64bit)915 var addend: i64 = if (is_64bit)
917 mem.readIntLittle(i64, self.code[out.offset..][0..8])916 mem.readIntLittle(i64, self.code.items[out.offset..][0..8])
918 else917 else
919 mem.readIntLittle(i32, self.code[out.offset..][0..4]);918 mem.readIntLittle(i32, self.code.items[out.offset..][0..4]);
920919
921 if (rel.r_extern == 0) {920 if (rel.r_extern == 0) {
922 assert(out.where == .local);921 assert(out.where == .local);
...@@ -970,7 +969,7 @@ fn parsePageOff(self: TextBlock, rel: macho.relocation_info, out: *Relocation, a...@@ -970,7 +969,7 @@ fn parsePageOff(self: TextBlock, rel: macho.relocation_info, out: *Relocation, a
970 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);969 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
971 const op_kind: ?Relocation.PageOff.OpKind = blk: {970 const op_kind: ?Relocation.PageOff.OpKind = blk: {
972 if (rel_type != .ARM64_RELOC_PAGEOFF12) break :blk null;971 if (rel_type != .ARM64_RELOC_PAGEOFF12) break :blk null;
973 const op_kind: Relocation.PageOff.OpKind = if (isArithmeticOp(self.code[out.offset..][0..4]))972 const op_kind: Relocation.PageOff.OpKind = if (isArithmeticOp(self.code.items[out.offset..][0..4]))
974 .arithmetic973 .arithmetic
975 else974 else
976 .load;975 .load;
...@@ -1013,7 +1012,7 @@ fn parseSigned(self: TextBlock, rel: macho.relocation_info, out: *Relocation, ct...@@ -1013,7 +1012,7 @@ fn parseSigned(self: TextBlock, rel: macho.relocation_info, out: *Relocation, ct
1013 .X86_64_RELOC_SIGNED_4 => 4,1012 .X86_64_RELOC_SIGNED_4 => 4,
1014 else => unreachable,1013 else => unreachable,
1015 };1014 };
1016 var addend: i64 = mem.readIntLittle(i32, self.code[out.offset..][0..4]) + correction;1015 var addend: i64 = mem.readIntLittle(i32, self.code.items[out.offset..][0..4]) + correction;
10171016
1018 if (rel.r_extern == 0) {1017 if (rel.r_extern == 0) {
1019 const source_sym = ctx.macho_file.locals.items[self.local_sym_index];1018 const source_sym = ctx.macho_file.locals.items[self.local_sym_index];
...@@ -1038,7 +1037,7 @@ fn parseLoad(self: TextBlock, rel: macho.relocation_info, out: *Relocation) void...@@ -1038,7 +1037,7 @@ fn parseLoad(self: TextBlock, rel: macho.relocation_info, out: *Relocation) void
10381037
1039 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);1038 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
1040 const addend: i32 = if (rel_type == .X86_64_RELOC_GOT)1039 const addend: i32 = if (rel_type == .X86_64_RELOC_GOT)
1041 mem.readIntLittle(i32, self.code[out.offset..][0..4])1040 mem.readIntLittle(i32, self.code.items[out.offset..][0..4])
1042 else1041 else
1043 0;1042 0;
10441043
...@@ -1173,7 +1172,7 @@ pub fn print_this(self: *const TextBlock, macho_file: MachO) void {...@@ -1173,7 +1172,7 @@ pub fn print_this(self: *const TextBlock, macho_file: MachO) void {
1173 }1172 }
1174 }1173 }
1175 }1174 }
1176 log.warn(" code.len = {}", .{self.code.len});1175 log.warn(" code.len = {}", .{self.code.items.len});
1177 if (self.relocs.items.len > 0) {1176 if (self.relocs.items.len > 0) {
1178 log.warn(" relocations:", .{});1177 log.warn(" relocations:", .{});
1179 for (self.relocs.items) |rel| {1178 for (self.relocs.items) |rel| {