authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-31 16:01:02+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-08-01 09:06:56+02:00
log0b15ba8334bc03b59a975e579aac01a6b3fc2109
tree267ab3306af2b4277b933770de1d3fd696260372
parentf023cdad7ca676977d9b5abd3d38677779aab211

macho: don't allocate Dylib on the heap

instead, immediately transfer ownership to MachO struct. Also, revert back to try-ok-fail parsing approach of objects, archives, and dylibs. It seems easier to try and fail than check if the file *is* of a certain type given that a dylib may be a stub and parsing yaml twice in a row seems very wasteful. Hint for the future: if we optimise yaml/TAPI parsing, this approach may be rethought!

4 files changed, 85 insertions(+), 77 deletions(-)

src/link/MachO.zig+13-34
......@@ -63,7 +63,7 @@ entry_addr: ?u64 = null,
6363
6464objects: std.ArrayListUnmanaged(Object) = .{},
6565archives: std.ArrayListUnmanaged(Archive) = .{},
66dylibs: std.ArrayListUnmanaged(*Dylib) = .{},
66dylibs: std.ArrayListUnmanaged(Dylib) = .{},
6767
6868next_dylib_ordinal: u16 = 1,
6969
......@@ -994,25 +994,15 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const
994994 const path = try std.fs.realpath(file_name, &buffer);
995995 break :full_path try self.base.allocator.dupe(u8, path);
996996 };
997 const file = try fs.cwd().openFile(full_path, .{});
997 defer self.base.allocator.free(full_path);
998998
999 if (try Object.isObject(file)) {
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);
999 if (try Object.createAndParseFromPath(self.base.allocator, arch, full_path)) |object| {
1000 try self.objects.append(self.base.allocator, object);
10061001 continue;
10071002 }
10081003
1009 if (try Archive.isArchive(file, arch)) {
1010 const archive = try self.archives.addOne(self.base.allocator);
1011 archive.* = .{
1012 .name = full_path,
1013 .file = file,
1014 };
1015 try archive.parse(self.base.allocator, arch);
1004 if (try Archive.createAndParseFromPath(self.base.allocator, arch, full_path)) |archive| {
1005 try self.archives.append(self.base.allocator, archive);
10161006 continue;
10171007 }
10181008
......@@ -1024,8 +1014,6 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const
10241014 continue;
10251015 }
10261016
1027 self.base.allocator.free(full_path);
1028 file.close();
10291017 log.warn("unknown filetype for positional input file: '{s}'", .{file_name});
10301018 }
10311019}
......@@ -1033,8 +1021,6 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const
10331021fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8) !void {
10341022 const arch = self.base.options.target.cpu.arch;
10351023 for (libs) |lib| {
1036 const file = try fs.cwd().openFile(lib, .{});
1037
10381024 if (try Dylib.createAndParseFromPath(self.base.allocator, arch, lib, .{
10391025 .syslibroot = syslibroot,
10401026 })) |dylibs| {
......@@ -1043,17 +1029,11 @@ fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8) !v
10431029 continue;
10441030 }
10451031
1046 if (try Archive.isArchive(file, arch)) {
1047 const archive = try self.archives.addOne(self.base.allocator);
1048 archive.* = .{
1049 .name = try self.base.allocator.dupe(u8, lib),
1050 .file = file,
1051 };
1052 try archive.parse(self.base.allocator, arch);
1032 if (try Archive.createAndParseFromPath(self.base.allocator, arch, lib)) |archive| {
1033 try self.archives.append(self.base.allocator, archive);
10531034 continue;
10541035 }
10551036
1056 file.close();
10571037 log.warn("unknown filetype for a library: '{s}'", .{lib});
10581038 }
10591039}
......@@ -2351,17 +2331,17 @@ fn resolveSymbols(self: *MachO) !void {
23512331 });
23522332 }
23532333
2354 var referenced = std.AutoHashMap(*Dylib, void).init(self.base.allocator);
2334 var referenced = std.AutoHashMap(u16, void).init(self.base.allocator);
23552335 defer referenced.deinit();
23562336
23572337 loop: for (self.undefs.items) |sym| {
23582338 if (symbolIsNull(sym)) continue;
23592339
23602340 const sym_name = self.getString(sym.n_strx);
2361 for (self.dylibs.items) |dylib| {
2341 for (self.dylibs.items) |*dylib, id| {
23622342 if (!dylib.symbols.contains(sym_name)) continue;
23632343
2364 if (!referenced.contains(dylib)) {
2344 if (!referenced.contains(@intCast(u16, id))) {
23652345 // Add LC_LOAD_DYLIB load command for each referenced dylib/stub.
23662346 dylib.ordinal = self.next_dylib_ordinal;
23672347 const dylib_id = dylib.id orelse unreachable;
......@@ -2375,7 +2355,7 @@ fn resolveSymbols(self: *MachO) !void {
23752355 errdefer dylib_cmd.deinit(self.base.allocator);
23762356 try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd });
23772357 self.next_dylib_ordinal += 1;
2378 try referenced.putNoClobber(dylib, {});
2358 try referenced.putNoClobber(@intCast(u16, id), {});
23792359 }
23802360
23812361 const resolv = self.symbol_resolver.getPtr(sym.n_strx) orelse unreachable;
......@@ -3365,9 +3345,8 @@ pub fn deinit(self: *MachO) void {
33653345 }
33663346 self.archives.deinit(self.base.allocator);
33673347
3368 for (self.dylibs.items) |dylib| {
3348 for (self.dylibs.items) |*dylib| {
33693349 dylib.deinit(self.base.allocator);
3370 self.base.allocator.destroy(dylib);
33713350 }
33723351 self.dylibs.deinit(self.base.allocator);
33733352
src/link/MachO/Archive.zig+35-21
......@@ -104,38 +104,52 @@ pub fn deinit(self: *Archive, allocator: *Allocator) void {
104104 allocator.free(self.name);
105105}
106106
107pub fn isArchive(file: fs.File, arch: Arch) !bool {
108 const Internal = struct {
109 fn isArchive(reader: anytype, a: Arch) !bool {
110 const offset = try fat.getLibraryOffset(reader, a);
111 try reader.context.seekTo(offset);
112 const magic = try reader.readBytesNoEof(SARMAG);
113 if (!mem.eql(u8, &magic, ARMAG)) return false;
114 const header = try reader.readStruct(ar_hdr);
115 return mem.eql(u8, &header.ar_fmag, ARFMAG);
116 }
107pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u8) !?Archive {
108 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
109 error.FileNotFound => return null,
110 else => |e| return e,
111 };
112 errdefer file.close();
113
114 const name = try allocator.dupe(u8, path);
115 errdefer allocator.free(name);
116
117 var archive = Archive{
118 .name = name,
119 .file = file,
117120 };
118 const is_archive = if (Internal.isArchive(file.reader(), arch)) |res|
119 res
120 else |err| switch (err) {
121 error.EndOfStream => false,
122 error.MismatchedCpuArchitecture => true, // TODO maybe this check should be done differently?
121
122 archive.parse(allocator, arch) catch |err| switch (err) {
123 error.EndOfStream, error.NotArchive => {
124 archive.deinit(allocator);
125 return null;
126 },
123127 else => |e| return e,
124128 };
125 try file.seekTo(0);
126 return is_archive;
129
130 return archive;
127131}
128132
129133pub fn parse(self: *Archive, allocator: *Allocator, arch: Arch) !void {
130 self.library_offset = try fat.getLibraryOffset(self.file.reader(), arch);
131 try self.file.seekTo(self.library_offset);
132134 const reader = self.file.reader();
135 self.library_offset = try fat.getLibraryOffset(reader, arch);
136 try self.file.seekTo(self.library_offset);
137
133138 const magic = try reader.readBytesNoEof(SARMAG);
139 if (!mem.eql(u8, &magic, ARMAG)) {
140 log.debug("invalid magic: expected '{s}', found '{s}'", .{ ARMAG, magic });
141 return error.NotArchive;
142 }
143
134144 self.header = try reader.readStruct(ar_hdr);
135 var embedded_name = try parseName(allocator, self.header.?, reader);
136 defer allocator.free(embedded_name);
145 if (!mem.eql(u8, &self.header.?.ar_fmag, ARFMAG)) {
146 log.debug("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, self.header.?.ar_fmag });
147 return error.NotArchive;
148 }
137149
150 var embedded_name = try parseName(allocator, self.header.?, reader);
138151 log.debug("parsing archive '{s}' at '{s}'", .{ embedded_name, self.name });
152 defer allocator.free(embedded_name);
139153
140154 try self.parseTableOfContents(allocator, reader);
141155 try reader.context.seekTo(0);
src/link/MachO/Dylib.zig+5-9
......@@ -148,20 +148,17 @@ pub fn createAndParseFromPath(
148148 arch: Arch,
149149 path: []const u8,
150150 opts: CreateOpts,
151) Error!?[]*Dylib {
151) Error!?[]Dylib {
152152 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
153153 error.FileNotFound => return null,
154154 else => |e| return e,
155155 };
156156 errdefer file.close();
157157
158 const dylib = try allocator.create(Dylib);
159 errdefer allocator.destroy(dylib);
160
161158 const name = try allocator.dupe(u8, path);
162159 errdefer allocator.free(name);
163160
164 dylib.* = .{
161 var dylib = Dylib{
165162 .name = name,
166163 .file = file,
167164 };
......@@ -172,7 +169,6 @@ pub fn createAndParseFromPath(
172169
173170 var lib_stub = LibStub.loadFromFile(allocator, file) catch {
174171 dylib.deinit(allocator);
175 allocator.destroy(dylib);
176172 return null;
177173 };
178174 defer lib_stub.deinit();
......@@ -191,12 +187,11 @@ pub fn createAndParseFromPath(
191187
192188 // TODO maybe this should be an error and facilitate auto-cleanup?
193189 dylib.deinit(allocator);
194 allocator.destroy(dylib);
195190 return null;
196191 }
197192 }
198193
199 var dylibs = std.ArrayList(*Dylib).init(allocator);
194 var dylibs = std.ArrayList(Dylib).init(allocator);
200195 defer dylibs.deinit();
201196
202197 try dylibs.append(dylib);
......@@ -449,7 +444,7 @@ pub fn parseDependentLibs(
449444 self: *Dylib,
450445 allocator: *Allocator,
451446 arch: Arch,
452 out: *std.ArrayList(*Dylib),
447 out: *std.ArrayList(Dylib),
453448 syslibroot: ?[]const u8,
454449) !void {
455450 outer: for (self.dependent_libs.items) |id| {
......@@ -489,6 +484,7 @@ pub fn parseDependentLibs(
489484 )) orelse {
490485 continue;
491486 };
487 defer allocator.free(dylibs);
492488
493489 try out.appendSlice(dylibs);
494490
src/link/MachO/Object.zig+32-13
......@@ -154,29 +154,47 @@ pub fn deinit(self: *Object, allocator: *Allocator) void {
154154 }
155155}
156156
157pub fn isObject(file: fs.File) !bool {
158 const Internal = struct {
159 fn isObject(reader: anytype) !bool {
160 const header = try reader.readStruct(macho.mach_header_64);
161 return header.filetype == macho.MH_OBJECT;
162 }
157pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u8) !?Object {
158 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
159 error.FileNotFound => return null,
160 else => |e| return e,
163161 };
164 const is_object = if (Internal.isObject(file.reader())) |res|
165 res
166 else |err| switch (err) {
167 error.EndOfStream => false,
162 errdefer file.close();
163
164 const name = try allocator.dupe(u8, path);
165 errdefer allocator.free(name);
166
167 var object = Object{
168 .name = name,
169 .file = file,
170 };
171
172 object.parse(allocator, arch) catch |err| switch (err) {
173 error.EndOfStream, error.NotObject => {
174 object.deinit(allocator);
175 return null;
176 },
168177 else => |e| return e,
169178 };
170 try file.seekTo(0);
171 return is_object;
179
180 return object;
172181}
173182
174183pub fn parse(self: *Object, allocator: *Allocator, arch: Arch) !void {
175 var reader = self.file.reader();
184 const reader = self.file.reader();
176185 if (self.file_offset) |offset| {
177186 try reader.context.seekTo(offset);
178187 }
188
179189 const header = try reader.readStruct(macho.mach_header_64);
190 if (header.filetype != macho.MH_OBJECT) {
191 log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{
192 macho.MH_OBJECT,
193 header.filetype,
194 });
195 return error.NotObject;
196 }
197
180198 const this_arch: Arch = switch (header.cputype) {
181199 macho.CPU_TYPE_ARM64 => .aarch64,
182200 macho.CPU_TYPE_X86_64 => .x86_64,
......@@ -189,6 +207,7 @@ pub fn parse(self: *Object, allocator: *Allocator, arch: Arch) !void {
189207 log.err("mismatched cpu architecture: expected {s}, found {s}", .{ arch, this_arch });
190208 return error.MismatchedCpuArchitecture;
191209 }
210
192211 self.header = header;
193212
194213 try self.readLoadCommands(allocator, reader);