authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-31 15:01:37+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-08-01 09:06:56+02:00
log06396ddd7d632704681bbd6278f18dcc2b6bf20c
tree068f069096fe341c19a63e0de66e52c19e22ad5b
parente73777333dd66570ddacdb7de56a390de01b33c5

macho: don't allocate Objects on the heap

instead, ownership is transferred to MachO. This makes Object management align closer with data-oriented design.

3 files changed, 46 insertions(+), 56 deletions(-)

src/link/MachO.zig+17-15
...@@ -61,7 +61,7 @@ header_pad: u16 = 0x1000,...@@ -61,7 +61,7 @@ header_pad: u16 = 0x1000,
61/// The absolute address of the entry point.61/// The absolute address of the entry point.
62entry_addr: ?u64 = null,62entry_addr: ?u64 = null,
6363
64objects: std.ArrayListUnmanaged(*Object) = .{},64objects: std.ArrayListUnmanaged(Object) = .{},
65archives: std.ArrayListUnmanaged(*Archive) = .{},65archives: std.ArrayListUnmanaged(*Archive) = .{},
66dylibs: std.ArrayListUnmanaged(*Dylib) = .{},66dylibs: std.ArrayListUnmanaged(*Dylib) = .{},
6767
...@@ -990,13 +990,19 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const...@@ -990,13 +990,19 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const
990 const arch = self.base.options.target.cpu.arch;990 const arch = self.base.options.target.cpu.arch;
991 for (files) |file_name| {991 for (files) |file_name| {
992 const full_path = full_path: {992 const full_path = full_path: {
993 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;993 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
994 const path = try std.fs.realpath(file_name, &buffer);994 const path = try std.fs.realpath(file_name, &buffer);
995 break :full_path try self.base.allocator.dupe(u8, path);995 break :full_path try self.base.allocator.dupe(u8, path);
996 };996 };
997 const file = try fs.cwd().openFile(full_path, .{});
997998
998 if (try Object.createAndParseFromPath(self.base.allocator, arch, full_path)) |object| {999 if (try Object.isObject(file)) {
999 try self.objects.append(self.base.allocator, object);1000 const object = try self.objects.addOne(self.base.allocator);
1001 object.* = .{
1002 .name = full_path,
1003 .file = file,
1004 };
1005 try object.parse(self.base.allocator, arch);
1000 continue;1006 continue;
1001 }1007 }
10021008
...@@ -1993,7 +1999,7 @@ fn writeStubHelperCommon(self: *MachO) !void {...@@ -1993,7 +1999,7 @@ fn writeStubHelperCommon(self: *MachO) !void {
1993}1999}
19942000
1995fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {2001fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
1996 const object = self.objects.items[object_id];2002 const object = &self.objects.items[object_id];
19972003
1998 log.debug("resolving symbols in '{s}'", .{object.name});2004 log.debug("resolving symbols in '{s}'", .{object.name});
19992005
...@@ -2228,13 +2234,10 @@ fn resolveSymbols(self: *MachO) !void {...@@ -2228,13 +2234,10 @@ fn resolveSymbols(self: *MachO) !void {
2228 };2234 };
2229 assert(offsets.items.len > 0);2235 assert(offsets.items.len > 0);
22302236
2231 const object = try archive.parseObject(
2232 self.base.allocator,
2233 self.base.options.target.cpu.arch,
2234 offsets.items[0],
2235 );
2236 const object_id = @intCast(u16, self.objects.items.len);2237 const object_id = @intCast(u16, self.objects.items.len);
2237 try self.objects.append(self.base.allocator, object);2238 const object = try self.objects.addOne(self.base.allocator);
2239 object.* = try archive.extractObject(self.base.allocator, offsets.items[0]);
2240 try object.parse(self.base.allocator, self.base.options.target.cpu.arch);
2238 try self.resolveSymbolsInObject(object_id);2241 try self.resolveSymbolsInObject(object_id);
22392242
2240 continue :loop;2243 continue :loop;
...@@ -2460,8 +2463,8 @@ fn resolveSymbols(self: *MachO) !void {...@@ -2460,8 +2463,8 @@ fn resolveSymbols(self: *MachO) !void {
2460}2463}
24612464
2462fn parseTextBlocks(self: *MachO) !void {2465fn parseTextBlocks(self: *MachO) !void {
2463 for (self.objects.items) |object| {2466 for (self.objects.items) |*object, object_id| {
2464 try object.parseTextBlocks(self.base.allocator, self);2467 try object.parseTextBlocks(self.base.allocator, @intCast(u16, object_id), self);
2465 }2468 }
2466}2469}
24672470
...@@ -3337,9 +3340,8 @@ pub fn deinit(self: *MachO) void {...@@ -3337,9 +3340,8 @@ pub fn deinit(self: *MachO) void {
3337 self.locals_free_list.deinit(self.base.allocator);3340 self.locals_free_list.deinit(self.base.allocator);
3338 self.symbol_resolver.deinit(self.base.allocator);3341 self.symbol_resolver.deinit(self.base.allocator);
33393342
3340 for (self.objects.items) |object| {3343 for (self.objects.items) |*object| {
3341 object.deinit(self.base.allocator);3344 object.deinit(self.base.allocator);
3342 self.base.allocator.destroy(object);
3343 }3345 }
3344 self.objects.deinit(self.base.allocator);3346 self.objects.deinit(self.base.allocator);
33453347
src/link/MachO/Archive.zig+3-7
...@@ -223,8 +223,7 @@ fn parseTableOfContents(self: *Archive, allocator: *Allocator, reader: anytype)...@@ -223,8 +223,7 @@ fn parseTableOfContents(self: *Archive, allocator: *Allocator, reader: anytype)
223 }223 }
224}224}
225225
226/// Caller owns the Object instance.226pub fn extractObject(self: Archive, allocator: *Allocator, offset: u32) !Object {
227pub fn parseObject(self: Archive, allocator: *Allocator, arch: Arch, offset: u32) !*Object {
228 var reader = self.file.reader();227 var reader = self.file.reader();
229 try reader.context.seekTo(offset + self.library_offset);228 try reader.context.seekTo(offset + self.library_offset);
230229
...@@ -246,16 +245,13 @@ pub fn parseObject(self: Archive, allocator: *Allocator, arch: Arch, offset: u32...@@ -246,16 +245,13 @@ pub fn parseObject(self: Archive, allocator: *Allocator, arch: Arch, offset: u32
246 break :name try std.fmt.allocPrint(allocator, "{s}({s})", .{ path, object_name });245 break :name try std.fmt.allocPrint(allocator, "{s}({s})", .{ path, object_name });
247 };246 };
248247
249 var object = try allocator.create(Object);248 var object = Object{
250 errdefer allocator.destroy(object);
251
252 object.* = .{
253 .file = try fs.cwd().openFile(self.name, .{}),249 .file = try fs.cwd().openFile(self.name, .{}),
254 .name = name,250 .name = name,
255 .file_offset = @intCast(u32, try reader.context.getPos()),251 .file_offset = @intCast(u32, try reader.context.getPos()),
256 .mtime = try self.header.?.date(),252 .mtime = try self.header.?.date(),
257 };253 };
258 try object.parse(allocator, arch);254
259 try reader.context.seekTo(0);255 try reader.context.seekTo(0);
260256
261 return object;257 return object;
src/link/MachO/Object.zig+26-34
...@@ -127,36 +127,6 @@ const DebugInfo = struct {...@@ -127,36 +127,6 @@ const DebugInfo = struct {
127 }127 }
128};128};
129129
130pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u8) !?*Object {
131 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
132 error.FileNotFound => return null,
133 else => |e| return e,
134 };
135 errdefer file.close();
136
137 const object = try allocator.create(Object);
138 errdefer allocator.destroy(object);
139
140 const name = try allocator.dupe(u8, path);
141 errdefer allocator.free(name);
142
143 object.* = .{
144 .name = name,
145 .file = file,
146 };
147
148 object.parse(allocator, arch) catch |err| switch (err) {
149 error.EndOfStream, error.NotObject => {
150 object.deinit(allocator);
151 allocator.destroy(object);
152 return null;
153 },
154 else => |e| return e,
155 };
156
157 return object;
158}
159
160pub fn deinit(self: *Object, allocator: *Allocator) void {130pub fn deinit(self: *Object, allocator: *Allocator) void {
161 for (self.load_commands.items) |*lc| {131 for (self.load_commands.items) |*lc| {
162 lc.deinit(allocator);132 lc.deinit(allocator);
...@@ -184,6 +154,22 @@ pub fn deinit(self: *Object, allocator: *Allocator) void {...@@ -184,6 +154,22 @@ pub fn deinit(self: *Object, allocator: *Allocator) void {
184 }154 }
185}155}
186156
157pub fn isObject(file: fs.File) !bool {
158 const reader = file.reader();
159 const is_object = blk: {
160 if (reader.readStruct(macho.mach_header_64)) |header| {
161 break :blk header.filetype == macho.MH_OBJECT;
162 } else |err| {
163 switch (err) {
164 error.EndOfStream => break :blk false,
165 else => |e| return e,
166 }
167 }
168 };
169 try file.seekTo(0);
170 return is_object;
171}
172
187pub fn parse(self: *Object, allocator: *Allocator, arch: Arch) !void {173pub fn parse(self: *Object, allocator: *Allocator, arch: Arch) !void {
188 var reader = self.file.reader();174 var reader = self.file.reader();
189 if (self.file_offset) |offset| {175 if (self.file_offset) |offset| {
...@@ -481,7 +467,12 @@ const TextBlockParser = struct {...@@ -481,7 +467,12 @@ const TextBlockParser = struct {
481 }467 }
482};468};
483469
484pub fn parseTextBlocks(self: *Object, allocator: *Allocator, macho_file: *MachO) !void {470pub fn parseTextBlocks(
471 self: *Object,
472 allocator: *Allocator,
473 object_id: u16,
474 macho_file: *MachO,
475) !void {
485 const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;476 const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;
486477
487 log.debug("analysing {s}", .{self.name});478 log.debug("analysing {s}", .{self.name});
...@@ -668,13 +659,14 @@ pub fn parseTextBlocks(self: *Object, allocator: *Allocator, macho_file: *MachO)...@@ -668,13 +659,14 @@ pub fn parseTextBlocks(self: *Object, allocator: *Allocator, macho_file: *MachO)
668 if (is_ext) {659 if (is_ext) {
669 if (macho_file.symbol_resolver.get(sym.n_strx)) |resolv| {660 if (macho_file.symbol_resolver.get(sym.n_strx)) |resolv| {
670 assert(resolv.where == .global);661 assert(resolv.where == .global);
671 const global_object = macho_file.objects.items[resolv.file];662 if (resolv.file != object_id) {
672 if (global_object != self) {
673 log.debug("deduping definition of {s} in {s}", .{663 log.debug("deduping definition of {s} in {s}", .{
674 macho_file.getString(sym.n_strx),664 macho_file.getString(sym.n_strx),
675 self.name,665 self.name,
676 });666 });
677 log.debug(" already defined in {s}", .{global_object.name});667 log.debug(" already defined in {s}", .{
668 macho_file.objects.items[resolv.file].name,
669 });
678 continue;670 continue;
679 }671 }
680 }672 }