| ... | @@ -52,6 +52,11 @@ pub const SearchStrategy = enum { | ... | @@ -52,6 +52,11 @@ pub const SearchStrategy = enum { |
| 52 | dylibs_first, | 52 | dylibs_first, |
| 53 | }; | 53 | }; |
| 54 | | 54 | |
| | 55 | const SystemLib = struct { |
| | 56 | needed: bool = false, |
| | 57 | weak: bool = false, |
| | 58 | }; |
| | 59 | |
| 55 | base: File, | 60 | base: File, |
| 56 | | 61 | |
| 57 | /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. | 62 | /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. |
| ... | @@ -768,7 +773,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No | ... | @@ -768,7 +773,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 768 | } | 773 | } |
| 769 | | 774 | |
| 770 | // Shared and static libraries passed via `-l` flag. | 775 | // Shared and static libraries passed via `-l` flag. |
| 771 | var candidate_libs = std.StringArrayHashMap(Compilation.SystemLib).init(arena); | 776 | var candidate_libs = std.StringArrayHashMap(SystemLib).init(arena); |
| 772 | | 777 | |
| 773 | const system_lib_names = self.base.options.system_libs.keys(); | 778 | const system_lib_names = self.base.options.system_libs.keys(); |
| 774 | for (system_lib_names) |system_lib_name| { | 779 | for (system_lib_names) |system_lib_name| { |
| ... | @@ -781,7 +786,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No | ... | @@ -781,7 +786,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 781 | } | 786 | } |
| 782 | | 787 | |
| 783 | const system_lib_info = self.base.options.system_libs.get(system_lib_name).?; | 788 | const system_lib_info = self.base.options.system_libs.get(system_lib_name).?; |
| 784 | try candidate_libs.put(system_lib_name, system_lib_info); | 789 | try candidate_libs.put(system_lib_name, .{ |
| | 790 | .needed = system_lib_info.needed, |
| | 791 | .weak = system_lib_info.weak, |
| | 792 | }); |
| 785 | } | 793 | } |
| 786 | | 794 | |
| 787 | var lib_dirs = std.ArrayList([]const u8).init(arena); | 795 | var lib_dirs = std.ArrayList([]const u8).init(arena); |
| ... | @@ -793,7 +801,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No | ... | @@ -793,7 +801,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 793 | } | 801 | } |
| 794 | } | 802 | } |
| 795 | | 803 | |
| 796 | var libs = std.StringArrayHashMap(Compilation.SystemLib).init(arena); | 804 | var libs = std.StringArrayHashMap(SystemLib).init(arena); |
| 797 | | 805 | |
| 798 | // Assume ld64 default -search_paths_first if no strategy specified. | 806 | // Assume ld64 default -search_paths_first if no strategy specified. |
| 799 | const search_strategy = self.base.options.search_strategy orelse .paths_first; | 807 | const search_strategy = self.base.options.search_strategy orelse .paths_first; |
| ... | @@ -890,7 +898,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No | ... | @@ -890,7 +898,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 890 | for (framework_dirs.items) |dir| { | 898 | for (framework_dirs.items) |dir| { |
| 891 | for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| { | 899 | for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| { |
| 892 | if (try resolveFramework(arena, dir, f_name, ext)) |full_path| { | 900 | if (try resolveFramework(arena, dir, f_name, ext)) |full_path| { |
| 893 | try libs.put(full_path, self.base.options.frameworks.get(f_name).?); | 901 | const info = self.base.options.frameworks.get(f_name).?; |
| | 902 | try libs.put(full_path, .{ |
| | 903 | .needed = info.needed, |
| | 904 | .weak = info.weak, |
| | 905 | }); |
| 894 | continue :outer; | 906 | continue :outer; |
| 895 | } | 907 | } |
| 896 | } | 908 | } |
| ... | @@ -1026,9 +1038,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No | ... | @@ -1026,9 +1038,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 1026 | try argv.append("-lc"); | 1038 | try argv.append("-lc"); |
| 1027 | | 1039 | |
| 1028 | for (self.base.options.system_libs.keys()) |l_name| { | 1040 | for (self.base.options.system_libs.keys()) |l_name| { |
| 1029 | const needed = self.base.options.system_libs.get(l_name).?.needed; | 1041 | const info = self.base.options.system_libs.get(l_name).?; |
| 1030 | const arg = if (needed) | 1042 | const arg = if (info.needed) |
| 1031 | try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name}) | 1043 | try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name}) |
| | 1044 | else if (info.weak) |
| | 1045 | try std.fmt.allocPrint(arena, "-weak-l{s}", .{l_name}) |
| 1032 | else | 1046 | else |
| 1033 | try std.fmt.allocPrint(arena, "-l{s}", .{l_name}); | 1047 | try std.fmt.allocPrint(arena, "-l{s}", .{l_name}); |
| 1034 | try argv.append(arg); | 1048 | try argv.append(arg); |
| ... | @@ -1039,9 +1053,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No | ... | @@ -1039,9 +1053,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 1039 | } | 1053 | } |
| 1040 | | 1054 | |
| 1041 | for (self.base.options.frameworks.keys()) |framework| { | 1055 | for (self.base.options.frameworks.keys()) |framework| { |
| 1042 | const needed = self.base.options.frameworks.get(framework).?.needed; | 1056 | const info = self.base.options.frameworks.get(framework).?; |
| 1043 | const arg = if (needed) | 1057 | const arg = if (info.needed) |
| 1044 | try std.fmt.allocPrint(arena, "-needed_framework {s}", .{framework}) | 1058 | try std.fmt.allocPrint(arena, "-needed_framework {s}", .{framework}) |
| | 1059 | else if (info.weak) |
| | 1060 | try std.fmt.allocPrint(arena, "-weak_framework {s}", .{framework}) |
| 1045 | else | 1061 | else |
| 1046 | try std.fmt.allocPrint(arena, "-framework {s}", .{framework}); | 1062 | try std.fmt.allocPrint(arena, "-framework {s}", .{framework}); |
| 1047 | try argv.append(arg); | 1063 | try argv.append(arg); |
| ... | @@ -1063,7 +1079,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No | ... | @@ -1063,7 +1079,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 1063 | Compilation.dump_argv(argv.items); | 1079 | Compilation.dump_argv(argv.items); |
| 1064 | } | 1080 | } |
| 1065 | | 1081 | |
| 1066 | var dependent_libs = std.fifo.LinearFifo(Dylib.Id, .Dynamic).init(self.base.allocator); | 1082 | var dependent_libs = std.fifo.LinearFifo(struct { |
| | 1083 | id: Dylib.Id, |
| | 1084 | parent: u16, |
| | 1085 | }, .Dynamic).init(self.base.allocator); |
| 1067 | defer dependent_libs.deinit(); | 1086 | defer dependent_libs.deinit(); |
| 1068 | try self.parseInputFiles(positionals.items, self.base.options.sysroot, &dependent_libs); | 1087 | try self.parseInputFiles(positionals.items, self.base.options.sysroot, &dependent_libs); |
| 1069 | try self.parseAndForceLoadStaticArchives(must_link_archives.keys()); | 1088 | try self.parseAndForceLoadStaticArchives(must_link_archives.keys()); |
| ... | @@ -1389,13 +1408,18 @@ const ParseDylibError = error{ | ... | @@ -1389,13 +1408,18 @@ const ParseDylibError = error{ |
| 1389 | | 1408 | |
| 1390 | const DylibCreateOpts = struct { | 1409 | const DylibCreateOpts = struct { |
| 1391 | syslibroot: ?[]const u8, | 1410 | syslibroot: ?[]const u8, |
| 1392 | dependent_libs: *std.fifo.LinearFifo(Dylib.Id, .Dynamic), | | |
| 1393 | id: ?Dylib.Id = null, | 1411 | id: ?Dylib.Id = null, |
| 1394 | is_dependent: bool = false, | 1412 | dependent: bool = false, |
| 1395 | is_needed: bool = false, | 1413 | needed: bool = false, |
| | 1414 | weak: bool = false, |
| 1396 | }; | 1415 | }; |
| 1397 | | 1416 | |
| 1398 | pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDylibError!bool { | 1417 | pub fn parseDylib( |
| | 1418 | self: *MachO, |
| | 1419 | path: []const u8, |
| | 1420 | dependent_libs: anytype, |
| | 1421 | opts: DylibCreateOpts, |
| | 1422 | ) ParseDylibError!bool { |
| 1399 | const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) { | 1423 | const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) { |
| 1400 | error.FileNotFound => return false, | 1424 | error.FileNotFound => return false, |
| 1401 | else => |e| return e, | 1425 | else => |e| return e, |
| ... | @@ -1405,12 +1429,19 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy | ... | @@ -1405,12 +1429,19 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy |
| 1405 | const name = try self.base.allocator.dupe(u8, path); | 1429 | const name = try self.base.allocator.dupe(u8, path); |
| 1406 | errdefer self.base.allocator.free(name); | 1430 | errdefer self.base.allocator.free(name); |
| 1407 | | 1431 | |
| | 1432 | const dylib_id = @intCast(u16, self.dylibs.items.len); |
| 1408 | var dylib = Dylib{ | 1433 | var dylib = Dylib{ |
| 1409 | .name = name, | 1434 | .name = name, |
| 1410 | .file = file, | 1435 | .file = file, |
| | 1436 | .weak = opts.weak, |
| 1411 | }; | 1437 | }; |
| 1412 | | 1438 | |
| 1413 | dylib.parse(self.base.allocator, self.base.options.target, opts.dependent_libs) catch |err| switch (err) { | 1439 | dylib.parse( |
| | 1440 | self.base.allocator, |
| | 1441 | self.base.options.target, |
| | 1442 | dylib_id, |
| | 1443 | dependent_libs, |
| | 1444 | ) catch |err| switch (err) { |
| 1414 | error.EndOfStream, error.NotDylib => { | 1445 | error.EndOfStream, error.NotDylib => { |
| 1415 | try file.seekTo(0); | 1446 | try file.seekTo(0); |
| 1416 | | 1447 | |
| ... | @@ -1420,7 +1451,13 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy | ... | @@ -1420,7 +1451,13 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy |
| 1420 | }; | 1451 | }; |
| 1421 | defer lib_stub.deinit(); | 1452 | defer lib_stub.deinit(); |
| 1422 | | 1453 | |
| 1423 | try dylib.parseFromStub(self.base.allocator, self.base.options.target, lib_stub, opts.dependent_libs); | 1454 | try dylib.parseFromStub( |
| | 1455 | self.base.allocator, |
| | 1456 | self.base.options.target, |
| | 1457 | lib_stub, |
| | 1458 | dylib_id, |
| | 1459 | dependent_libs, |
| | 1460 | ); |
| 1424 | }, | 1461 | }, |
| 1425 | else => |e| return e, | 1462 | else => |e| return e, |
| 1426 | }; | 1463 | }; |
| ... | @@ -1438,13 +1475,12 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy | ... | @@ -1438,13 +1475,12 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy |
| 1438 | } | 1475 | } |
| 1439 | } | 1476 | } |
| 1440 | | 1477 | |
| 1441 | const dylib_id = @intCast(u16, self.dylibs.items.len); | | |
| 1442 | try self.dylibs.append(self.base.allocator, dylib); | 1478 | try self.dylibs.append(self.base.allocator, dylib); |
| 1443 | try self.dylibs_map.putNoClobber(self.base.allocator, dylib.id.?.name, dylib_id); | 1479 | try self.dylibs_map.putNoClobber(self.base.allocator, dylib.id.?.name, dylib_id); |
| 1444 | | 1480 | |
| 1445 | const should_link_dylib_even_if_unreachable = blk: { | 1481 | const should_link_dylib_even_if_unreachable = blk: { |
| 1446 | if (self.base.options.dead_strip_dylibs and !opts.is_needed) break :blk false; | 1482 | if (self.base.options.dead_strip_dylibs and !opts.needed) break :blk false; |
| 1447 | break :blk !(opts.is_dependent or self.referenced_dylibs.contains(dylib_id)); | 1483 | break :blk !(opts.dependent or self.referenced_dylibs.contains(dylib_id)); |
| 1448 | }; | 1484 | }; |
| 1449 | | 1485 | |
| 1450 | if (should_link_dylib_even_if_unreachable) { | 1486 | if (should_link_dylib_even_if_unreachable) { |
| ... | @@ -1467,9 +1503,8 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const | ... | @@ -1467,9 +1503,8 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const |
| 1467 | | 1503 | |
| 1468 | if (try self.parseObject(full_path)) continue; | 1504 | if (try self.parseObject(full_path)) continue; |
| 1469 | if (try self.parseArchive(full_path, false)) continue; | 1505 | if (try self.parseArchive(full_path, false)) continue; |
| 1470 | if (try self.parseDylib(full_path, .{ | 1506 | if (try self.parseDylib(full_path, dependent_libs, .{ |
| 1471 | .syslibroot = syslibroot, | 1507 | .syslibroot = syslibroot, |
| 1472 | .dependent_libs = dependent_libs, | | |
| 1473 | })) continue; | 1508 | })) continue; |
| 1474 | | 1509 | |
| 1475 | log.warn("unknown filetype for positional input file: '{s}'", .{file_name}); | 1510 | log.warn("unknown filetype for positional input file: '{s}'", .{file_name}); |
| ... | @@ -1494,17 +1529,17 @@ fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !voi | ... | @@ -1494,17 +1529,17 @@ fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !voi |
| 1494 | fn parseLibs( | 1529 | fn parseLibs( |
| 1495 | self: *MachO, | 1530 | self: *MachO, |
| 1496 | lib_names: []const []const u8, | 1531 | lib_names: []const []const u8, |
| 1497 | lib_infos: []const Compilation.SystemLib, | 1532 | lib_infos: []const SystemLib, |
| 1498 | syslibroot: ?[]const u8, | 1533 | syslibroot: ?[]const u8, |
| 1499 | dependent_libs: anytype, | 1534 | dependent_libs: anytype, |
| 1500 | ) !void { | 1535 | ) !void { |
| 1501 | for (lib_names) |lib, i| { | 1536 | for (lib_names) |lib, i| { |
| 1502 | const lib_info = lib_infos[i]; | 1537 | const lib_info = lib_infos[i]; |
| 1503 | log.debug("parsing lib path '{s}'", .{lib}); | 1538 | log.debug("parsing lib path '{s}'", .{lib}); |
| 1504 | if (try self.parseDylib(lib, .{ | 1539 | if (try self.parseDylib(lib, dependent_libs, .{ |
| 1505 | .syslibroot = syslibroot, | 1540 | .syslibroot = syslibroot, |
| 1506 | .dependent_libs = dependent_libs, | 1541 | .needed = lib_info.needed, |
| 1507 | .is_needed = lib_info.needed, | 1542 | .weak = lib_info.weak, |
| 1508 | })) continue; | 1543 | })) continue; |
| 1509 | if (try self.parseArchive(lib, false)) continue; | 1544 | if (try self.parseArchive(lib, false)) continue; |
| 1510 | | 1545 | |
| ... | @@ -1522,20 +1557,21 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any | ... | @@ -1522,20 +1557,21 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any |
| 1522 | const arena = arena_alloc.allocator(); | 1557 | const arena = arena_alloc.allocator(); |
| 1523 | defer arena_alloc.deinit(); | 1558 | defer arena_alloc.deinit(); |
| 1524 | | 1559 | |
| 1525 | while (dependent_libs.readItem()) |*id| { | 1560 | while (dependent_libs.readItem()) |*dep_id| { |
| 1526 | defer id.deinit(self.base.allocator); | 1561 | defer dep_id.id.deinit(self.base.allocator); |
| 1527 | | 1562 | |
| 1528 | if (self.dylibs_map.contains(id.name)) continue; | 1563 | if (self.dylibs_map.contains(dep_id.id.name)) continue; |
| 1529 | | 1564 | |
| | 1565 | const weak = self.dylibs.items[dep_id.parent].weak; |
| 1530 | const has_ext = blk: { | 1566 | const has_ext = blk: { |
| 1531 | const basename = fs.path.basename(id.name); | 1567 | const basename = fs.path.basename(dep_id.id.name); |
| 1532 | break :blk mem.lastIndexOfScalar(u8, basename, '.') != null; | 1568 | break :blk mem.lastIndexOfScalar(u8, basename, '.') != null; |
| 1533 | }; | 1569 | }; |
| 1534 | const extension = if (has_ext) fs.path.extension(id.name) else ""; | 1570 | const extension = if (has_ext) fs.path.extension(dep_id.id.name) else ""; |
| 1535 | const without_ext = if (has_ext) blk: { | 1571 | const without_ext = if (has_ext) blk: { |
| 1536 | const index = mem.lastIndexOfScalar(u8, id.name, '.') orelse unreachable; | 1572 | const index = mem.lastIndexOfScalar(u8, dep_id.id.name, '.') orelse unreachable; |
| 1537 | break :blk id.name[0..index]; | 1573 | break :blk dep_id.id.name[0..index]; |
| 1538 | } else id.name; | 1574 | } else dep_id.id.name; |
| 1539 | | 1575 | |
| 1540 | for (&[_][]const u8{ extension, ".tbd" }) |ext| { | 1576 | for (&[_][]const u8{ extension, ".tbd" }) |ext| { |
| 1541 | const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ without_ext, ext }); | 1577 | const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ without_ext, ext }); |
| ... | @@ -1543,15 +1579,15 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any | ... | @@ -1543,15 +1579,15 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any |
| 1543 | | 1579 | |
| 1544 | log.debug("trying dependency at fully resolved path {s}", .{full_path}); | 1580 | log.debug("trying dependency at fully resolved path {s}", .{full_path}); |
| 1545 | | 1581 | |
| 1546 | const did_parse_successfully = try self.parseDylib(full_path, .{ | 1582 | const did_parse_successfully = try self.parseDylib(full_path, dependent_libs, .{ |
| 1547 | .id = id.*, | 1583 | .id = dep_id.id, |
| 1548 | .syslibroot = syslibroot, | 1584 | .syslibroot = syslibroot, |
| 1549 | .is_dependent = true, | 1585 | .dependent = true, |
| 1550 | .dependent_libs = dependent_libs, | 1586 | .weak = weak, |
| 1551 | }); | 1587 | }); |
| 1552 | if (did_parse_successfully) break; | 1588 | if (did_parse_successfully) break; |
| 1553 | } else { | 1589 | } else { |
| 1554 | log.warn("unable to resolve dependency {s}", .{id.name}); | 1590 | log.warn("unable to resolve dependency {s}", .{dep_id.id.name}); |
| 1555 | } | 1591 | } |
| 1556 | } | 1592 | } |
| 1557 | } | 1593 | } |
| ... | @@ -3441,6 +3477,7 @@ fn addLoadDylibLC(self: *MachO, id: u16) !void { | ... | @@ -3441,6 +3477,7 @@ fn addLoadDylibLC(self: *MachO, id: u16) !void { |
| 3441 | const dylib_id = dylib.id orelse unreachable; | 3477 | const dylib_id = dylib.id orelse unreachable; |
| 3442 | var dylib_cmd = try macho.createLoadDylibCommand( | 3478 | var dylib_cmd = try macho.createLoadDylibCommand( |
| 3443 | self.base.allocator, | 3479 | self.base.allocator, |
| | 3480 | if (dylib.weak) .LOAD_WEAK_DYLIB else .LOAD_DYLIB, |
| 3444 | dylib_id.name, | 3481 | dylib_id.name, |
| 3445 | dylib_id.timestamp, | 3482 | dylib_id.timestamp, |
| 3446 | dylib_id.current_version, | 3483 | dylib_id.current_version, |
| ... | @@ -4885,13 +4922,13 @@ fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -4885,13 +4922,13 @@ fn populateMissingMetadata(self: *MachO) !void { |
| 4885 | std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 }; | 4922 | std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 }; |
| 4886 | var dylib_cmd = try macho.createLoadDylibCommand( | 4923 | var dylib_cmd = try macho.createLoadDylibCommand( |
| 4887 | self.base.allocator, | 4924 | self.base.allocator, |
| | 4925 | .ID_DYLIB, |
| 4888 | install_name, | 4926 | install_name, |
| 4889 | 2, | 4927 | 2, |
| 4890 | current_version.major << 16 | current_version.minor << 8 | current_version.patch, | 4928 | current_version.major << 16 | current_version.minor << 8 | current_version.patch, |
| 4891 | compat_version.major << 16 | compat_version.minor << 8 | compat_version.patch, | 4929 | compat_version.major << 16 | compat_version.minor << 8 | compat_version.patch, |
| 4892 | ); | 4930 | ); |
| 4893 | errdefer dylib_cmd.deinit(self.base.allocator); | 4931 | errdefer dylib_cmd.deinit(self.base.allocator); |
| 4894 | dylib_cmd.inner.cmd = .ID_DYLIB; | | |
| 4895 | try self.load_commands.append(self.base.allocator, .{ .dylib = dylib_cmd }); | 4932 | try self.load_commands.append(self.base.allocator, .{ .dylib = dylib_cmd }); |
| 4896 | self.load_commands_dirty = true; | 4933 | self.load_commands_dirty = true; |
| 4897 | } | 4934 | } |