authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-06-19 16:12:29+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-06-24 14:45:45+02:00
log96a0479db2d876bc1b3136b26cff4004a212ba6a
tree569a646a7ad922fc0876c88c7f0fe86533c3854e
parent089577a71df6e22bf1cc3f83489fd2af548c5b81

zld: parse lib stubs as tbds on the linker line


5 files changed, 175 insertions(+), 103 deletions(-)

src/link/MachO/Archive.zig+3-3
...@@ -234,8 +234,8 @@ pub fn parseObject(self: Archive, offset: u32) !*Object {...@@ -234,8 +234,8 @@ pub fn parseObject(self: Archive, offset: u32) !*Object {
234 return object;234 return object;
235}235}
236236
237pub fn isArchive(file: fs.File) !bool {237pub fn isArchive(file: fs.File) bool {
238 const magic = try file.reader().readBytesNoEof(Archive.SARMAG);238 const magic = file.reader().readBytesNoEof(Archive.SARMAG) catch return false;
239 try file.seekTo(0);239 file.seekTo(0) catch return false;
240 return mem.eql(u8, &magic, Archive.ARMAG);240 return mem.eql(u8, &magic, Archive.ARMAG);
241}241}
src/link/MachO/Dylib.zig+85-3
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1const Dylib = @This();1const Dylib = @This();
22
3const std = @import("std");3const std = @import("std");
4const assert = std.debug.assert;
4const fs = std.fs;5const fs = std.fs;
5const log = std.log.scoped(.dylib);6const log = std.log.scoped(.dylib);
6const macho = std.macho;7const macho = std.macho;
...@@ -8,6 +9,7 @@ const mem = std.mem;...@@ -8,6 +9,7 @@ const mem = std.mem;
89
9const Allocator = mem.Allocator;10const Allocator = mem.Allocator;
10const Symbol = @import("Symbol.zig");11const Symbol = @import("Symbol.zig");
12const LibStub = @import("../tapi.zig").LibStub;
1113
12usingnamespace @import("commands.zig");14usingnamespace @import("commands.zig");
1315
...@@ -184,8 +186,88 @@ pub fn parseSymbols(self: *Dylib) !void {...@@ -184,8 +186,88 @@ pub fn parseSymbols(self: *Dylib) !void {
184 }186 }
185}187}
186188
187pub fn isDylib(file: fs.File) !bool {189pub fn isDylib(file: fs.File) bool {
188 const header = try file.reader().readStruct(macho.mach_header_64);190 const header = file.reader().readStruct(macho.mach_header_64) catch return false;
189 try file.seekTo(0);191 file.seekTo(0) catch return false;
190 return header.filetype == macho.MH_DYLIB;192 return header.filetype == macho.MH_DYLIB;
191}193}
194
195pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void {
196 assert(lib_stub.inner.len > 0);
197
198 log.debug("parsing shared library from stub '{s}'", .{self.name.?});
199
200 const umbrella_lib = lib_stub.inner[0];
201 self.id = .{
202 .name = try self.allocator.dupe(u8, umbrella_lib.install_name),
203 // TODO parse from the stub
204 .timestamp = 2,
205 .current_version = 0,
206 .compatibility_version = 0,
207 };
208
209 const target_string: []const u8 = switch (self.arch.?) {
210 .aarch64 => "arm64-macos",
211 .x86_64 => "x86_64-macos",
212 else => unreachable,
213 };
214
215 for (lib_stub.inner) |stub| {
216 if (!hasTarget(stub.targets, target_string)) continue;
217
218 if (stub.exports) |exports| {
219 for (exports) |exp| {
220 if (!hasTarget(exp.targets, target_string)) continue;
221
222 for (exp.symbols) |sym_name| {
223 if (self.symbols.contains(sym_name)) continue;
224
225 const name = try self.allocator.dupe(u8, sym_name);
226 const proxy = try self.allocator.create(Symbol.Proxy);
227 errdefer self.allocator.destroy(proxy);
228
229 proxy.* = .{
230 .base = .{
231 .@"type" = .proxy,
232 .name = name,
233 },
234 .dylib = self,
235 };
236
237 try self.symbols.putNoClobber(self.allocator, name, &proxy.base);
238 }
239 }
240 }
241
242 if (stub.reexports) |reexports| {
243 for (reexports) |reexp| {
244 if (!hasTarget(reexp.targets, target_string)) continue;
245
246 for (reexp.symbols) |sym_name| {
247 if (self.symbols.contains(sym_name)) continue;
248
249 const name = try self.allocator.dupe(u8, sym_name);
250 const proxy = try self.allocator.create(Symbol.Proxy);
251 errdefer self.allocator.destroy(proxy);
252
253 proxy.* = .{
254 .base = .{
255 .@"type" = .proxy,
256 .name = name,
257 },
258 .dylib = self,
259 };
260
261 try self.symbols.putNoClobber(self.allocator, name, &proxy.base);
262 }
263 }
264 }
265 }
266}
267
268fn hasTarget(targets: []const []const u8, target: []const u8) bool {
269 for (targets) |t| {
270 if (mem.eql(u8, t, target)) return true;
271 }
272 return false;
273}
src/link/MachO/Object.zig+3-3
...@@ -534,8 +534,8 @@ pub fn parseDataInCode(self: *Object) !void {...@@ -534,8 +534,8 @@ pub fn parseDataInCode(self: *Object) !void {
534 }534 }
535}535}
536536
537pub fn isObject(file: fs.File) !bool {537pub fn isObject(file: fs.File) bool {
538 const header = try file.reader().readStruct(macho.mach_header_64);538 const header = file.reader().readStruct(macho.mach_header_64) catch return false;
539 try file.seekTo(0);539 file.seekTo(0) catch return false;
540 return header.filetype == macho.MH_OBJECT;540 return header.filetype == macho.MH_OBJECT;
541}541}
src/link/MachO/Zld.zig+73-93
...@@ -243,14 +243,16 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {...@@ -243,14 +243,16 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
243 object,243 object,
244 archive,244 archive,
245 dylib,245 dylib,
246 stub,
246 },247 },
247 file: fs.File,248 file: fs.File,
248 name: []const u8,249 name: []const u8,
250 stub: ?LibStub = null,
249 };251 };
250 var classified = std.ArrayList(Input).init(self.allocator);252 var classified = std.ArrayList(Input).init(self.allocator);
251 defer classified.deinit();253 defer classified.deinit();
252254
253 // First, classify input files: object, archive or dylib.255 // First, classify input files: object, archive, dylib or stub (tbd).
254 for (files) |file_name| {256 for (files) |file_name| {
255 const file = try fs.cwd().openFile(file_name, .{});257 const file = try fs.cwd().openFile(file_name, .{});
256 const full_path = full_path: {258 const full_path = full_path: {
...@@ -260,7 +262,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {...@@ -260,7 +262,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
260 };262 };
261263
262 try_object: {264 try_object: {
263 if (!(try Object.isObject(file))) break :try_object;265 if (!Object.isObject(file)) break :try_object;
264 try classified.append(.{266 try classified.append(.{
265 .kind = .object,267 .kind = .object,
266 .file = file,268 .file = file,
...@@ -270,7 +272,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {...@@ -270,7 +272,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
270 }272 }
271273
272 try_archive: {274 try_archive: {
273 if (!(try Archive.isArchive(file))) break :try_archive;275 if (!Archive.isArchive(file)) break :try_archive;
274 try classified.append(.{276 try classified.append(.{
275 .kind = .archive,277 .kind = .archive,
276 .file = file,278 .file = file,
...@@ -280,7 +282,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {...@@ -280,7 +282,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
280 }282 }
281283
282 try_dylib: {284 try_dylib: {
283 if (!(try Dylib.isDylib(file))) break :try_dylib;285 if (!Dylib.isDylib(file)) break :try_dylib;
284 try classified.append(.{286 try classified.append(.{
285 .kind = .dylib,287 .kind = .dylib,
286 .file = file,288 .file = file,
...@@ -289,6 +291,19 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {...@@ -289,6 +291,19 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
289 continue;291 continue;
290 }292 }
291293
294 try_stub: {
295 var lib_stub = LibStub.loadFromFile(self.allocator, file) catch |_| {
296 break :try_stub;
297 };
298 try classified.append(.{
299 .kind = .stub,
300 .file = file,
301 .name = full_path,
302 .stub = lib_stub,
303 });
304 continue;
305 }
306
292 file.close();307 file.close();
293 log.warn("unknown filetype for positional input file: '{s}'", .{file_name});308 log.warn("unknown filetype for positional input file: '{s}'", .{file_name});
294 }309 }
...@@ -318,7 +333,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {...@@ -318,7 +333,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
318 try archive.parse();333 try archive.parse();
319 try self.archives.append(self.allocator, archive);334 try self.archives.append(self.allocator, archive);
320 },335 },
321 .dylib => {336 .dylib, .stub => {
322 const dylib = try self.allocator.create(Dylib);337 const dylib = try self.allocator.create(Dylib);
323 errdefer self.allocator.destroy(dylib);338 errdefer self.allocator.destroy(dylib);
324339
...@@ -329,7 +344,11 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {...@@ -329,7 +344,11 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
329 dylib.ordinal = @intCast(u16, self.dylibs.items.len) + 1;344 dylib.ordinal = @intCast(u16, self.dylibs.items.len) + 1;
330345
331 // TODO Defer parsing of the dylibs until they are actually needed346 // TODO Defer parsing of the dylibs until they are actually needed
332 try dylib.parse();347 if (input.stub) |stub| {
348 try dylib.parseFromStub(stub);
349 } else {
350 try dylib.parse();
351 }
333 try self.dylibs.append(self.allocator, dylib);352 try self.dylibs.append(self.allocator, dylib);
334353
335 // Add LC_LOAD_DYLIB command354 // Add LC_LOAD_DYLIB command
...@@ -353,7 +372,7 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void {...@@ -353,7 +372,7 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void {
353 for (libs) |lib| {372 for (libs) |lib| {
354 const file = try fs.cwd().openFile(lib, .{});373 const file = try fs.cwd().openFile(lib, .{});
355374
356 if (try Dylib.isDylib(file)) {375 if (Dylib.isDylib(file)) {
357 const dylib = try self.allocator.create(Dylib);376 const dylib = try self.allocator.create(Dylib);
358 errdefer self.allocator.destroy(dylib);377 errdefer self.allocator.destroy(dylib);
359378
...@@ -379,28 +398,55 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void {...@@ -379,28 +398,55 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void {
379 errdefer dylib_cmd.deinit(self.allocator);398 errdefer dylib_cmd.deinit(self.allocator);
380399
381 try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });400 try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
382 } else if (try Archive.isArchive(file)) {
383 const archive = try self.allocator.create(Archive);
384 errdefer self.allocator.destroy(archive);
385
386 archive.* = Archive.init(self.allocator);
387 archive.arch = self.arch.?;
388 archive.name = try self.allocator.dupe(u8, lib);
389 archive.file = file;
390 try archive.parse();
391 try self.archives.append(self.allocator, archive);
392 } else {401 } else {
393 file.close();402 // Try tbd stub file next.
394 log.warn("unknown filetype for a library: '{s}'", .{lib});403 if (LibStub.loadFromFile(self.allocator, file)) |*lib_stub| {
395 }404 defer lib_stub.deinit();
396 }405
397}406 const dylib = try self.allocator.create(Dylib);
407 errdefer self.allocator.destroy(dylib);
408
409 dylib.* = Dylib.init(self.allocator);
410 dylib.arch = self.arch.?;
411 dylib.name = try self.allocator.dupe(u8, lib);
412 dylib.file = file;
413 dylib.ordinal = @intCast(u16, self.dylibs.items.len) + 1;
414
415 try dylib.parseFromStub(lib_stub.*);
416 try self.dylibs.append(self.allocator, dylib);
417
418 // Add LC_LOAD_DYLIB command
419 const dylib_id = dylib.id orelse unreachable;
420 var dylib_cmd = try createLoadDylibCommand(
421 self.allocator,
422 dylib_id.name,
423 dylib_id.timestamp,
424 dylib_id.current_version,
425 dylib_id.compatibility_version,
426 );
427 errdefer dylib_cmd.deinit(self.allocator);
398428
399fn hasTarget(targets: []const []const u8, target: []const u8) bool {429 try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
400 for (targets) |t| {430 } else |_| {
401 if (mem.eql(u8, t, target)) return true;431 // TODO this entire logic has to be cleaned up.
432 try file.seekTo(0);
433 if (Archive.isArchive(file)) {
434 const archive = try self.allocator.create(Archive);
435 errdefer self.allocator.destroy(archive);
436
437 archive.* = Archive.init(self.allocator);
438 archive.arch = self.arch.?;
439 archive.name = try self.allocator.dupe(u8, lib);
440 archive.file = file;
441 try archive.parse();
442 try self.archives.append(self.allocator, archive);
443 } else {
444 file.close();
445 log.warn("unknown filetype for a library: '{s}'", .{lib});
446 }
447 }
448 }
402 }449 }
403 return false;
404}450}
405451
406fn parseLibSystem(self: *Zld, lib_system_path: []const u8) !void {452fn parseLibSystem(self: *Zld, lib_system_path: []const u8) !void {
...@@ -418,73 +464,7 @@ fn parseLibSystem(self: *Zld, lib_system_path: []const u8) !void {...@@ -418,73 +464,7 @@ fn parseLibSystem(self: *Zld, lib_system_path: []const u8) !void {
418 dylib.file = file;464 dylib.file = file;
419 dylib.ordinal = @intCast(u16, self.dylibs.items.len) + 1;465 dylib.ordinal = @intCast(u16, self.dylibs.items.len) + 1;
420466
421 const umbrella_lib = lib_stub.inner[0];467 try dylib.parseFromStub(lib_stub);
422 dylib.id = .{
423 .name = try self.allocator.dupe(u8, umbrella_lib.install_name),
424 // TODO parse from the stub
425 .timestamp = 2,
426 .current_version = 0,
427 .compatibility_version = 0,
428 };
429
430 const target_string: []const u8 = switch (self.arch.?) {
431 .aarch64 => "arm64-macos",
432 .x86_64 => "x86_64-macos",
433 else => unreachable,
434 };
435
436 for (lib_stub.inner) |stub| {
437 if (!hasTarget(stub.targets, target_string)) continue;
438
439 if (stub.exports) |exports| {
440 for (exports) |exp| {
441 if (!hasTarget(exp.targets, target_string)) continue;
442
443 for (exp.symbols) |sym_name| {
444 if (dylib.symbols.contains(sym_name)) continue;
445
446 const name = try self.allocator.dupe(u8, sym_name);
447 const proxy = try self.allocator.create(Symbol.Proxy);
448 errdefer self.allocator.destroy(proxy);
449
450 proxy.* = .{
451 .base = .{
452 .@"type" = .proxy,
453 .name = name,
454 },
455 .dylib = dylib,
456 };
457
458 try dylib.symbols.putNoClobber(self.allocator, name, &proxy.base);
459 }
460 }
461 }
462
463 if (stub.reexports) |reexports| {
464 for (reexports) |reexp| {
465 if (!hasTarget(reexp.targets, target_string)) continue;
466
467 for (reexp.symbols) |sym_name| {
468 if (dylib.symbols.contains(sym_name)) continue;
469
470 const name = try self.allocator.dupe(u8, sym_name);
471 const proxy = try self.allocator.create(Symbol.Proxy);
472 errdefer self.allocator.destroy(proxy);
473
474 proxy.* = .{
475 .base = .{
476 .@"type" = .proxy,
477 .name = name,
478 },
479 .dylib = dylib,
480 };
481
482 try dylib.symbols.putNoClobber(self.allocator, name, &proxy.base);
483 }
484 }
485 }
486 }
487
488 try self.dylibs.append(self.allocator, dylib);468 try self.dylibs.append(self.allocator, dylib);
489469
490 // Add LC_LOAD_DYLIB command470 // Add LC_LOAD_DYLIB command
src/link/tapi.zig+11-1
...@@ -58,7 +58,17 @@ pub const LibStub = struct {...@@ -58,7 +58,17 @@ pub const LibStub = struct {
58 .inner = undefined,58 .inner = undefined,
59 };59 };
6060
61 lib_stub.inner = try lib_stub.yaml.parse([]Tbd);61 lib_stub.inner = lib_stub.yaml.parse([]Tbd) catch |err| blk: {
62 switch (err) {
63 error.TypeMismatch => {
64 // TODO clean this up.
65 var out = try lib_stub.yaml.arena.allocator.alloc(Tbd, 1);
66 out[0] = try lib_stub.yaml.parse(Tbd);
67 break :blk out;
68 },
69 else => |e| return e,
70 }
71 };
6272
63 return lib_stub;73 return lib_stub;
64 }74 }