| ... | ... | @@ -52,6 +52,11 @@ pub const SearchStrategy = enum { |
| 52 | 52 | dylibs_first, |
| 53 | 53 | }; |
| 54 | 54 | |
| 55 | const SystemLib = struct { |
| 56 | needed: bool = false, |
| 57 | weak: bool = false, |
| 58 | }; |
| 59 | |
| 55 | 60 | base: File, |
| 56 | 61 | |
| 57 | 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 | 773 | } |
| 769 | 774 | |
| 770 | 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 | 778 | const system_lib_names = self.base.options.system_libs.keys(); |
| 774 | 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 | 786 | } |
| 782 | 787 | |
| 783 | 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 | 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 | 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 | 806 | // Assume ld64 default -search_paths_first if no strategy specified. |
| 799 | 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 | 898 | for (framework_dirs.items) |dir| { |
| 891 | 899 | for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| { |
| 892 | 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 | 906 | continue :outer; |
| 895 | 907 | } |
| 896 | 908 | } |
| ... | ... | @@ -1026,9 +1038,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 1026 | 1038 | try argv.append("-lc"); |
| 1027 | 1039 | |
| 1028 | 1040 | for (self.base.options.system_libs.keys()) |l_name| { |
| 1029 | | const needed = self.base.options.system_libs.get(l_name).?.needed; |
| 1030 | | const arg = if (needed) |
| 1041 | const info = self.base.options.system_libs.get(l_name).?; |
| 1042 | const arg = if (info.needed) |
| 1031 | 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 | 1046 | else |
| 1033 | 1047 | try std.fmt.allocPrint(arena, "-l{s}", .{l_name}); |
| 1034 | 1048 | try argv.append(arg); |
| ... | ... | @@ -1039,9 +1053,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 1039 | 1053 | } |
| 1040 | 1054 | |
| 1041 | 1055 | for (self.base.options.frameworks.keys()) |framework| { |
| 1042 | | const needed = self.base.options.frameworks.get(framework).?.needed; |
| 1043 | | const arg = if (needed) |
| 1056 | const info = self.base.options.frameworks.get(framework).?; |
| 1057 | const arg = if (info.needed) |
| 1044 | 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 | 1061 | else |
| 1046 | 1062 | try std.fmt.allocPrint(arena, "-framework {s}", .{framework}); |
| 1047 | 1063 | try argv.append(arg); |
| ... | ... | @@ -1063,7 +1079,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 1063 | 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 | 1086 | defer dependent_libs.deinit(); |
| 1068 | 1087 | try self.parseInputFiles(positionals.items, self.base.options.sysroot, &dependent_libs); |
| 1069 | 1088 | try self.parseAndForceLoadStaticArchives(must_link_archives.keys()); |
| ... | ... | @@ -1389,13 +1408,18 @@ const ParseDylibError = error{ |
| 1389 | 1408 | |
| 1390 | 1409 | const DylibCreateOpts = struct { |
| 1391 | 1410 | syslibroot: ?[]const u8, |
| 1392 | | dependent_libs: *std.fifo.LinearFifo(Dylib.Id, .Dynamic), |
| 1393 | 1411 | id: ?Dylib.Id = null, |
| 1394 | | is_dependent: bool = false, |
| 1395 | | is_needed: bool = false, |
| 1412 | dependent: 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 | 1423 | const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) { |
| 1400 | 1424 | error.FileNotFound => return false, |
| 1401 | 1425 | else => |e| return e, |
| ... | ... | @@ -1405,12 +1429,19 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy |
| 1405 | 1429 | const name = try self.base.allocator.dupe(u8, path); |
| 1406 | 1430 | errdefer self.base.allocator.free(name); |
| 1407 | 1431 | |
| 1432 | const dylib_id = @intCast(u16, self.dylibs.items.len); |
| 1408 | 1433 | var dylib = Dylib{ |
| 1409 | 1434 | .name = name, |
| 1410 | 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 | 1445 | error.EndOfStream, error.NotDylib => { |
| 1415 | 1446 | try file.seekTo(0); |
| 1416 | 1447 | |
| ... | ... | @@ -1420,7 +1451,13 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy |
| 1420 | 1451 | }; |
| 1421 | 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 | 1462 | else => |e| return e, |
| 1426 | 1463 | }; |
| ... | ... | @@ -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 | 1478 | try self.dylibs.append(self.base.allocator, dylib); |
| 1443 | 1479 | try self.dylibs_map.putNoClobber(self.base.allocator, dylib.id.?.name, dylib_id); |
| 1444 | 1480 | |
| 1445 | 1481 | const should_link_dylib_even_if_unreachable = blk: { |
| 1446 | | if (self.base.options.dead_strip_dylibs and !opts.is_needed) break :blk false; |
| 1447 | | break :blk !(opts.is_dependent or self.referenced_dylibs.contains(dylib_id)); |
| 1482 | if (self.base.options.dead_strip_dylibs and !opts.needed) break :blk false; |
| 1483 | break :blk !(opts.dependent or self.referenced_dylibs.contains(dylib_id)); |
| 1448 | 1484 | }; |
| 1449 | 1485 | |
| 1450 | 1486 | if (should_link_dylib_even_if_unreachable) { |
| ... | ... | @@ -1467,9 +1503,8 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const |
| 1467 | 1503 | |
| 1468 | 1504 | if (try self.parseObject(full_path)) continue; |
| 1469 | 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 | 1507 | .syslibroot = syslibroot, |
| 1472 | | .dependent_libs = dependent_libs, |
| 1473 | 1508 | })) continue; |
| 1474 | 1509 | |
| 1475 | 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 | 1529 | fn parseLibs( |
| 1495 | 1530 | self: *MachO, |
| 1496 | 1531 | lib_names: []const []const u8, |
| 1497 | | lib_infos: []const Compilation.SystemLib, |
| 1532 | lib_infos: []const SystemLib, |
| 1498 | 1533 | syslibroot: ?[]const u8, |
| 1499 | 1534 | dependent_libs: anytype, |
| 1500 | 1535 | ) !void { |
| 1501 | 1536 | for (lib_names) |lib, i| { |
| 1502 | 1537 | const lib_info = lib_infos[i]; |
| 1503 | 1538 | log.debug("parsing lib path '{s}'", .{lib}); |
| 1504 | | if (try self.parseDylib(lib, .{ |
| 1539 | if (try self.parseDylib(lib, dependent_libs, .{ |
| 1505 | 1540 | .syslibroot = syslibroot, |
| 1506 | | .dependent_libs = dependent_libs, |
| 1507 | | .is_needed = lib_info.needed, |
| 1541 | .needed = lib_info.needed, |
| 1542 | .weak = lib_info.weak, |
| 1508 | 1543 | })) continue; |
| 1509 | 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 | 1557 | const arena = arena_alloc.allocator(); |
| 1523 | 1558 | defer arena_alloc.deinit(); |
| 1524 | 1559 | |
| 1525 | | while (dependent_libs.readItem()) |*id| { |
| 1526 | | defer id.deinit(self.base.allocator); |
| 1560 | while (dependent_libs.readItem()) |*dep_id| { |
| 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 | 1566 | const has_ext = blk: { |
| 1531 | | const basename = fs.path.basename(id.name); |
| 1567 | const basename = fs.path.basename(dep_id.id.name); |
| 1532 | 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 | 1571 | const without_ext = if (has_ext) blk: { |
| 1536 | | const index = mem.lastIndexOfScalar(u8, id.name, '.') orelse unreachable; |
| 1537 | | break :blk id.name[0..index]; |
| 1538 | | } else id.name; |
| 1572 | const index = mem.lastIndexOfScalar(u8, dep_id.id.name, '.') orelse unreachable; |
| 1573 | break :blk dep_id.id.name[0..index]; |
| 1574 | } else dep_id.id.name; |
| 1539 | 1575 | |
| 1540 | 1576 | for (&[_][]const u8{ extension, ".tbd" }) |ext| { |
| 1541 | 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 | 1579 | |
| 1544 | 1580 | log.debug("trying dependency at fully resolved path {s}", .{full_path}); |
| 1545 | 1581 | |
| 1546 | | const did_parse_successfully = try self.parseDylib(full_path, .{ |
| 1547 | | .id = id.*, |
| 1582 | const did_parse_successfully = try self.parseDylib(full_path, dependent_libs, .{ |
| 1583 | .id = dep_id.id, |
| 1548 | 1584 | .syslibroot = syslibroot, |
| 1549 | | .is_dependent = true, |
| 1550 | | .dependent_libs = dependent_libs, |
| 1585 | .dependent = true, |
| 1586 | .weak = weak, |
| 1551 | 1587 | }); |
| 1552 | 1588 | if (did_parse_successfully) break; |
| 1553 | 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 | 3477 | const dylib_id = dylib.id orelse unreachable; |
| 3442 | 3478 | var dylib_cmd = try macho.createLoadDylibCommand( |
| 3443 | 3479 | self.base.allocator, |
| 3480 | if (dylib.weak) .LOAD_WEAK_DYLIB else .LOAD_DYLIB, |
| 3444 | 3481 | dylib_id.name, |
| 3445 | 3482 | dylib_id.timestamp, |
| 3446 | 3483 | dylib_id.current_version, |
| ... | ... | @@ -4885,13 +4922,13 @@ fn populateMissingMetadata(self: *MachO) !void { |
| 4885 | 4922 | std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 }; |
| 4886 | 4923 | var dylib_cmd = try macho.createLoadDylibCommand( |
| 4887 | 4924 | self.base.allocator, |
| 4925 | .ID_DYLIB, |
| 4888 | 4926 | install_name, |
| 4889 | 4927 | 2, |
| 4890 | 4928 | current_version.major << 16 | current_version.minor << 8 | current_version.patch, |
| 4891 | 4929 | compat_version.major << 16 | compat_version.minor << 8 | compat_version.patch, |
| 4892 | 4930 | ); |
| 4893 | 4931 | errdefer dylib_cmd.deinit(self.base.allocator); |
| 4894 | | dylib_cmd.inner.cmd = .ID_DYLIB; |
| 4895 | 4932 | try self.load_commands.append(self.base.allocator, .{ .dylib = dylib_cmd }); |
| 4896 | 4933 | self.load_commands_dirty = true; |
| 4897 | 4934 | } |