| ... | ... | @@ -396,11 +396,17 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 396 | 396 | self.dylibs_map.clearRetainingCapacity(); |
| 397 | 397 | self.referenced_dylibs.clearRetainingCapacity(); |
| 398 | 398 | |
| 399 | const cpu_arch = self.base.options.target.cpu.arch; |
| 399 | 400 | var dependent_libs = std.fifo.LinearFifo(struct { |
| 400 | 401 | id: Dylib.Id, |
| 401 | 402 | parent: u16, |
| 402 | 403 | }, .Dynamic).init(arena); |
| 403 | 404 | |
| 405 | var parse_error_ctx: union { |
| 406 | none: void, |
| 407 | detected_arch: std.Target.Cpu.Arch, |
| 408 | } = .{ .none = {} }; |
| 409 | |
| 404 | 410 | for (libs.keys(), libs.values()) |path, lib| { |
| 405 | 411 | const in_file = try std.fs.cwd().openFile(path, .{}); |
| 406 | 412 | defer in_file.close(); |
| ... | ... | @@ -411,15 +417,28 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 411 | 417 | lib, |
| 412 | 418 | false, |
| 413 | 419 | &dependent_libs, |
| 414 | | &self.base.options, |
| 415 | | ) catch |err| { |
| 416 | | // TODO convert to error |
| 417 | | log.err("{s}: parsing library failed with err {s}", .{ path, @errorName(err) }); |
| 418 | | continue; |
| 420 | &parse_error_ctx, |
| 421 | ) catch |err| switch (err) { |
| 422 | error.UnknownFileType => try self.reportParseError(path, "unknown file type", .{}), |
| 423 | error.MissingArchFatLib => try self.reportParseError( |
| 424 | path, |
| 425 | "missing architecture in universal file, expected '{s}'", |
| 426 | .{@tagName(cpu_arch)}, |
| 427 | ), |
| 428 | error.InvalidArch => try self.reportParseError( |
| 429 | path, |
| 430 | "invalid architecture '{s}', expected '{s}'", |
| 431 | .{ @tagName(parse_error_ctx.detected_arch), @tagName(cpu_arch) }, |
| 432 | ), |
| 433 | else => |e| try self.reportParseError( |
| 434 | path, |
| 435 | "parsing library failed with error '{s}'", |
| 436 | .{@errorName(e)}, |
| 437 | ), |
| 419 | 438 | }; |
| 420 | 439 | } |
| 421 | 440 | |
| 422 | | self.parseDependentLibs(&dependent_libs, &self.base.options) catch |err| { |
| 441 | self.parseDependentLibs(&dependent_libs, &parse_error_ctx) catch |err| { |
| 423 | 442 | // TODO convert to error |
| 424 | 443 | log.err("parsing dependent libraries failed with err {s}", .{@errorName(err)}); |
| 425 | 444 | }; |
| ... | ... | @@ -710,19 +729,19 @@ pub fn parsePositional( |
| 710 | 729 | path: []const u8, |
| 711 | 730 | must_link: bool, |
| 712 | 731 | dependent_libs: anytype, |
| 713 | | link_options: *const link.Options, |
| 732 | error_ctx: anytype, |
| 714 | 733 | ) !void { |
| 715 | 734 | const tracy = trace(@src()); |
| 716 | 735 | defer tracy.end(); |
| 717 | 736 | |
| 718 | 737 | if (Object.isObject(file)) { |
| 719 | | try self.parseObject(file, path, link_options); |
| 738 | try self.parseObject(file, path, error_ctx); |
| 720 | 739 | } else { |
| 721 | 740 | try self.parseLibrary(file, path, .{ |
| 722 | 741 | .path = null, |
| 723 | 742 | .needed = false, |
| 724 | 743 | .weak = false, |
| 725 | | }, must_link, dependent_libs, link_options); |
| 744 | }, must_link, dependent_libs, error_ctx); |
| 726 | 745 | } |
| 727 | 746 | } |
| 728 | 747 | |
| ... | ... | @@ -730,7 +749,7 @@ fn parseObject( |
| 730 | 749 | self: *MachO, |
| 731 | 750 | file: std.fs.File, |
| 732 | 751 | path: []const u8, |
| 733 | | link_options: *const link.Options, |
| 752 | error_ctx: anytype, |
| 734 | 753 | ) !void { |
| 735 | 754 | const tracy = trace(@src()); |
| 736 | 755 | defer tracy.end(); |
| ... | ... | @@ -758,15 +777,11 @@ fn parseObject( |
| 758 | 777 | macho.CPU_TYPE_X86_64 => .x86_64, |
| 759 | 778 | else => unreachable, |
| 760 | 779 | }; |
| 761 | | const self_cpu_arch = link_options.target.cpu.arch; |
| 780 | const self_cpu_arch = self.base.options.target.cpu.arch; |
| 762 | 781 | |
| 763 | 782 | if (self_cpu_arch != cpu_arch) { |
| 764 | | // TODO convert into an error |
| 765 | | log.err("{s}: invalid architecture '{s}', expected '{s}'", .{ |
| 766 | | path, |
| 767 | | @tagName(cpu_arch), |
| 768 | | @tagName(self_cpu_arch), |
| 769 | | }); |
| 783 | error_ctx.* = .{ .detected_arch = cpu_arch }; |
| 784 | return error.InvalidArch; |
| 770 | 785 | } |
| 771 | 786 | } |
| 772 | 787 | |
| ... | ... | @@ -777,70 +792,50 @@ pub fn parseLibrary( |
| 777 | 792 | lib: link.SystemLib, |
| 778 | 793 | must_link: bool, |
| 779 | 794 | dependent_libs: anytype, |
| 780 | | link_options: *const link.Options, |
| 795 | error_ctx: anytype, |
| 781 | 796 | ) !void { |
| 782 | 797 | const tracy = trace(@src()); |
| 783 | 798 | defer tracy.end(); |
| 784 | 799 | |
| 785 | | const cpu_arch = link_options.target.cpu.arch; |
| 800 | const cpu_arch = self.base.options.target.cpu.arch; |
| 786 | 801 | |
| 787 | 802 | if (fat.isFatLibrary(file)) { |
| 788 | | const offset = self.parseFatLibrary(file, path, cpu_arch) catch |err| switch (err) { |
| 789 | | error.MissingArch => return, |
| 790 | | else => |e| return e, |
| 791 | | }; |
| 803 | const offset = try self.parseFatLibrary(file, cpu_arch); |
| 792 | 804 | try file.seekTo(offset); |
| 793 | 805 | |
| 794 | 806 | if (Archive.isArchive(file, offset)) { |
| 795 | | try self.parseArchive(path, offset, must_link, cpu_arch); |
| 807 | try self.parseArchive(path, offset, must_link, cpu_arch, error_ctx); |
| 796 | 808 | } else if (Dylib.isDylib(file, offset)) { |
| 797 | | try self.parseDylib(file, path, offset, dependent_libs, link_options, .{ |
| 809 | try self.parseDylib(file, path, offset, dependent_libs, .{ |
| 798 | 810 | .needed = lib.needed, |
| 799 | 811 | .weak = lib.weak, |
| 800 | | }); |
| 801 | | } else { |
| 802 | | // TODO convert into an error |
| 803 | | log.err("{s}: unknown file type", .{path}); |
| 804 | | return; |
| 805 | | } |
| 812 | }, error_ctx); |
| 813 | } else return error.UnknownFileType; |
| 806 | 814 | } else if (Archive.isArchive(file, 0)) { |
| 807 | | try self.parseArchive(path, 0, must_link, cpu_arch); |
| 815 | try self.parseArchive(path, 0, must_link, cpu_arch, error_ctx); |
| 808 | 816 | } else if (Dylib.isDylib(file, 0)) { |
| 809 | | try self.parseDylib(file, path, 0, dependent_libs, link_options, .{ |
| 817 | try self.parseDylib(file, path, 0, dependent_libs, .{ |
| 810 | 818 | .needed = lib.needed, |
| 811 | 819 | .weak = lib.weak, |
| 812 | | }); |
| 820 | }, error_ctx); |
| 813 | 821 | } else { |
| 814 | | self.parseLibStub(file, path, dependent_libs, link_options, .{ |
| 822 | self.parseLibStub(file, path, dependent_libs, .{ |
| 815 | 823 | .needed = lib.needed, |
| 816 | 824 | .weak = lib.weak, |
| 817 | 825 | }) catch |err| switch (err) { |
| 818 | | error.NotLibStub, error.UnexpectedToken => { |
| 819 | | // TODO convert into an error |
| 820 | | log.err("{s}: unknown file type", .{path}); |
| 821 | | return; |
| 822 | | }, |
| 826 | error.NotLibStub, error.UnexpectedToken => return error.UnknownFileType, |
| 823 | 827 | else => |e| return e, |
| 824 | 828 | }; |
| 825 | 829 | } |
| 826 | 830 | } |
| 827 | 831 | |
| 828 | | pub fn parseFatLibrary( |
| 829 | | self: *MachO, |
| 830 | | file: std.fs.File, |
| 831 | | path: []const u8, |
| 832 | | cpu_arch: std.Target.Cpu.Arch, |
| 833 | | ) !u64 { |
| 832 | pub fn parseFatLibrary(self: *MachO, file: std.fs.File, cpu_arch: std.Target.Cpu.Arch) !u64 { |
| 834 | 833 | _ = self; |
| 835 | 834 | var buffer: [2]fat.Arch = undefined; |
| 836 | 835 | const fat_archs = try fat.parseArchs(file, &buffer); |
| 837 | 836 | const offset = for (fat_archs) |arch| { |
| 838 | 837 | if (arch.tag == cpu_arch) break arch.offset; |
| 839 | | } else { |
| 840 | | // TODO convert into an error |
| 841 | | log.err("{s}: missing arch in universal file: expected {s}", .{ path, @tagName(cpu_arch) }); |
| 842 | | return error.MissingArch; |
| 843 | | }; |
| 838 | } else return error.MissingArchFatLib; |
| 844 | 839 | return offset; |
| 845 | 840 | } |
| 846 | 841 | |
| ... | ... | @@ -850,13 +845,13 @@ fn parseArchive( |
| 850 | 845 | fat_offset: u64, |
| 851 | 846 | must_link: bool, |
| 852 | 847 | cpu_arch: std.Target.Cpu.Arch, |
| 848 | error_ctx: anytype, |
| 853 | 849 | ) !void { |
| 854 | 850 | const gpa = self.base.allocator; |
| 855 | 851 | |
| 856 | 852 | // We take ownership of the file so that we can store it for the duration of symbol resolution. |
| 857 | 853 | // TODO we shouldn't need to do that and could pre-parse the archive like we do for zld/ELF? |
| 858 | 854 | const file = try std.fs.cwd().openFile(path, .{}); |
| 859 | | errdefer file.close(); |
| 860 | 855 | try file.seekTo(fat_offset); |
| 861 | 856 | |
| 862 | 857 | var archive = Archive{ |
| ... | ... | @@ -882,13 +877,8 @@ fn parseArchive( |
| 882 | 877 | else => unreachable, |
| 883 | 878 | }; |
| 884 | 879 | if (cpu_arch != parsed_cpu_arch) { |
| 885 | | // TODO convert into an error |
| 886 | | log.err("{s}: invalid architecture in archive '{s}', expected '{s}'", .{ |
| 887 | | path, |
| 888 | | @tagName(parsed_cpu_arch), |
| 889 | | @tagName(cpu_arch), |
| 890 | | }); |
| 891 | | return error.MissingArch; |
| 880 | error_ctx.* = .{ .detected_arch = parsed_cpu_arch }; |
| 881 | return error.InvalidArch; |
| 892 | 882 | } |
| 893 | 883 | } |
| 894 | 884 | |
| ... | ... | @@ -923,11 +913,11 @@ fn parseDylib( |
| 923 | 913 | path: []const u8, |
| 924 | 914 | offset: u64, |
| 925 | 915 | dependent_libs: anytype, |
| 926 | | link_options: *const link.Options, |
| 927 | 916 | dylib_options: DylibOpts, |
| 917 | error_ctx: anytype, |
| 928 | 918 | ) !void { |
| 929 | 919 | const gpa = self.base.allocator; |
| 930 | | const self_cpu_arch = link_options.target.cpu.arch; |
| 920 | const self_cpu_arch = self.base.options.target.cpu.arch; |
| 931 | 921 | |
| 932 | 922 | const file_stat = try file.stat(); |
| 933 | 923 | const file_size = math.cast(usize, file_stat.size - offset) orelse return error.Overflow; |
| ... | ... | @@ -952,18 +942,13 @@ fn parseDylib( |
| 952 | 942 | else => unreachable, |
| 953 | 943 | }; |
| 954 | 944 | if (self_cpu_arch != cpu_arch) { |
| 955 | | // TODO convert into an error |
| 956 | | log.err("{s}: invalid architecture '{s}', expected '{s}'", .{ |
| 957 | | path, |
| 958 | | @tagName(cpu_arch), |
| 959 | | @tagName(self_cpu_arch), |
| 960 | | }); |
| 961 | | return error.MissingArch; |
| 945 | error_ctx.* = .{ .detected_arch = cpu_arch }; |
| 946 | return error.InvalidArch; |
| 962 | 947 | } |
| 963 | 948 | |
| 964 | 949 | // TODO verify platform |
| 965 | 950 | |
| 966 | | self.addDylib(dylib, link_options, .{ |
| 951 | self.addDylib(dylib, .{ |
| 967 | 952 | .needed = dylib_options.needed, |
| 968 | 953 | .weak = dylib_options.weak, |
| 969 | 954 | }) catch |err| switch (err) { |
| ... | ... | @@ -977,7 +962,6 @@ fn parseLibStub( |
| 977 | 962 | file: std.fs.File, |
| 978 | 963 | path: []const u8, |
| 979 | 964 | dependent_libs: anytype, |
| 980 | | link_options: *const link.Options, |
| 981 | 965 | dylib_options: DylibOpts, |
| 982 | 966 | ) !void { |
| 983 | 967 | const gpa = self.base.allocator; |
| ... | ... | @@ -993,14 +977,14 @@ fn parseLibStub( |
| 993 | 977 | |
| 994 | 978 | try dylib.parseFromStub( |
| 995 | 979 | gpa, |
| 996 | | link_options.target, |
| 980 | self.base.options.target, |
| 997 | 981 | lib_stub, |
| 998 | 982 | @intCast(self.dylibs.items.len), // TODO defer it till later |
| 999 | 983 | dependent_libs, |
| 1000 | 984 | path, |
| 1001 | 985 | ); |
| 1002 | 986 | |
| 1003 | | self.addDylib(dylib, link_options, .{ |
| 987 | self.addDylib(dylib, .{ |
| 1004 | 988 | .needed = dylib_options.needed, |
| 1005 | 989 | .weak = dylib_options.weak, |
| 1006 | 990 | }) catch |err| switch (err) { |
| ... | ... | @@ -1009,12 +993,7 @@ fn parseLibStub( |
| 1009 | 993 | }; |
| 1010 | 994 | } |
| 1011 | 995 | |
| 1012 | | fn addDylib( |
| 1013 | | self: *MachO, |
| 1014 | | dylib: Dylib, |
| 1015 | | link_options: *const link.Options, |
| 1016 | | dylib_options: DylibOpts, |
| 1017 | | ) !void { |
| 996 | fn addDylib(self: *MachO, dylib: Dylib, dylib_options: DylibOpts) !void { |
| 1018 | 997 | if (dylib_options.id) |id| { |
| 1019 | 998 | if (dylib.id.?.current_version < id.compatibility_version) { |
| 1020 | 999 | // TODO convert into an error |
| ... | ... | @@ -1034,7 +1013,7 @@ fn addDylib( |
| 1034 | 1013 | try self.dylibs.append(gpa, dylib); |
| 1035 | 1014 | |
| 1036 | 1015 | const should_link_dylib_even_if_unreachable = blk: { |
| 1037 | | if (link_options.dead_strip_dylibs and !dylib_options.needed) break :blk false; |
| 1016 | if (self.base.options.dead_strip_dylibs and !dylib_options.needed) break :blk false; |
| 1038 | 1017 | break :blk !(dylib_options.dependent or self.referenced_dylibs.contains(gop.value_ptr.*)); |
| 1039 | 1018 | }; |
| 1040 | 1019 | |
| ... | ... | @@ -1043,7 +1022,7 @@ fn addDylib( |
| 1043 | 1022 | } |
| 1044 | 1023 | } |
| 1045 | 1024 | |
| 1046 | | pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype, link_options: *const link.Options) !void { |
| 1025 | pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype, error_ctx: anytype) !void { |
| 1047 | 1026 | const tracy = trace(@src()); |
| 1048 | 1027 | defer tracy.end(); |
| 1049 | 1028 | |
| ... | ... | @@ -1075,7 +1054,7 @@ pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype, link_options: * |
| 1075 | 1054 | |
| 1076 | 1055 | for (&[_][]const u8{ extension, ".tbd" }) |ext| { |
| 1077 | 1056 | const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ without_ext, ext }); |
| 1078 | | const full_path = if (link_options.sysroot) |root| |
| 1057 | const full_path = if (self.base.options.sysroot) |root| |
| 1079 | 1058 | try fs.path.join(arena, &.{ root, with_ext }) |
| 1080 | 1059 | else |
| 1081 | 1060 | with_ext; |
| ... | ... | @@ -1089,21 +1068,18 @@ pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype, link_options: * |
| 1089 | 1068 | log.debug("trying dependency at fully resolved path {s}", .{full_path}); |
| 1090 | 1069 | |
| 1091 | 1070 | const offset: u64 = if (fat.isFatLibrary(file)) blk: { |
| 1092 | | const offset = self.parseFatLibrary(file, full_path, link_options.target.cpu.arch) catch |err| switch (err) { |
| 1093 | | error.MissingArch => break, |
| 1094 | | else => |e| return e, |
| 1095 | | }; |
| 1071 | const offset = try self.parseFatLibrary(file, self.base.options.target.cpu.arch); |
| 1096 | 1072 | try file.seekTo(offset); |
| 1097 | 1073 | break :blk offset; |
| 1098 | 1074 | } else 0; |
| 1099 | 1075 | |
| 1100 | 1076 | if (Dylib.isDylib(file, offset)) { |
| 1101 | | try self.parseDylib(file, full_path, offset, dependent_libs, link_options, .{ |
| 1077 | try self.parseDylib(file, full_path, offset, dependent_libs, .{ |
| 1102 | 1078 | .dependent = true, |
| 1103 | 1079 | .weak = weak, |
| 1104 | | }); |
| 1080 | }, error_ctx); |
| 1105 | 1081 | } else { |
| 1106 | | self.parseLibStub(file, full_path, dependent_libs, link_options, .{ |
| 1082 | self.parseLibStub(file, full_path, dependent_libs, .{ |
| 1107 | 1083 | .dependent = true, |
| 1108 | 1084 | .weak = weak, |
| 1109 | 1085 | }) catch |err| switch (err) { |
| ... | ... | @@ -4836,6 +4812,18 @@ pub fn getSectionPrecedence(header: macho.section_64) u8 { |
| 4836 | 4812 | return (@as(u8, @intCast(segment_precedence)) << 4) + section_precedence; |
| 4837 | 4813 | } |
| 4838 | 4814 | |
| 4815 | pub fn reportParseError(self: *MachO, path: []const u8, comptime format: []const u8, args: anytype) !void { |
| 4816 | const gpa = self.base.allocator; |
| 4817 | try self.misc_errors.ensureUnusedCapacity(gpa, 1); |
| 4818 | var notes = try gpa.alloc(File.ErrorMsg, 1); |
| 4819 | errdefer gpa.free(notes); |
| 4820 | notes[0] = .{ .msg = try std.fmt.allocPrint(gpa, "while parsing {s}", .{path}) }; |
| 4821 | self.misc_errors.appendAssumeCapacity(.{ |
| 4822 | .msg = try std.fmt.allocPrint(gpa, format, args), |
| 4823 | .notes = notes, |
| 4824 | }); |
| 4825 | } |
| 4826 | |
| 4839 | 4827 | pub fn reportUndefined(self: *MachO) error{OutOfMemory}!void { |
| 4840 | 4828 | const gpa = self.base.allocator; |
| 4841 | 4829 | const count = self.unresolved.count(); |