| author | |
| committer | |
| log | 2c68fb3d7ce077fba711747ee7b05b2fa0df6bcc |
| tree | 291b3aec840358c08a36c78c468c54fdc34727f0 |
| parent | 42e0850d78e63fcc602dd0e167ac90dfb3cfec02 |
21 files changed, 3042 insertions(+), 4119 deletions(-)
src/link/MachO.zig+1173-218| ... | @@ -405,9 +405,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No | ... | @@ -405,9 +405,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 405 | const in_file = try std.fs.cwd().openFile(path, .{}); | 405 | const in_file = try std.fs.cwd().openFile(path, .{}); |
| 406 | defer in_file.close(); | 406 | defer in_file.close(); |
| 407 | 407 | ||
| 408 | parseLibrary( | 408 | self.parseLibrary( |
| 409 | self, | ||
| 410 | self.base.allocator, | ||
| 411 | in_file, | 409 | in_file, |
| 412 | path, | 410 | path, |
| 413 | lib, | 411 | lib, |
| ... | @@ -421,24 +419,15 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No | ... | @@ -421,24 +419,15 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 421 | }; | 419 | }; |
| 422 | } | 420 | } |
| 423 | 421 | ||
| 424 | parseDependentLibs(self, self.base.allocator, &dependent_libs, &self.base.options) catch |err| { | 422 | self.parseDependentLibs(&dependent_libs, &self.base.options) catch |err| { |
| 425 | // TODO convert to error | 423 | // TODO convert to error |
| 426 | log.err("parsing dependent libraries failed with err {s}", .{@errorName(err)}); | 424 | log.err("parsing dependent libraries failed with err {s}", .{@errorName(err)}); |
| 427 | }; | 425 | }; |
| 428 | } | 426 | } |
| 429 | 427 | ||
| 430 | if (self.dyld_stub_binder_index == null) { | ||
| 431 | self.dyld_stub_binder_index = try self.addUndefined("dyld_stub_binder", .add_got); | ||
| 432 | } | ||
| 433 | if (!self.base.options.single_threaded) { | ||
| 434 | _ = try self.addUndefined("__tlv_bootstrap", .none); | ||
| 435 | } | ||
| 436 | |||
| 437 | try self.createMhExecuteHeaderSymbol(); | ||
| 438 | |||
| 439 | var actions = std.ArrayList(ResolveAction).init(self.base.allocator); | 428 | var actions = std.ArrayList(ResolveAction).init(self.base.allocator); |
| 440 | defer actions.deinit(); | 429 | defer actions.deinit(); |
| 441 | try self.resolveSymbolsInDylibs(&actions); | 430 | try self.resolveSymbols(&actions); |
| 442 | 431 | ||
| 443 | if (self.getEntryPoint() == null) { | 432 | if (self.getEntryPoint() == null) { |
| 444 | self.error_flags.no_entry_point_found = true; | 433 | self.error_flags.no_entry_point_found = true; |
| ... | @@ -527,14 +516,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No | ... | @@ -527,14 +516,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 527 | 516 | ||
| 528 | try self.writeLinkeditSegmentData(); | 517 | try self.writeLinkeditSegmentData(); |
| 529 | 518 | ||
| 530 | const target = self.base.options.target; | 519 | var codesig: ?CodeSignature = if (self.requiresCodeSignature()) blk: { |
| 531 | const requires_codesig = blk: { | ||
| 532 | if (self.base.options.entitlements) |_| break :blk true; | ||
| 533 | if (target.cpu.arch == .aarch64 and (target.os.tag == .macos or target.abi == .simulator)) | ||
| 534 | break :blk true; | ||
| 535 | break :blk false; | ||
| 536 | }; | ||
| 537 | var codesig: ?CodeSignature = if (requires_codesig) blk: { | ||
| 538 | // Preallocate space for the code signature. | 520 | // Preallocate space for the code signature. |
| 539 | // We need to do this at this stage so that we have the load commands with proper values | 521 | // We need to do this at this stage so that we have the load commands with proper values |
| 540 | // written out to the file. | 522 | // written out to the file. |
| ... | @@ -596,14 +578,14 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No | ... | @@ -596,14 +578,14 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 596 | 578 | ||
| 597 | try load_commands.writeLoadDylibLCs(self.dylibs.items, self.referenced_dylibs.keys(), lc_writer); | 579 | try load_commands.writeLoadDylibLCs(self.dylibs.items, self.referenced_dylibs.keys(), lc_writer); |
| 598 | 580 | ||
| 599 | if (requires_codesig) { | 581 | if (codesig != null) { |
| 600 | try lc_writer.writeStruct(self.codesig_cmd); | 582 | try lc_writer.writeStruct(self.codesig_cmd); |
| 601 | } | 583 | } |
| 602 | 584 | ||
| 603 | const ncmds = load_commands.calcNumOfLCs(lc_buffer.items); | 585 | const ncmds = load_commands.calcNumOfLCs(lc_buffer.items); |
| 604 | try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64)); | 586 | try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64)); |
| 605 | try self.writeHeader(ncmds, @as(u32, @intCast(lc_buffer.items.len))); | 587 | try self.writeHeader(ncmds, @as(u32, @intCast(lc_buffer.items.len))); |
| 606 | try self.writeUuid(comp, uuid_cmd_offset, requires_codesig); | 588 | try self.writeUuid(comp, uuid_cmd_offset, codesig != null); |
| 607 | 589 | ||
| 608 | if (codesig) |*csig| { | 590 | if (codesig) |*csig| { |
| 609 | try self.writeCodeSignature(comp, csig); // code signing always comes last | 591 | try self.writeCodeSignature(comp, csig); // code signing always comes last |
| ... | @@ -729,8 +711,7 @@ fn resolveLib( | ... | @@ -729,8 +711,7 @@ fn resolveLib( |
| 729 | } | 711 | } |
| 730 | 712 | ||
| 731 | pub fn parsePositional( | 713 | pub fn parsePositional( |
| 732 | ctx: anytype, | 714 | self: *MachO, |
| 733 | gpa: Allocator, | ||
| 734 | file: std.fs.File, | 715 | file: std.fs.File, |
| 735 | path: []const u8, | 716 | path: []const u8, |
| 736 | must_link: bool, | 717 | must_link: bool, |
| ... | @@ -741,9 +722,9 @@ pub fn parsePositional( | ... | @@ -741,9 +722,9 @@ pub fn parsePositional( |
| 741 | defer tracy.end(); | 722 | defer tracy.end(); |
| 742 | 723 | ||
| 743 | if (Object.isObject(file)) { | 724 | if (Object.isObject(file)) { |
| 744 | try parseObject(ctx, gpa, file, path, link_options); | 725 | try self.parseObject(file, path, link_options); |
| 745 | } else { | 726 | } else { |
| 746 | try parseLibrary(ctx, gpa, file, path, .{ | 727 | try self.parseLibrary(file, path, .{ |
| 747 | .path = null, | 728 | .path = null, |
| 748 | .needed = false, | 729 | .needed = false, |
| 749 | .weak = false, | 730 | .weak = false, |
| ... | @@ -752,8 +733,7 @@ pub fn parsePositional( | ... | @@ -752,8 +733,7 @@ pub fn parsePositional( |
| 752 | } | 733 | } |
| 753 | 734 | ||
| 754 | fn parseObject( | 735 | fn parseObject( |
| 755 | ctx: anytype, | 736 | self: *MachO, |
| 756 | gpa: Allocator, | ||
| 757 | file: std.fs.File, | 737 | file: std.fs.File, |
| 758 | path: []const u8, | 738 | path: []const u8, |
| 759 | link_options: *const link.Options, | 739 | link_options: *const link.Options, |
| ... | @@ -761,6 +741,7 @@ fn parseObject( | ... | @@ -761,6 +741,7 @@ fn parseObject( |
| 761 | const tracy = trace(@src()); | 741 | const tracy = trace(@src()); |
| 762 | defer tracy.end(); | 742 | defer tracy.end(); |
| 763 | 743 | ||
| 744 | const gpa = self.base.allocator; | ||
| 764 | const mtime: u64 = mtime: { | 745 | const mtime: u64 = mtime: { |
| 765 | const stat = file.stat() catch break :mtime 0; | 746 | const stat = file.stat() catch break :mtime 0; |
| 766 | break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000))); | 747 | break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000))); |
| ... | @@ -776,7 +757,7 @@ fn parseObject( | ... | @@ -776,7 +757,7 @@ fn parseObject( |
| 776 | }; | 757 | }; |
| 777 | errdefer object.deinit(gpa); | 758 | errdefer object.deinit(gpa); |
| 778 | try object.parse(gpa); | 759 | try object.parse(gpa); |
| 779 | try ctx.objects.append(gpa, object); | 760 | try self.objects.append(gpa, object); |
| 780 | 761 | ||
| 781 | const cpu_arch: std.Target.Cpu.Arch = switch (object.header.cputype) { | 762 | const cpu_arch: std.Target.Cpu.Arch = switch (object.header.cputype) { |
| 782 | macho.CPU_TYPE_ARM64 => .aarch64, | 763 | macho.CPU_TYPE_ARM64 => .aarch64, |
| ... | @@ -796,8 +777,7 @@ fn parseObject( | ... | @@ -796,8 +777,7 @@ fn parseObject( |
| 796 | } | 777 | } |
| 797 | 778 | ||
| 798 | pub fn parseLibrary( | 779 | pub fn parseLibrary( |
| 799 | ctx: anytype, | 780 | self: *MachO, |
| 800 | gpa: Allocator, | ||
| 801 | file: std.fs.File, | 781 | file: std.fs.File, |
| 802 | path: []const u8, | 782 | path: []const u8, |
| 803 | lib: link.SystemLib, | 783 | lib: link.SystemLib, |
| ... | @@ -811,16 +791,16 @@ pub fn parseLibrary( | ... | @@ -811,16 +791,16 @@ pub fn parseLibrary( |
| 811 | const cpu_arch = link_options.target.cpu.arch; | 791 | const cpu_arch = link_options.target.cpu.arch; |
| 812 | 792 | ||
| 813 | if (fat.isFatLibrary(file)) { | 793 | if (fat.isFatLibrary(file)) { |
| 814 | const offset = parseFatLibrary(ctx, file, path, cpu_arch) catch |err| switch (err) { | 794 | const offset = self.parseFatLibrary(file, path, cpu_arch) catch |err| switch (err) { |
| 815 | error.MissingArch => return, | 795 | error.MissingArch => return, |
| 816 | else => |e| return e, | 796 | else => |e| return e, |
| 817 | }; | 797 | }; |
| 818 | try file.seekTo(offset); | 798 | try file.seekTo(offset); |
| 819 | 799 | ||
| 820 | if (Archive.isArchive(file, offset)) { | 800 | if (Archive.isArchive(file, offset)) { |
| 821 | try parseArchive(ctx, gpa, path, offset, must_link, cpu_arch); | 801 | try self.parseArchive(path, offset, must_link, cpu_arch); |
| 822 | } else if (Dylib.isDylib(file, offset)) { | 802 | } else if (Dylib.isDylib(file, offset)) { |
| 823 | try parseDylib(ctx, gpa, file, path, offset, dependent_libs, link_options, .{ | 803 | try self.parseDylib(file, path, offset, dependent_libs, link_options, .{ |
| 824 | .needed = lib.needed, | 804 | .needed = lib.needed, |
| 825 | .weak = lib.weak, | 805 | .weak = lib.weak, |
| 826 | }); | 806 | }); |
| ... | @@ -830,14 +810,14 @@ pub fn parseLibrary( | ... | @@ -830,14 +810,14 @@ pub fn parseLibrary( |
| 830 | return; | 810 | return; |
| 831 | } | 811 | } |
| 832 | } else if (Archive.isArchive(file, 0)) { | 812 | } else if (Archive.isArchive(file, 0)) { |
| 833 | try parseArchive(ctx, gpa, path, 0, must_link, cpu_arch); | 813 | try self.parseArchive(path, 0, must_link, cpu_arch); |
| 834 | } else if (Dylib.isDylib(file, 0)) { | 814 | } else if (Dylib.isDylib(file, 0)) { |
| 835 | try parseDylib(ctx, gpa, file, path, 0, dependent_libs, link_options, .{ | 815 | try self.parseDylib(file, path, 0, dependent_libs, link_options, .{ |
| 836 | .needed = lib.needed, | 816 | .needed = lib.needed, |
| 837 | .weak = lib.weak, | 817 | .weak = lib.weak, |
| 838 | }); | 818 | }); |
| 839 | } else { | 819 | } else { |
| 840 | parseLibStub(ctx, gpa, file, path, dependent_libs, link_options, .{ | 820 | self.parseLibStub(file, path, dependent_libs, link_options, .{ |
| 841 | .needed = lib.needed, | 821 | .needed = lib.needed, |
| 842 | .weak = lib.weak, | 822 | .weak = lib.weak, |
| 843 | }) catch |err| switch (err) { | 823 | }) catch |err| switch (err) { |
| ... | @@ -852,12 +832,12 @@ pub fn parseLibrary( | ... | @@ -852,12 +832,12 @@ pub fn parseLibrary( |
| 852 | } | 832 | } |
| 853 | 833 | ||
| 854 | pub fn parseFatLibrary( | 834 | pub fn parseFatLibrary( |
| 855 | ctx: anytype, | 835 | self: *MachO, |
| 856 | file: std.fs.File, | 836 | file: std.fs.File, |
| 857 | path: []const u8, | 837 | path: []const u8, |
| 858 | cpu_arch: std.Target.Cpu.Arch, | 838 | cpu_arch: std.Target.Cpu.Arch, |
| 859 | ) !u64 { | 839 | ) !u64 { |
| 860 | _ = ctx; | 840 | _ = self; |
| 861 | var buffer: [2]fat.Arch = undefined; | 841 | var buffer: [2]fat.Arch = undefined; |
| 862 | const fat_archs = try fat.parseArchs(file, &buffer); | 842 | const fat_archs = try fat.parseArchs(file, &buffer); |
| 863 | const offset = for (fat_archs) |arch| { | 843 | const offset = for (fat_archs) |arch| { |
| ... | @@ -871,13 +851,13 @@ pub fn parseFatLibrary( | ... | @@ -871,13 +851,13 @@ pub fn parseFatLibrary( |
| 871 | } | 851 | } |
| 872 | 852 | ||
| 873 | fn parseArchive( | 853 | fn parseArchive( |
| 874 | ctx: anytype, | 854 | self: *MachO, |
| 875 | gpa: Allocator, | ||
| 876 | path: []const u8, | 855 | path: []const u8, |
| 877 | fat_offset: u64, | 856 | fat_offset: u64, |
| 878 | must_link: bool, | 857 | must_link: bool, |
| 879 | cpu_arch: std.Target.Cpu.Arch, | 858 | cpu_arch: std.Target.Cpu.Arch, |
| 880 | ) !void { | 859 | ) !void { |
| 860 | const gpa = self.base.allocator; | ||
| 881 | 861 | ||
| 882 | // We take ownership of the file so that we can store it for the duration of symbol resolution. | 862 | // We take ownership of the file so that we can store it for the duration of symbol resolution. |
| 883 | // TODO we shouldn't need to do that and could pre-parse the archive like we do for zld/ELF? | 863 | // TODO we shouldn't need to do that and could pre-parse the archive like we do for zld/ELF? |
| ... | @@ -929,10 +909,10 @@ fn parseArchive( | ... | @@ -929,10 +909,10 @@ fn parseArchive( |
| 929 | } | 909 | } |
| 930 | for (offsets.keys()) |off| { | 910 | for (offsets.keys()) |off| { |
| 931 | const object = try archive.parseObject(gpa, off); | 911 | const object = try archive.parseObject(gpa, off); |
| 932 | try ctx.objects.append(gpa, object); | 912 | try self.objects.append(gpa, object); |
| 933 | } | 913 | } |
| 934 | } else { | 914 | } else { |
| 935 | try ctx.archives.append(gpa, archive); | 915 | try self.archives.append(gpa, archive); |
| 936 | } | 916 | } |
| 937 | } | 917 | } |
| 938 | 918 | ||
| ... | @@ -944,8 +924,7 @@ const DylibOpts = struct { | ... | @@ -944,8 +924,7 @@ const DylibOpts = struct { |
| 944 | }; | 924 | }; |
| 945 | 925 | ||
| 946 | fn parseDylib( | 926 | fn parseDylib( |
| 947 | ctx: anytype, | 927 | self: *MachO, |
| 948 | gpa: Allocator, | ||
| 949 | file: std.fs.File, | 928 | file: std.fs.File, |
| 950 | path: []const u8, | 929 | path: []const u8, |
| 951 | offset: u64, | 930 | offset: u64, |
| ... | @@ -953,6 +932,7 @@ fn parseDylib( | ... | @@ -953,6 +932,7 @@ fn parseDylib( |
| 953 | link_options: *const link.Options, | 932 | link_options: *const link.Options, |
| 954 | dylib_options: DylibOpts, | 933 | dylib_options: DylibOpts, |
| 955 | ) !void { | 934 | ) !void { |
| 935 | const gpa = self.base.allocator; | ||
| 956 | const self_cpu_arch = link_options.target.cpu.arch; | 936 | const self_cpu_arch = link_options.target.cpu.arch; |
| 957 | 937 | ||
| 958 | const file_stat = try file.stat(); | 938 | const file_stat = try file.stat(); |
| ... | @@ -968,7 +948,7 @@ fn parseDylib( | ... | @@ -968,7 +948,7 @@ fn parseDylib( |
| 968 | 948 | ||
| 969 | try dylib.parseFromBinary( | 949 | try dylib.parseFromBinary( |
| 970 | gpa, | 950 | gpa, |
| 971 | @intCast(ctx.dylibs.items.len), // TODO defer it till later | 951 | @intCast(self.dylibs.items.len), // TODO defer it till later |
| 972 | dependent_libs, | 952 | dependent_libs, |
| 973 | path, | 953 | path, |
| 974 | contents, | 954 | contents, |
| ... | @@ -991,7 +971,7 @@ fn parseDylib( | ... | @@ -991,7 +971,7 @@ fn parseDylib( |
| 991 | 971 | ||
| 992 | // TODO verify platform | 972 | // TODO verify platform |
| 993 | 973 | ||
| 994 | addDylib(ctx, gpa, dylib, link_options, .{ | 974 | self.addDylib(dylib, link_options, .{ |
| 995 | .needed = dylib_options.needed, | 975 | .needed = dylib_options.needed, |
| 996 | .weak = dylib_options.weak, | 976 | .weak = dylib_options.weak, |
| 997 | }) catch |err| switch (err) { | 977 | }) catch |err| switch (err) { |
| ... | @@ -1001,14 +981,14 @@ fn parseDylib( | ... | @@ -1001,14 +981,14 @@ fn parseDylib( |
| 1001 | } | 981 | } |
| 1002 | 982 | ||
| 1003 | fn parseLibStub( | 983 | fn parseLibStub( |
| 1004 | ctx: anytype, | 984 | self: *MachO, |
| 1005 | gpa: Allocator, | ||
| 1006 | file: std.fs.File, | 985 | file: std.fs.File, |
| 1007 | path: []const u8, | 986 | path: []const u8, |
| 1008 | dependent_libs: anytype, | 987 | dependent_libs: anytype, |
| 1009 | link_options: *const link.Options, | 988 | link_options: *const link.Options, |
| 1010 | dylib_options: DylibOpts, | 989 | dylib_options: DylibOpts, |
| 1011 | ) !void { | 990 | ) !void { |
| 991 | const gpa = self.base.allocator; | ||
| 1012 | var lib_stub = try LibStub.loadFromFile(gpa, file); | 992 | var lib_stub = try LibStub.loadFromFile(gpa, file); |
| 1013 | defer lib_stub.deinit(); | 993 | defer lib_stub.deinit(); |
| 1014 | 994 | ||
| ... | @@ -1023,12 +1003,12 @@ fn parseLibStub( | ... | @@ -1023,12 +1003,12 @@ fn parseLibStub( |
| 1023 | gpa, | 1003 | gpa, |
| 1024 | link_options.target, | 1004 | link_options.target, |
| 1025 | lib_stub, | 1005 | lib_stub, |
| 1026 | @intCast(ctx.dylibs.items.len), // TODO defer it till later | 1006 | @intCast(self.dylibs.items.len), // TODO defer it till later |
| 1027 | dependent_libs, | 1007 | dependent_libs, |
| 1028 | path, | 1008 | path, |
| 1029 | ); | 1009 | ); |
| 1030 | 1010 | ||
| 1031 | addDylib(ctx, gpa, dylib, link_options, .{ | 1011 | self.addDylib(dylib, link_options, .{ |
| 1032 | .needed = dylib_options.needed, | 1012 | .needed = dylib_options.needed, |
| 1033 | .weak = dylib_options.weak, | 1013 | .weak = dylib_options.weak, |
| 1034 | }) catch |err| switch (err) { | 1014 | }) catch |err| switch (err) { |
| ... | @@ -1038,8 +1018,7 @@ fn parseLibStub( | ... | @@ -1038,8 +1018,7 @@ fn parseLibStub( |
| 1038 | } | 1018 | } |
| 1039 | 1019 | ||
| 1040 | fn addDylib( | 1020 | fn addDylib( |
| 1041 | ctx: anytype, | 1021 | self: *MachO, |
| 1042 | gpa: Allocator, | ||
| 1043 | dylib: Dylib, | 1022 | dylib: Dylib, |
| 1044 | link_options: *const link.Options, | 1023 | link_options: *const link.Options, |
| 1045 | dylib_options: DylibOpts, | 1024 | dylib_options: DylibOpts, |
| ... | @@ -1055,28 +1034,24 @@ fn addDylib( | ... | @@ -1055,28 +1034,24 @@ fn addDylib( |
| 1055 | } | 1034 | } |
| 1056 | } | 1035 | } |
| 1057 | 1036 | ||
| 1058 | const gop = try ctx.dylibs_map.getOrPut(gpa, dylib.id.?.name); | 1037 | const gpa = self.base.allocator; |
| 1038 | const gop = try self.dylibs_map.getOrPut(gpa, dylib.id.?.name); | ||
| 1059 | if (gop.found_existing) return error.DylibAlreadyExists; | 1039 | if (gop.found_existing) return error.DylibAlreadyExists; |
| 1060 | 1040 | ||
| 1061 | gop.value_ptr.* = @as(u16, @intCast(ctx.dylibs.items.len)); | 1041 | gop.value_ptr.* = @as(u16, @intCast(self.dylibs.items.len)); |
| 1062 | try ctx.dylibs.append(gpa, dylib); | 1042 | try self.dylibs.append(gpa, dylib); |
| 1063 | 1043 | ||
| 1064 | const should_link_dylib_even_if_unreachable = blk: { | 1044 | const should_link_dylib_even_if_unreachable = blk: { |
| 1065 | if (link_options.dead_strip_dylibs and !dylib_options.needed) break :blk false; | 1045 | if (link_options.dead_strip_dylibs and !dylib_options.needed) break :blk false; |
| 1066 | break :blk !(dylib_options.dependent or ctx.referenced_dylibs.contains(gop.value_ptr.*)); | 1046 | break :blk !(dylib_options.dependent or self.referenced_dylibs.contains(gop.value_ptr.*)); |
| 1067 | }; | 1047 | }; |
| 1068 | 1048 | ||
| 1069 | if (should_link_dylib_even_if_unreachable) { | 1049 | if (should_link_dylib_even_if_unreachable) { |
| 1070 | try ctx.referenced_dylibs.putNoClobber(gpa, gop.value_ptr.*, {}); | 1050 | try self.referenced_dylibs.putNoClobber(gpa, gop.value_ptr.*, {}); |
| 1071 | } | 1051 | } |
| 1072 | } | 1052 | } |
| 1073 | 1053 | ||
| 1074 | pub fn parseDependentLibs( | 1054 | pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype, link_options: *const link.Options) !void { |
| 1075 | ctx: anytype, | ||
| 1076 | gpa: Allocator, | ||
| 1077 | dependent_libs: anytype, | ||
| 1078 | link_options: *const link.Options, | ||
| 1079 | ) !void { | ||
| 1080 | const tracy = trace(@src()); | 1055 | const tracy = trace(@src()); |
| 1081 | defer tracy.end(); | 1056 | defer tracy.end(); |
| 1082 | 1057 | ||
| ... | @@ -1085,6 +1060,7 @@ pub fn parseDependentLibs( | ... | @@ -1085,6 +1060,7 @@ pub fn parseDependentLibs( |
| 1085 | // 2) afterwards, we parse dependents of the included dylibs | 1060 | // 2) afterwards, we parse dependents of the included dylibs |
| 1086 | // TODO this should not be performed if the user specifies `-flat_namespace` flag. | 1061 | // TODO this should not be performed if the user specifies `-flat_namespace` flag. |
| 1087 | // See ld64 manpages. | 1062 | // See ld64 manpages. |
| 1063 | const gpa = self.base.allocator; | ||
| 1088 | var arena_alloc = std.heap.ArenaAllocator.init(gpa); | 1064 | var arena_alloc = std.heap.ArenaAllocator.init(gpa); |
| 1089 | const arena = arena_alloc.allocator(); | 1065 | const arena = arena_alloc.allocator(); |
| 1090 | defer arena_alloc.deinit(); | 1066 | defer arena_alloc.deinit(); |
| ... | @@ -1092,9 +1068,9 @@ pub fn parseDependentLibs( | ... | @@ -1092,9 +1068,9 @@ pub fn parseDependentLibs( |
| 1092 | outer: while (dependent_libs.readItem()) |dep_id| { | 1068 | outer: while (dependent_libs.readItem()) |dep_id| { |
| 1093 | defer dep_id.id.deinit(gpa); | 1069 | defer dep_id.id.deinit(gpa); |
| 1094 | 1070 | ||
| 1095 | if (ctx.dylibs_map.contains(dep_id.id.name)) continue; | 1071 | if (self.dylibs_map.contains(dep_id.id.name)) continue; |
| 1096 | 1072 | ||
| 1097 | const weak = ctx.dylibs.items[dep_id.parent].weak; | 1073 | const weak = self.dylibs.items[dep_id.parent].weak; |
| 1098 | const has_ext = blk: { | 1074 | const has_ext = blk: { |
| 1099 | const basename = fs.path.basename(dep_id.id.name); | 1075 | const basename = fs.path.basename(dep_id.id.name); |
| 1100 | break :blk mem.lastIndexOfScalar(u8, basename, '.') != null; | 1076 | break :blk mem.lastIndexOfScalar(u8, basename, '.') != null; |
| ... | @@ -1121,7 +1097,7 @@ pub fn parseDependentLibs( | ... | @@ -1121,7 +1097,7 @@ pub fn parseDependentLibs( |
| 1121 | log.debug("trying dependency at fully resolved path {s}", .{full_path}); | 1097 | log.debug("trying dependency at fully resolved path {s}", .{full_path}); |
| 1122 | 1098 | ||
| 1123 | const offset: u64 = if (fat.isFatLibrary(file)) blk: { | 1099 | const offset: u64 = if (fat.isFatLibrary(file)) blk: { |
| 1124 | const offset = parseFatLibrary(ctx, file, full_path, link_options.target.cpu.arch) catch |err| switch (err) { | 1100 | const offset = self.parseFatLibrary(file, full_path, link_options.target.cpu.arch) catch |err| switch (err) { |
| 1125 | error.MissingArch => break, | 1101 | error.MissingArch => break, |
| 1126 | else => |e| return e, | 1102 | else => |e| return e, |
| 1127 | }; | 1103 | }; |
| ... | @@ -1130,12 +1106,12 @@ pub fn parseDependentLibs( | ... | @@ -1130,12 +1106,12 @@ pub fn parseDependentLibs( |
| 1130 | } else 0; | 1106 | } else 0; |
| 1131 | 1107 | ||
| 1132 | if (Dylib.isDylib(file, offset)) { | 1108 | if (Dylib.isDylib(file, offset)) { |
| 1133 | try parseDylib(ctx, gpa, file, full_path, offset, dependent_libs, link_options, .{ | 1109 | try self.parseDylib(file, full_path, offset, dependent_libs, link_options, .{ |
| 1134 | .dependent = true, | 1110 | .dependent = true, |
| 1135 | .weak = weak, | 1111 | .weak = weak, |
| 1136 | }); | 1112 | }); |
| 1137 | } else { | 1113 | } else { |
| 1138 | parseLibStub(ctx, gpa, file, full_path, dependent_libs, link_options, .{ | 1114 | self.parseLibStub(file, full_path, dependent_libs, link_options, .{ |
| 1139 | .dependent = true, | 1115 | .dependent = true, |
| 1140 | .weak = weak, | 1116 | .weak = weak, |
| 1141 | }) catch |err| switch (err) { | 1117 | }) catch |err| switch (err) { |
| ... | @@ -1394,7 +1370,7 @@ fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void { | ... | @@ -1394,7 +1370,7 @@ fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void { |
| 1394 | } | 1370 | } |
| 1395 | } | 1371 | } |
| 1396 | 1372 | ||
| 1397 | pub fn allocateSpecialSymbols(self: anytype) !void { | 1373 | pub fn allocateSpecialSymbols(self: *MachO) !void { |
| 1398 | for (&[_][]const u8{ | 1374 | for (&[_][]const u8{ |
| 1399 | "___dso_handle", | 1375 | "___dso_handle", |
| 1400 | "__mh_execute_header", | 1376 | "__mh_execute_header", |
| ... | @@ -1432,24 +1408,82 @@ pub fn createAtom(self: *MachO, sym_index: u32, opts: CreateAtomOpts) !Atom.Inde | ... | @@ -1432,24 +1408,82 @@ pub fn createAtom(self: *MachO, sym_index: u32, opts: CreateAtomOpts) !Atom.Inde |
| 1432 | return index; | 1408 | return index; |
| 1433 | } | 1409 | } |
| 1434 | 1410 | ||
| 1411 | pub fn createTentativeDefAtoms(self: *MachO) !void { | ||
| 1412 | const gpa = self.base.allocator; | ||
| 1413 | |||
| 1414 | for (self.globals.items) |global| { | ||
| 1415 | const sym = self.getSymbolPtr(global); | ||
| 1416 | if (!sym.tentative()) continue; | ||
| 1417 | if (sym.n_desc == N_DEAD) continue; | ||
| 1418 | |||
| 1419 | log.debug("creating tentative definition for ATOM(%{d}, '{s}') in object({?})", .{ | ||
| 1420 | global.sym_index, self.getSymbolName(global), global.file, | ||
| 1421 | }); | ||
| 1422 | |||
| 1423 | // Convert any tentative definition into a regular symbol and allocate | ||
| 1424 | // text blocks for each tentative definition. | ||
| 1425 | const size = sym.n_value; | ||
| 1426 | const alignment = (sym.n_desc >> 8) & 0x0f; | ||
| 1427 | |||
| 1428 | if (self.bss_section_index == null) { | ||
| 1429 | self.bss_section_index = try self.initSection("__DATA", "__bss", .{ | ||
| 1430 | .flags = macho.S_ZEROFILL, | ||
| 1431 | }); | ||
| 1432 | } | ||
| 1433 | |||
| 1434 | sym.* = .{ | ||
| 1435 | .n_strx = sym.n_strx, | ||
| 1436 | .n_type = macho.N_SECT | macho.N_EXT, | ||
| 1437 | .n_sect = self.bss_section_index.? + 1, | ||
| 1438 | .n_desc = 0, | ||
| 1439 | .n_value = 0, | ||
| 1440 | }; | ||
| 1441 | |||
| 1442 | const atom_index = try self.createAtom(global.sym_index, .{ | ||
| 1443 | .size = size, | ||
| 1444 | .alignment = alignment, | ||
| 1445 | }); | ||
| 1446 | const atom = self.getAtomPtr(atom_index); | ||
| 1447 | atom.file = global.file; | ||
| 1448 | |||
| 1449 | self.addAtomToSection(atom_index); | ||
| 1450 | |||
| 1451 | assert(global.getFile() != null); | ||
| 1452 | const object = &self.objects.items[global.getFile().?]; | ||
| 1453 | try object.atoms.append(gpa, atom_index); | ||
| 1454 | object.atom_by_index_table[global.sym_index] = atom_index; | ||
| 1455 | } | ||
| 1456 | } | ||
| 1457 | |||
| 1435 | fn createDyldPrivateAtom(self: *MachO) !void { | 1458 | fn createDyldPrivateAtom(self: *MachO) !void { |
| 1436 | if (self.dyld_private_atom_index != null) return; | 1459 | if (self.dyld_private_atom_index != null) return; |
| 1437 | 1460 | ||
| 1438 | const sym_index = try self.allocateSymbol(); | 1461 | const sym_index = try self.allocateSymbol(); |
| 1439 | const atom_index = try self.createAtom(sym_index, .{}); | 1462 | const atom_index = try self.createAtom(sym_index, .{ |
| 1463 | .size = @sizeOf(u64), | ||
| 1464 | .alignment = 3, | ||
| 1465 | }); | ||
| 1440 | try self.atom_by_index_table.putNoClobber(self.base.allocator, sym_index, atom_index); | 1466 | try self.atom_by_index_table.putNoClobber(self.base.allocator, sym_index, atom_index); |
| 1441 | const atom = self.getAtomPtr(atom_index); | ||
| 1442 | atom.size = @sizeOf(u64); | ||
| 1443 | 1467 | ||
| 1468 | if (self.data_section_index == null) { | ||
| 1469 | self.data_section_index = try self.initSection("__DATA", "__data", .{}); | ||
| 1470 | } | ||
| 1471 | |||
| 1472 | const atom = self.getAtom(atom_index); | ||
| 1444 | const sym = atom.getSymbolPtr(self); | 1473 | const sym = atom.getSymbolPtr(self); |
| 1445 | sym.n_type = macho.N_SECT; | 1474 | sym.n_type = macho.N_SECT; |
| 1446 | sym.n_sect = self.data_section_index.? + 1; | 1475 | sym.n_sect = self.data_section_index.? + 1; |
| 1447 | self.dyld_private_atom_index = atom_index; | 1476 | self.dyld_private_atom_index = atom_index; |
| 1448 | 1477 | ||
| 1449 | sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64)); | 1478 | switch (self.mode) { |
| 1450 | log.debug("allocated dyld_private atom at 0x{x}", .{sym.n_value}); | 1479 | .zld => self.addAtomToSection(atom_index), |
| 1451 | var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64); | 1480 | .incremental => { |
| 1452 | try self.writeAtom(atom_index, &buffer); | 1481 | sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64)); |
| 1482 | log.debug("allocated dyld_private atom at 0x{x}", .{sym.n_value}); | ||
| 1483 | var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64); | ||
| 1484 | try self.writeAtom(atom_index, &buffer); | ||
| 1485 | }, | ||
| 1486 | } | ||
| 1453 | } | 1487 | } |
| 1454 | 1488 | ||
| 1455 | fn createThreadLocalDescriptorAtom(self: *MachO, sym_name: []const u8, target: SymbolWithLoc) !Atom.Index { | 1489 | fn createThreadLocalDescriptorAtom(self: *MachO, sym_name: []const u8, target: SymbolWithLoc) !Atom.Index { |
| ... | @@ -1485,7 +1519,7 @@ fn createThreadLocalDescriptorAtom(self: *MachO, sym_name: []const u8, target: S | ... | @@ -1485,7 +1519,7 @@ fn createThreadLocalDescriptorAtom(self: *MachO, sym_name: []const u8, target: S |
| 1485 | return atom_index; | 1519 | return atom_index; |
| 1486 | } | 1520 | } |
| 1487 | 1521 | ||
| 1488 | fn createMhExecuteHeaderSymbol(self: *MachO) !void { | 1522 | pub fn createMhExecuteHeaderSymbol(self: *MachO) !void { |
| 1489 | if (self.base.options.output_mode != .Exe) return; | 1523 | if (self.base.options.output_mode != .Exe) return; |
| 1490 | 1524 | ||
| 1491 | const gpa = self.base.allocator; | 1525 | const gpa = self.base.allocator; |
| ... | @@ -1501,10 +1535,17 @@ fn createMhExecuteHeaderSymbol(self: *MachO) !void { | ... | @@ -1501,10 +1535,17 @@ fn createMhExecuteHeaderSymbol(self: *MachO) !void { |
| 1501 | }; | 1535 | }; |
| 1502 | 1536 | ||
| 1503 | const gop = try self.getOrPutGlobalPtr("__mh_execute_header"); | 1537 | const gop = try self.getOrPutGlobalPtr("__mh_execute_header"); |
| 1538 | if (gop.found_existing) { | ||
| 1539 | const global = gop.value_ptr.*; | ||
| 1540 | if (global.getFile()) |file| { | ||
| 1541 | const global_object = &self.objects.items[file]; | ||
| 1542 | global_object.globals_lookup[global.sym_index] = self.getGlobalIndex("__mh_execute_header").?; | ||
| 1543 | } | ||
| 1544 | } | ||
| 1504 | gop.value_ptr.* = sym_loc; | 1545 | gop.value_ptr.* = sym_loc; |
| 1505 | } | 1546 | } |
| 1506 | 1547 | ||
| 1507 | fn createDsoHandleSymbol(self: *MachO) !void { | 1548 | pub fn createDsoHandleSymbol(self: *MachO) !void { |
| 1508 | const global = self.getGlobalPtr("___dso_handle") orelse return; | 1549 | const global = self.getGlobalPtr("___dso_handle") orelse return; |
| 1509 | if (!self.getSymbol(global.*).undf()) return; | 1550 | if (!self.getSymbol(global.*).undf()) return; |
| 1510 | 1551 | ||
| ... | @@ -1519,10 +1560,51 @@ fn createDsoHandleSymbol(self: *MachO) !void { | ... | @@ -1519,10 +1560,51 @@ fn createDsoHandleSymbol(self: *MachO) !void { |
| 1519 | .n_desc = macho.N_WEAK_DEF, | 1560 | .n_desc = macho.N_WEAK_DEF, |
| 1520 | .n_value = 0, | 1561 | .n_value = 0, |
| 1521 | }; | 1562 | }; |
| 1563 | const global_index = self.getGlobalIndex("___dso_handle").?; | ||
| 1564 | if (global.getFile()) |file| { | ||
| 1565 | const global_object = &self.objects.items[file]; | ||
| 1566 | global_object.globals_lookup[global.sym_index] = global_index; | ||
| 1567 | } | ||
| 1522 | global.* = sym_loc; | 1568 | global.* = sym_loc; |
| 1523 | _ = self.unresolved.swapRemove(self.getGlobalIndex("___dso_handle").?); | 1569 | _ = self.unresolved.swapRemove(self.getGlobalIndex("___dso_handle").?); |
| 1524 | } | 1570 | } |
| 1525 | 1571 | ||
| 1572 | pub fn resolveSymbols(self: *MachO, actions: *std.ArrayList(ResolveAction)) !void { | ||
| 1573 | // We add the specified entrypoint as the first unresolved symbols so that | ||
| 1574 | // we search for it in libraries should there be no object files specified | ||
| 1575 | // on the linker line. | ||
| 1576 | if (self.base.options.output_mode == .Exe) { | ||
| 1577 | const entry_name = self.base.options.entry orelse load_commands.default_entry_point; | ||
| 1578 | _ = try self.addUndefined(entry_name, .none); | ||
| 1579 | } | ||
| 1580 | |||
| 1581 | // Force resolution of any symbols requested by the user. | ||
| 1582 | for (self.base.options.force_undefined_symbols.keys()) |sym_name| { | ||
| 1583 | _ = try self.addUndefined(sym_name, .none); | ||
| 1584 | } | ||
| 1585 | |||
| 1586 | for (self.objects.items, 0..) |_, object_id| { | ||
| 1587 | try self.resolveSymbolsInObject(@as(u32, @intCast(object_id))); | ||
| 1588 | } | ||
| 1589 | |||
| 1590 | try self.resolveSymbolsInArchives(); | ||
| 1591 | |||
| 1592 | // Finally, force resolution of dyld_stub_binder if there are imports | ||
| 1593 | // requested. | ||
| 1594 | if (self.unresolved.count() > 0 and self.dyld_stub_binder_index == null) { | ||
| 1595 | self.dyld_stub_binder_index = try self.addUndefined("dyld_stub_binder", .add_got); | ||
| 1596 | } | ||
| 1597 | if (!self.base.options.single_threaded and self.mode == .incremental) { | ||
| 1598 | _ = try self.addUndefined("__tlv_bootstrap", .none); | ||
| 1599 | } | ||
| 1600 | |||
| 1601 | try self.resolveSymbolsInDylibs(actions); | ||
| 1602 | |||
| 1603 | try self.createMhExecuteHeaderSymbol(); | ||
| 1604 | try self.createDsoHandleSymbol(); | ||
| 1605 | try self.resolveSymbolsAtLoading(); | ||
| 1606 | } | ||
| 1607 | |||
| 1526 | fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void { | 1608 | fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void { |
| 1527 | const gpa = self.base.allocator; | 1609 | const gpa = self.base.allocator; |
| 1528 | const sym = self.getSymbol(current); | 1610 | const sym = self.getSymbol(current); |
| ... | @@ -1536,6 +1618,7 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void { | ... | @@ -1536,6 +1618,7 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void { |
| 1536 | } | 1618 | } |
| 1537 | return; | 1619 | return; |
| 1538 | } | 1620 | } |
| 1621 | const global_index = self.getGlobalIndex(sym_name).?; | ||
| 1539 | const global = gop.value_ptr.*; | 1622 | const global = gop.value_ptr.*; |
| 1540 | const global_sym = self.getSymbol(global); | 1623 | const global_sym = self.getSymbol(global); |
| 1541 | 1624 | ||
| ... | @@ -1566,7 +1649,22 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void { | ... | @@ -1566,7 +1649,22 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void { |
| 1566 | const sym_is_weak = sym.sect() and (sym.weakDef() or sym.pext()); | 1649 | const sym_is_weak = sym.sect() and (sym.weakDef() or sym.pext()); |
| 1567 | const global_is_weak = global_sym.sect() and (global_sym.weakDef() or global_sym.pext()); | 1650 | const global_is_weak = global_sym.sect() and (global_sym.weakDef() or global_sym.pext()); |
| 1568 | 1651 | ||
| 1569 | if (sym_is_strong and global_is_strong) return error.MultipleSymbolDefinitions; | 1652 | if (sym_is_strong and global_is_strong) { |
| 1653 | log.err("symbol '{s}' defined multiple times", .{sym_name}); | ||
| 1654 | if (global.getFile()) |file| { | ||
| 1655 | log.err(" first definition in '{s}'", .{self.objects.items[file].name}); | ||
| 1656 | } | ||
| 1657 | if (current.getFile()) |file| { | ||
| 1658 | log.err(" next definition in '{s}'", .{self.objects.items[file].name}); | ||
| 1659 | } | ||
| 1660 | return error.MultipleSymbolDefinitions; | ||
| 1661 | } | ||
| 1662 | |||
| 1663 | if (current.getFile()) |file| { | ||
| 1664 | const object = &self.objects.items[file]; | ||
| 1665 | object.globals_lookup[current.sym_index] = global_index; | ||
| 1666 | } | ||
| 1667 | |||
| 1570 | if (global_is_strong) return; | 1668 | if (global_is_strong) return; |
| 1571 | if (sym_is_weak and global_is_weak) return; | 1669 | if (sym_is_weak and global_is_weak) return; |
| 1572 | if (sym.tentative() and global_sym.tentative()) { | 1670 | if (sym.tentative() and global_sym.tentative()) { |
| ... | @@ -1574,11 +1672,88 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void { | ... | @@ -1574,11 +1672,88 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void { |
| 1574 | } | 1672 | } |
| 1575 | if (sym.undf() and !sym.tentative()) return; | 1673 | if (sym.undf() and !sym.tentative()) return; |
| 1576 | 1674 | ||
| 1577 | _ = self.unresolved.swapRemove(self.getGlobalIndex(sym_name).?); | 1675 | if (global.getFile()) |file| { |
| 1676 | const global_object = &self.objects.items[file]; | ||
| 1677 | global_object.globals_lookup[global.sym_index] = global_index; | ||
| 1678 | } | ||
| 1679 | _ = self.unresolved.swapRemove(global_index); | ||
| 1578 | 1680 | ||
| 1579 | gop.value_ptr.* = current; | 1681 | gop.value_ptr.* = current; |
| 1580 | } | 1682 | } |
| 1581 | 1683 | ||
| 1684 | fn resolveSymbolsInObject(self: *MachO, object_id: u32) !void { | ||
| 1685 | const object = &self.objects.items[object_id]; | ||
| 1686 | const in_symtab = object.in_symtab orelse return; | ||
| 1687 | |||
| 1688 | log.debug("resolving symbols in '{s}'", .{object.name}); | ||
| 1689 | |||
| 1690 | var sym_index: u32 = 0; | ||
| 1691 | while (sym_index < in_symtab.len) : (sym_index += 1) { | ||
| 1692 | const sym = &object.symtab[sym_index]; | ||
| 1693 | const sym_name = object.getSymbolName(sym_index); | ||
| 1694 | |||
| 1695 | if (sym.stab()) { | ||
| 1696 | log.err("unhandled symbol type: stab", .{}); | ||
| 1697 | log.err(" symbol '{s}'", .{sym_name}); | ||
| 1698 | log.err(" first definition in '{s}'", .{object.name}); | ||
| 1699 | return error.UnhandledSymbolType; | ||
| 1700 | } | ||
| 1701 | |||
| 1702 | if (sym.indr()) { | ||
| 1703 | log.err("unhandled symbol type: indirect", .{}); | ||
| 1704 | log.err(" symbol '{s}'", .{sym_name}); | ||
| 1705 | log.err(" first definition in '{s}'", .{object.name}); | ||
| 1706 | return error.UnhandledSymbolType; | ||
| 1707 | } | ||
| 1708 | |||
| 1709 | if (sym.abs()) { | ||
| 1710 | log.err("unhandled symbol type: absolute", .{}); | ||
| 1711 | log.err(" symbol '{s}'", .{sym_name}); | ||
| 1712 | log.err(" first definition in '{s}'", .{object.name}); | ||
| 1713 | return error.UnhandledSymbolType; | ||
| 1714 | } | ||
| 1715 | |||
| 1716 | if (sym.sect() and !sym.ext()) { | ||
| 1717 | log.debug("symbol '{s}' local to object {s}; skipping...", .{ | ||
| 1718 | sym_name, | ||
| 1719 | object.name, | ||
| 1720 | }); | ||
| 1721 | continue; | ||
| 1722 | } | ||
| 1723 | |||
| 1724 | try self.resolveGlobalSymbol(.{ .sym_index = sym_index, .file = object_id + 1 }); | ||
| 1725 | } | ||
| 1726 | } | ||
| 1727 | |||
| 1728 | fn resolveSymbolsInArchives(self: *MachO) !void { | ||
| 1729 | if (self.archives.items.len == 0) return; | ||
| 1730 | |||
| 1731 | const gpa = self.base.allocator; | ||
| 1732 | var next_sym: usize = 0; | ||
| 1733 | loop: while (next_sym < self.unresolved.count()) { | ||
| 1734 | const global = self.globals.items[self.unresolved.keys()[next_sym]]; | ||
| 1735 | const sym_name = self.getSymbolName(global); | ||
| 1736 | |||
| 1737 | for (self.archives.items) |archive| { | ||
| 1738 | // Check if the entry exists in a static archive. | ||
| 1739 | const offsets = archive.toc.get(sym_name) orelse { | ||
| 1740 | // No hit. | ||
| 1741 | continue; | ||
| 1742 | }; | ||
| 1743 | assert(offsets.items.len > 0); | ||
| 1744 | |||
| 1745 | const object_id = @as(u16, @intCast(self.objects.items.len)); | ||
| 1746 | const object = try archive.parseObject(gpa, offsets.items[0]); | ||
| 1747 | try self.objects.append(gpa, object); | ||
| 1748 | try self.resolveSymbolsInObject(object_id); | ||
| 1749 | |||
| 1750 | continue :loop; | ||
| 1751 | } | ||
| 1752 | |||
| 1753 | next_sym += 1; | ||
| 1754 | } | ||
| 1755 | } | ||
| 1756 | |||
| 1582 | fn resolveSymbolsInDylibs(self: *MachO, actions: *std.ArrayList(ResolveAction)) !void { | 1757 | fn resolveSymbolsInDylibs(self: *MachO, actions: *std.ArrayList(ResolveAction)) !void { |
| 1583 | if (self.dylibs.items.len == 0) return; | 1758 | if (self.dylibs.items.len == 0) return; |
| 1584 | 1759 | ||
| ... | @@ -1608,6 +1783,7 @@ fn resolveSymbolsInDylibs(self: *MachO, actions: *std.ArrayList(ResolveAction)) | ... | @@ -1608,6 +1783,7 @@ fn resolveSymbolsInDylibs(self: *MachO, actions: *std.ArrayList(ResolveAction)) |
| 1608 | 1783 | ||
| 1609 | if (self.unresolved.fetchSwapRemove(global_index)) |entry| blk: { | 1784 | if (self.unresolved.fetchSwapRemove(global_index)) |entry| blk: { |
| 1610 | if (!sym.undf()) break :blk; | 1785 | if (!sym.undf()) break :blk; |
| 1786 | if (self.mode == .zld) break :blk; | ||
| 1611 | try actions.append(.{ .kind = entry.value, .target = global }); | 1787 | try actions.append(.{ .kind = entry.value, .target = global }); |
| 1612 | } | 1788 | } |
| 1613 | 1789 | ||
| ... | @@ -1618,6 +1794,42 @@ fn resolveSymbolsInDylibs(self: *MachO, actions: *std.ArrayList(ResolveAction)) | ... | @@ -1618,6 +1794,42 @@ fn resolveSymbolsInDylibs(self: *MachO, actions: *std.ArrayList(ResolveAction)) |
| 1618 | } | 1794 | } |
| 1619 | } | 1795 | } |
| 1620 | 1796 | ||
| 1797 | fn resolveSymbolsAtLoading(self: *MachO) !void { | ||
| 1798 | const is_lib = self.base.options.output_mode == .Lib; | ||
| 1799 | const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; | ||
| 1800 | const allow_undef = is_dyn_lib and (self.base.options.allow_shlib_undefined orelse false); | ||
| 1801 | |||
| 1802 | var next_sym: usize = 0; | ||
| 1803 | while (next_sym < self.unresolved.count()) { | ||
| 1804 | const global_index = self.unresolved.keys()[next_sym]; | ||
| 1805 | const global = self.globals.items[global_index]; | ||
| 1806 | const sym = self.getSymbolPtr(global); | ||
| 1807 | |||
| 1808 | if (sym.discarded()) { | ||
| 1809 | sym.* = .{ | ||
| 1810 | .n_strx = 0, | ||
| 1811 | .n_type = macho.N_UNDF, | ||
| 1812 | .n_sect = 0, | ||
| 1813 | .n_desc = 0, | ||
| 1814 | .n_value = 0, | ||
| 1815 | }; | ||
| 1816 | _ = self.unresolved.swapRemove(global_index); | ||
| 1817 | continue; | ||
| 1818 | } else if (allow_undef) { | ||
| 1819 | const n_desc = @as( | ||
| 1820 | u16, | ||
| 1821 | @bitCast(macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP * @as(i16, @intCast(macho.N_SYMBOL_RESOLVER))), | ||
| 1822 | ); | ||
| 1823 | sym.n_type = macho.N_EXT; | ||
| 1824 | sym.n_desc = n_desc; | ||
| 1825 | _ = self.unresolved.swapRemove(global_index); | ||
| 1826 | continue; | ||
| 1827 | } | ||
| 1828 | |||
| 1829 | next_sym += 1; | ||
| 1830 | } | ||
| 1831 | } | ||
| 1832 | |||
| 1621 | pub fn deinit(self: *MachO) void { | 1833 | pub fn deinit(self: *MachO) void { |
| 1622 | const gpa = self.base.allocator; | 1834 | const gpa = self.base.allocator; |
| 1623 | 1835 | ||
| ... | @@ -1638,7 +1850,6 @@ pub fn deinit(self: *MachO) void { | ... | @@ -1638,7 +1850,6 @@ pub fn deinit(self: *MachO) void { |
| 1638 | self.thunks.deinit(gpa); | 1850 | self.thunks.deinit(gpa); |
| 1639 | 1851 | ||
| 1640 | self.strtab.deinit(gpa); | 1852 | self.strtab.deinit(gpa); |
| 1641 | |||
| 1642 | self.locals.deinit(gpa); | 1853 | self.locals.deinit(gpa); |
| 1643 | self.globals.deinit(gpa); | 1854 | self.globals.deinit(gpa); |
| 1644 | self.locals_free_list.deinit(gpa); | 1855 | self.locals_free_list.deinit(gpa); |
| ... | @@ -1653,6 +1864,14 @@ pub fn deinit(self: *MachO) void { | ... | @@ -1653,6 +1864,14 @@ pub fn deinit(self: *MachO) void { |
| 1653 | self.resolver.deinit(gpa); | 1864 | self.resolver.deinit(gpa); |
| 1654 | } | 1865 | } |
| 1655 | 1866 | ||
| 1867 | for (self.objects.items) |*object| { | ||
| 1868 | object.deinit(gpa); | ||
| 1869 | } | ||
| 1870 | self.objects.deinit(gpa); | ||
| 1871 | for (self.archives.items) |*archive| { | ||
| 1872 | archive.deinit(gpa); | ||
| 1873 | } | ||
| 1874 | self.archives.deinit(gpa); | ||
| 1656 | for (self.dylibs.items) |*dylib| { | 1875 | for (self.dylibs.items) |*dylib| { |
| 1657 | dylib.deinit(gpa); | 1876 | dylib.deinit(gpa); |
| 1658 | } | 1877 | } |
| ... | @@ -1842,20 +2061,55 @@ fn allocateGlobal(self: *MachO) !u32 { | ... | @@ -1842,20 +2061,55 @@ fn allocateGlobal(self: *MachO) !u32 { |
| 1842 | return index; | 2061 | return index; |
| 1843 | } | 2062 | } |
| 1844 | 2063 | ||
| 1845 | fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void { | 2064 | pub fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void { |
| 1846 | if (self.got_table.lookup.contains(target)) return; | 2065 | if (self.got_table.lookup.contains(target)) return; |
| 1847 | const got_index = try self.got_table.allocateEntry(self.base.allocator, target); | 2066 | const got_index = try self.got_table.allocateEntry(self.base.allocator, target); |
| 1848 | try self.writeOffsetTableEntry(got_index); | 2067 | if (self.got_section_index == null) { |
| 1849 | self.got_table_count_dirty = true; | 2068 | self.got_section_index = try self.initSection("__DATA_CONST", "__got", .{ |
| 1850 | self.markRelocsDirtyByTarget(target); | 2069 | .flags = macho.S_NON_LAZY_SYMBOL_POINTERS, |
| 2070 | }); | ||
| 2071 | } | ||
| 2072 | if (self.mode == .incremental) { | ||
| 2073 | try self.writeOffsetTableEntry(got_index); | ||
| 2074 | self.got_table_count_dirty = true; | ||
| 2075 | self.markRelocsDirtyByTarget(target); | ||
| 2076 | } | ||
| 1851 | } | 2077 | } |
| 1852 | 2078 | ||
| 1853 | fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void { | 2079 | pub fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void { |
| 1854 | if (self.stub_table.lookup.contains(target)) return; | 2080 | if (self.stub_table.lookup.contains(target)) return; |
| 1855 | const stub_index = try self.stub_table.allocateEntry(self.base.allocator, target); | 2081 | const stub_index = try self.stub_table.allocateEntry(self.base.allocator, target); |
| 1856 | try self.writeStubTableEntry(stub_index); | 2082 | if (self.stubs_section_index == null) { |
| 1857 | self.stub_table_count_dirty = true; | 2083 | self.stubs_section_index = try self.initSection("__TEXT", "__stubs", .{ |
| 1858 | self.markRelocsDirtyByTarget(target); | 2084 | .flags = macho.S_SYMBOL_STUBS | |
| 2085 | macho.S_ATTR_PURE_INSTRUCTIONS | | ||
| 2086 | macho.S_ATTR_SOME_INSTRUCTIONS, | ||
| 2087 | .reserved2 = stubs.stubSize(self.base.options.target.cpu.arch), | ||
| 2088 | }); | ||
| 2089 | self.stub_helper_section_index = try self.initSection("__TEXT", "__stub_helper", .{ | ||
| 2090 | .flags = macho.S_REGULAR | | ||
| 2091 | macho.S_ATTR_PURE_INSTRUCTIONS | | ||
| 2092 | macho.S_ATTR_SOME_INSTRUCTIONS, | ||
| 2093 | }); | ||
| 2094 | self.la_symbol_ptr_section_index = try self.initSection("__DATA", "__la_symbol_ptr", .{ | ||
| 2095 | .flags = macho.S_LAZY_SYMBOL_POINTERS, | ||
| 2096 | }); | ||
| 2097 | } | ||
| 2098 | if (self.mode == .incremental) { | ||
| 2099 | try self.writeStubTableEntry(stub_index); | ||
| 2100 | self.stub_table_count_dirty = true; | ||
| 2101 | self.markRelocsDirtyByTarget(target); | ||
| 2102 | } | ||
| 2103 | } | ||
| 2104 | |||
| 2105 | pub fn addTlvPtrEntry(self: *MachO, target: SymbolWithLoc) !void { | ||
| 2106 | if (self.tlv_ptr_table.lookup.contains(target)) return; | ||
| 2107 | _ = try self.tlv_ptr_table.allocateEntry(self.gpa, target); | ||
| 2108 | if (self.tlv_ptr_section_index == null) { | ||
| 2109 | self.tlv_ptr_section_index = try self.initSection("__DATA", "__thread_ptrs", .{ | ||
| 2110 | .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS, | ||
| 2111 | }); | ||
| 2112 | } | ||
| 1859 | } | 2113 | } |
| 1860 | 2114 | ||
| 1861 | pub fn updateFunc(self: *MachO, mod: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) !void { | 2115 | pub fn updateFunc(self: *MachO, mod: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) !void { |
| ... | @@ -2758,16 +3012,10 @@ const InitSectionOpts = struct { | ... | @@ -2758,16 +3012,10 @@ const InitSectionOpts = struct { |
| 2758 | reserved2: u32 = 0, | 3012 | reserved2: u32 = 0, |
| 2759 | }; | 3013 | }; |
| 2760 | 3014 | ||
| 2761 | pub fn initSection( | 3015 | pub fn initSection(self: *MachO, segname: []const u8, sectname: []const u8, opts: InitSectionOpts) !u8 { |
| 2762 | gpa: Allocator, | ||
| 2763 | ctx: anytype, | ||
| 2764 | segname: []const u8, | ||
| 2765 | sectname: []const u8, | ||
| 2766 | opts: InitSectionOpts, | ||
| 2767 | ) !u8 { | ||
| 2768 | log.debug("creating section '{s},{s}'", .{ segname, sectname }); | 3016 | log.debug("creating section '{s},{s}'", .{ segname, sectname }); |
| 2769 | const index = @as(u8, @intCast(ctx.sections.slice().len)); | 3017 | const index = @as(u8, @intCast(self.sections.slice().len)); |
| 2770 | try ctx.sections.append(gpa, .{ | 3018 | try self.sections.append(self.base.allocator, .{ |
| 2771 | .segment_index = undefined, // Segments will be created automatically later down the pipeline | 3019 | .segment_index = undefined, // Segments will be created automatically later down the pipeline |
| 2772 | .header = .{ | 3020 | .header = .{ |
| 2773 | .sectname = makeStaticString(sectname), | 3021 | .sectname = makeStaticString(sectname), |
| ... | @@ -2822,7 +3070,7 @@ fn allocateSection(self: *MachO, segname: []const u8, sectname: []const u8, opts | ... | @@ -2822,7 +3070,7 @@ fn allocateSection(self: *MachO, segname: []const u8, sectname: []const u8, opts |
| 2822 | .cmdsize = @sizeOf(macho.segment_command_64) + @sizeOf(macho.section_64), | 3070 | .cmdsize = @sizeOf(macho.segment_command_64) + @sizeOf(macho.section_64), |
| 2823 | }; | 3071 | }; |
| 2824 | 3072 | ||
| 2825 | const sect_id = try initSection(gpa, self, sectname, segname, .{ | 3073 | const sect_id = try self.initSection(sectname, segname, .{ |
| 2826 | .flags = opts.flags, | 3074 | .flags = opts.flags, |
| 2827 | .reserved2 = opts.reserved2, | 3075 | .reserved2 = opts.reserved2, |
| 2828 | }); | 3076 | }); |
| ... | @@ -2918,10 +3166,29 @@ fn growSectionVirtualMemory(self: *MachO, sect_id: u8, needed_size: u64) !void { | ... | @@ -2918,10 +3166,29 @@ fn growSectionVirtualMemory(self: *MachO, sect_id: u8, needed_size: u64) !void { |
| 2918 | } | 3166 | } |
| 2919 | } | 3167 | } |
| 2920 | 3168 | ||
| 3169 | pub fn addAtomToSection(self: *MachO, atom_index: Atom.Index) void { | ||
| 3170 | assert(self.mode == .zld); | ||
| 3171 | const atom = self.getAtomPtr(atom_index); | ||
| 3172 | const sym = self.getSymbol(atom.getSymbolWithLoc()); | ||
| 3173 | var section = self.sections.get(sym.n_sect - 1); | ||
| 3174 | if (section.header.size > 0) { | ||
| 3175 | const last_atom = self.getAtomPtr(section.last_atom_index.?); | ||
| 3176 | last_atom.next_index = atom_index; | ||
| 3177 | atom.prev_index = section.last_atom_index; | ||
| 3178 | } else { | ||
| 3179 | section.first_atom_index = atom_index; | ||
| 3180 | } | ||
| 3181 | section.last_atom_index = atom_index; | ||
| 3182 | section.header.size += atom.size; | ||
| 3183 | self.sections.set(sym.n_sect - 1, section); | ||
| 3184 | } | ||
| 3185 | |||
| 2921 | fn allocateAtom(self: *MachO, atom_index: Atom.Index, new_atom_size: u64, alignment: u64) !u64 { | 3186 | fn allocateAtom(self: *MachO, atom_index: Atom.Index, new_atom_size: u64, alignment: u64) !u64 { |
| 2922 | const tracy = trace(@src()); | 3187 | const tracy = trace(@src()); |
| 2923 | defer tracy.end(); | 3188 | defer tracy.end(); |
| 2924 | 3189 | ||
| 3190 | assert(self.mode == .incremental); | ||
| 3191 | |||
| 2925 | const atom = self.getAtom(atom_index); | 3192 | const atom = self.getAtom(atom_index); |
| 2926 | const sect_id = atom.getSymbol(self).n_sect - 1; | 3193 | const sect_id = atom.getSymbol(self).n_sect - 1; |
| 2927 | const segment = self.getSegmentPtr(sect_id); | 3194 | const segment = self.getSegmentPtr(sect_id); |
| ... | @@ -3048,7 +3315,7 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8, lib_name: ?[]const u8) !u | ... | @@ -3048,7 +3315,7 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8, lib_name: ?[]const u8) !u |
| 3048 | return self.addUndefined(sym_name, .add_stub); | 3315 | return self.addUndefined(sym_name, .add_stub); |
| 3049 | } | 3316 | } |
| 3050 | 3317 | ||
| 3051 | pub fn writeSegmentHeaders(self: anytype, writer: anytype) !void { | 3318 | pub fn writeSegmentHeaders(self: *MachO, writer: anytype) !void { |
| 3052 | for (self.segments.items, 0..) |seg, i| { | 3319 | for (self.segments.items, 0..) |seg, i| { |
| 3053 | const indexes = self.getSectionIndexes(@as(u8, @intCast(i))); | 3320 | const indexes = self.getSectionIndexes(@as(u8, @intCast(i))); |
| 3054 | var out_seg = seg; | 3321 | var out_seg = seg; |
| ... | @@ -3075,7 +3342,7 @@ pub fn writeSegmentHeaders(self: anytype, writer: anytype) !void { | ... | @@ -3075,7 +3342,7 @@ pub fn writeSegmentHeaders(self: anytype, writer: anytype) !void { |
| 3075 | } | 3342 | } |
| 3076 | } | 3343 | } |
| 3077 | 3344 | ||
| 3078 | fn writeLinkeditSegmentData(self: *MachO) !void { | 3345 | pub fn writeLinkeditSegmentData(self: *MachO) !void { |
| 3079 | const page_size = getPageSize(self.base.options.target.cpu.arch); | 3346 | const page_size = getPageSize(self.base.options.target.cpu.arch); |
| 3080 | const seg = self.getLinkeditSegmentPtr(); | 3347 | const seg = self.getLinkeditSegmentPtr(); |
| 3081 | seg.filesize = 0; | 3348 | seg.filesize = 0; |
| ... | @@ -3092,29 +3359,29 @@ fn writeLinkeditSegmentData(self: *MachO) !void { | ... | @@ -3092,29 +3359,29 @@ fn writeLinkeditSegmentData(self: *MachO) !void { |
| 3092 | } | 3359 | } |
| 3093 | 3360 | ||
| 3094 | try self.writeDyldInfoData(); | 3361 | try self.writeDyldInfoData(); |
| 3362 | // TODO handle this better | ||
| 3363 | if (self.mode == .zld) { | ||
| 3364 | try self.writeFunctionStarts(); | ||
| 3365 | try self.writeDataInCode(); | ||
| 3366 | } | ||
| 3095 | try self.writeSymtabs(); | 3367 | try self.writeSymtabs(); |
| 3096 | 3368 | ||
| 3097 | seg.vmsize = mem.alignForward(u64, seg.filesize, page_size); | 3369 | seg.vmsize = mem.alignForward(u64, seg.filesize, page_size); |
| 3098 | } | 3370 | } |
| 3099 | 3371 | ||
| 3100 | pub fn collectRebaseDataFromTableSection( | 3372 | fn collectRebaseDataFromTableSection(self: *MachO, sect_id: u8, rebase: *Rebase, table: anytype) !void { |
| 3101 | gpa: Allocator, | 3373 | const gpa = self.base.allocator; |
| 3102 | ctx: anytype, | 3374 | const header = self.sections.items(.header)[sect_id]; |
| 3103 | sect_id: u8, | 3375 | const segment_index = self.sections.items(.segment_index)[sect_id]; |
| 3104 | rebase: *Rebase, | 3376 | const segment = self.segments.items[segment_index]; |
| 3105 | table: anytype, | ||
| 3106 | ) !void { | ||
| 3107 | const header = ctx.sections.items(.header)[sect_id]; | ||
| 3108 | const segment_index = ctx.sections.items(.segment_index)[sect_id]; | ||
| 3109 | const segment = ctx.segments.items[segment_index]; | ||
| 3110 | const base_offset = header.addr - segment.vmaddr; | 3377 | const base_offset = header.addr - segment.vmaddr; |
| 3111 | const is_got = if (ctx.got_section_index) |index| index == sect_id else false; | 3378 | const is_got = if (self.got_section_index) |index| index == sect_id else false; |
| 3112 | 3379 | ||
| 3113 | try rebase.entries.ensureUnusedCapacity(gpa, table.entries.items.len); | 3380 | try rebase.entries.ensureUnusedCapacity(gpa, table.entries.items.len); |
| 3114 | 3381 | ||
| 3115 | for (table.entries.items, 0..) |entry, i| { | 3382 | for (table.entries.items, 0..) |entry, i| { |
| 3116 | if (!table.lookup.contains(entry)) continue; | 3383 | if (!table.lookup.contains(entry)) continue; |
| 3117 | const sym = ctx.getSymbol(entry); | 3384 | const sym = self.getSymbol(entry); |
| 3118 | if (is_got and sym.undf()) continue; | 3385 | if (is_got and sym.undf()) continue; |
| 3119 | const offset = i * @sizeOf(u64); | 3386 | const offset = i * @sizeOf(u64); |
| 3120 | log.debug(" | rebase at {x}", .{base_offset + offset}); | 3387 | log.debug(" | rebase at {x}", .{base_offset + offset}); |
| ... | @@ -3152,34 +3419,105 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void { | ... | @@ -3152,34 +3419,105 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void { |
| 3152 | } | 3419 | } |
| 3153 | } | 3420 | } |
| 3154 | 3421 | ||
| 3155 | try collectRebaseDataFromTableSection(gpa, self, self.got_section_index.?, rebase, self.got_table); | 3422 | // Unpack GOT entries |
| 3156 | try collectRebaseDataFromTableSection(gpa, self, self.la_symbol_ptr_section_index.?, rebase, self.stub_table); | 3423 | if (self.got_section_index) |sect_id| { |
| 3424 | try self.collectRebaseDataFromTableSection(sect_id, rebase, self.got_table); | ||
| 3425 | } | ||
| 3426 | |||
| 3427 | // Next, unpack __la_symbol_ptr entries | ||
| 3428 | if (self.la_symbol_ptr_section_index) |sect_id| { | ||
| 3429 | try self.collectRebaseDataFromTableSection(sect_id, rebase, self.stub_table); | ||
| 3430 | } | ||
| 3431 | |||
| 3432 | // Finally, unpack the rest. | ||
| 3433 | const cpu_arch = self.base.options.target.cpu.arch; | ||
| 3434 | for (self.objects.items) |*object| { | ||
| 3435 | for (object.atoms.items) |atom_index| { | ||
| 3436 | const atom = self.getAtom(atom_index); | ||
| 3437 | const sym = self.getSymbol(atom.getSymbolWithLoc()); | ||
| 3438 | if (sym.n_desc == N_DEAD) continue; | ||
| 3439 | |||
| 3440 | const sect_id = sym.n_sect - 1; | ||
| 3441 | const section = self.sections.items(.header)[sect_id]; | ||
| 3442 | const segment_id = self.sections.items(.segment_index)[sect_id]; | ||
| 3443 | const segment = self.segments.items[segment_id]; | ||
| 3444 | if (segment.maxprot & macho.PROT.WRITE == 0) continue; | ||
| 3445 | switch (section.type()) { | ||
| 3446 | macho.S_LITERAL_POINTERS, | ||
| 3447 | macho.S_REGULAR, | ||
| 3448 | macho.S_MOD_INIT_FUNC_POINTERS, | ||
| 3449 | macho.S_MOD_TERM_FUNC_POINTERS, | ||
| 3450 | => {}, | ||
| 3451 | else => continue, | ||
| 3452 | } | ||
| 3453 | |||
| 3454 | log.debug(" ATOM({d}, %{d}, '{s}')", .{ | ||
| 3455 | atom_index, | ||
| 3456 | atom.sym_index, | ||
| 3457 | self.getSymbolName(atom.getSymbolWithLoc()), | ||
| 3458 | }); | ||
| 3459 | |||
| 3460 | const code = Atom.getAtomCode(self, atom_index); | ||
| 3461 | const relocs = Atom.getAtomRelocs(self, atom_index); | ||
| 3462 | const ctx = Atom.getRelocContext(self, atom_index); | ||
| 3463 | |||
| 3464 | for (relocs) |rel| { | ||
| 3465 | switch (cpu_arch) { | ||
| 3466 | .aarch64 => { | ||
| 3467 | const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)); | ||
| 3468 | if (rel_type != .ARM64_RELOC_UNSIGNED) continue; | ||
| 3469 | if (rel.r_length != 3) continue; | ||
| 3470 | }, | ||
| 3471 | .x86_64 => { | ||
| 3472 | const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type)); | ||
| 3473 | if (rel_type != .X86_64_RELOC_UNSIGNED) continue; | ||
| 3474 | if (rel.r_length != 3) continue; | ||
| 3475 | }, | ||
| 3476 | else => unreachable, | ||
| 3477 | } | ||
| 3478 | const target = Atom.parseRelocTarget(self, .{ | ||
| 3479 | .object_id = atom.getFile().?, | ||
| 3480 | .rel = rel, | ||
| 3481 | .code = code, | ||
| 3482 | .base_offset = ctx.base_offset, | ||
| 3483 | .base_addr = ctx.base_addr, | ||
| 3484 | }); | ||
| 3485 | const target_sym = self.getSymbol(target); | ||
| 3486 | if (target_sym.undf()) continue; | ||
| 3487 | |||
| 3488 | const base_offset = @as(i32, @intCast(sym.n_value - segment.vmaddr)); | ||
| 3489 | const rel_offset = rel.r_address - ctx.base_offset; | ||
| 3490 | const offset = @as(u64, @intCast(base_offset + rel_offset)); | ||
| 3491 | log.debug(" | rebase at {x}", .{offset}); | ||
| 3492 | |||
| 3493 | try rebase.entries.append(self.gpa, .{ | ||
| 3494 | .offset = offset, | ||
| 3495 | .segment_id = segment_id, | ||
| 3496 | }); | ||
| 3497 | } | ||
| 3498 | } | ||
| 3499 | } | ||
| 3157 | 3500 | ||
| 3158 | try rebase.finalize(gpa); | 3501 | try rebase.finalize(gpa); |
| 3159 | } | 3502 | } |
| 3160 | 3503 | ||
| 3161 | pub fn collectBindDataFromTableSection( | 3504 | fn collectBindDataFromTableSection(self: *MachO, sect_id: u8, bind: anytype, table: anytype) !void { |
| 3162 | gpa: Allocator, | 3505 | const gpa = self.base.allocator; |
| 3163 | ctx: anytype, | 3506 | const header = self.sections.items(.header)[sect_id]; |
| 3164 | sect_id: u8, | 3507 | const segment_index = self.sections.items(.segment_index)[sect_id]; |
| 3165 | bind: anytype, | 3508 | const segment = self.segments.items[segment_index]; |
| 3166 | table: anytype, | ||
| 3167 | ) !void { | ||
| 3168 | const header = ctx.sections.items(.header)[sect_id]; | ||
| 3169 | const segment_index = ctx.sections.items(.segment_index)[sect_id]; | ||
| 3170 | const segment = ctx.segments.items[segment_index]; | ||
| 3171 | const base_offset = header.addr - segment.vmaddr; | 3509 | const base_offset = header.addr - segment.vmaddr; |
| 3172 | 3510 | ||
| 3173 | try bind.entries.ensureUnusedCapacity(gpa, table.entries.items.len); | 3511 | try bind.entries.ensureUnusedCapacity(gpa, table.entries.items.len); |
| 3174 | 3512 | ||
| 3175 | for (table.entries.items, 0..) |entry, i| { | 3513 | for (table.entries.items, 0..) |entry, i| { |
| 3176 | if (!table.lookup.contains(entry)) continue; | 3514 | if (!table.lookup.contains(entry)) continue; |
| 3177 | const bind_sym = ctx.getSymbol(entry); | 3515 | const bind_sym = self.getSymbol(entry); |
| 3178 | if (!bind_sym.undf()) continue; | 3516 | if (!bind_sym.undf()) continue; |
| 3179 | const offset = i * @sizeOf(u64); | 3517 | const offset = i * @sizeOf(u64); |
| 3180 | log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{ | 3518 | log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{ |
| 3181 | base_offset + offset, | 3519 | base_offset + offset, |
| 3182 | ctx.getSymbolName(entry), | 3520 | self.getSymbolName(entry), |
| 3183 | @divTrunc(@as(i16, @bitCast(bind_sym.n_desc)), macho.N_SYMBOL_RESOLVER), | 3521 | @divTrunc(@as(i16, @bitCast(bind_sym.n_desc)), macho.N_SYMBOL_RESOLVER), |
| 3184 | }); | 3522 | }); |
| 3185 | if (bind_sym.weakRef()) { | 3523 | if (bind_sym.weakRef()) { |
| ... | @@ -3235,13 +3573,105 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void { | ... | @@ -3235,13 +3573,105 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void { |
| 3235 | } | 3573 | } |
| 3236 | } | 3574 | } |
| 3237 | 3575 | ||
| 3238 | // Gather GOT pointers | 3576 | // Unpack GOT pointers |
| 3239 | try collectBindDataFromTableSection(gpa, self, self.got_section_index.?, bind, self.got_table); | 3577 | if (self.got_section_index) |sect_id| { |
| 3578 | try self.collectBindDataFromTableSection(sect_id, bind, self.got_table); | ||
| 3579 | } | ||
| 3580 | |||
| 3581 | // Next, unpack TLV pointers section | ||
| 3582 | if (self.tlv_ptr_section_index) |sect_id| { | ||
| 3583 | try self.collectBindDataFromTableSection(sect_id, bind, self.tlv_ptr_table); | ||
| 3584 | } | ||
| 3585 | |||
| 3586 | // Finally, unpack the rest. | ||
| 3587 | const cpu_arch = self.base.options.target.cpu.arch; | ||
| 3588 | for (self.objects.items) |*object| { | ||
| 3589 | for (object.atoms.items) |atom_index| { | ||
| 3590 | const atom = self.getAtom(atom_index); | ||
| 3591 | const sym = self.getSymbol(atom.getSymbolWithLoc()); | ||
| 3592 | if (sym.n_desc == N_DEAD) continue; | ||
| 3593 | |||
| 3594 | const sect_id = sym.n_sect - 1; | ||
| 3595 | const section = self.sections.items(.header)[sect_id]; | ||
| 3596 | const segment_id = self.sections.items(.segment_index)[sect_id]; | ||
| 3597 | const segment = self.segments.items[segment_id]; | ||
| 3598 | if (segment.maxprot & macho.PROT.WRITE == 0) continue; | ||
| 3599 | switch (section.type()) { | ||
| 3600 | macho.S_LITERAL_POINTERS, | ||
| 3601 | macho.S_REGULAR, | ||
| 3602 | macho.S_MOD_INIT_FUNC_POINTERS, | ||
| 3603 | macho.S_MOD_TERM_FUNC_POINTERS, | ||
| 3604 | => {}, | ||
| 3605 | else => continue, | ||
| 3606 | } | ||
| 3607 | |||
| 3608 | log.debug(" ATOM({d}, %{d}, '{s}')", .{ | ||
| 3609 | atom_index, | ||
| 3610 | atom.sym_index, | ||
| 3611 | self.getSymbolName(atom.getSymbolWithLoc()), | ||
| 3612 | }); | ||
| 3613 | |||
| 3614 | const code = Atom.getAtomCode(self, atom_index); | ||
| 3615 | const relocs = Atom.getAtomRelocs(self, atom_index); | ||
| 3616 | const ctx = Atom.getRelocContext(self, atom_index); | ||
| 3617 | |||
| 3618 | for (relocs) |rel| { | ||
| 3619 | switch (cpu_arch) { | ||
| 3620 | .aarch64 => { | ||
| 3621 | const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)); | ||
| 3622 | if (rel_type != .ARM64_RELOC_UNSIGNED) continue; | ||
| 3623 | if (rel.r_length != 3) continue; | ||
| 3624 | }, | ||
| 3625 | .x86_64 => { | ||
| 3626 | const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type)); | ||
| 3627 | if (rel_type != .X86_64_RELOC_UNSIGNED) continue; | ||
| 3628 | if (rel.r_length != 3) continue; | ||
| 3629 | }, | ||
| 3630 | else => unreachable, | ||
| 3631 | } | ||
| 3632 | |||
| 3633 | const global = Atom.parseRelocTarget(self, .{ | ||
| 3634 | .object_id = atom.getFile().?, | ||
| 3635 | .rel = rel, | ||
| 3636 | .code = code, | ||
| 3637 | .base_offset = ctx.base_offset, | ||
| 3638 | .base_addr = ctx.base_addr, | ||
| 3639 | }); | ||
| 3640 | const bind_sym_name = self.getSymbolName(global); | ||
| 3641 | const bind_sym = self.getSymbol(global); | ||
| 3642 | if (!bind_sym.undf()) continue; | ||
| 3643 | |||
| 3644 | const base_offset = sym.n_value - segment.vmaddr; | ||
| 3645 | const rel_offset = @as(u32, @intCast(rel.r_address - ctx.base_offset)); | ||
| 3646 | const offset = @as(u64, @intCast(base_offset + rel_offset)); | ||
| 3647 | const addend = mem.readIntLittle(i64, code[rel_offset..][0..8]); | ||
| 3648 | |||
| 3649 | const dylib_ordinal = @divTrunc(@as(i16, @bitCast(bind_sym.n_desc)), macho.N_SYMBOL_RESOLVER); | ||
| 3650 | log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{ | ||
| 3651 | base_offset, | ||
| 3652 | bind_sym_name, | ||
| 3653 | dylib_ordinal, | ||
| 3654 | }); | ||
| 3655 | log.debug(" | with addend {x}", .{addend}); | ||
| 3656 | if (bind_sym.weakRef()) { | ||
| 3657 | log.debug(" | marking as weak ref ", .{}); | ||
| 3658 | } | ||
| 3659 | try bind.entries.append(self.gpa, .{ | ||
| 3660 | .target = global, | ||
| 3661 | .offset = offset, | ||
| 3662 | .segment_id = segment_id, | ||
| 3663 | .addend = addend, | ||
| 3664 | }); | ||
| 3665 | } | ||
| 3666 | } | ||
| 3667 | } | ||
| 3668 | |||
| 3240 | try bind.finalize(gpa, self); | 3669 | try bind.finalize(gpa, self); |
| 3241 | } | 3670 | } |
| 3242 | 3671 | ||
| 3243 | fn collectLazyBindData(self: *MachO, bind: anytype) !void { | 3672 | fn collectLazyBindData(self: *MachO, bind: anytype) !void { |
| 3244 | try collectBindDataFromTableSection(self.base.allocator, self, self.la_symbol_ptr_section_index.?, bind, self.stub_table); | 3673 | const sect_id = self.la_symbol_ptr_section_index orelse return; |
| 3674 | try self.collectBindDataFromTableSection(sect_id, bind, self.stub_table); | ||
| 3245 | try bind.finalize(self.base.allocator, self); | 3675 | try bind.finalize(self.base.allocator, self); |
| 3246 | } | 3676 | } |
| 3247 | 3677 | ||
| ... | @@ -3259,6 +3689,7 @@ fn collectExportData(self: *MachO, trie: *Trie) !void { | ... | @@ -3259,6 +3689,7 @@ fn collectExportData(self: *MachO, trie: *Trie) !void { |
| 3259 | 3689 | ||
| 3260 | if (sym.undf()) continue; | 3690 | if (sym.undf()) continue; |
| 3261 | assert(sym.ext()); | 3691 | assert(sym.ext()); |
| 3692 | if (sym.n_desc == N_DEAD) continue; | ||
| 3262 | 3693 | ||
| 3263 | const sym_name = self.getSymbolName(global); | 3694 | const sym_name = self.getSymbolName(global); |
| 3264 | log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value }); | 3695 | log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value }); |
| ... | @@ -3349,12 +3780,7 @@ fn writeDyldInfoData(self: *MachO) !void { | ... | @@ -3349,12 +3780,7 @@ fn writeDyldInfoData(self: *MachO) !void { |
| 3349 | }); | 3780 | }); |
| 3350 | 3781 | ||
| 3351 | try self.base.file.?.pwriteAll(buffer, rebase_off); | 3782 | try self.base.file.?.pwriteAll(buffer, rebase_off); |
| 3352 | try populateLazyBindOffsetsInStubHelper( | 3783 | try self.populateLazyBindOffsetsInStubHelper(lazy_bind); |
| 3353 | self, | ||
| 3354 | self.base.options.target.cpu.arch, | ||
| 3355 | self.base.file.?, | ||
| 3356 | lazy_bind, | ||
| 3357 | ); | ||
| 3358 | 3784 | ||
| 3359 | self.dyld_info_cmd.rebase_off = @as(u32, @intCast(rebase_off)); | 3785 | self.dyld_info_cmd.rebase_off = @as(u32, @intCast(rebase_off)); |
| 3360 | self.dyld_info_cmd.rebase_size = @as(u32, @intCast(rebase_size_aligned)); | 3786 | self.dyld_info_cmd.rebase_size = @as(u32, @intCast(rebase_size_aligned)); |
| ... | @@ -3366,19 +3792,15 @@ fn writeDyldInfoData(self: *MachO) !void { | ... | @@ -3366,19 +3792,15 @@ fn writeDyldInfoData(self: *MachO) !void { |
| 3366 | self.dyld_info_cmd.export_size = @as(u32, @intCast(export_size_aligned)); | 3792 | self.dyld_info_cmd.export_size = @as(u32, @intCast(export_size_aligned)); |
| 3367 | } | 3793 | } |
| 3368 | 3794 | ||
| 3369 | pub fn populateLazyBindOffsetsInStubHelper( | 3795 | fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: anytype) !void { |
| 3370 | ctx: anytype, | ||
| 3371 | cpu_arch: std.Target.Cpu.Arch, | ||
| 3372 | file: fs.File, | ||
| 3373 | lazy_bind: anytype, | ||
| 3374 | ) !void { | ||
| 3375 | if (lazy_bind.size() == 0) return; | 3796 | if (lazy_bind.size() == 0) return; |
| 3376 | 3797 | ||
| 3377 | const stub_helper_section_index = ctx.stub_helper_section_index.?; | 3798 | const stub_helper_section_index = self.stub_helper_section_index.?; |
| 3378 | // assert(ctx.stub_helper_preamble_allocated); | 3799 | // assert(ctx.stub_helper_preamble_allocated); |
| 3379 | 3800 | ||
| 3380 | const header = ctx.sections.items(.header)[stub_helper_section_index]; | 3801 | const header = self.sections.items(.header)[stub_helper_section_index]; |
| 3381 | 3802 | ||
| 3803 | const cpu_arch = self.base.options.target.cpu.arch; | ||
| 3382 | const preamble_size = stubs.stubHelperPreambleSize(cpu_arch); | 3804 | const preamble_size = stubs.stubHelperPreambleSize(cpu_arch); |
| 3383 | const stub_size = stubs.stubHelperSize(cpu_arch); | 3805 | const stub_size = stubs.stubHelperSize(cpu_arch); |
| 3384 | const stub_offset = stubs.stubOffsetInStubHelper(cpu_arch); | 3806 | const stub_offset = stubs.stubOffsetInStubHelper(cpu_arch); |
| ... | @@ -3389,14 +3811,175 @@ pub fn populateLazyBindOffsetsInStubHelper( | ... | @@ -3389,14 +3811,175 @@ pub fn populateLazyBindOffsetsInStubHelper( |
| 3389 | 3811 | ||
| 3390 | log.debug("writing lazy bind offset 0x{x} ({s}) in stub helper at 0x{x}", .{ | 3812 | log.debug("writing lazy bind offset 0x{x} ({s}) in stub helper at 0x{x}", .{ |
| 3391 | bind_offset, | 3813 | bind_offset, |
| 3392 | ctx.getSymbolName(lazy_bind.entries.items[index].target), | 3814 | self.getSymbolName(lazy_bind.entries.items[index].target), |
| 3393 | file_offset, | 3815 | file_offset, |
| 3394 | }); | 3816 | }); |
| 3395 | 3817 | ||
| 3396 | try file.pwriteAll(mem.asBytes(&bind_offset), file_offset); | 3818 | try self.base.file.?.pwriteAll(mem.asBytes(&bind_offset), file_offset); |
| 3397 | } | 3819 | } |
| 3398 | } | 3820 | } |
| 3399 | 3821 | ||
| 3822 | const asc_u64 = std.sort.asc(u64); | ||
| 3823 | |||
| 3824 | fn addSymbolToFunctionStarts(self: *MachO, sym_loc: SymbolWithLoc, addresses: *std.ArrayList(u64)) !void { | ||
| 3825 | const sym = self.getSymbol(sym_loc); | ||
| 3826 | if (sym.n_strx == 0) return; | ||
| 3827 | if (sym.n_desc == MachO.N_DEAD) return; | ||
| 3828 | if (self.symbolIsTemp(sym_loc)) return; | ||
| 3829 | try addresses.append(sym.n_value); | ||
| 3830 | } | ||
| 3831 | |||
| 3832 | fn writeFunctionStarts(self: *MachO) !void { | ||
| 3833 | const gpa = self.base.allocator; | ||
| 3834 | const seg = self.segments.items[self.header_segment_cmd_index.?]; | ||
| 3835 | |||
| 3836 | // We need to sort by address first | ||
| 3837 | var addresses = std.ArrayList(u64).init(gpa); | ||
| 3838 | defer addresses.deinit(); | ||
| 3839 | |||
| 3840 | for (self.objects.items) |object| { | ||
| 3841 | for (object.exec_atoms.items) |atom_index| { | ||
| 3842 | const atom = self.getAtom(atom_index); | ||
| 3843 | const sym_loc = atom.getSymbolWithLoc(); | ||
| 3844 | try self.addSymbolToFunctionStarts(sym_loc, &addresses); | ||
| 3845 | |||
| 3846 | var it = Atom.getInnerSymbolsIterator(self, atom_index); | ||
| 3847 | while (it.next()) |inner_sym_loc| { | ||
| 3848 | try self.addSymbolToFunctionStarts(inner_sym_loc, &addresses); | ||
| 3849 | } | ||
| 3850 | } | ||
| 3851 | } | ||
| 3852 | |||
| 3853 | mem.sort(u64, addresses.items, {}, asc_u64); | ||
| 3854 | |||
| 3855 | var offsets = std.ArrayList(u32).init(gpa); | ||
| 3856 | defer offsets.deinit(); | ||
| 3857 | try offsets.ensureTotalCapacityPrecise(addresses.items.len); | ||
| 3858 | |||
| 3859 | var last_off: u32 = 0; | ||
| 3860 | for (addresses.items) |addr| { | ||
| 3861 | const offset = @as(u32, @intCast(addr - seg.vmaddr)); | ||
| 3862 | const diff = offset - last_off; | ||
| 3863 | |||
| 3864 | if (diff == 0) continue; | ||
| 3865 | |||
| 3866 | offsets.appendAssumeCapacity(diff); | ||
| 3867 | last_off = offset; | ||
| 3868 | } | ||
| 3869 | |||
| 3870 | var buffer = std.ArrayList(u8).init(gpa); | ||
| 3871 | defer buffer.deinit(); | ||
| 3872 | |||
| 3873 | const max_size = @as(usize, @intCast(offsets.items.len * @sizeOf(u64))); | ||
| 3874 | try buffer.ensureTotalCapacity(max_size); | ||
| 3875 | |||
| 3876 | for (offsets.items) |offset| { | ||
| 3877 | try std.leb.writeULEB128(buffer.writer(), offset); | ||
| 3878 | } | ||
| 3879 | |||
| 3880 | const link_seg = self.getLinkeditSegmentPtr(); | ||
| 3881 | const offset = link_seg.fileoff + link_seg.filesize; | ||
| 3882 | assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64))); | ||
| 3883 | const needed_size = buffer.items.len; | ||
| 3884 | const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64)); | ||
| 3885 | const padding = math.cast(usize, needed_size_aligned - needed_size) orelse return error.Overflow; | ||
| 3886 | if (padding > 0) { | ||
| 3887 | try buffer.ensureUnusedCapacity(padding); | ||
| 3888 | buffer.appendNTimesAssumeCapacity(0, padding); | ||
| 3889 | } | ||
| 3890 | link_seg.filesize = offset + needed_size_aligned - link_seg.fileoff; | ||
| 3891 | |||
| 3892 | log.debug("writing function starts info from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned }); | ||
| 3893 | |||
| 3894 | try self.base.file.?.pwriteAll(buffer.items, offset); | ||
| 3895 | |||
| 3896 | self.function_starts_cmd.dataoff = @as(u32, @intCast(offset)); | ||
| 3897 | self.function_starts_cmd.datasize = @as(u32, @intCast(needed_size_aligned)); | ||
| 3898 | } | ||
| 3899 | |||
| 3900 | fn filterDataInCode( | ||
| 3901 | dices: []const macho.data_in_code_entry, | ||
| 3902 | start_addr: u64, | ||
| 3903 | end_addr: u64, | ||
| 3904 | ) []const macho.data_in_code_entry { | ||
| 3905 | const Predicate = struct { | ||
| 3906 | addr: u64, | ||
| 3907 | |||
| 3908 | pub fn predicate(self: @This(), dice: macho.data_in_code_entry) bool { | ||
| 3909 | return dice.offset >= self.addr; | ||
| 3910 | } | ||
| 3911 | }; | ||
| 3912 | |||
| 3913 | const start = MachO.lsearch(macho.data_in_code_entry, dices, Predicate{ .addr = start_addr }); | ||
| 3914 | const end = MachO.lsearch(macho.data_in_code_entry, dices[start..], Predicate{ .addr = end_addr }) + start; | ||
| 3915 | |||
| 3916 | return dices[start..end]; | ||
| 3917 | } | ||
| 3918 | |||
| 3919 | pub fn writeDataInCode(self: *MachO) !void { | ||
| 3920 | const gpa = self.base.allocator; | ||
| 3921 | var out_dice = std.ArrayList(macho.data_in_code_entry).init(gpa); | ||
| 3922 | defer out_dice.deinit(); | ||
| 3923 | |||
| 3924 | const text_sect_id = self.text_section_index orelse return; | ||
| 3925 | const text_sect_header = self.sections.items(.header)[text_sect_id]; | ||
| 3926 | |||
| 3927 | for (self.objects.items) |object| { | ||
| 3928 | if (!object.hasDataInCode()) continue; | ||
| 3929 | const dice = object.data_in_code.items; | ||
| 3930 | try out_dice.ensureUnusedCapacity(dice.len); | ||
| 3931 | |||
| 3932 | for (object.exec_atoms.items) |atom_index| { | ||
| 3933 | const atom = self.getAtom(atom_index); | ||
| 3934 | const sym = self.getSymbol(atom.getSymbolWithLoc()); | ||
| 3935 | if (sym.n_desc == MachO.N_DEAD) continue; | ||
| 3936 | |||
| 3937 | const source_addr = if (object.getSourceSymbol(atom.sym_index)) |source_sym| | ||
| 3938 | source_sym.n_value | ||
| 3939 | else blk: { | ||
| 3940 | const nbase = @as(u32, @intCast(object.in_symtab.?.len)); | ||
| 3941 | const source_sect_id = @as(u8, @intCast(atom.sym_index - nbase)); | ||
| 3942 | break :blk object.getSourceSection(source_sect_id).addr; | ||
| 3943 | }; | ||
| 3944 | const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size); | ||
| 3945 | const base = math.cast(u32, sym.n_value - text_sect_header.addr + text_sect_header.offset) orelse | ||
| 3946 | return error.Overflow; | ||
| 3947 | |||
| 3948 | for (filtered_dice) |single| { | ||
| 3949 | const offset = math.cast(u32, single.offset - source_addr + base) orelse | ||
| 3950 | return error.Overflow; | ||
| 3951 | out_dice.appendAssumeCapacity(.{ | ||
| 3952 | .offset = offset, | ||
| 3953 | .length = single.length, | ||
| 3954 | .kind = single.kind, | ||
| 3955 | }); | ||
| 3956 | } | ||
| 3957 | } | ||
| 3958 | } | ||
| 3959 | |||
| 3960 | const seg = self.getLinkeditSegmentPtr(); | ||
| 3961 | const offset = seg.fileoff + seg.filesize; | ||
| 3962 | assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64))); | ||
| 3963 | const needed_size = out_dice.items.len * @sizeOf(macho.data_in_code_entry); | ||
| 3964 | const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64)); | ||
| 3965 | seg.filesize = offset + needed_size_aligned - seg.fileoff; | ||
| 3966 | |||
| 3967 | const buffer = try gpa.alloc(u8, math.cast(usize, needed_size_aligned) orelse return error.Overflow); | ||
| 3968 | defer gpa.free(buffer); | ||
| 3969 | { | ||
| 3970 | const src = mem.sliceAsBytes(out_dice.items); | ||
| 3971 | @memcpy(buffer[0..src.len], src); | ||
| 3972 | @memset(buffer[src.len..], 0); | ||
| 3973 | } | ||
| 3974 | |||
| 3975 | log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned }); | ||
| 3976 | |||
| 3977 | try self.base.file.?.pwriteAll(buffer, offset); | ||
| 3978 | |||
| 3979 | self.data_in_code_cmd.dataoff = @as(u32, @intCast(offset)); | ||
| 3980 | self.data_in_code_cmd.datasize = @as(u32, @intCast(needed_size_aligned)); | ||
| 3981 | } | ||
| 3982 | |||
| 3400 | fn writeSymtabs(self: *MachO) !void { | 3983 | fn writeSymtabs(self: *MachO) !void { |
| 3401 | var ctx = try self.writeSymtab(); | 3984 | var ctx = try self.writeSymtab(); |
| 3402 | defer ctx.imports_table.deinit(); | 3985 | defer ctx.imports_table.deinit(); |
| ... | @@ -3404,18 +3987,38 @@ fn writeSymtabs(self: *MachO) !void { | ... | @@ -3404,18 +3987,38 @@ fn writeSymtabs(self: *MachO) !void { |
| 3404 | try self.writeStrtab(); | 3987 | try self.writeStrtab(); |
| 3405 | } | 3988 | } |
| 3406 | 3989 | ||
| 3990 | fn addLocalToSymtab(self: *MachO, sym_loc: SymbolWithLoc, locals: *std.ArrayList(macho.nlist_64)) !void { | ||
| 3991 | const sym = self.getSymbol(sym_loc); | ||
| 3992 | if (sym.n_strx == 0) return; // no name, skip | ||
| 3993 | if (sym.n_desc == MachO.N_DEAD) return; // garbage-collected, skip | ||
| 3994 | if (sym.ext()) return; // an export lands in its own symtab section, skip | ||
| 3995 | if (self.symbolIsTemp(sym_loc)) return; // local temp symbol, skip | ||
| 3996 | var out_sym = sym; | ||
| 3997 | out_sym.n_strx = try self.strtab.insert(self.base.allocator, self.getSymbolName(sym_loc)); | ||
| 3998 | try locals.append(out_sym); | ||
| 3999 | } | ||
| 4000 | |||
| 3407 | fn writeSymtab(self: *MachO) !SymtabCtx { | 4001 | fn writeSymtab(self: *MachO) !SymtabCtx { |
| 3408 | const gpa = self.base.allocator; | 4002 | const gpa = self.base.allocator; |
| 3409 | 4003 | ||
| 3410 | var locals = std.ArrayList(macho.nlist_64).init(gpa); | 4004 | var locals = std.ArrayList(macho.nlist_64).init(gpa); |
| 3411 | defer locals.deinit(); | 4005 | defer locals.deinit(); |
| 3412 | 4006 | ||
| 3413 | for (self.locals.items, 0..) |sym, sym_id| { | 4007 | for (0..self.locals.items) |sym_id| { |
| 3414 | if (sym.n_strx == 0) continue; // no name, skip | 4008 | try self.addLocalToSymtab(.{ .sym_index = @intCast(sym_id) }); |
| 3415 | const sym_loc = SymbolWithLoc{ .sym_index = @as(u32, @intCast(sym_id)) }; | 4009 | } |
| 3416 | if (self.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip | 4010 | |
| 3417 | if (self.getGlobal(self.getSymbolName(sym_loc)) != null) continue; // global symbol is either an export or import, skip | 4011 | for (self.objects.items) |object| { |
| 3418 | try locals.append(sym); | 4012 | for (object.atoms.items) |atom_index| { |
| 4013 | const atom = self.getAtom(atom_index); | ||
| 4014 | const sym_loc = atom.getSymbolWithLoc(); | ||
| 4015 | try self.addLocalToSymtab(sym_loc, &locals); | ||
| 4016 | |||
| 4017 | var it = Atom.getInnerSymbolsIterator(self, atom_index); | ||
| 4018 | while (it.next()) |inner_sym_loc| { | ||
| 4019 | try self.addLocalToSymtab(inner_sym_loc, &locals); | ||
| 4020 | } | ||
| 4021 | } | ||
| 3419 | } | 4022 | } |
| 3420 | 4023 | ||
| 3421 | var exports = std.ArrayList(macho.nlist_64).init(gpa); | 4024 | var exports = std.ArrayList(macho.nlist_64).init(gpa); |
| ... | @@ -3424,6 +4027,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx { | ... | @@ -3424,6 +4027,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx { |
| 3424 | for (self.globals.items) |global| { | 4027 | for (self.globals.items) |global| { |
| 3425 | const sym = self.getSymbol(global); | 4028 | const sym = self.getSymbol(global); |
| 3426 | if (sym.undf()) continue; // import, skip | 4029 | if (sym.undf()) continue; // import, skip |
| 4030 | if (sym.n_desc == N_DEAD) continue; | ||
| 3427 | var out_sym = sym; | 4031 | var out_sym = sym; |
| 3428 | out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global)); | 4032 | out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global)); |
| 3429 | try exports.append(out_sym); | 4033 | try exports.append(out_sym); |
| ... | @@ -3438,6 +4042,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx { | ... | @@ -3438,6 +4042,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx { |
| 3438 | const sym = self.getSymbol(global); | 4042 | const sym = self.getSymbol(global); |
| 3439 | if (sym.n_strx == 0) continue; // no name, skip | 4043 | if (sym.n_strx == 0) continue; // no name, skip |
| 3440 | if (!sym.undf()) continue; // not an import, skip | 4044 | if (!sym.undf()) continue; // not an import, skip |
| 4045 | if (sym.n_desc == N_DEAD) continue; | ||
| 3441 | const new_index = @as(u32, @intCast(imports.items.len)); | 4046 | const new_index = @as(u32, @intCast(imports.items.len)); |
| 3442 | var out_sym = sym; | 4047 | var out_sym = sym; |
| 3443 | out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global)); | 4048 | out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global)); |
| ... | @@ -3445,6 +4050,15 @@ fn writeSymtab(self: *MachO) !SymtabCtx { | ... | @@ -3445,6 +4050,15 @@ fn writeSymtab(self: *MachO) !SymtabCtx { |
| 3445 | try imports_table.putNoClobber(global, new_index); | 4050 | try imports_table.putNoClobber(global, new_index); |
| 3446 | } | 4051 | } |
| 3447 | 4052 | ||
| 4053 | // We generate stabs last in order to ensure that the strtab always has debug info | ||
| 4054 | // strings trailing | ||
| 4055 | if (!self.base.options.strip) { | ||
| 4056 | assert(self.d_sym == null); // TODO | ||
| 4057 | for (self.objects.items) |object| { | ||
| 4058 | try self.generateSymbolStabs(object, &locals); | ||
| 4059 | } | ||
| 4060 | } | ||
| 4061 | |||
| 3448 | const nlocals = @as(u32, @intCast(locals.items.len)); | 4062 | const nlocals = @as(u32, @intCast(locals.items.len)); |
| 3449 | const nexports = @as(u32, @intCast(exports.items.len)); | 4063 | const nexports = @as(u32, @intCast(exports.items.len)); |
| 3450 | const nimports = @as(u32, @intCast(imports.items.len)); | 4064 | const nimports = @as(u32, @intCast(imports.items.len)); |
| ... | @@ -3478,7 +4092,218 @@ fn writeSymtab(self: *MachO) !SymtabCtx { | ... | @@ -3478,7 +4092,218 @@ fn writeSymtab(self: *MachO) !SymtabCtx { |
| 3478 | }; | 4092 | }; |
| 3479 | } | 4093 | } |
| 3480 | 4094 | ||
| 3481 | fn writeStrtab(self: *MachO) !void { | 4095 | fn generateSymbolStabs( |
| 4096 | self: *MachO, | ||
| 4097 | object: Object, | ||
| 4098 | locals: *std.ArrayList(macho.nlist_64), | ||
| 4099 | ) !void { | ||
| 4100 | log.debug("generating stabs for '{s}'", .{object.name}); | ||
| 4101 | |||
| 4102 | const gpa = self.base.allocator; | ||
| 4103 | var debug_info = object.parseDwarfInfo(); | ||
| 4104 | |||
| 4105 | var lookup = DwarfInfo.AbbrevLookupTable.init(gpa); | ||
| 4106 | defer lookup.deinit(); | ||
| 4107 | try lookup.ensureUnusedCapacity(std.math.maxInt(u8)); | ||
| 4108 | |||
| 4109 | // We assume there is only one CU. | ||
| 4110 | var cu_it = debug_info.getCompileUnitIterator(); | ||
| 4111 | const compile_unit = while (try cu_it.next()) |cu| { | ||
| 4112 | const offset = math.cast(usize, cu.cuh.debug_abbrev_offset) orelse return error.Overflow; | ||
| 4113 | try debug_info.genAbbrevLookupByKind(offset, &lookup); | ||
| 4114 | break cu; | ||
| 4115 | } else { | ||
| 4116 | log.debug("no compile unit found in debug info in {s}; skipping", .{object.name}); | ||
| 4117 | return; | ||
| 4118 | }; | ||
| 4119 | |||
| 4120 | var abbrev_it = compile_unit.getAbbrevEntryIterator(debug_info); | ||
| 4121 | const cu_entry: DwarfInfo.AbbrevEntry = while (try abbrev_it.next(lookup)) |entry| switch (entry.tag) { | ||
| 4122 | dwarf.TAG.compile_unit => break entry, | ||
| 4123 | else => continue, | ||
| 4124 | } else { | ||
| 4125 | log.debug("missing DWARF_TAG_compile_unit tag in {s}; skipping", .{object.name}); | ||
| 4126 | return; | ||
| 4127 | }; | ||
| 4128 | |||
| 4129 | var maybe_tu_name: ?[]const u8 = null; | ||
| 4130 | var maybe_tu_comp_dir: ?[]const u8 = null; | ||
| 4131 | var attr_it = cu_entry.getAttributeIterator(debug_info, compile_unit.cuh); | ||
| 4132 | |||
| 4133 | while (try attr_it.next()) |attr| switch (attr.name) { | ||
| 4134 | dwarf.AT.comp_dir => maybe_tu_comp_dir = attr.getString(debug_info, compile_unit.cuh) orelse continue, | ||
| 4135 | dwarf.AT.name => maybe_tu_name = attr.getString(debug_info, compile_unit.cuh) orelse continue, | ||
| 4136 | else => continue, | ||
| 4137 | }; | ||
| 4138 | |||
| 4139 | if (maybe_tu_name == null or maybe_tu_comp_dir == null) { | ||
| 4140 | log.debug("missing DWARF_AT_comp_dir and DWARF_AT_name attributes {s}; skipping", .{object.name}); | ||
| 4141 | return; | ||
| 4142 | } | ||
| 4143 | |||
| 4144 | const tu_name = maybe_tu_name.?; | ||
| 4145 | const tu_comp_dir = maybe_tu_comp_dir.?; | ||
| 4146 | |||
| 4147 | // Open scope | ||
| 4148 | try locals.ensureUnusedCapacity(3); | ||
| 4149 | locals.appendAssumeCapacity(.{ | ||
| 4150 | .n_strx = try self.strtab.insert(gpa, tu_comp_dir), | ||
| 4151 | .n_type = macho.N_SO, | ||
| 4152 | .n_sect = 0, | ||
| 4153 | .n_desc = 0, | ||
| 4154 | .n_value = 0, | ||
| 4155 | }); | ||
| 4156 | locals.appendAssumeCapacity(.{ | ||
| 4157 | .n_strx = try self.strtab.insert(gpa, tu_name), | ||
| 4158 | .n_type = macho.N_SO, | ||
| 4159 | .n_sect = 0, | ||
| 4160 | .n_desc = 0, | ||
| 4161 | .n_value = 0, | ||
| 4162 | }); | ||
| 4163 | locals.appendAssumeCapacity(.{ | ||
| 4164 | .n_strx = try self.strtab.insert(gpa, object.name), | ||
| 4165 | .n_type = macho.N_OSO, | ||
| 4166 | .n_sect = 0, | ||
| 4167 | .n_desc = 1, | ||
| 4168 | .n_value = object.mtime, | ||
| 4169 | }); | ||
| 4170 | |||
| 4171 | var stabs_buf: [4]macho.nlist_64 = undefined; | ||
| 4172 | |||
| 4173 | var name_lookup: ?DwarfInfo.SubprogramLookupByName = if (object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS == 0) blk: { | ||
| 4174 | var name_lookup = DwarfInfo.SubprogramLookupByName.init(gpa); | ||
| 4175 | errdefer name_lookup.deinit(); | ||
| 4176 | try name_lookup.ensureUnusedCapacity(@as(u32, @intCast(object.atoms.items.len))); | ||
| 4177 | try debug_info.genSubprogramLookupByName(compile_unit, lookup, &name_lookup); | ||
| 4178 | break :blk name_lookup; | ||
| 4179 | } else null; | ||
| 4180 | defer if (name_lookup) |*nl| nl.deinit(); | ||
| 4181 | |||
| 4182 | for (object.atoms.items) |atom_index| { | ||
| 4183 | const atom = self.getAtom(atom_index); | ||
| 4184 | const stabs = try self.generateSymbolStabsForSymbol( | ||
| 4185 | atom_index, | ||
| 4186 | atom.getSymbolWithLoc(), | ||
| 4187 | name_lookup, | ||
| 4188 | &stabs_buf, | ||
| 4189 | ); | ||
| 4190 | try locals.appendSlice(stabs); | ||
| 4191 | |||
| 4192 | var it = Atom.getInnerSymbolsIterator(self, atom_index); | ||
| 4193 | while (it.next()) |sym_loc| { | ||
| 4194 | const contained_stabs = try self.generateSymbolStabsForSymbol( | ||
| 4195 | atom_index, | ||
| 4196 | sym_loc, | ||
| 4197 | name_lookup, | ||
| 4198 | &stabs_buf, | ||
| 4199 | ); | ||
| 4200 | try locals.appendSlice(contained_stabs); | ||
| 4201 | } | ||
| 4202 | } | ||
| 4203 | |||
| 4204 | // Close scope | ||
| 4205 | try locals.append(.{ | ||
| 4206 | .n_strx = 0, | ||
| 4207 | .n_type = macho.N_SO, | ||
| 4208 | .n_sect = 0, | ||
| 4209 | .n_desc = 0, | ||
| 4210 | .n_value = 0, | ||
| 4211 | }); | ||
| 4212 | } | ||
| 4213 | |||
| 4214 | fn generateSymbolStabsForSymbol( | ||
| 4215 | self: *MachO, | ||
| 4216 | atom_index: Atom.Index, | ||
| 4217 | sym_loc: SymbolWithLoc, | ||
| 4218 | lookup: ?DwarfInfo.SubprogramLookupByName, | ||
| 4219 | buf: *[4]macho.nlist_64, | ||
| 4220 | ) ![]const macho.nlist_64 { | ||
| 4221 | const gpa = self.base.allocator; | ||
| 4222 | const object = self.objects.items[sym_loc.getFile().?]; | ||
| 4223 | const sym = self.getSymbol(sym_loc); | ||
| 4224 | const sym_name = self.getSymbolName(sym_loc); | ||
| 4225 | const header = self.sections.items(.header)[sym.n_sect - 1]; | ||
| 4226 | |||
| 4227 | if (sym.n_strx == 0) return buf[0..0]; | ||
| 4228 | if (self.symbolIsTemp(sym_loc)) return buf[0..0]; | ||
| 4229 | |||
| 4230 | if (!header.isCode()) { | ||
| 4231 | // Since we are not dealing with machine code, it's either a global or a static depending | ||
| 4232 | // on the linkage scope. | ||
| 4233 | if (sym.sect() and sym.ext()) { | ||
| 4234 | // Global gets an N_GSYM stab type. | ||
| 4235 | buf[0] = .{ | ||
| 4236 | .n_strx = try self.strtab.insert(gpa, sym_name), | ||
| 4237 | .n_type = macho.N_GSYM, | ||
| 4238 | .n_sect = sym.n_sect, | ||
| 4239 | .n_desc = 0, | ||
| 4240 | .n_value = 0, | ||
| 4241 | }; | ||
| 4242 | } else { | ||
| 4243 | // Local static gets an N_STSYM stab type. | ||
| 4244 | buf[0] = .{ | ||
| 4245 | .n_strx = try self.strtab.insert(gpa, sym_name), | ||
| 4246 | .n_type = macho.N_STSYM, | ||
| 4247 | .n_sect = sym.n_sect, | ||
| 4248 | .n_desc = 0, | ||
| 4249 | .n_value = sym.n_value, | ||
| 4250 | }; | ||
| 4251 | } | ||
| 4252 | return buf[0..1]; | ||
| 4253 | } | ||
| 4254 | |||
| 4255 | const size: u64 = size: { | ||
| 4256 | if (object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0) { | ||
| 4257 | break :size self.getAtom(atom_index).size; | ||
| 4258 | } | ||
| 4259 | |||
| 4260 | // Since we don't have subsections to work with, we need to infer the size of each function | ||
| 4261 | // the slow way by scanning the debug info for matching symbol names and extracting | ||
| 4262 | // the symbol's DWARF_AT_low_pc and DWARF_AT_high_pc values. | ||
| 4263 | const source_sym = object.getSourceSymbol(sym_loc.sym_index) orelse return buf[0..0]; | ||
| 4264 | const subprogram = lookup.?.get(sym_name[1..]) orelse return buf[0..0]; | ||
| 4265 | |||
| 4266 | if (subprogram.addr <= source_sym.n_value and source_sym.n_value < subprogram.addr + subprogram.size) { | ||
| 4267 | break :size subprogram.size; | ||
| 4268 | } else { | ||
| 4269 | log.debug("no stab found for {s}", .{sym_name}); | ||
| 4270 | return buf[0..0]; | ||
| 4271 | } | ||
| 4272 | }; | ||
| 4273 | |||
| 4274 | buf[0] = .{ | ||
| 4275 | .n_strx = 0, | ||
| 4276 | .n_type = macho.N_BNSYM, | ||
| 4277 | .n_sect = sym.n_sect, | ||
| 4278 | .n_desc = 0, | ||
| 4279 | .n_value = sym.n_value, | ||
| 4280 | }; | ||
| 4281 | buf[1] = .{ | ||
| 4282 | .n_strx = try self.strtab.insert(gpa, sym_name), | ||
| 4283 | .n_type = macho.N_FUN, | ||
| 4284 | .n_sect = sym.n_sect, | ||
| 4285 | .n_desc = 0, | ||
| 4286 | .n_value = sym.n_value, | ||
| 4287 | }; | ||
| 4288 | buf[2] = .{ | ||
| 4289 | .n_strx = 0, | ||
| 4290 | .n_type = macho.N_FUN, | ||
| 4291 | .n_sect = 0, | ||
| 4292 | .n_desc = 0, | ||
| 4293 | .n_value = size, | ||
| 4294 | }; | ||
| 4295 | buf[3] = .{ | ||
| 4296 | .n_strx = 0, | ||
| 4297 | .n_type = macho.N_ENSYM, | ||
| 4298 | .n_sect = sym.n_sect, | ||
| 4299 | .n_desc = 0, | ||
| 4300 | .n_value = size, | ||
| 4301 | }; | ||
| 4302 | |||
| 4303 | return buf; | ||
| 4304 | } | ||
| 4305 | |||
| 4306 | pub fn writeStrtab(self: *MachO) !void { | ||
| 3482 | const gpa = self.base.allocator; | 4307 | const gpa = self.base.allocator; |
| 3483 | const seg = self.getLinkeditSegmentPtr(); | 4308 | const seg = self.getLinkeditSegmentPtr(); |
| 3484 | const offset = seg.fileoff + seg.filesize; | 4309 | const offset = seg.fileoff + seg.filesize; |
| ... | @@ -3507,7 +4332,7 @@ const SymtabCtx = struct { | ... | @@ -3507,7 +4332,7 @@ const SymtabCtx = struct { |
| 3507 | imports_table: std.AutoHashMap(SymbolWithLoc, u32), | 4332 | imports_table: std.AutoHashMap(SymbolWithLoc, u32), |
| 3508 | }; | 4333 | }; |
| 3509 | 4334 | ||
| 3510 | fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void { | 4335 | pub fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void { |
| 3511 | const gpa = self.base.allocator; | 4336 | const gpa = self.base.allocator; |
| 3512 | const nstubs = @as(u32, @intCast(self.stub_table.lookup.count())); | 4337 | const nstubs = @as(u32, @intCast(self.stub_table.lookup.count())); |
| 3513 | const ngot_entries = @as(u32, @intCast(self.got_table.lookup.count())); | 4338 | const ngot_entries = @as(u32, @intCast(self.got_table.lookup.count())); |
| ... | @@ -3582,7 +4407,7 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void { | ... | @@ -3582,7 +4407,7 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void { |
| 3582 | self.dysymtab_cmd.nindirectsyms = nindirectsyms; | 4407 | self.dysymtab_cmd.nindirectsyms = nindirectsyms; |
| 3583 | } | 4408 | } |
| 3584 | 4409 | ||
| 3585 | fn writeUuid(self: *MachO, comp: *const Compilation, uuid_cmd_offset: u32, has_codesig: bool) !void { | 4410 | pub fn writeUuid(self: *MachO, comp: *const Compilation, uuid_cmd_offset: u32, has_codesig: bool) !void { |
| 3586 | const file_size = if (!has_codesig) blk: { | 4411 | const file_size = if (!has_codesig) blk: { |
| 3587 | const seg = self.getLinkeditSegmentPtr(); | 4412 | const seg = self.getLinkeditSegmentPtr(); |
| 3588 | break :blk seg.fileoff + seg.filesize; | 4413 | break :blk seg.fileoff + seg.filesize; |
| ... | @@ -3592,7 +4417,7 @@ fn writeUuid(self: *MachO, comp: *const Compilation, uuid_cmd_offset: u32, has_c | ... | @@ -3592,7 +4417,7 @@ fn writeUuid(self: *MachO, comp: *const Compilation, uuid_cmd_offset: u32, has_c |
| 3592 | try self.base.file.?.pwriteAll(&self.uuid_cmd.uuid, offset); | 4417 | try self.base.file.?.pwriteAll(&self.uuid_cmd.uuid, offset); |
| 3593 | } | 4418 | } |
| 3594 | 4419 | ||
| 3595 | fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void { | 4420 | pub fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void { |
| 3596 | const seg = self.getLinkeditSegmentPtr(); | 4421 | const seg = self.getLinkeditSegmentPtr(); |
| 3597 | // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file | 4422 | // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file |
| 3598 | // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271 | 4423 | // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271 |
| ... | @@ -3609,8 +4434,9 @@ fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void { | ... | @@ -3609,8 +4434,9 @@ fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void { |
| 3609 | self.codesig_cmd.datasize = @as(u32, @intCast(needed_size)); | 4434 | self.codesig_cmd.datasize = @as(u32, @intCast(needed_size)); |
| 3610 | } | 4435 | } |
| 3611 | 4436 | ||
| 3612 | fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *CodeSignature) !void { | 4437 | pub fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *CodeSignature) !void { |
| 3613 | const seg = self.getSegment(self.text_section_index.?); | 4438 | const seg_id = self.header_segment_cmd_index.?; |
| 4439 | const seg = self.segments.items[seg_id]; | ||
| 3614 | const offset = self.codesig_cmd.dataoff; | 4440 | const offset = self.codesig_cmd.dataoff; |
| 3615 | 4441 | ||
| 3616 | var buffer = std.ArrayList(u8).init(self.base.allocator); | 4442 | var buffer = std.ArrayList(u8).init(self.base.allocator); |
| ... | @@ -3634,14 +4460,10 @@ fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *CodeSig | ... | @@ -3634,14 +4460,10 @@ fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *CodeSig |
| 3634 | } | 4460 | } |
| 3635 | 4461 | ||
| 3636 | /// Writes Mach-O file header. | 4462 | /// Writes Mach-O file header. |
| 3637 | fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void { | 4463 | pub fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void { |
| 3638 | var header: macho.mach_header_64 = .{}; | 4464 | var header: macho.mach_header_64 = .{}; |
| 3639 | header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL; | 4465 | header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL; |
| 3640 | 4466 | ||
| 3641 | if (!self.base.options.single_threaded) { | ||
| 3642 | header.flags |= macho.MH_HAS_TLV_DESCRIPTORS; | ||
| 3643 | } | ||
| 3644 | |||
| 3645 | switch (self.base.options.target.cpu.arch) { | 4467 | switch (self.base.options.target.cpu.arch) { |
| 3646 | .aarch64 => { | 4468 | .aarch64 => { |
| 3647 | header.cputype = macho.CPU_TYPE_ARM64; | 4469 | header.cputype = macho.CPU_TYPE_ARM64; |
| ... | @@ -3666,6 +4488,13 @@ fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void { | ... | @@ -3666,6 +4488,13 @@ fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void { |
| 3666 | else => unreachable, | 4488 | else => unreachable, |
| 3667 | } | 4489 | } |
| 3668 | 4490 | ||
| 4491 | if (self.thread_vars_section_index) |sect_id| { | ||
| 4492 | header.flags |= macho.MH_HAS_TLV_DESCRIPTORS; | ||
| 4493 | if (self.sections.items(.header)[sect_id].size > 0) { | ||
| 4494 | header.flags |= macho.MH_HAS_TLV_DESCRIPTORS; | ||
| 4495 | } | ||
| 4496 | } | ||
| 4497 | |||
| 3669 | header.ncmds = ncmds; | 4498 | header.ncmds = ncmds; |
| 3670 | header.sizeofcmds = sizeofcmds; | 4499 | header.sizeofcmds = sizeofcmds; |
| 3671 | 4500 | ||
| ... | @@ -3830,20 +4659,33 @@ pub fn symbolIsTemp(self: *MachO, sym_with_loc: SymbolWithLoc) bool { | ... | @@ -3830,20 +4659,33 @@ pub fn symbolIsTemp(self: *MachO, sym_with_loc: SymbolWithLoc) bool { |
| 3830 | 4659 | ||
| 3831 | /// Returns pointer-to-symbol described by `sym_with_loc` descriptor. | 4660 | /// Returns pointer-to-symbol described by `sym_with_loc` descriptor. |
| 3832 | pub fn getSymbolPtr(self: *MachO, sym_with_loc: SymbolWithLoc) *macho.nlist_64 { | 4661 | pub fn getSymbolPtr(self: *MachO, sym_with_loc: SymbolWithLoc) *macho.nlist_64 { |
| 3833 | assert(sym_with_loc.getFile() == null); | 4662 | if (sym_with_loc.getFile()) |file| { |
| 3834 | return &self.locals.items[sym_with_loc.sym_index]; | 4663 | const object = &self.objects.items[file]; |
| 4664 | return &object.symtab[sym_with_loc.sym_index]; | ||
| 4665 | } else { | ||
| 4666 | return &self.locals.items[sym_with_loc.sym_index]; | ||
| 4667 | } | ||
| 3835 | } | 4668 | } |
| 3836 | 4669 | ||
| 3837 | /// Returns symbol described by `sym_with_loc` descriptor. | 4670 | /// Returns symbol described by `sym_with_loc` descriptor. |
| 3838 | pub fn getSymbol(self: *const MachO, sym_with_loc: SymbolWithLoc) macho.nlist_64 { | 4671 | pub fn getSymbol(self: *const MachO, sym_with_loc: SymbolWithLoc) macho.nlist_64 { |
| 3839 | assert(sym_with_loc.getFile() == null); | 4672 | if (sym_with_loc.getFile()) |file| { |
| 3840 | return self.locals.items[sym_with_loc.sym_index]; | 4673 | const object = &self.objects.items[file]; |
| 4674 | return object.symtab[sym_with_loc.sym_index]; | ||
| 4675 | } else { | ||
| 4676 | return self.locals.items[sym_with_loc.sym_index]; | ||
| 4677 | } | ||
| 3841 | } | 4678 | } |
| 3842 | 4679 | ||
| 3843 | /// Returns name of the symbol described by `sym_with_loc` descriptor. | 4680 | /// Returns name of the symbol described by `sym_with_loc` descriptor. |
| 3844 | pub fn getSymbolName(self: *const MachO, sym_with_loc: SymbolWithLoc) []const u8 { | 4681 | pub fn getSymbolName(self: *const MachO, sym_with_loc: SymbolWithLoc) []const u8 { |
| 3845 | const sym = self.getSymbol(sym_with_loc); | 4682 | if (sym_with_loc.getFile()) |file| { |
| 3846 | return self.strtab.get(sym.n_strx).?; | 4683 | const object = self.objects.items[file]; |
| 4684 | return object.getSymbolName(sym_with_loc.sym_index); | ||
| 4685 | } else { | ||
| 4686 | const sym = self.locals.items[sym_with_loc.sym_index]; | ||
| 4687 | return self.strtab.get(sym.n_strx).?; | ||
| 4688 | } | ||
| 3847 | } | 4689 | } |
| 3848 | 4690 | ||
| 3849 | /// Returns pointer to the global entry for `name` if one exists. | 4691 | /// Returns pointer to the global entry for `name` if one exists. |
| ... | @@ -3945,6 +4787,19 @@ pub inline fn getPageSize(cpu_arch: std.Target.Cpu.Arch) u16 { | ... | @@ -3945,6 +4787,19 @@ pub inline fn getPageSize(cpu_arch: std.Target.Cpu.Arch) u16 { |
| 3945 | }; | 4787 | }; |
| 3946 | } | 4788 | } |
| 3947 | 4789 | ||
| 4790 | pub inline fn requiresThunks(self: MachO) bool { | ||
| 4791 | return self.base.options.target.cpu.arch == .aarch64; | ||
| 4792 | } | ||
| 4793 | |||
| 4794 | pub fn requiresCodeSignature(self: MachO) bool { | ||
| 4795 | if (self.base.options.entitlements) |_| return true; | ||
| 4796 | const cpu_arch = self.base.options.target.cpu.arch; | ||
| 4797 | const os_tag = self.base.options.target.os.tag; | ||
| 4798 | const abi = self.base.options.target.abi; | ||
| 4799 | if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) return true; | ||
| 4800 | return false; | ||
| 4801 | } | ||
| 4802 | |||
| 3948 | pub fn getSegmentPrecedence(segname: []const u8) u4 { | 4803 | pub fn getSegmentPrecedence(segname: []const u8) u4 { |
| 3949 | if (mem.eql(u8, segname, "__PAGEZERO")) return 0x0; | 4804 | if (mem.eql(u8, segname, "__PAGEZERO")) return 0x0; |
| 3950 | if (mem.eql(u8, segname, "__TEXT")) return 0x1; | 4805 | if (mem.eql(u8, segname, "__TEXT")) return 0x1; |
| ... | @@ -3988,24 +4843,26 @@ pub fn getSectionPrecedence(header: macho.section_64) u8 { | ... | @@ -3988,24 +4843,26 @@ pub fn getSectionPrecedence(header: macho.section_64) u8 { |
| 3988 | return (@as(u8, @intCast(segment_precedence)) << 4) + section_precedence; | 4843 | return (@as(u8, @intCast(segment_precedence)) << 4) + section_precedence; |
| 3989 | } | 4844 | } |
| 3990 | 4845 | ||
| 3991 | pub fn reportUndefined(self: *MachO, ctx: anytype) !void { | 4846 | pub fn reportUndefined(self: *MachO) !void { |
| 3992 | const count = ctx.unresolved.count(); | 4847 | const count = self.unresolved.count(); |
| 3993 | if (count == 0) return; | 4848 | if (count == 0) return; |
| 3994 | 4849 | ||
| 3995 | const gpa = self.base.allocator; | 4850 | const gpa = self.base.allocator; |
| 3996 | 4851 | ||
| 3997 | try self.misc_errors.ensureUnusedCapacity(gpa, count); | 4852 | try self.misc_errors.ensureUnusedCapacity(gpa, count); |
| 3998 | 4853 | ||
| 3999 | for (ctx.unresolved.keys()) |global_index| { | 4854 | for (self.unresolved.keys()) |global_index| { |
| 4000 | const global = ctx.globals.items[global_index]; | 4855 | const global = self.globals.items[global_index]; |
| 4001 | const sym_name = ctx.getSymbolName(global); | 4856 | const sym_name = self.getSymbolName(global); |
| 4002 | 4857 | ||
| 4003 | const nnotes: usize = if (global.getFile() == null) @as(usize, 0) else 1; | 4858 | const nnotes: usize = if (global.getFile() == null) @as(usize, 0) else 1; |
| 4004 | var notes = try std.ArrayList(File.ErrorMsg).initCapacity(gpa, nnotes); | 4859 | var notes = try std.ArrayList(File.ErrorMsg).initCapacity(gpa, nnotes); |
| 4005 | defer notes.deinit(); | 4860 | defer notes.deinit(); |
| 4006 | 4861 | ||
| 4007 | if (global.getFile()) |file| { | 4862 | if (global.getFile()) |file| { |
| 4008 | const note = try std.fmt.allocPrint(gpa, "referenced in {s}", .{ctx.objects.items[file].name}); | 4863 | const note = try std.fmt.allocPrint(gpa, "referenced in {s}", .{ |
| 4864 | self.objects.items[file].name, | ||
| 4865 | }); | ||
| 4009 | notes.appendAssumeCapacity(.{ .msg = note }); | 4866 | notes.appendAssumeCapacity(.{ .msg = note }); |
| 4010 | } | 4867 | } |
| 4011 | 4868 | ||
| ... | @@ -4051,6 +4908,19 @@ pub fn lsearch(comptime T: type, haystack: []align(1) const T, predicate: anytyp | ... | @@ -4051,6 +4908,19 @@ pub fn lsearch(comptime T: type, haystack: []align(1) const T, predicate: anytyp |
| 4051 | return i; | 4908 | return i; |
| 4052 | } | 4909 | } |
| 4053 | 4910 | ||
| 4911 | pub fn logSegments(self: *MachO) void { | ||
| 4912 | log.debug("segments:", .{}); | ||
| 4913 | for (self.segments.items, 0..) |segment, i| { | ||
| 4914 | log.debug(" segment({d}): {s} @{x} ({x}), sizeof({x})", .{ | ||
| 4915 | i, | ||
| 4916 | segment.segName(), | ||
| 4917 | segment.fileoff, | ||
| 4918 | segment.vmaddr, | ||
| 4919 | segment.vmsize, | ||
| 4920 | }); | ||
| 4921 | } | ||
| 4922 | } | ||
| 4923 | |||
| 4054 | pub fn logSections(self: *MachO) void { | 4924 | pub fn logSections(self: *MachO) void { |
| 4055 | log.debug("sections:", .{}); | 4925 | log.debug("sections:", .{}); |
| 4056 | for (self.sections.items(.header), 0..) |header, i| { | 4926 | for (self.sections.items(.header), 0..) |header, i| { |
| ... | @@ -4065,9 +4935,7 @@ pub fn logSections(self: *MachO) void { | ... | @@ -4065,9 +4935,7 @@ pub fn logSections(self: *MachO) void { |
| 4065 | } | 4935 | } |
| 4066 | } | 4936 | } |
| 4067 | 4937 | ||
| 4068 | fn logSymAttributes(sym: macho.nlist_64, buf: *[4]u8) []const u8 { | 4938 | fn logSymAttributes(sym: macho.nlist_64, buf: []u8) []const u8 { |
| 4069 | @memset(buf[0..4], '_'); | ||
| 4070 | @memset(buf[4..], ' '); | ||
| 4071 | if (sym.sect()) { | 4939 | if (sym.sect()) { |
| 4072 | buf[0] = 's'; | 4940 | buf[0] = 's'; |
| 4073 | } | 4941 | } |
| ... | @@ -4090,56 +4958,110 @@ fn logSymAttributes(sym: macho.nlist_64, buf: *[4]u8) []const u8 { | ... | @@ -4090,56 +4958,110 @@ fn logSymAttributes(sym: macho.nlist_64, buf: *[4]u8) []const u8 { |
| 4090 | pub fn logSymtab(self: *MachO) void { | 4958 | pub fn logSymtab(self: *MachO) void { |
| 4091 | var buf: [4]u8 = undefined; | 4959 | var buf: [4]u8 = undefined; |
| 4092 | 4960 | ||
| 4093 | log.debug("symtab:", .{}); | 4961 | const scoped_log = std.log.scoped(.symtab); |
| 4962 | |||
| 4963 | scoped_log.debug("locals:", .{}); | ||
| 4964 | for (self.objects.items, 0..) |object, id| { | ||
| 4965 | scoped_log.debug(" object({d}): {s}", .{ id, object.name }); | ||
| 4966 | if (object.in_symtab == null) continue; | ||
| 4967 | for (object.symtab, 0..) |sym, sym_id| { | ||
| 4968 | @memset(&buf, '_'); | ||
| 4969 | scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s}", .{ | ||
| 4970 | sym_id, | ||
| 4971 | object.getSymbolName(@as(u32, @intCast(sym_id))), | ||
| 4972 | sym.n_value, | ||
| 4973 | sym.n_sect, | ||
| 4974 | logSymAttributes(sym, &buf), | ||
| 4975 | }); | ||
| 4976 | } | ||
| 4977 | } | ||
| 4978 | scoped_log.debug(" object(-1)", .{}); | ||
| 4094 | for (self.locals.items, 0..) |sym, sym_id| { | 4979 | for (self.locals.items, 0..) |sym, sym_id| { |
| 4095 | const where = if (sym.undf() and !sym.tentative()) "ord" else "sect"; | 4980 | if (sym.undf()) continue; |
| 4096 | const def_index = if (sym.undf() and !sym.tentative()) | 4981 | scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s}", .{ |
| 4097 | @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER) | ||
| 4098 | else | ||
| 4099 | sym.n_sect + 1; | ||
| 4100 | log.debug(" %{d}: {?s} @{x} in {s}({d}), {s}", .{ | ||
| 4101 | sym_id, | 4982 | sym_id, |
| 4102 | self.strtab.get(sym.n_strx), | 4983 | self.strtab.get(sym.n_strx).?, |
| 4103 | sym.n_value, | 4984 | sym.n_value, |
| 4104 | where, | 4985 | sym.n_sect, |
| 4105 | def_index, | ||
| 4106 | logSymAttributes(sym, &buf), | 4986 | logSymAttributes(sym, &buf), |
| 4107 | }); | 4987 | }); |
| 4108 | } | 4988 | } |
| 4109 | 4989 | ||
| 4110 | log.debug("globals table:", .{}); | 4990 | scoped_log.debug("exports:", .{}); |
| 4111 | for (self.globals.items) |global| { | 4991 | for (self.globals.items, 0..) |global, i| { |
| 4112 | const name = self.getSymbolName(global); | 4992 | const sym = self.getSymbol(global); |
| 4113 | log.debug(" {s} => %{d} in object({?d})", .{ name, global.sym_index, global.file }); | 4993 | if (sym.undf()) continue; |
| 4994 | if (sym.n_desc == MachO.N_DEAD) continue; | ||
| 4995 | scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s} (def in object({?}))", .{ | ||
| 4996 | i, | ||
| 4997 | self.getSymbolName(global), | ||
| 4998 | sym.n_value, | ||
| 4999 | sym.n_sect, | ||
| 5000 | logSymAttributes(sym, &buf), | ||
| 5001 | global.file, | ||
| 5002 | }); | ||
| 5003 | } | ||
| 5004 | |||
| 5005 | scoped_log.debug("imports:", .{}); | ||
| 5006 | for (self.globals.items, 0..) |global, i| { | ||
| 5007 | const sym = self.getSymbol(global); | ||
| 5008 | if (!sym.undf()) continue; | ||
| 5009 | if (sym.n_desc == MachO.N_DEAD) continue; | ||
| 5010 | const ord = @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER); | ||
| 5011 | scoped_log.debug(" %{d}: {s} @{x} in ord({d}), {s}", .{ | ||
| 5012 | i, | ||
| 5013 | self.getSymbolName(global), | ||
| 5014 | sym.n_value, | ||
| 5015 | ord, | ||
| 5016 | logSymAttributes(sym, &buf), | ||
| 5017 | }); | ||
| 4114 | } | 5018 | } |
| 4115 | 5019 | ||
| 4116 | log.debug("GOT entries:", .{}); | 5020 | scoped_log.debug("GOT entries:", .{}); |
| 4117 | log.debug("{}", .{self.got_table}); | 5021 | scoped_log.debug("{}", .{self.got_table}); |
| 5022 | |||
| 5023 | scoped_log.debug("TLV pointers:", .{}); | ||
| 5024 | scoped_log.debug("{}", .{self.tlv_ptr_table}); | ||
| 4118 | 5025 | ||
| 4119 | log.debug("stubs entries:", .{}); | 5026 | scoped_log.debug("stubs entries:", .{}); |
| 4120 | log.debug("{}", .{self.stub_table}); | 5027 | scoped_log.debug("{}", .{self.stubs_table}); |
| 5028 | |||
| 5029 | scoped_log.debug("thunks:", .{}); | ||
| 5030 | for (self.thunks.items, 0..) |thunk, i| { | ||
| 5031 | scoped_log.debug(" thunk({d})", .{i}); | ||
| 5032 | const slice = thunk.targets.slice(); | ||
| 5033 | for (slice.items(.tag), slice.items(.target), 0..) |tag, target, j| { | ||
| 5034 | const atom_index = @as(u32, @intCast(thunk.getStartAtomIndex() + j)); | ||
| 5035 | const atom = self.getAtom(atom_index); | ||
| 5036 | const atom_sym = self.getSymbol(atom.getSymbolWithLoc()); | ||
| 5037 | const target_addr = switch (tag) { | ||
| 5038 | .stub => self.getStubsEntryAddress(target).?, | ||
| 5039 | .atom => self.getSymbol(target).n_value, | ||
| 5040 | }; | ||
| 5041 | scoped_log.debug(" {d}@{x} => {s}({s}@{x})", .{ | ||
| 5042 | j, | ||
| 5043 | atom_sym.n_value, | ||
| 5044 | @tagName(tag), | ||
| 5045 | self.getSymbolName(target), | ||
| 5046 | target_addr, | ||
| 5047 | }); | ||
| 5048 | } | ||
| 5049 | } | ||
| 4121 | } | 5050 | } |
| 4122 | 5051 | ||
| 4123 | pub fn logAtoms(self: *MachO) void { | 5052 | pub fn logAtoms(self: *MachO) void { |
| 4124 | log.debug("atoms:", .{}); | 5053 | log.debug("atoms:", .{}); |
| 4125 | |||
| 4126 | const slice = self.sections.slice(); | 5054 | const slice = self.sections.slice(); |
| 4127 | for (slice.items(.last_atom_index), 0..) |last_atom_index, i| { | 5055 | for (slice.items(.first_atom_index), 0..) |first_atom_index, sect_id| { |
| 4128 | var atom_index = last_atom_index orelse continue; | 5056 | var atom_index = first_atom_index orelse continue; |
| 4129 | const header = slice.items(.header)[i]; | 5057 | const header = slice.items(.header)[sect_id]; |
| 4130 | |||
| 4131 | while (true) { | ||
| 4132 | const atom = self.getAtom(atom_index); | ||
| 4133 | if (atom.prev_index) |prev_index| { | ||
| 4134 | atom_index = prev_index; | ||
| 4135 | } else break; | ||
| 4136 | } | ||
| 4137 | 5058 | ||
| 4138 | log.debug("{s},{s}", .{ header.segName(), header.sectName() }); | 5059 | log.debug("{s},{s}", .{ header.segName(), header.sectName() }); |
| 4139 | 5060 | ||
| 4140 | while (true) { | 5061 | while (true) { |
| 4141 | self.logAtom(atom_index); | ||
| 4142 | const atom = self.getAtom(atom_index); | 5062 | const atom = self.getAtom(atom_index); |
| 5063 | self.logAtom(atom_index, log); | ||
| 5064 | |||
| 4143 | if (atom.next_index) |next_index| { | 5065 | if (atom.next_index) |next_index| { |
| 4144 | atom_index = next_index; | 5066 | atom_index = next_index; |
| 4145 | } else break; | 5067 | } else break; |
| ... | @@ -4147,18 +5069,50 @@ pub fn logAtoms(self: *MachO) void { | ... | @@ -4147,18 +5069,50 @@ pub fn logAtoms(self: *MachO) void { |
| 4147 | } | 5069 | } |
| 4148 | } | 5070 | } |
| 4149 | 5071 | ||
| 4150 | pub fn logAtom(self: *MachO, atom_index: Atom.Index) void { | 5072 | pub fn logAtom(self: *MachO, atom_index: Atom.Index, logger: anytype) void { |
| 5073 | if (!build_options.enable_logging) return; | ||
| 5074 | |||
| 4151 | const atom = self.getAtom(atom_index); | 5075 | const atom = self.getAtom(atom_index); |
| 4152 | const sym = atom.getSymbol(self); | 5076 | const sym = self.getSymbol(atom.getSymbolWithLoc()); |
| 4153 | const sym_name = atom.getName(self); | 5077 | const sym_name = self.getSymbolName(atom.getSymbolWithLoc()); |
| 4154 | log.debug(" ATOM(%{?d}, '{s}') @ {x} sizeof({x}) in object({?d}) in sect({d})", .{ | 5078 | logger.debug(" ATOM({d}, %{d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({?}) in sect({d})", .{ |
| 4155 | atom.getSymbolIndex(), | 5079 | atom_index, |
| 5080 | atom.sym_index, | ||
| 4156 | sym_name, | 5081 | sym_name, |
| 4157 | sym.n_value, | 5082 | sym.n_value, |
| 4158 | atom.size, | 5083 | atom.size, |
| 4159 | atom.file, | 5084 | atom.alignment, |
| 4160 | sym.n_sect + 1, | 5085 | atom.getFile(), |
| 5086 | sym.n_sect, | ||
| 4161 | }); | 5087 | }); |
| 5088 | |||
| 5089 | if (atom.getFile() != null) { | ||
| 5090 | var it = Atom.getInnerSymbolsIterator(self, atom_index); | ||
| 5091 | while (it.next()) |sym_loc| { | ||
| 5092 | const inner = self.getSymbol(sym_loc); | ||
| 5093 | const inner_name = self.getSymbolName(sym_loc); | ||
| 5094 | const offset = Atom.calcInnerSymbolOffset(self, atom_index, sym_loc.sym_index); | ||
| 5095 | |||
| 5096 | logger.debug(" (%{d}, '{s}') @ {x} ({x})", .{ | ||
| 5097 | sym_loc.sym_index, | ||
| 5098 | inner_name, | ||
| 5099 | inner.n_value, | ||
| 5100 | offset, | ||
| 5101 | }); | ||
| 5102 | } | ||
| 5103 | |||
| 5104 | if (Atom.getSectionAlias(self, atom_index)) |sym_loc| { | ||
| 5105 | const alias = self.getSymbol(sym_loc); | ||
| 5106 | const alias_name = self.getSymbolName(sym_loc); | ||
| 5107 | |||
| 5108 | logger.debug(" (%{d}, '{s}') @ {x} ({x})", .{ | ||
| 5109 | sym_loc.sym_index, | ||
| 5110 | alias_name, | ||
| 5111 | alias.n_value, | ||
| 5112 | 0, | ||
| 5113 | }); | ||
| 5114 | } | ||
| 5115 | } | ||
| 4162 | } | 5116 | } |
| 4163 | 5117 | ||
| 4164 | const MachO = @This(); | 5118 | const MachO = @This(); |
| ... | @@ -4197,6 +5151,7 @@ const Cache = std.Build.Cache; | ... | @@ -4197,6 +5151,7 @@ const Cache = std.Build.Cache; |
| 4197 | const CodeSignature = @import("MachO/CodeSignature.zig"); | 5151 | const CodeSignature = @import("MachO/CodeSignature.zig"); |
| 4198 | const Compilation = @import("../Compilation.zig"); | 5152 | const Compilation = @import("../Compilation.zig"); |
| 4199 | const Dwarf = File.Dwarf; | 5153 | const Dwarf = File.Dwarf; |
| 5154 | const DwarfInfo = @import("DwarfInfo.zig"); | ||
| 4200 | const Dylib = @import("MachO/Dylib.zig"); | 5155 | const Dylib = @import("MachO/Dylib.zig"); |
| 4201 | const File = link.File; | 5156 | const File = link.File; |
| 4202 | const Object = @import("MachO/Object.zig"); | 5157 | const Object = @import("MachO/Object.zig"); |
src/link/MachO/Archive.zig+12-12| ... | @@ -1,15 +1,3 @@ | ... | @@ -1,15 +1,3 @@ |
| 1 | const Archive = @This(); | ||
| 2 | |||
| 3 | const std = @import("std"); | ||
| 4 | const assert = std.debug.assert; | ||
| 5 | const fs = std.fs; | ||
| 6 | const log = std.log.scoped(.link); | ||
| 7 | const macho = std.macho; | ||
| 8 | const mem = std.mem; | ||
| 9 | |||
| 10 | const Allocator = mem.Allocator; | ||
| 11 | const Object = @import("Object.zig"); | ||
| 12 | |||
| 13 | file: fs.File, | 1 | file: fs.File, |
| 14 | fat_offset: u64, | 2 | fat_offset: u64, |
| 15 | name: []const u8, | 3 | name: []const u8, |
| ... | @@ -215,3 +203,15 @@ pub fn parseObject(self: Archive, gpa: Allocator, offset: u32) !Object { | ... | @@ -215,3 +203,15 @@ pub fn parseObject(self: Archive, gpa: Allocator, offset: u32) !Object { |
| 215 | 203 | ||
| 216 | return object; | 204 | return object; |
| 217 | } | 205 | } |
| 206 | |||
| 207 | const Archive = @This(); | ||
| 208 | |||
| 209 | const std = @import("std"); | ||
| 210 | const assert = std.debug.assert; | ||
| 211 | const fs = std.fs; | ||
| 212 | const log = std.log.scoped(.link); | ||
| 213 | const macho = std.macho; | ||
| 214 | const mem = std.mem; | ||
| 215 | |||
| 216 | const Allocator = mem.Allocator; | ||
| 217 | const Object = @import("Object.zig"); |
src/link/MachO/Atom.zig+174-220| ... | @@ -105,8 +105,7 @@ pub fn freeListEligible(self: Atom, macho_file: *MachO) bool { | ... | @@ -105,8 +105,7 @@ pub fn freeListEligible(self: Atom, macho_file: *MachO) bool { |
| 105 | return surplus >= MachO.min_text_capacity; | 105 | return surplus >= MachO.min_text_capacity; |
| 106 | } | 106 | } |
| 107 | 107 | ||
| 108 | pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 { | 108 | pub fn getOutputSection(macho_file: *MachO, sect: macho.section_64) !?u8 { |
| 109 | const gpa = zld.gpa; | ||
| 110 | const segname = sect.segName(); | 109 | const segname = sect.segName(); |
| 111 | const sectname = sect.sectName(); | 110 | const sectname = sect.sectName(); |
| 112 | const res: ?u8 = blk: { | 111 | const res: ?u8 = blk: { |
| ... | @@ -126,20 +125,14 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 { | ... | @@ -126,20 +125,14 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 { |
| 126 | } | 125 | } |
| 127 | 126 | ||
| 128 | if (sect.isCode()) { | 127 | if (sect.isCode()) { |
| 129 | if (zld.text_section_index == null) { | 128 | if (macho_file.text_section_index == null) { |
| 130 | zld.text_section_index = try MachO.initSection( | 129 | macho_file.text_section_index = try macho_file.initSection("__TEXT", "__text", .{ |
| 131 | gpa, | 130 | .flags = macho.S_REGULAR | |
| 132 | zld, | 131 | macho.S_ATTR_PURE_INSTRUCTIONS | |
| 133 | "__TEXT", | 132 | macho.S_ATTR_SOME_INSTRUCTIONS, |
| 134 | "__text", | 133 | }); |
| 135 | .{ | ||
| 136 | .flags = macho.S_REGULAR | | ||
| 137 | macho.S_ATTR_PURE_INSTRUCTIONS | | ||
| 138 | macho.S_ATTR_SOME_INSTRUCTIONS, | ||
| 139 | }, | ||
| 140 | ); | ||
| 141 | } | 134 | } |
| 142 | break :blk zld.text_section_index.?; | 135 | break :blk macho_file.text_section_index.?; |
| 143 | } | 136 | } |
| 144 | 137 | ||
| 145 | if (sect.isDebug()) { | 138 | if (sect.isDebug()) { |
| ... | @@ -151,42 +144,26 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 { | ... | @@ -151,42 +144,26 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 { |
| 151 | macho.S_8BYTE_LITERALS, | 144 | macho.S_8BYTE_LITERALS, |
| 152 | macho.S_16BYTE_LITERALS, | 145 | macho.S_16BYTE_LITERALS, |
| 153 | => { | 146 | => { |
| 154 | break :blk zld.getSectionByName("__TEXT", "__const") orelse try MachO.initSection( | 147 | break :blk macho_file.getSectionByName("__TEXT", "__const") orelse |
| 155 | gpa, | 148 | try macho_file.initSection("__TEXT", "__const", .{}); |
| 156 | zld, | ||
| 157 | "__TEXT", | ||
| 158 | "__const", | ||
| 159 | .{}, | ||
| 160 | ); | ||
| 161 | }, | 149 | }, |
| 162 | macho.S_CSTRING_LITERALS => { | 150 | macho.S_CSTRING_LITERALS => { |
| 163 | if (mem.startsWith(u8, sectname, "__objc")) { | 151 | if (mem.startsWith(u8, sectname, "__objc")) { |
| 164 | break :blk zld.getSectionByName(segname, sectname) orelse try MachO.initSection( | 152 | break :blk macho_file.getSectionByName(segname, sectname) orelse |
| 165 | gpa, | 153 | try macho_file.initSection(segname, sectname, .{}); |
| 166 | zld, | ||
| 167 | segname, | ||
| 168 | sectname, | ||
| 169 | .{}, | ||
| 170 | ); | ||
| 171 | } | 154 | } |
| 172 | break :blk zld.getSectionByName("__TEXT", "__cstring") orelse try MachO.initSection( | 155 | break :blk macho_file.getSectionByName("__TEXT", "__cstring") orelse |
| 173 | gpa, | 156 | try macho_file.initSection("__TEXT", "__cstring", .{ |
| 174 | zld, | 157 | .flags = macho.S_CSTRING_LITERALS, |
| 175 | "__TEXT", | 158 | }); |
| 176 | "__cstring", | ||
| 177 | .{ .flags = macho.S_CSTRING_LITERALS }, | ||
| 178 | ); | ||
| 179 | }, | 159 | }, |
| 180 | macho.S_MOD_INIT_FUNC_POINTERS, | 160 | macho.S_MOD_INIT_FUNC_POINTERS, |
| 181 | macho.S_MOD_TERM_FUNC_POINTERS, | 161 | macho.S_MOD_TERM_FUNC_POINTERS, |
| 182 | => { | 162 | => { |
| 183 | break :blk zld.getSectionByName("__DATA_CONST", sectname) orelse try MachO.initSection( | 163 | break :blk macho_file.getSectionByName("__DATA_CONST", sectname) orelse |
| 184 | gpa, | 164 | try macho_file.initSection("__DATA_CONST", sectname, .{ |
| 185 | zld, | 165 | .flags = sect.flags, |
| 186 | "__DATA_CONST", | 166 | }); |
| 187 | sectname, | ||
| 188 | .{ .flags = sect.flags }, | ||
| 189 | ); | ||
| 190 | }, | 167 | }, |
| 191 | macho.S_LITERAL_POINTERS, | 168 | macho.S_LITERAL_POINTERS, |
| 192 | macho.S_ZEROFILL, | 169 | macho.S_ZEROFILL, |
| ... | @@ -195,23 +172,14 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 { | ... | @@ -195,23 +172,14 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 { |
| 195 | macho.S_THREAD_LOCAL_REGULAR, | 172 | macho.S_THREAD_LOCAL_REGULAR, |
| 196 | macho.S_THREAD_LOCAL_ZEROFILL, | 173 | macho.S_THREAD_LOCAL_ZEROFILL, |
| 197 | => { | 174 | => { |
| 198 | break :blk zld.getSectionByName(segname, sectname) orelse try MachO.initSection( | 175 | break :blk macho_file.getSectionByName(segname, sectname) orelse |
| 199 | gpa, | 176 | try macho_file.initSection(segname, sectname, .{ |
| 200 | zld, | 177 | .flags = sect.flags, |
| 201 | segname, | 178 | }); |
| 202 | sectname, | ||
| 203 | .{ .flags = sect.flags }, | ||
| 204 | ); | ||
| 205 | }, | 179 | }, |
| 206 | macho.S_COALESCED => { | 180 | macho.S_COALESCED => { |
| 207 | break :blk zld.getSectionByName(segname, sectname) orelse try MachO.initSection( | 181 | break :blk macho_file.getSectionByName(segname, sectname) orelse |
| 208 | gpa, | 182 | try macho_file.initSection(segname, sectname, .{}); |
| 209 | zld, | ||
| 210 | |||
| 211 | segname, | ||
| 212 | sectname, | ||
| 213 | .{}, | ||
| 214 | ); | ||
| 215 | }, | 183 | }, |
| 216 | macho.S_REGULAR => { | 184 | macho.S_REGULAR => { |
| 217 | if (mem.eql(u8, segname, "__TEXT")) { | 185 | if (mem.eql(u8, segname, "__TEXT")) { |
| ... | @@ -221,13 +189,8 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 { | ... | @@ -221,13 +189,8 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 { |
| 221 | mem.eql(u8, sectname, "__gosymtab") or | 189 | mem.eql(u8, sectname, "__gosymtab") or |
| 222 | mem.eql(u8, sectname, "__gopclntab")) | 190 | mem.eql(u8, sectname, "__gopclntab")) |
| 223 | { | 191 | { |
| 224 | break :blk zld.getSectionByName("__TEXT", sectname) orelse try MachO.initSection( | 192 | break :blk macho_file.getSectionByName("__TEXT", sectname) orelse |
| 225 | gpa, | 193 | try macho_file.initSection("__TEXT", sectname, .{}); |
| 226 | zld, | ||
| 227 | "__TEXT", | ||
| 228 | sectname, | ||
| 229 | .{}, | ||
| 230 | ); | ||
| 231 | } | 194 | } |
| 232 | } | 195 | } |
| 233 | if (mem.eql(u8, segname, "__DATA")) { | 196 | if (mem.eql(u8, segname, "__DATA")) { |
| ... | @@ -236,33 +199,17 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 { | ... | @@ -236,33 +199,17 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 { |
| 236 | mem.eql(u8, sectname, "__objc_classlist") or | 199 | mem.eql(u8, sectname, "__objc_classlist") or |
| 237 | mem.eql(u8, sectname, "__objc_imageinfo")) | 200 | mem.eql(u8, sectname, "__objc_imageinfo")) |
| 238 | { | 201 | { |
| 239 | break :blk zld.getSectionByName("__DATA_CONST", sectname) orelse try MachO.initSection( | 202 | break :blk macho_file.getSectionByName("__DATA_CONST", sectname) orelse |
| 240 | gpa, | 203 | try macho_file.initSection("__DATA_CONST", sectname, .{}); |
| 241 | zld, | ||
| 242 | "__DATA_CONST", | ||
| 243 | sectname, | ||
| 244 | .{}, | ||
| 245 | ); | ||
| 246 | } else if (mem.eql(u8, sectname, "__data")) { | 204 | } else if (mem.eql(u8, sectname, "__data")) { |
| 247 | if (zld.data_section_index == null) { | 205 | if (macho_file.data_section_index == null) { |
| 248 | zld.data_section_index = try MachO.initSection( | 206 | macho_file.data_section_index = try macho_file.initSection("__DATA", "__data", .{}); |
| 249 | gpa, | ||
| 250 | zld, | ||
| 251 | "__DATA", | ||
| 252 | "__data", | ||
| 253 | .{}, | ||
| 254 | ); | ||
| 255 | } | 207 | } |
| 256 | break :blk zld.data_section_index.?; | 208 | break :blk macho_file.data_section_index.?; |
| 257 | } | 209 | } |
| 258 | } | 210 | } |
| 259 | break :blk zld.getSectionByName(segname, sectname) orelse try MachO.initSection( | 211 | break :blk macho_file.getSectionByName(segname, sectname) orelse |
| 260 | gpa, | 212 | try macho_file.initSection(segname, sectname, .{}); |
| 261 | zld, | ||
| 262 | segname, | ||
| 263 | sectname, | ||
| 264 | .{}, | ||
| 265 | ); | ||
| 266 | }, | 213 | }, |
| 267 | else => break :blk null, | 214 | else => break :blk null, |
| 268 | } | 215 | } |
| ... | @@ -270,29 +217,29 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 { | ... | @@ -270,29 +217,29 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 { |
| 270 | 217 | ||
| 271 | // TODO we can do this directly in the selection logic above. | 218 | // TODO we can do this directly in the selection logic above. |
| 272 | // Or is it not worth it? | 219 | // Or is it not worth it? |
| 273 | if (zld.data_const_section_index == null) { | 220 | if (macho_file.data_const_section_index == null) { |
| 274 | if (zld.getSectionByName("__DATA_CONST", "__const")) |index| { | 221 | if (macho_file.getSectionByName("__DATA_CONST", "__const")) |index| { |
| 275 | zld.data_const_section_index = index; | 222 | macho_file.data_const_section_index = index; |
| 276 | } | 223 | } |
| 277 | } | 224 | } |
| 278 | if (zld.thread_vars_section_index == null) { | 225 | if (macho_file.thread_vars_section_index == null) { |
| 279 | if (zld.getSectionByName("__DATA", "__thread_vars")) |index| { | 226 | if (macho_file.getSectionByName("__DATA", "__thread_vars")) |index| { |
| 280 | zld.thread_vars_section_index = index; | 227 | macho_file.thread_vars_section_index = index; |
| 281 | } | 228 | } |
| 282 | } | 229 | } |
| 283 | if (zld.thread_data_section_index == null) { | 230 | if (macho_file.thread_data_section_index == null) { |
| 284 | if (zld.getSectionByName("__DATA", "__thread_data")) |index| { | 231 | if (macho_file.getSectionByName("__DATA", "__thread_data")) |index| { |
| 285 | zld.thread_data_section_index = index; | 232 | macho_file.thread_data_section_index = index; |
| 286 | } | 233 | } |
| 287 | } | 234 | } |
| 288 | if (zld.thread_bss_section_index == null) { | 235 | if (macho_file.thread_bss_section_index == null) { |
| 289 | if (zld.getSectionByName("__DATA", "__thread_bss")) |index| { | 236 | if (macho_file.getSectionByName("__DATA", "__thread_bss")) |index| { |
| 290 | zld.thread_bss_section_index = index; | 237 | macho_file.thread_bss_section_index = index; |
| 291 | } | 238 | } |
| 292 | } | 239 | } |
| 293 | if (zld.bss_section_index == null) { | 240 | if (macho_file.bss_section_index == null) { |
| 294 | if (zld.getSectionByName("__DATA", "__bss")) |index| { | 241 | if (macho_file.getSectionByName("__DATA", "__bss")) |index| { |
| 295 | zld.bss_section_index = index; | 242 | macho_file.bss_section_index = index; |
| 296 | } | 243 | } |
| 297 | } | 244 | } |
| 298 | 245 | ||
| ... | @@ -383,8 +330,8 @@ const InnerSymIterator = struct { | ... | @@ -383,8 +330,8 @@ const InnerSymIterator = struct { |
| 383 | 330 | ||
| 384 | /// Returns an iterator over potentially contained symbols. | 331 | /// Returns an iterator over potentially contained symbols. |
| 385 | /// Panics when called on a synthetic Atom. | 332 | /// Panics when called on a synthetic Atom. |
| 386 | pub fn getInnerSymbolsIterator(zld: *Zld, atom_index: Index) InnerSymIterator { | 333 | pub fn getInnerSymbolsIterator(macho_file: *MachO, atom_index: Index) InnerSymIterator { |
| 387 | const atom = zld.getAtom(atom_index); | 334 | const atom = macho_file.getAtom(atom_index); |
| 388 | assert(atom.getFile() != null); | 335 | assert(atom.getFile() != null); |
| 389 | return .{ | 336 | return .{ |
| 390 | .sym_index = atom.inner_sym_index, | 337 | .sym_index = atom.inner_sym_index, |
| ... | @@ -397,11 +344,11 @@ pub fn getInnerSymbolsIterator(zld: *Zld, atom_index: Index) InnerSymIterator { | ... | @@ -397,11 +344,11 @@ pub fn getInnerSymbolsIterator(zld: *Zld, atom_index: Index) InnerSymIterator { |
| 397 | /// An alias symbol is used to represent the start of an input section | 344 | /// An alias symbol is used to represent the start of an input section |
| 398 | /// if there were no symbols defined within that range. | 345 | /// if there were no symbols defined within that range. |
| 399 | /// Alias symbols are only used on x86_64. | 346 | /// Alias symbols are only used on x86_64. |
| 400 | pub fn getSectionAlias(zld: *Zld, atom_index: Index) ?SymbolWithLoc { | 347 | pub fn getSectionAlias(macho_file: *MachO, atom_index: Index) ?SymbolWithLoc { |
| 401 | const atom = zld.getAtom(atom_index); | 348 | const atom = macho_file.getAtom(atom_index); |
| 402 | assert(atom.getFile() != null); | 349 | assert(atom.getFile() != null); |
| 403 | 350 | ||
| 404 | const object = zld.objects.items[atom.getFile().?]; | 351 | const object = macho_file.objects.items[atom.getFile().?]; |
| 405 | const nbase = @as(u32, @intCast(object.in_symtab.?.len)); | 352 | const nbase = @as(u32, @intCast(object.in_symtab.?.len)); |
| 406 | const ntotal = @as(u32, @intCast(object.symtab.len)); | 353 | const ntotal = @as(u32, @intCast(object.symtab.len)); |
| 407 | var sym_index: u32 = nbase; | 354 | var sym_index: u32 = nbase; |
| ... | @@ -418,13 +365,13 @@ pub fn getSectionAlias(zld: *Zld, atom_index: Index) ?SymbolWithLoc { | ... | @@ -418,13 +365,13 @@ pub fn getSectionAlias(zld: *Zld, atom_index: Index) ?SymbolWithLoc { |
| 418 | 365 | ||
| 419 | /// Given an index into a contained symbol within, calculates an offset wrt | 366 | /// Given an index into a contained symbol within, calculates an offset wrt |
| 420 | /// the start of this Atom. | 367 | /// the start of this Atom. |
| 421 | pub fn calcInnerSymbolOffset(zld: *Zld, atom_index: Index, sym_index: u32) u64 { | 368 | pub fn calcInnerSymbolOffset(macho_file: *MachO, atom_index: Index, sym_index: u32) u64 { |
| 422 | const atom = zld.getAtom(atom_index); | 369 | const atom = macho_file.getAtom(atom_index); |
| 423 | assert(atom.getFile() != null); | 370 | assert(atom.getFile() != null); |
| 424 | 371 | ||
| 425 | if (atom.sym_index == sym_index) return 0; | 372 | if (atom.sym_index == sym_index) return 0; |
| 426 | 373 | ||
| 427 | const object = zld.objects.items[atom.getFile().?]; | 374 | const object = macho_file.objects.items[atom.getFile().?]; |
| 428 | const source_sym = object.getSourceSymbol(sym_index).?; | 375 | const source_sym = object.getSourceSymbol(sym_index).?; |
| 429 | const base_addr = if (object.getSourceSymbol(atom.sym_index)) |sym| | 376 | const base_addr = if (object.getSourceSymbol(atom.sym_index)) |sym| |
| 430 | sym.n_value | 377 | sym.n_value |
| ... | @@ -437,14 +384,14 @@ pub fn calcInnerSymbolOffset(zld: *Zld, atom_index: Index, sym_index: u32) u64 { | ... | @@ -437,14 +384,14 @@ pub fn calcInnerSymbolOffset(zld: *Zld, atom_index: Index, sym_index: u32) u64 { |
| 437 | return source_sym.n_value - base_addr; | 384 | return source_sym.n_value - base_addr; |
| 438 | } | 385 | } |
| 439 | 386 | ||
| 440 | pub fn scanAtomRelocs(zld: *Zld, atom_index: Index, relocs: []align(1) const macho.relocation_info) !void { | 387 | pub fn scanAtomRelocs(macho_file: *MachO, atom_index: Index, relocs: []align(1) const macho.relocation_info) !void { |
| 441 | const arch = zld.options.target.cpu.arch; | 388 | const arch = macho_file.base.options.target.cpu.arch; |
| 442 | const atom = zld.getAtom(atom_index); | 389 | const atom = macho_file.getAtom(atom_index); |
| 443 | assert(atom.getFile() != null); // synthetic atoms do not have relocs | 390 | assert(atom.getFile() != null); // synthetic atoms do not have relocs |
| 444 | 391 | ||
| 445 | return switch (arch) { | 392 | return switch (arch) { |
| 446 | .aarch64 => scanAtomRelocsArm64(zld, atom_index, relocs), | 393 | .aarch64 => scanAtomRelocsArm64(macho_file, atom_index, relocs), |
| 447 | .x86_64 => scanAtomRelocsX86(zld, atom_index, relocs), | 394 | .x86_64 => scanAtomRelocsX86(macho_file, atom_index, relocs), |
| 448 | else => unreachable, | 395 | else => unreachable, |
| 449 | }; | 396 | }; |
| 450 | } | 397 | } |
| ... | @@ -454,11 +401,11 @@ const RelocContext = struct { | ... | @@ -454,11 +401,11 @@ const RelocContext = struct { |
| 454 | base_offset: i32 = 0, | 401 | base_offset: i32 = 0, |
| 455 | }; | 402 | }; |
| 456 | 403 | ||
| 457 | pub fn getRelocContext(zld: *Zld, atom_index: Index) RelocContext { | 404 | pub fn getRelocContext(macho_file: *MachO, atom_index: Index) RelocContext { |
| 458 | const atom = zld.getAtom(atom_index); | 405 | const atom = macho_file.getAtom(atom_index); |
| 459 | assert(atom.getFile() != null); // synthetic atoms do not have relocs | 406 | assert(atom.getFile() != null); // synthetic atoms do not have relocs |
| 460 | 407 | ||
| 461 | const object = zld.objects.items[atom.getFile().?]; | 408 | const object = macho_file.objects.items[atom.getFile().?]; |
| 462 | if (object.getSourceSymbol(atom.sym_index)) |source_sym| { | 409 | if (object.getSourceSymbol(atom.sym_index)) |source_sym| { |
| 463 | const source_sect = object.getSourceSection(source_sym.n_sect - 1); | 410 | const source_sect = object.getSourceSection(source_sym.n_sect - 1); |
| 464 | return .{ | 411 | return .{ |
| ... | @@ -475,7 +422,7 @@ pub fn getRelocContext(zld: *Zld, atom_index: Index) RelocContext { | ... | @@ -475,7 +422,7 @@ pub fn getRelocContext(zld: *Zld, atom_index: Index) RelocContext { |
| 475 | }; | 422 | }; |
| 476 | } | 423 | } |
| 477 | 424 | ||
| 478 | pub fn parseRelocTarget(zld: *Zld, ctx: struct { | 425 | pub fn parseRelocTarget(macho_file: *MachO, ctx: struct { |
| 479 | object_id: u32, | 426 | object_id: u32, |
| 480 | rel: macho.relocation_info, | 427 | rel: macho.relocation_info, |
| 481 | code: []const u8, | 428 | code: []const u8, |
| ... | @@ -485,7 +432,7 @@ pub fn parseRelocTarget(zld: *Zld, ctx: struct { | ... | @@ -485,7 +432,7 @@ pub fn parseRelocTarget(zld: *Zld, ctx: struct { |
| 485 | const tracy = trace(@src()); | 432 | const tracy = trace(@src()); |
| 486 | defer tracy.end(); | 433 | defer tracy.end(); |
| 487 | 434 | ||
| 488 | const object = &zld.objects.items[ctx.object_id]; | 435 | const object = &macho_file.objects.items[ctx.object_id]; |
| 489 | log.debug("parsing reloc target in object({d}) '{s}' ", .{ ctx.object_id, object.name }); | 436 | log.debug("parsing reloc target in object({d}) '{s}' ", .{ ctx.object_id, object.name }); |
| 490 | 437 | ||
| 491 | const sym_index = if (ctx.rel.r_extern == 0) sym_index: { | 438 | const sym_index = if (ctx.rel.r_extern == 0) sym_index: { |
| ... | @@ -498,7 +445,7 @@ pub fn parseRelocTarget(zld: *Zld, ctx: struct { | ... | @@ -498,7 +445,7 @@ pub fn parseRelocTarget(zld: *Zld, ctx: struct { |
| 498 | else | 445 | else |
| 499 | mem.readIntLittle(u32, ctx.code[rel_offset..][0..4]); | 446 | mem.readIntLittle(u32, ctx.code[rel_offset..][0..4]); |
| 500 | } else blk: { | 447 | } else blk: { |
| 501 | assert(zld.options.target.cpu.arch == .x86_64); | 448 | assert(macho_file.base.options.target.cpu.arch == .x86_64); |
| 502 | const correction: u3 = switch (@as(macho.reloc_type_x86_64, @enumFromInt(ctx.rel.r_type))) { | 449 | const correction: u3 = switch (@as(macho.reloc_type_x86_64, @enumFromInt(ctx.rel.r_type))) { |
| 503 | .X86_64_RELOC_SIGNED => 0, | 450 | .X86_64_RELOC_SIGNED => 0, |
| 504 | .X86_64_RELOC_SIGNED_1 => 1, | 451 | .X86_64_RELOC_SIGNED_1 => 1, |
| ... | @@ -517,35 +464,39 @@ pub fn parseRelocTarget(zld: *Zld, ctx: struct { | ... | @@ -517,35 +464,39 @@ pub fn parseRelocTarget(zld: *Zld, ctx: struct { |
| 517 | } else object.reverse_symtab_lookup[ctx.rel.r_symbolnum]; | 464 | } else object.reverse_symtab_lookup[ctx.rel.r_symbolnum]; |
| 518 | 465 | ||
| 519 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = ctx.object_id + 1 }; | 466 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = ctx.object_id + 1 }; |
| 520 | const sym = zld.getSymbol(sym_loc); | 467 | const sym = macho_file.getSymbol(sym_loc); |
| 521 | const target = if (sym.sect() and !sym.ext()) | 468 | const target = if (sym.sect() and !sym.ext()) |
| 522 | sym_loc | 469 | sym_loc |
| 523 | else if (object.getGlobal(sym_index)) |global_index| | 470 | else if (object.getGlobal(sym_index)) |global_index| |
| 524 | zld.globals.items[global_index] | 471 | macho_file.globals.items[global_index] |
| 525 | else | 472 | else |
| 526 | sym_loc; | 473 | sym_loc; |
| 527 | log.debug(" | target %{d} ('{s}') in object({?d})", .{ | 474 | log.debug(" | target %{d} ('{s}') in object({?d})", .{ |
| 528 | target.sym_index, | 475 | target.sym_index, |
| 529 | zld.getSymbolName(target), | 476 | macho_file.getSymbolName(target), |
| 530 | target.getFile(), | 477 | target.getFile(), |
| 531 | }); | 478 | }); |
| 532 | return target; | 479 | return target; |
| 533 | } | 480 | } |
| 534 | 481 | ||
| 535 | pub fn getRelocTargetAtomIndex(zld: *Zld, target: SymbolWithLoc) ?Index { | 482 | pub fn getRelocTargetAtomIndex(macho_file: *MachO, target: SymbolWithLoc) ?Index { |
| 536 | if (target.getFile() == null) { | 483 | if (target.getFile() == null) { |
| 537 | const target_sym_name = zld.getSymbolName(target); | 484 | const target_sym_name = macho_file.getSymbolName(target); |
| 538 | if (mem.eql(u8, "__mh_execute_header", target_sym_name)) return null; | 485 | if (mem.eql(u8, "__mh_execute_header", target_sym_name)) return null; |
| 539 | if (mem.eql(u8, "___dso_handle", target_sym_name)) return null; | 486 | if (mem.eql(u8, "___dso_handle", target_sym_name)) return null; |
| 540 | 487 | ||
| 541 | unreachable; // referenced symbol not found | 488 | unreachable; // referenced symbol not found |
| 542 | } | 489 | } |
| 543 | 490 | ||
| 544 | const object = zld.objects.items[target.getFile().?]; | 491 | const object = macho_file.objects.items[target.getFile().?]; |
| 545 | return object.getAtomIndexForSymbol(target.sym_index); | 492 | return object.getAtomIndexForSymbol(target.sym_index); |
| 546 | } | 493 | } |
| 547 | 494 | ||
| 548 | fn scanAtomRelocsArm64(zld: *Zld, atom_index: Index, relocs: []align(1) const macho.relocation_info) !void { | 495 | fn scanAtomRelocsArm64( |
| 496 | macho_file: *MachO, | ||
| 497 | atom_index: Index, | ||
| 498 | relocs: []align(1) const macho.relocation_info, | ||
| 499 | ) !void { | ||
| 549 | for (relocs) |rel| { | 500 | for (relocs) |rel| { |
| 550 | const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)); | 501 | const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)); |
| 551 | 502 | ||
| ... | @@ -556,8 +507,8 @@ fn scanAtomRelocsArm64(zld: *Zld, atom_index: Index, relocs: []align(1) const ma | ... | @@ -556,8 +507,8 @@ fn scanAtomRelocsArm64(zld: *Zld, atom_index: Index, relocs: []align(1) const ma |
| 556 | 507 | ||
| 557 | if (rel.r_extern == 0) continue; | 508 | if (rel.r_extern == 0) continue; |
| 558 | 509 | ||
| 559 | const atom = zld.getAtom(atom_index); | 510 | const atom = macho_file.getAtom(atom_index); |
| 560 | const object = &zld.objects.items[atom.getFile().?]; | 511 | const object = &macho_file.objects.items[atom.getFile().?]; |
| 561 | const sym_index = object.reverse_symtab_lookup[rel.r_symbolnum]; | 512 | const sym_index = object.reverse_symtab_lookup[rel.r_symbolnum]; |
| 562 | const sym_loc = SymbolWithLoc{ | 513 | const sym_loc = SymbolWithLoc{ |
| 563 | .sym_index = sym_index, | 514 | .sym_index = sym_index, |
| ... | @@ -565,35 +516,39 @@ fn scanAtomRelocsArm64(zld: *Zld, atom_index: Index, relocs: []align(1) const ma | ... | @@ -565,35 +516,39 @@ fn scanAtomRelocsArm64(zld: *Zld, atom_index: Index, relocs: []align(1) const ma |
| 565 | }; | 516 | }; |
| 566 | 517 | ||
| 567 | const target = if (object.getGlobal(sym_index)) |global_index| | 518 | const target = if (object.getGlobal(sym_index)) |global_index| |
| 568 | zld.globals.items[global_index] | 519 | macho_file.globals.items[global_index] |
| 569 | else | 520 | else |
| 570 | sym_loc; | 521 | sym_loc; |
| 571 | 522 | ||
| 572 | switch (rel_type) { | 523 | switch (rel_type) { |
| 573 | .ARM64_RELOC_BRANCH26 => { | 524 | .ARM64_RELOC_BRANCH26 => { |
| 574 | // TODO rewrite relocation | 525 | // TODO rewrite relocation |
| 575 | const sym = zld.getSymbol(target); | 526 | const sym = macho_file.getSymbol(target); |
| 576 | if (sym.undf()) try zld.addStubEntry(target); | 527 | if (sym.undf()) try macho_file.addStubEntry(target); |
| 577 | }, | 528 | }, |
| 578 | .ARM64_RELOC_GOT_LOAD_PAGE21, | 529 | .ARM64_RELOC_GOT_LOAD_PAGE21, |
| 579 | .ARM64_RELOC_GOT_LOAD_PAGEOFF12, | 530 | .ARM64_RELOC_GOT_LOAD_PAGEOFF12, |
| 580 | .ARM64_RELOC_POINTER_TO_GOT, | 531 | .ARM64_RELOC_POINTER_TO_GOT, |
| 581 | => { | 532 | => { |
| 582 | // TODO rewrite relocation | 533 | // TODO rewrite relocation |
| 583 | try zld.addGotEntry(target); | 534 | try macho_file.addGotEntry(target); |
| 584 | }, | 535 | }, |
| 585 | .ARM64_RELOC_TLVP_LOAD_PAGE21, | 536 | .ARM64_RELOC_TLVP_LOAD_PAGE21, |
| 586 | .ARM64_RELOC_TLVP_LOAD_PAGEOFF12, | 537 | .ARM64_RELOC_TLVP_LOAD_PAGEOFF12, |
| 587 | => { | 538 | => { |
| 588 | const sym = zld.getSymbol(target); | 539 | const sym = macho_file.getSymbol(target); |
| 589 | if (sym.undf()) try zld.addTlvPtrEntry(target); | 540 | if (sym.undf()) try macho_file.addTlvPtrEntry(target); |
| 590 | }, | 541 | }, |
| 591 | else => {}, | 542 | else => {}, |
| 592 | } | 543 | } |
| 593 | } | 544 | } |
| 594 | } | 545 | } |
| 595 | 546 | ||
| 596 | fn scanAtomRelocsX86(zld: *Zld, atom_index: Index, relocs: []align(1) const macho.relocation_info) !void { | 547 | fn scanAtomRelocsX86( |
| 548 | macho_file: *MachO, | ||
| 549 | atom_index: Index, | ||
| 550 | relocs: []align(1) const macho.relocation_info, | ||
| 551 | ) !void { | ||
| 597 | for (relocs) |rel| { | 552 | for (relocs) |rel| { |
| 598 | const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type)); | 553 | const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type)); |
| 599 | 554 | ||
| ... | @@ -604,8 +559,8 @@ fn scanAtomRelocsX86(zld: *Zld, atom_index: Index, relocs: []align(1) const mach | ... | @@ -604,8 +559,8 @@ fn scanAtomRelocsX86(zld: *Zld, atom_index: Index, relocs: []align(1) const mach |
| 604 | 559 | ||
| 605 | if (rel.r_extern == 0) continue; | 560 | if (rel.r_extern == 0) continue; |
| 606 | 561 | ||
| 607 | const atom = zld.getAtom(atom_index); | 562 | const atom = macho_file.getAtom(atom_index); |
| 608 | const object = &zld.objects.items[atom.getFile().?]; | 563 | const object = &macho_file.objects.items[atom.getFile().?]; |
| 609 | const sym_index = object.reverse_symtab_lookup[rel.r_symbolnum]; | 564 | const sym_index = object.reverse_symtab_lookup[rel.r_symbolnum]; |
| 610 | const sym_loc = SymbolWithLoc{ | 565 | const sym_loc = SymbolWithLoc{ |
| 611 | .sym_index = sym_index, | 566 | .sym_index = sym_index, |
| ... | @@ -613,23 +568,23 @@ fn scanAtomRelocsX86(zld: *Zld, atom_index: Index, relocs: []align(1) const mach | ... | @@ -613,23 +568,23 @@ fn scanAtomRelocsX86(zld: *Zld, atom_index: Index, relocs: []align(1) const mach |
| 613 | }; | 568 | }; |
| 614 | 569 | ||
| 615 | const target = if (object.getGlobal(sym_index)) |global_index| | 570 | const target = if (object.getGlobal(sym_index)) |global_index| |
| 616 | zld.globals.items[global_index] | 571 | macho_file.globals.items[global_index] |
| 617 | else | 572 | else |
| 618 | sym_loc; | 573 | sym_loc; |
| 619 | 574 | ||
| 620 | switch (rel_type) { | 575 | switch (rel_type) { |
| 621 | .X86_64_RELOC_BRANCH => { | 576 | .X86_64_RELOC_BRANCH => { |
| 622 | // TODO rewrite relocation | 577 | // TODO rewrite relocation |
| 623 | const sym = zld.getSymbol(target); | 578 | const sym = macho_file.getSymbol(target); |
| 624 | if (sym.undf()) try zld.addStubEntry(target); | 579 | if (sym.undf()) try macho_file.addStubEntry(target); |
| 625 | }, | 580 | }, |
| 626 | .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => { | 581 | .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => { |
| 627 | // TODO rewrite relocation | 582 | // TODO rewrite relocation |
| 628 | try zld.addGotEntry(target); | 583 | try macho_file.addGotEntry(target); |
| 629 | }, | 584 | }, |
| 630 | .X86_64_RELOC_TLV => { | 585 | .X86_64_RELOC_TLV => { |
| 631 | const sym = zld.getSymbol(target); | 586 | const sym = macho_file.getSymbol(target); |
| 632 | if (sym.undf()) try zld.addTlvPtrEntry(target); | 587 | if (sym.undf()) try macho_file.addTlvPtrEntry(target); |
| 633 | }, | 588 | }, |
| 634 | else => {}, | 589 | else => {}, |
| 635 | } | 590 | } |
| ... | @@ -637,53 +592,53 @@ fn scanAtomRelocsX86(zld: *Zld, atom_index: Index, relocs: []align(1) const mach | ... | @@ -637,53 +592,53 @@ fn scanAtomRelocsX86(zld: *Zld, atom_index: Index, relocs: []align(1) const mach |
| 637 | } | 592 | } |
| 638 | 593 | ||
| 639 | pub fn resolveRelocs( | 594 | pub fn resolveRelocs( |
| 640 | zld: *Zld, | 595 | macho_file: *MachO, |
| 641 | atom_index: Index, | 596 | atom_index: Index, |
| 642 | atom_code: []u8, | 597 | atom_code: []u8, |
| 643 | atom_relocs: []align(1) const macho.relocation_info, | 598 | atom_relocs: []align(1) const macho.relocation_info, |
| 644 | ) !void { | 599 | ) !void { |
| 645 | const arch = zld.options.target.cpu.arch; | 600 | const arch = macho_file.base.options.target.cpu.arch; |
| 646 | const atom = zld.getAtom(atom_index); | 601 | const atom = macho_file.getAtom(atom_index); |
| 647 | assert(atom.getFile() != null); // synthetic atoms do not have relocs | 602 | assert(atom.getFile() != null); // synthetic atoms do not have relocs |
| 648 | 603 | ||
| 649 | log.debug("resolving relocations in ATOM(%{d}, '{s}')", .{ | 604 | log.debug("resolving relocations in ATOM(%{d}, '{s}')", .{ |
| 650 | atom.sym_index, | 605 | atom.sym_index, |
| 651 | zld.getSymbolName(atom.getSymbolWithLoc()), | 606 | macho_file.getSymbolName(atom.getSymbolWithLoc()), |
| 652 | }); | 607 | }); |
| 653 | 608 | ||
| 654 | const ctx = getRelocContext(zld, atom_index); | 609 | const ctx = getRelocContext(macho_file, atom_index); |
| 655 | 610 | ||
| 656 | return switch (arch) { | 611 | return switch (arch) { |
| 657 | .aarch64 => resolveRelocsArm64(zld, atom_index, atom_code, atom_relocs, ctx), | 612 | .aarch64 => resolveRelocsArm64(macho_file, atom_index, atom_code, atom_relocs, ctx), |
| 658 | .x86_64 => resolveRelocsX86(zld, atom_index, atom_code, atom_relocs, ctx), | 613 | .x86_64 => resolveRelocsX86(macho_file, atom_index, atom_code, atom_relocs, ctx), |
| 659 | else => unreachable, | 614 | else => unreachable, |
| 660 | }; | 615 | }; |
| 661 | } | 616 | } |
| 662 | 617 | ||
| 663 | pub fn getRelocTargetAddress(zld: *Zld, target: SymbolWithLoc, is_tlv: bool) !u64 { | 618 | pub fn getRelocTargetAddress(macho_file: *MachO, target: SymbolWithLoc, is_tlv: bool) !u64 { |
| 664 | const target_atom_index = getRelocTargetAtomIndex(zld, target) orelse { | 619 | const target_atom_index = getRelocTargetAtomIndex(macho_file, target) orelse { |
| 665 | // If there is no atom for target, we still need to check for special, atom-less | 620 | // If there is no atom for target, we still need to check for special, atom-less |
| 666 | // symbols such as `___dso_handle`. | 621 | // symbols such as `___dso_handle`. |
| 667 | const target_name = zld.getSymbolName(target); | 622 | const target_name = macho_file.getSymbolName(target); |
| 668 | const atomless_sym = zld.getSymbol(target); | 623 | const atomless_sym = macho_file.getSymbol(target); |
| 669 | log.debug(" | atomless target '{s}'", .{target_name}); | 624 | log.debug(" | atomless target '{s}'", .{target_name}); |
| 670 | return atomless_sym.n_value; | 625 | return atomless_sym.n_value; |
| 671 | }; | 626 | }; |
| 672 | const target_atom = zld.getAtom(target_atom_index); | 627 | const target_atom = macho_file.getAtom(target_atom_index); |
| 673 | log.debug(" | target ATOM(%{d}, '{s}') in object({?})", .{ | 628 | log.debug(" | target ATOM(%{d}, '{s}') in object({?})", .{ |
| 674 | target_atom.sym_index, | 629 | target_atom.sym_index, |
| 675 | zld.getSymbolName(target_atom.getSymbolWithLoc()), | 630 | macho_file.getSymbolName(target_atom.getSymbolWithLoc()), |
| 676 | target_atom.getFile(), | 631 | target_atom.getFile(), |
| 677 | }); | 632 | }); |
| 678 | 633 | ||
| 679 | const target_sym = zld.getSymbol(target_atom.getSymbolWithLoc()); | 634 | const target_sym = macho_file.getSymbol(target_atom.getSymbolWithLoc()); |
| 680 | assert(target_sym.n_desc != MachO.N_DEAD); | 635 | assert(target_sym.n_desc != MachO.N_DEAD); |
| 681 | 636 | ||
| 682 | // If `target` is contained within the target atom, pull its address value. | 637 | // If `target` is contained within the target atom, pull its address value. |
| 683 | const offset = if (target_atom.getFile() != null) blk: { | 638 | const offset = if (target_atom.getFile() != null) blk: { |
| 684 | const object = zld.objects.items[target_atom.getFile().?]; | 639 | const object = macho_file.objects.items[target_atom.getFile().?]; |
| 685 | break :blk if (object.getSourceSymbol(target.sym_index)) |_| | 640 | break :blk if (object.getSourceSymbol(target.sym_index)) |_| |
| 686 | Atom.calcInnerSymbolOffset(zld, target_atom_index, target.sym_index) | 641 | Atom.calcInnerSymbolOffset(macho_file, target_atom_index, target.sym_index) |
| 687 | else | 642 | else |
| 688 | 0; // section alias | 643 | 0; // section alias |
| 689 | } else 0; | 644 | } else 0; |
| ... | @@ -694,9 +649,9 @@ pub fn getRelocTargetAddress(zld: *Zld, target: SymbolWithLoc, is_tlv: bool) !u6 | ... | @@ -694,9 +649,9 @@ pub fn getRelocTargetAddress(zld: *Zld, target: SymbolWithLoc, is_tlv: bool) !u6 |
| 694 | // * wrt to __thread_data if defined, then | 649 | // * wrt to __thread_data if defined, then |
| 695 | // * wrt to __thread_bss | 650 | // * wrt to __thread_bss |
| 696 | const sect_id: u16 = sect_id: { | 651 | const sect_id: u16 = sect_id: { |
| 697 | if (zld.thread_data_section_index) |i| { | 652 | if (macho_file.thread_data_section_index) |i| { |
| 698 | break :sect_id i; | 653 | break :sect_id i; |
| 699 | } else if (zld.thread_bss_section_index) |i| { | 654 | } else if (macho_file.thread_bss_section_index) |i| { |
| 700 | break :sect_id i; | 655 | break :sect_id i; |
| 701 | } else { | 656 | } else { |
| 702 | log.err("threadlocal variables present but no initializer sections found", .{}); | 657 | log.err("threadlocal variables present but no initializer sections found", .{}); |
| ... | @@ -705,20 +660,20 @@ pub fn getRelocTargetAddress(zld: *Zld, target: SymbolWithLoc, is_tlv: bool) !u6 | ... | @@ -705,20 +660,20 @@ pub fn getRelocTargetAddress(zld: *Zld, target: SymbolWithLoc, is_tlv: bool) !u6 |
| 705 | return error.FailedToResolveRelocationTarget; | 660 | return error.FailedToResolveRelocationTarget; |
| 706 | } | 661 | } |
| 707 | }; | 662 | }; |
| 708 | break :base_address zld.sections.items(.header)[sect_id].addr; | 663 | break :base_address macho_file.sections.items(.header)[sect_id].addr; |
| 709 | } else 0; | 664 | } else 0; |
| 710 | return target_sym.n_value + offset - base_address; | 665 | return target_sym.n_value + offset - base_address; |
| 711 | } | 666 | } |
| 712 | 667 | ||
| 713 | fn resolveRelocsArm64( | 668 | fn resolveRelocsArm64( |
| 714 | zld: *Zld, | 669 | macho_file: *MachO, |
| 715 | atom_index: Index, | 670 | atom_index: Index, |
| 716 | atom_code: []u8, | 671 | atom_code: []u8, |
| 717 | atom_relocs: []align(1) const macho.relocation_info, | 672 | atom_relocs: []align(1) const macho.relocation_info, |
| 718 | context: RelocContext, | 673 | context: RelocContext, |
| 719 | ) !void { | 674 | ) !void { |
| 720 | const atom = zld.getAtom(atom_index); | 675 | const atom = macho_file.getAtom(atom_index); |
| 721 | const object = zld.objects.items[atom.getFile().?]; | 676 | const object = macho_file.objects.items[atom.getFile().?]; |
| 722 | 677 | ||
| 723 | var addend: ?i64 = null; | 678 | var addend: ?i64 = null; |
| 724 | var subtractor: ?SymbolWithLoc = null; | 679 | var subtractor: ?SymbolWithLoc = null; |
| ... | @@ -745,7 +700,7 @@ fn resolveRelocsArm64( | ... | @@ -745,7 +700,7 @@ fn resolveRelocsArm64( |
| 745 | atom.getFile(), | 700 | atom.getFile(), |
| 746 | }); | 701 | }); |
| 747 | 702 | ||
| 748 | subtractor = parseRelocTarget(zld, .{ | 703 | subtractor = parseRelocTarget(macho_file, .{ |
| 749 | .object_id = atom.getFile().?, | 704 | .object_id = atom.getFile().?, |
| 750 | .rel = rel, | 705 | .rel = rel, |
| 751 | .code = atom_code, | 706 | .code = atom_code, |
| ... | @@ -757,7 +712,7 @@ fn resolveRelocsArm64( | ... | @@ -757,7 +712,7 @@ fn resolveRelocsArm64( |
| 757 | else => {}, | 712 | else => {}, |
| 758 | } | 713 | } |
| 759 | 714 | ||
| 760 | const target = parseRelocTarget(zld, .{ | 715 | const target = parseRelocTarget(macho_file, .{ |
| 761 | .object_id = atom.getFile().?, | 716 | .object_id = atom.getFile().?, |
| 762 | .rel = rel, | 717 | .rel = rel, |
| 763 | .code = atom_code, | 718 | .code = atom_code, |
| ... | @@ -770,26 +725,26 @@ fn resolveRelocsArm64( | ... | @@ -770,26 +725,26 @@ fn resolveRelocsArm64( |
| 770 | @tagName(rel_type), | 725 | @tagName(rel_type), |
| 771 | rel.r_address, | 726 | rel.r_address, |
| 772 | target.sym_index, | 727 | target.sym_index, |
| 773 | zld.getSymbolName(target), | 728 | macho_file.getSymbolName(target), |
| 774 | target.getFile(), | 729 | target.getFile(), |
| 775 | }); | 730 | }); |
| 776 | 731 | ||
| 777 | const source_addr = blk: { | 732 | const source_addr = blk: { |
| 778 | const source_sym = zld.getSymbol(atom.getSymbolWithLoc()); | 733 | const source_sym = macho_file.getSymbol(atom.getSymbolWithLoc()); |
| 779 | break :blk source_sym.n_value + rel_offset; | 734 | break :blk source_sym.n_value + rel_offset; |
| 780 | }; | 735 | }; |
| 781 | const target_addr = blk: { | 736 | const target_addr = blk: { |
| 782 | if (relocRequiresGot(zld, rel)) break :blk zld.getGotEntryAddress(target).?; | 737 | if (relocRequiresGot(macho_file, rel)) break :blk macho_file.getGotEntryAddress(target).?; |
| 783 | if (relocIsTlv(zld, rel) and zld.getSymbol(target).undf()) | 738 | if (relocIsTlv(macho_file, rel) and macho_file.getSymbol(target).undf()) |
| 784 | break :blk zld.getTlvPtrEntryAddress(target).?; | 739 | break :blk macho_file.getTlvPtrEntryAddress(target).?; |
| 785 | if (relocIsStub(zld, rel) and zld.getSymbol(target).undf()) | 740 | if (relocIsStub(macho_file, rel) and macho_file.getSymbol(target).undf()) |
| 786 | break :blk zld.getStubsEntryAddress(target).?; | 741 | break :blk macho_file.getStubsEntryAddress(target).?; |
| 787 | const is_tlv = is_tlv: { | 742 | const is_tlv = is_tlv: { |
| 788 | const source_sym = zld.getSymbol(atom.getSymbolWithLoc()); | 743 | const source_sym = macho_file.getSymbol(atom.getSymbolWithLoc()); |
| 789 | const header = zld.sections.items(.header)[source_sym.n_sect - 1]; | 744 | const header = macho_file.sections.items(.header)[source_sym.n_sect - 1]; |
| 790 | break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES; | 745 | break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES; |
| 791 | }; | 746 | }; |
| 792 | break :blk try getRelocTargetAddress(zld, target, is_tlv); | 747 | break :blk try getRelocTargetAddress(macho_file, target, is_tlv); |
| 793 | }; | 748 | }; |
| 794 | 749 | ||
| 795 | log.debug(" | source_addr = 0x{x}", .{source_addr}); | 750 | log.debug(" | source_addr = 0x{x}", .{source_addr}); |
| ... | @@ -797,9 +752,9 @@ fn resolveRelocsArm64( | ... | @@ -797,9 +752,9 @@ fn resolveRelocsArm64( |
| 797 | switch (rel_type) { | 752 | switch (rel_type) { |
| 798 | .ARM64_RELOC_BRANCH26 => { | 753 | .ARM64_RELOC_BRANCH26 => { |
| 799 | log.debug(" source {s} (object({?})), target {s}", .{ | 754 | log.debug(" source {s} (object({?})), target {s}", .{ |
| 800 | zld.getSymbolName(atom.getSymbolWithLoc()), | 755 | macho_file.getSymbolName(atom.getSymbolWithLoc()), |
| 801 | atom.getFile(), | 756 | atom.getFile(), |
| 802 | zld.getSymbolName(target), | 757 | macho_file.getSymbolName(target), |
| 803 | }); | 758 | }); |
| 804 | 759 | ||
| 805 | const displacement = if (Relocation.calcPcRelativeDisplacementArm64( | 760 | const displacement = if (Relocation.calcPcRelativeDisplacementArm64( |
| ... | @@ -809,13 +764,13 @@ fn resolveRelocsArm64( | ... | @@ -809,13 +764,13 @@ fn resolveRelocsArm64( |
| 809 | log.debug(" | target_addr = 0x{x}", .{target_addr}); | 764 | log.debug(" | target_addr = 0x{x}", .{target_addr}); |
| 810 | break :blk disp; | 765 | break :blk disp; |
| 811 | } else |_| blk: { | 766 | } else |_| blk: { |
| 812 | const thunk_index = zld.thunk_table.get(atom_index).?; | 767 | const thunk_index = macho_file.thunk_table.get(atom_index).?; |
| 813 | const thunk = zld.thunks.items[thunk_index]; | 768 | const thunk = macho_file.thunks.items[thunk_index]; |
| 814 | const thunk_sym_loc = if (zld.getSymbol(target).undf()) | 769 | const thunk_sym_loc = if (macho_file.getSymbol(target).undf()) |
| 815 | thunk.getTrampoline(zld, .stub, target).? | 770 | thunk.getTrampoline(macho_file, .stub, target).? |
| 816 | else | 771 | else |
| 817 | thunk.getTrampoline(zld, .atom, target).?; | 772 | thunk.getTrampoline(macho_file, .atom, target).?; |
| 818 | const thunk_addr = zld.getSymbol(thunk_sym_loc).n_value; | 773 | const thunk_addr = macho_file.getSymbol(thunk_sym_loc).n_value; |
| 819 | log.debug(" | target_addr = 0x{x} (thunk)", .{thunk_addr}); | 774 | log.debug(" | target_addr = 0x{x} (thunk)", .{thunk_addr}); |
| 820 | break :blk try Relocation.calcPcRelativeDisplacementArm64(source_addr, thunk_addr); | 775 | break :blk try Relocation.calcPcRelativeDisplacementArm64(source_addr, thunk_addr); |
| 821 | }; | 776 | }; |
| ... | @@ -944,7 +899,7 @@ fn resolveRelocsArm64( | ... | @@ -944,7 +899,7 @@ fn resolveRelocsArm64( |
| 944 | } | 899 | } |
| 945 | }; | 900 | }; |
| 946 | 901 | ||
| 947 | var inst = if (zld.tlv_ptr_table.lookup.contains(target)) aarch64.Instruction{ | 902 | var inst = if (macho_file.tlv_ptr_table.lookup.contains(target)) aarch64.Instruction{ |
| 948 | .load_store_register = .{ | 903 | .load_store_register = .{ |
| 949 | .rt = reg_info.rd, | 904 | .rt = reg_info.rd, |
| 950 | .rn = reg_info.rn, | 905 | .rn = reg_info.rn, |
| ... | @@ -992,7 +947,7 @@ fn resolveRelocsArm64( | ... | @@ -992,7 +947,7 @@ fn resolveRelocsArm64( |
| 992 | 947 | ||
| 993 | const result = blk: { | 948 | const result = blk: { |
| 994 | if (subtractor) |sub| { | 949 | if (subtractor) |sub| { |
| 995 | const sym = zld.getSymbol(sub); | 950 | const sym = macho_file.getSymbol(sub); |
| 996 | break :blk @as(i64, @intCast(target_addr)) - @as(i64, @intCast(sym.n_value)) + ptr_addend; | 951 | break :blk @as(i64, @intCast(target_addr)) - @as(i64, @intCast(sym.n_value)) + ptr_addend; |
| 997 | } else { | 952 | } else { |
| 998 | break :blk @as(i64, @intCast(target_addr)) + ptr_addend; | 953 | break :blk @as(i64, @intCast(target_addr)) + ptr_addend; |
| ... | @@ -1016,14 +971,14 @@ fn resolveRelocsArm64( | ... | @@ -1016,14 +971,14 @@ fn resolveRelocsArm64( |
| 1016 | } | 971 | } |
| 1017 | 972 | ||
| 1018 | fn resolveRelocsX86( | 973 | fn resolveRelocsX86( |
| 1019 | zld: *Zld, | 974 | macho_file: *MachO, |
| 1020 | atom_index: Index, | 975 | atom_index: Index, |
| 1021 | atom_code: []u8, | 976 | atom_code: []u8, |
| 1022 | atom_relocs: []align(1) const macho.relocation_info, | 977 | atom_relocs: []align(1) const macho.relocation_info, |
| 1023 | context: RelocContext, | 978 | context: RelocContext, |
| 1024 | ) !void { | 979 | ) !void { |
| 1025 | const atom = zld.getAtom(atom_index); | 980 | const atom = macho_file.getAtom(atom_index); |
| 1026 | const object = zld.objects.items[atom.getFile().?]; | 981 | const object = macho_file.objects.items[atom.getFile().?]; |
| 1027 | 982 | ||
| 1028 | var subtractor: ?SymbolWithLoc = null; | 983 | var subtractor: ?SymbolWithLoc = null; |
| 1029 | 984 | ||
| ... | @@ -1041,7 +996,7 @@ fn resolveRelocsX86( | ... | @@ -1041,7 +996,7 @@ fn resolveRelocsX86( |
| 1041 | atom.getFile(), | 996 | atom.getFile(), |
| 1042 | }); | 997 | }); |
| 1043 | 998 | ||
| 1044 | subtractor = parseRelocTarget(zld, .{ | 999 | subtractor = parseRelocTarget(macho_file, .{ |
| 1045 | .object_id = atom.getFile().?, | 1000 | .object_id = atom.getFile().?, |
| 1046 | .rel = rel, | 1001 | .rel = rel, |
| 1047 | .code = atom_code, | 1002 | .code = atom_code, |
| ... | @@ -1053,7 +1008,7 @@ fn resolveRelocsX86( | ... | @@ -1053,7 +1008,7 @@ fn resolveRelocsX86( |
| 1053 | else => {}, | 1008 | else => {}, |
| 1054 | } | 1009 | } |
| 1055 | 1010 | ||
| 1056 | const target = parseRelocTarget(zld, .{ | 1011 | const target = parseRelocTarget(macho_file, .{ |
| 1057 | .object_id = atom.getFile().?, | 1012 | .object_id = atom.getFile().?, |
| 1058 | .rel = rel, | 1013 | .rel = rel, |
| 1059 | .code = atom_code, | 1014 | .code = atom_code, |
| ... | @@ -1066,26 +1021,26 @@ fn resolveRelocsX86( | ... | @@ -1066,26 +1021,26 @@ fn resolveRelocsX86( |
| 1066 | @tagName(rel_type), | 1021 | @tagName(rel_type), |
| 1067 | rel.r_address, | 1022 | rel.r_address, |
| 1068 | target.sym_index, | 1023 | target.sym_index, |
| 1069 | zld.getSymbolName(target), | 1024 | macho_file.getSymbolName(target), |
| 1070 | target.getFile(), | 1025 | target.getFile(), |
| 1071 | }); | 1026 | }); |
| 1072 | 1027 | ||
| 1073 | const source_addr = blk: { | 1028 | const source_addr = blk: { |
| 1074 | const source_sym = zld.getSymbol(atom.getSymbolWithLoc()); | 1029 | const source_sym = macho_file.getSymbol(atom.getSymbolWithLoc()); |
| 1075 | break :blk source_sym.n_value + rel_offset; | 1030 | break :blk source_sym.n_value + rel_offset; |
| 1076 | }; | 1031 | }; |
| 1077 | const target_addr = blk: { | 1032 | const target_addr = blk: { |
| 1078 | if (relocRequiresGot(zld, rel)) break :blk zld.getGotEntryAddress(target).?; | 1033 | if (relocRequiresGot(macho_file, rel)) break :blk macho_file.getGotEntryAddress(target).?; |
| 1079 | if (relocIsStub(zld, rel) and zld.getSymbol(target).undf()) | 1034 | if (relocIsStub(macho_file, rel) and macho_file.getSymbol(target).undf()) |
| 1080 | break :blk zld.getStubsEntryAddress(target).?; | 1035 | break :blk macho_file.getStubsEntryAddress(target).?; |
| 1081 | if (relocIsTlv(zld, rel) and zld.getSymbol(target).undf()) | 1036 | if (relocIsTlv(macho_file, rel) and macho_file.getSymbol(target).undf()) |
| 1082 | break :blk zld.getTlvPtrEntryAddress(target).?; | 1037 | break :blk macho_file.getTlvPtrEntryAddress(target).?; |
| 1083 | const is_tlv = is_tlv: { | 1038 | const is_tlv = is_tlv: { |
| 1084 | const source_sym = zld.getSymbol(atom.getSymbolWithLoc()); | 1039 | const source_sym = macho_file.getSymbol(atom.getSymbolWithLoc()); |
| 1085 | const header = zld.sections.items(.header)[source_sym.n_sect - 1]; | 1040 | const header = macho_file.sections.items(.header)[source_sym.n_sect - 1]; |
| 1086 | break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES; | 1041 | break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES; |
| 1087 | }; | 1042 | }; |
| 1088 | break :blk try getRelocTargetAddress(zld, target, is_tlv); | 1043 | break :blk try getRelocTargetAddress(macho_file, target, is_tlv); |
| 1089 | }; | 1044 | }; |
| 1090 | 1045 | ||
| 1091 | log.debug(" | source_addr = 0x{x}", .{source_addr}); | 1046 | log.debug(" | source_addr = 0x{x}", .{source_addr}); |
| ... | @@ -1115,7 +1070,7 @@ fn resolveRelocsX86( | ... | @@ -1115,7 +1070,7 @@ fn resolveRelocsX86( |
| 1115 | log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr}); | 1070 | log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr}); |
| 1116 | const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0); | 1071 | const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0); |
| 1117 | 1072 | ||
| 1118 | if (zld.tlv_ptr_table.lookup.get(target) == null) { | 1073 | if (macho_file.tlv_ptr_table.lookup.get(target) == null) { |
| 1119 | // We need to rewrite the opcode from movq to leaq. | 1074 | // We need to rewrite the opcode from movq to leaq. |
| 1120 | atom_code[rel_offset - 2] = 0x8d; | 1075 | atom_code[rel_offset - 2] = 0x8d; |
| 1121 | } | 1076 | } |
| ... | @@ -1170,7 +1125,7 @@ fn resolveRelocsX86( | ... | @@ -1170,7 +1125,7 @@ fn resolveRelocsX86( |
| 1170 | 1125 | ||
| 1171 | const result = blk: { | 1126 | const result = blk: { |
| 1172 | if (subtractor) |sub| { | 1127 | if (subtractor) |sub| { |
| 1173 | const sym = zld.getSymbol(sub); | 1128 | const sym = macho_file.getSymbol(sub); |
| 1174 | break :blk @as(i64, @intCast(target_addr)) - @as(i64, @intCast(sym.n_value)) + addend; | 1129 | break :blk @as(i64, @intCast(target_addr)) - @as(i64, @intCast(sym.n_value)) + addend; |
| 1175 | } else { | 1130 | } else { |
| 1176 | break :blk @as(i64, @intCast(target_addr)) + addend; | 1131 | break :blk @as(i64, @intCast(target_addr)) + addend; |
| ... | @@ -1192,10 +1147,10 @@ fn resolveRelocsX86( | ... | @@ -1192,10 +1147,10 @@ fn resolveRelocsX86( |
| 1192 | } | 1147 | } |
| 1193 | } | 1148 | } |
| 1194 | 1149 | ||
| 1195 | pub fn getAtomCode(zld: *Zld, atom_index: Index) []const u8 { | 1150 | pub fn getAtomCode(macho_file: *MachO, atom_index: Index) []const u8 { |
| 1196 | const atom = zld.getAtom(atom_index); | 1151 | const atom = macho_file.getAtom(atom_index); |
| 1197 | assert(atom.getFile() != null); // Synthetic atom shouldn't need to inquire for code. | 1152 | assert(atom.getFile() != null); // Synthetic atom shouldn't need to inquire for code. |
| 1198 | const object = zld.objects.items[atom.getFile().?]; | 1153 | const object = macho_file.objects.items[atom.getFile().?]; |
| 1199 | const source_sym = object.getSourceSymbol(atom.sym_index) orelse { | 1154 | const source_sym = object.getSourceSymbol(atom.sym_index) orelse { |
| 1200 | // If there was no matching symbol present in the source symtab, this means | 1155 | // If there was no matching symbol present in the source symtab, this means |
| 1201 | // we are dealing with either an entire section, or part of it, but also | 1156 | // we are dealing with either an entire section, or part of it, but also |
| ... | @@ -1216,10 +1171,10 @@ pub fn getAtomCode(zld: *Zld, atom_index: Index) []const u8 { | ... | @@ -1216,10 +1171,10 @@ pub fn getAtomCode(zld: *Zld, atom_index: Index) []const u8 { |
| 1216 | return code[offset..][0..code_len]; | 1171 | return code[offset..][0..code_len]; |
| 1217 | } | 1172 | } |
| 1218 | 1173 | ||
| 1219 | pub fn getAtomRelocs(zld: *Zld, atom_index: Index) []const macho.relocation_info { | 1174 | pub fn getAtomRelocs(macho_file: *MachO, atom_index: Index) []const macho.relocation_info { |
| 1220 | const atom = zld.getAtom(atom_index); | 1175 | const atom = macho_file.getAtom(atom_index); |
| 1221 | assert(atom.getFile() != null); // Synthetic atom shouldn't need to unique for relocs. | 1176 | assert(atom.getFile() != null); // Synthetic atom shouldn't need to unique for relocs. |
| 1222 | const object = zld.objects.items[atom.getFile().?]; | 1177 | const object = macho_file.objects.items[atom.getFile().?]; |
| 1223 | const cache = object.relocs_lookup[atom.sym_index]; | 1178 | const cache = object.relocs_lookup[atom.sym_index]; |
| 1224 | 1179 | ||
| 1225 | const source_sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: { | 1180 | const source_sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: { |
| ... | @@ -1238,8 +1193,8 @@ pub fn getAtomRelocs(zld: *Zld, atom_index: Index) []const macho.relocation_info | ... | @@ -1238,8 +1193,8 @@ pub fn getAtomRelocs(zld: *Zld, atom_index: Index) []const macho.relocation_info |
| 1238 | return relocs[cache.start..][0..cache.len]; | 1193 | return relocs[cache.start..][0..cache.len]; |
| 1239 | } | 1194 | } |
| 1240 | 1195 | ||
| 1241 | pub fn relocRequiresGot(zld: *Zld, rel: macho.relocation_info) bool { | 1196 | pub fn relocRequiresGot(macho_file: *MachO, rel: macho.relocation_info) bool { |
| 1242 | switch (zld.options.target.cpu.arch) { | 1197 | switch (macho_file.base.options.target.cpu.arch) { |
| 1243 | .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) { | 1198 | .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) { |
| 1244 | .ARM64_RELOC_GOT_LOAD_PAGE21, | 1199 | .ARM64_RELOC_GOT_LOAD_PAGE21, |
| 1245 | .ARM64_RELOC_GOT_LOAD_PAGEOFF12, | 1200 | .ARM64_RELOC_GOT_LOAD_PAGEOFF12, |
| ... | @@ -1257,8 +1212,8 @@ pub fn relocRequiresGot(zld: *Zld, rel: macho.relocation_info) bool { | ... | @@ -1257,8 +1212,8 @@ pub fn relocRequiresGot(zld: *Zld, rel: macho.relocation_info) bool { |
| 1257 | } | 1212 | } |
| 1258 | } | 1213 | } |
| 1259 | 1214 | ||
| 1260 | pub fn relocIsTlv(zld: *Zld, rel: macho.relocation_info) bool { | 1215 | pub fn relocIsTlv(macho_file: *MachO, rel: macho.relocation_info) bool { |
| 1261 | switch (zld.options.target.cpu.arch) { | 1216 | switch (macho_file.base.options.target.cpu.arch) { |
| 1262 | .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) { | 1217 | .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) { |
| 1263 | .ARM64_RELOC_TLVP_LOAD_PAGE21, | 1218 | .ARM64_RELOC_TLVP_LOAD_PAGE21, |
| 1264 | .ARM64_RELOC_TLVP_LOAD_PAGEOFF12, | 1219 | .ARM64_RELOC_TLVP_LOAD_PAGEOFF12, |
| ... | @@ -1273,8 +1228,8 @@ pub fn relocIsTlv(zld: *Zld, rel: macho.relocation_info) bool { | ... | @@ -1273,8 +1228,8 @@ pub fn relocIsTlv(zld: *Zld, rel: macho.relocation_info) bool { |
| 1273 | } | 1228 | } |
| 1274 | } | 1229 | } |
| 1275 | 1230 | ||
| 1276 | pub fn relocIsStub(zld: *Zld, rel: macho.relocation_info) bool { | 1231 | pub fn relocIsStub(macho_file: *MachO, rel: macho.relocation_info) bool { |
| 1277 | switch (zld.options.target.cpu.arch) { | 1232 | switch (macho_file.base.options.target.cpu.arch) { |
| 1278 | .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) { | 1233 | .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) { |
| 1279 | .ARM64_RELOC_BRANCH26 => return true, | 1234 | .ARM64_RELOC_BRANCH26 => return true, |
| 1280 | else => return false, | 1235 | else => return false, |
| ... | @@ -1305,4 +1260,3 @@ const Arch = std.Target.Cpu.Arch; | ... | @@ -1305,4 +1260,3 @@ const Arch = std.Target.Cpu.Arch; |
| 1305 | const MachO = @import("../MachO.zig"); | 1260 | const MachO = @import("../MachO.zig"); |
| 1306 | pub const Relocation = @import("Relocation.zig"); | 1261 | pub const Relocation = @import("Relocation.zig"); |
| 1307 | const SymbolWithLoc = MachO.SymbolWithLoc; | 1262 | const SymbolWithLoc = MachO.SymbolWithLoc; |
| 1308 | const Zld = @import("zld.zig").Zld; |
src/link/MachO/CodeSignature.zig+182-182| ... | @@ -1,17 +1,175 @@ | ... | @@ -1,17 +1,175 @@ |
| 1 | const CodeSignature = @This(); | 1 | page_size: u16, |
| 2 | code_directory: CodeDirectory, | ||
| 3 | requirements: ?Requirements = null, | ||
| 4 | entitlements: ?Entitlements = null, | ||
| 5 | signature: ?Signature = null, | ||
| 2 | 6 | ||
| 3 | const std = @import("std"); | 7 | pub fn init(page_size: u16) CodeSignature { |
| 4 | const assert = std.debug.assert; | 8 | return .{ |
| 5 | const fs = std.fs; | 9 | .page_size = page_size, |
| 6 | const log = std.log.scoped(.link); | 10 | .code_directory = CodeDirectory.init(page_size), |
| 7 | const macho = std.macho; | 11 | }; |
| 8 | const mem = std.mem; | 12 | } |
| 9 | const testing = std.testing; | ||
| 10 | 13 | ||
| 11 | const Allocator = mem.Allocator; | 14 | pub fn deinit(self: *CodeSignature, allocator: Allocator) void { |
| 12 | const Compilation = @import("../../Compilation.zig"); | 15 | self.code_directory.deinit(allocator); |
| 13 | const Hasher = @import("hasher.zig").ParallelHasher; | 16 | if (self.requirements) |*req| { |
| 14 | const Sha256 = std.crypto.hash.sha2.Sha256; | 17 | req.deinit(allocator); |
| 18 | } | ||
| 19 | if (self.entitlements) |*ents| { | ||
| 20 | ents.deinit(allocator); | ||
| 21 | } | ||
| 22 | if (self.signature) |*sig| { | ||
| 23 | sig.deinit(allocator); | ||
| 24 | } | ||
| 25 | } | ||
| 26 | |||
| 27 | pub fn addEntitlements(self: *CodeSignature, allocator: Allocator, path: []const u8) !void { | ||
| 28 | const file = try fs.cwd().openFile(path, .{}); | ||
| 29 | defer file.close(); | ||
| 30 | const inner = try file.readToEndAlloc(allocator, std.math.maxInt(u32)); | ||
| 31 | self.entitlements = .{ .inner = inner }; | ||
| 32 | } | ||
| 33 | |||
| 34 | pub const WriteOpts = struct { | ||
| 35 | file: fs.File, | ||
| 36 | exec_seg_base: u64, | ||
| 37 | exec_seg_limit: u64, | ||
| 38 | file_size: u32, | ||
| 39 | output_mode: std.builtin.OutputMode, | ||
| 40 | }; | ||
| 41 | |||
| 42 | pub fn writeAdhocSignature( | ||
| 43 | self: *CodeSignature, | ||
| 44 | comp: *const Compilation, | ||
| 45 | opts: WriteOpts, | ||
| 46 | writer: anytype, | ||
| 47 | ) !void { | ||
| 48 | const gpa = comp.gpa; | ||
| 49 | |||
| 50 | var header: macho.SuperBlob = .{ | ||
| 51 | .magic = macho.CSMAGIC_EMBEDDED_SIGNATURE, | ||
| 52 | .length = @sizeOf(macho.SuperBlob), | ||
| 53 | .count = 0, | ||
| 54 | }; | ||
| 55 | |||
| 56 | var blobs = std.ArrayList(Blob).init(gpa); | ||
| 57 | defer blobs.deinit(); | ||
| 58 | |||
| 59 | self.code_directory.inner.execSegBase = opts.exec_seg_base; | ||
| 60 | self.code_directory.inner.execSegLimit = opts.exec_seg_limit; | ||
| 61 | self.code_directory.inner.execSegFlags = if (opts.output_mode == .Exe) macho.CS_EXECSEG_MAIN_BINARY else 0; | ||
| 62 | self.code_directory.inner.codeLimit = opts.file_size; | ||
| 63 | |||
| 64 | const total_pages = @as(u32, @intCast(mem.alignForward(usize, opts.file_size, self.page_size) / self.page_size)); | ||
| 65 | |||
| 66 | try self.code_directory.code_slots.ensureTotalCapacityPrecise(gpa, total_pages); | ||
| 67 | self.code_directory.code_slots.items.len = total_pages; | ||
| 68 | self.code_directory.inner.nCodeSlots = total_pages; | ||
| 69 | |||
| 70 | // Calculate hash for each page (in file) and write it to the buffer | ||
| 71 | var hasher = Hasher(Sha256){ .allocator = gpa, .thread_pool = comp.thread_pool }; | ||
| 72 | try hasher.hash(opts.file, self.code_directory.code_slots.items, .{ | ||
| 73 | .chunk_size = self.page_size, | ||
| 74 | .max_file_size = opts.file_size, | ||
| 75 | }); | ||
| 76 | |||
| 77 | try blobs.append(.{ .code_directory = &self.code_directory }); | ||
| 78 | header.length += @sizeOf(macho.BlobIndex); | ||
| 79 | header.count += 1; | ||
| 80 | |||
| 81 | var hash: [hash_size]u8 = undefined; | ||
| 82 | |||
| 83 | if (self.requirements) |*req| { | ||
| 84 | var buf = std.ArrayList(u8).init(gpa); | ||
| 85 | defer buf.deinit(); | ||
| 86 | try req.write(buf.writer()); | ||
| 87 | Sha256.hash(buf.items, &hash, .{}); | ||
| 88 | self.code_directory.addSpecialHash(req.slotType(), hash); | ||
| 89 | |||
| 90 | try blobs.append(.{ .requirements = req }); | ||
| 91 | header.count += 1; | ||
| 92 | header.length += @sizeOf(macho.BlobIndex) + req.size(); | ||
| 93 | } | ||
| 94 | |||
| 95 | if (self.entitlements) |*ents| { | ||
| 96 | var buf = std.ArrayList(u8).init(gpa); | ||
| 97 | defer buf.deinit(); | ||
| 98 | try ents.write(buf.writer()); | ||
| 99 | Sha256.hash(buf.items, &hash, .{}); | ||
| 100 | self.code_directory.addSpecialHash(ents.slotType(), hash); | ||
| 101 | |||
| 102 | try blobs.append(.{ .entitlements = ents }); | ||
| 103 | header.count += 1; | ||
| 104 | header.length += @sizeOf(macho.BlobIndex) + ents.size(); | ||
| 105 | } | ||
| 106 | |||
| 107 | if (self.signature) |*sig| { | ||
| 108 | try blobs.append(.{ .signature = sig }); | ||
| 109 | header.count += 1; | ||
| 110 | header.length += @sizeOf(macho.BlobIndex) + sig.size(); | ||
| 111 | } | ||
| 112 | |||
| 113 | self.code_directory.inner.hashOffset = | ||
| 114 | @sizeOf(macho.CodeDirectory) + @as(u32, @intCast(self.code_directory.ident.len + 1 + self.code_directory.inner.nSpecialSlots * hash_size)); | ||
| 115 | self.code_directory.inner.length = self.code_directory.size(); | ||
| 116 | header.length += self.code_directory.size(); | ||
| 117 | |||
| 118 | try writer.writeIntBig(u32, header.magic); | ||
| 119 | try writer.writeIntBig(u32, header.length); | ||
| 120 | try writer.writeIntBig(u32, header.count); | ||
| 121 | |||
| 122 | var offset: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) * @as(u32, @intCast(blobs.items.len)); | ||
| 123 | for (blobs.items) |blob| { | ||
| 124 | try writer.writeIntBig(u32, blob.slotType()); | ||
| 125 | try writer.writeIntBig(u32, offset); | ||
| 126 | offset += blob.size(); | ||
| 127 | } | ||
| 128 | |||
| 129 | for (blobs.items) |blob| { | ||
| 130 | try blob.write(writer); | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | pub fn size(self: CodeSignature) u32 { | ||
| 135 | var ssize: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) + self.code_directory.size(); | ||
| 136 | if (self.requirements) |req| { | ||
| 137 | ssize += @sizeOf(macho.BlobIndex) + req.size(); | ||
| 138 | } | ||
| 139 | if (self.entitlements) |ent| { | ||
| 140 | ssize += @sizeOf(macho.BlobIndex) + ent.size(); | ||
| 141 | } | ||
| 142 | if (self.signature) |sig| { | ||
| 143 | ssize += @sizeOf(macho.BlobIndex) + sig.size(); | ||
| 144 | } | ||
| 145 | return ssize; | ||
| 146 | } | ||
| 147 | |||
| 148 | pub fn estimateSize(self: CodeSignature, file_size: u64) u32 { | ||
| 149 | var ssize: u64 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) + self.code_directory.size(); | ||
| 150 | // Approx code slots | ||
| 151 | const total_pages = mem.alignForward(u64, file_size, self.page_size) / self.page_size; | ||
| 152 | ssize += total_pages * hash_size; | ||
| 153 | var n_special_slots: u32 = 0; | ||
| 154 | if (self.requirements) |req| { | ||
| 155 | ssize += @sizeOf(macho.BlobIndex) + req.size(); | ||
| 156 | n_special_slots = @max(n_special_slots, req.slotType()); | ||
| 157 | } | ||
| 158 | if (self.entitlements) |ent| { | ||
| 159 | ssize += @sizeOf(macho.BlobIndex) + ent.size() + hash_size; | ||
| 160 | n_special_slots = @max(n_special_slots, ent.slotType()); | ||
| 161 | } | ||
| 162 | if (self.signature) |sig| { | ||
| 163 | ssize += @sizeOf(macho.BlobIndex) + sig.size(); | ||
| 164 | } | ||
| 165 | ssize += n_special_slots * hash_size; | ||
| 166 | return @as(u32, @intCast(mem.alignForward(u64, ssize, @sizeOf(u64)))); | ||
| 167 | } | ||
| 168 | |||
| 169 | pub fn clear(self: *CodeSignature, allocator: Allocator) void { | ||
| 170 | self.code_directory.deinit(allocator); | ||
| 171 | self.code_directory = CodeDirectory.init(self.page_size); | ||
| 172 | } | ||
| 15 | 173 | ||
| 16 | const hash_size = Sha256.digest_length; | 174 | const hash_size = Sha256.digest_length; |
| 17 | 175 | ||
| ... | @@ -218,175 +376,17 @@ const Signature = struct { | ... | @@ -218,175 +376,17 @@ const Signature = struct { |
| 218 | } | 376 | } |
| 219 | }; | 377 | }; |
| 220 | 378 | ||
| 221 | page_size: u16, | 379 | const CodeSignature = @This(); |
| 222 | code_directory: CodeDirectory, | ||
| 223 | requirements: ?Requirements = null, | ||
| 224 | entitlements: ?Entitlements = null, | ||
| 225 | signature: ?Signature = null, | ||
| 226 | |||
| 227 | pub fn init(page_size: u16) CodeSignature { | ||
| 228 | return .{ | ||
| 229 | .page_size = page_size, | ||
| 230 | .code_directory = CodeDirectory.init(page_size), | ||
| 231 | }; | ||
| 232 | } | ||
| 233 | |||
| 234 | pub fn deinit(self: *CodeSignature, allocator: Allocator) void { | ||
| 235 | self.code_directory.deinit(allocator); | ||
| 236 | if (self.requirements) |*req| { | ||
| 237 | req.deinit(allocator); | ||
| 238 | } | ||
| 239 | if (self.entitlements) |*ents| { | ||
| 240 | ents.deinit(allocator); | ||
| 241 | } | ||
| 242 | if (self.signature) |*sig| { | ||
| 243 | sig.deinit(allocator); | ||
| 244 | } | ||
| 245 | } | ||
| 246 | |||
| 247 | pub fn addEntitlements(self: *CodeSignature, allocator: Allocator, path: []const u8) !void { | ||
| 248 | const file = try fs.cwd().openFile(path, .{}); | ||
| 249 | defer file.close(); | ||
| 250 | const inner = try file.readToEndAlloc(allocator, std.math.maxInt(u32)); | ||
| 251 | self.entitlements = .{ .inner = inner }; | ||
| 252 | } | ||
| 253 | |||
| 254 | pub const WriteOpts = struct { | ||
| 255 | file: fs.File, | ||
| 256 | exec_seg_base: u64, | ||
| 257 | exec_seg_limit: u64, | ||
| 258 | file_size: u32, | ||
| 259 | output_mode: std.builtin.OutputMode, | ||
| 260 | }; | ||
| 261 | |||
| 262 | pub fn writeAdhocSignature( | ||
| 263 | self: *CodeSignature, | ||
| 264 | comp: *const Compilation, | ||
| 265 | opts: WriteOpts, | ||
| 266 | writer: anytype, | ||
| 267 | ) !void { | ||
| 268 | const gpa = comp.gpa; | ||
| 269 | |||
| 270 | var header: macho.SuperBlob = .{ | ||
| 271 | .magic = macho.CSMAGIC_EMBEDDED_SIGNATURE, | ||
| 272 | .length = @sizeOf(macho.SuperBlob), | ||
| 273 | .count = 0, | ||
| 274 | }; | ||
| 275 | |||
| 276 | var blobs = std.ArrayList(Blob).init(gpa); | ||
| 277 | defer blobs.deinit(); | ||
| 278 | |||
| 279 | self.code_directory.inner.execSegBase = opts.exec_seg_base; | ||
| 280 | self.code_directory.inner.execSegLimit = opts.exec_seg_limit; | ||
| 281 | self.code_directory.inner.execSegFlags = if (opts.output_mode == .Exe) macho.CS_EXECSEG_MAIN_BINARY else 0; | ||
| 282 | self.code_directory.inner.codeLimit = opts.file_size; | ||
| 283 | |||
| 284 | const total_pages = @as(u32, @intCast(mem.alignForward(usize, opts.file_size, self.page_size) / self.page_size)); | ||
| 285 | |||
| 286 | try self.code_directory.code_slots.ensureTotalCapacityPrecise(gpa, total_pages); | ||
| 287 | self.code_directory.code_slots.items.len = total_pages; | ||
| 288 | self.code_directory.inner.nCodeSlots = total_pages; | ||
| 289 | |||
| 290 | // Calculate hash for each page (in file) and write it to the buffer | ||
| 291 | var hasher = Hasher(Sha256){ .allocator = gpa, .thread_pool = comp.thread_pool }; | ||
| 292 | try hasher.hash(opts.file, self.code_directory.code_slots.items, .{ | ||
| 293 | .chunk_size = self.page_size, | ||
| 294 | .max_file_size = opts.file_size, | ||
| 295 | }); | ||
| 296 | |||
| 297 | try blobs.append(.{ .code_directory = &self.code_directory }); | ||
| 298 | header.length += @sizeOf(macho.BlobIndex); | ||
| 299 | header.count += 1; | ||
| 300 | |||
| 301 | var hash: [hash_size]u8 = undefined; | ||
| 302 | |||
| 303 | if (self.requirements) |*req| { | ||
| 304 | var buf = std.ArrayList(u8).init(gpa); | ||
| 305 | defer buf.deinit(); | ||
| 306 | try req.write(buf.writer()); | ||
| 307 | Sha256.hash(buf.items, &hash, .{}); | ||
| 308 | self.code_directory.addSpecialHash(req.slotType(), hash); | ||
| 309 | |||
| 310 | try blobs.append(.{ .requirements = req }); | ||
| 311 | header.count += 1; | ||
| 312 | header.length += @sizeOf(macho.BlobIndex) + req.size(); | ||
| 313 | } | ||
| 314 | |||
| 315 | if (self.entitlements) |*ents| { | ||
| 316 | var buf = std.ArrayList(u8).init(gpa); | ||
| 317 | defer buf.deinit(); | ||
| 318 | try ents.write(buf.writer()); | ||
| 319 | Sha256.hash(buf.items, &hash, .{}); | ||
| 320 | self.code_directory.addSpecialHash(ents.slotType(), hash); | ||
| 321 | |||
| 322 | try blobs.append(.{ .entitlements = ents }); | ||
| 323 | header.count += 1; | ||
| 324 | header.length += @sizeOf(macho.BlobIndex) + ents.size(); | ||
| 325 | } | ||
| 326 | |||
| 327 | if (self.signature) |*sig| { | ||
| 328 | try blobs.append(.{ .signature = sig }); | ||
| 329 | header.count += 1; | ||
| 330 | header.length += @sizeOf(macho.BlobIndex) + sig.size(); | ||
| 331 | } | ||
| 332 | |||
| 333 | self.code_directory.inner.hashOffset = | ||
| 334 | @sizeOf(macho.CodeDirectory) + @as(u32, @intCast(self.code_directory.ident.len + 1 + self.code_directory.inner.nSpecialSlots * hash_size)); | ||
| 335 | self.code_directory.inner.length = self.code_directory.size(); | ||
| 336 | header.length += self.code_directory.size(); | ||
| 337 | |||
| 338 | try writer.writeIntBig(u32, header.magic); | ||
| 339 | try writer.writeIntBig(u32, header.length); | ||
| 340 | try writer.writeIntBig(u32, header.count); | ||
| 341 | |||
| 342 | var offset: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) * @as(u32, @intCast(blobs.items.len)); | ||
| 343 | for (blobs.items) |blob| { | ||
| 344 | try writer.writeIntBig(u32, blob.slotType()); | ||
| 345 | try writer.writeIntBig(u32, offset); | ||
| 346 | offset += blob.size(); | ||
| 347 | } | ||
| 348 | |||
| 349 | for (blobs.items) |blob| { | ||
| 350 | try blob.write(writer); | ||
| 351 | } | ||
| 352 | } | ||
| 353 | |||
| 354 | pub fn size(self: CodeSignature) u32 { | ||
| 355 | var ssize: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) + self.code_directory.size(); | ||
| 356 | if (self.requirements) |req| { | ||
| 357 | ssize += @sizeOf(macho.BlobIndex) + req.size(); | ||
| 358 | } | ||
| 359 | if (self.entitlements) |ent| { | ||
| 360 | ssize += @sizeOf(macho.BlobIndex) + ent.size(); | ||
| 361 | } | ||
| 362 | if (self.signature) |sig| { | ||
| 363 | ssize += @sizeOf(macho.BlobIndex) + sig.size(); | ||
| 364 | } | ||
| 365 | return ssize; | ||
| 366 | } | ||
| 367 | 380 | ||
| 368 | pub fn estimateSize(self: CodeSignature, file_size: u64) u32 { | 381 | const std = @import("std"); |
| 369 | var ssize: u64 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) + self.code_directory.size(); | 382 | const assert = std.debug.assert; |
| 370 | // Approx code slots | 383 | const fs = std.fs; |
| 371 | const total_pages = mem.alignForward(u64, file_size, self.page_size) / self.page_size; | 384 | const log = std.log.scoped(.link); |
| 372 | ssize += total_pages * hash_size; | 385 | const macho = std.macho; |
| 373 | var n_special_slots: u32 = 0; | 386 | const mem = std.mem; |
| 374 | if (self.requirements) |req| { | 387 | const testing = std.testing; |
| 375 | ssize += @sizeOf(macho.BlobIndex) + req.size(); | ||
| 376 | n_special_slots = @max(n_special_slots, req.slotType()); | ||
| 377 | } | ||
| 378 | if (self.entitlements) |ent| { | ||
| 379 | ssize += @sizeOf(macho.BlobIndex) + ent.size() + hash_size; | ||
| 380 | n_special_slots = @max(n_special_slots, ent.slotType()); | ||
| 381 | } | ||
| 382 | if (self.signature) |sig| { | ||
| 383 | ssize += @sizeOf(macho.BlobIndex) + sig.size(); | ||
| 384 | } | ||
| 385 | ssize += n_special_slots * hash_size; | ||
| 386 | return @as(u32, @intCast(mem.alignForward(u64, ssize, @sizeOf(u64)))); | ||
| 387 | } | ||
| 388 | 388 | ||
| 389 | pub fn clear(self: *CodeSignature, allocator: Allocator) void { | 389 | const Allocator = mem.Allocator; |
| 390 | self.code_directory.deinit(allocator); | 390 | const Compilation = @import("../../Compilation.zig"); |
| 391 | self.code_directory = CodeDirectory.init(self.page_size); | 391 | const Hasher = @import("hasher.zig").ParallelHasher; |
| 392 | } | 392 | const Sha256 = std.crypto.hash.sha2.Sha256; |
src/link/MachO/DebugSymbols.zig+23-23| ... | @@ -1,26 +1,3 @@ | ... | @@ -1,26 +1,3 @@ |
| 1 | const DebugSymbols = @This(); | ||
| 2 | |||
| 3 | const std = @import("std"); | ||
| 4 | const build_options = @import("build_options"); | ||
| 5 | const assert = std.debug.assert; | ||
| 6 | const fs = std.fs; | ||
| 7 | const link = @import("../../link.zig"); | ||
| 8 | const load_commands = @import("load_commands.zig"); | ||
| 9 | const log = std.log.scoped(.dsym); | ||
| 10 | const macho = std.macho; | ||
| 11 | const makeStaticString = MachO.makeStaticString; | ||
| 12 | const math = std.math; | ||
| 13 | const mem = std.mem; | ||
| 14 | const padToIdeal = MachO.padToIdeal; | ||
| 15 | const trace = @import("../../tracy.zig").trace; | ||
| 16 | |||
| 17 | const Allocator = mem.Allocator; | ||
| 18 | const Dwarf = @import("../Dwarf.zig"); | ||
| 19 | const MachO = @import("../MachO.zig"); | ||
| 20 | const Module = @import("../../Module.zig"); | ||
| 21 | const StringTable = @import("../strtab.zig").StringTable; | ||
| 22 | const Type = @import("../../type.zig").Type; | ||
| 23 | |||
| 24 | allocator: Allocator, | 1 | allocator: Allocator, |
| 25 | dwarf: Dwarf, | 2 | dwarf: Dwarf, |
| 26 | file: fs.File, | 3 | file: fs.File, |
| ... | @@ -569,3 +546,26 @@ pub fn getSection(self: DebugSymbols, sect: u8) macho.section_64 { | ... | @@ -569,3 +546,26 @@ pub fn getSection(self: DebugSymbols, sect: u8) macho.section_64 { |
| 569 | assert(sect < self.sections.items.len); | 546 | assert(sect < self.sections.items.len); |
| 570 | return self.sections.items[sect]; | 547 | return self.sections.items[sect]; |
| 571 | } | 548 | } |
| 549 | |||
| 550 | const DebugSymbols = @This(); | ||
| 551 | |||
| 552 | const std = @import("std"); | ||
| 553 | const build_options = @import("build_options"); | ||
| 554 | const assert = std.debug.assert; | ||
| 555 | const fs = std.fs; | ||
| 556 | const link = @import("../../link.zig"); | ||
| 557 | const load_commands = @import("load_commands.zig"); | ||
| 558 | const log = std.log.scoped(.dsym); | ||
| 559 | const macho = std.macho; | ||
| 560 | const makeStaticString = MachO.makeStaticString; | ||
| 561 | const math = std.math; | ||
| 562 | const mem = std.mem; | ||
| 563 | const padToIdeal = MachO.padToIdeal; | ||
| 564 | const trace = @import("../../tracy.zig").trace; | ||
| 565 | |||
| 566 | const Allocator = mem.Allocator; | ||
| 567 | const Dwarf = @import("../Dwarf.zig"); | ||
| 568 | const MachO = @import("../MachO.zig"); | ||
| 569 | const Module = @import("../../Module.zig"); | ||
| 570 | const StringTable = @import("../strtab.zig").StringTable; | ||
| 571 | const Type = @import("../../type.zig").Type; |
src/link/MachO/DwarfInfo.zig+14-14| ... | @@ -1,17 +1,3 @@ | ... | @@ -1,17 +1,3 @@ |
| 1 | const DwarfInfo = @This(); | ||
| 2 | |||
| 3 | const std = @import("std"); | ||
| 4 | const assert = std.debug.assert; | ||
| 5 | const dwarf = std.dwarf; | ||
| 6 | const leb = std.leb; | ||
| 7 | const log = std.log.scoped(.macho); | ||
| 8 | const math = std.math; | ||
| 9 | const mem = std.mem; | ||
| 10 | |||
| 11 | const Allocator = mem.Allocator; | ||
| 12 | pub const AbbrevLookupTable = std.AutoHashMap(u64, struct { pos: usize, len: usize }); | ||
| 13 | pub const SubprogramLookupByName = std.StringHashMap(struct { addr: u64, size: u64 }); | ||
| 14 | |||
| 15 | debug_info: []const u8, | 1 | debug_info: []const u8, |
| 16 | debug_abbrev: []const u8, | 2 | debug_abbrev: []const u8, |
| 17 | debug_str: []const u8, | 3 | debug_str: []const u8, |
| ... | @@ -501,3 +487,17 @@ fn getString(self: DwarfInfo, off: u64) []const u8 { | ... | @@ -501,3 +487,17 @@ fn getString(self: DwarfInfo, off: u64) []const u8 { |
| 501 | assert(off < self.debug_str.len); | 487 | assert(off < self.debug_str.len); |
| 502 | return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.debug_str.ptr + @as(usize, @intCast(off)))), 0); | 488 | return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.debug_str.ptr + @as(usize, @intCast(off)))), 0); |
| 503 | } | 489 | } |
| 490 | |||
| 491 | const DwarfInfo = @This(); | ||
| 492 | |||
| 493 | const std = @import("std"); | ||
| 494 | const assert = std.debug.assert; | ||
| 495 | const dwarf = std.dwarf; | ||
| 496 | const leb = std.leb; | ||
| 497 | const log = std.log.scoped(.macho); | ||
| 498 | const math = std.math; | ||
| 499 | const mem = std.mem; | ||
| 500 | |||
| 501 | const Allocator = mem.Allocator; | ||
| 502 | pub const AbbrevLookupTable = std.AutoHashMap(u64, struct { pos: usize, len: usize }); | ||
| 503 | pub const SubprogramLookupByName = std.StringHashMap(struct { addr: u64, size: u64 }); |
src/link/MachO/Dylib.zig+20-20| ... | @@ -1,23 +1,3 @@ | ... | @@ -1,23 +1,3 @@ |
| 1 | const Dylib = @This(); | ||
| 2 | |||
| 3 | const std = @import("std"); | ||
| 4 | const assert = std.debug.assert; | ||
| 5 | const fs = std.fs; | ||
| 6 | const fmt = std.fmt; | ||
| 7 | const log = std.log.scoped(.link); | ||
| 8 | const macho = std.macho; | ||
| 9 | const math = std.math; | ||
| 10 | const mem = std.mem; | ||
| 11 | const fat = @import("fat.zig"); | ||
| 12 | const tapi = @import("../tapi.zig"); | ||
| 13 | |||
| 14 | const Allocator = mem.Allocator; | ||
| 15 | const CrossTarget = std.zig.CrossTarget; | ||
| 16 | const LibStub = tapi.LibStub; | ||
| 17 | const LoadCommandIterator = macho.LoadCommandIterator; | ||
| 18 | const MachO = @import("../MachO.zig"); | ||
| 19 | const Tbd = tapi.Tbd; | ||
| 20 | |||
| 21 | id: ?Id = null, | 1 | id: ?Id = null, |
| 22 | weak: bool = false, | 2 | weak: bool = false, |
| 23 | /// Header is only set if Dylib is parsed directly from a binary and not a stub file. | 3 | /// Header is only set if Dylib is parsed directly from a binary and not a stub file. |
| ... | @@ -546,3 +526,23 @@ pub fn parseFromStub( | ... | @@ -546,3 +526,23 @@ pub fn parseFromStub( |
| 546 | } | 526 | } |
| 547 | } | 527 | } |
| 548 | } | 528 | } |
| 529 | |||
| 530 | const Dylib = @This(); | ||
| 531 | |||
| 532 | const std = @import("std"); | ||
| 533 | const assert = std.debug.assert; | ||
| 534 | const fs = std.fs; | ||
| 535 | const fmt = std.fmt; | ||
| 536 | const log = std.log.scoped(.link); | ||
| 537 | const macho = std.macho; | ||
| 538 | const math = std.math; | ||
| 539 | const mem = std.mem; | ||
| 540 | const fat = @import("fat.zig"); | ||
| 541 | const tapi = @import("../tapi.zig"); | ||
| 542 | |||
| 543 | const Allocator = mem.Allocator; | ||
| 544 | const CrossTarget = std.zig.CrossTarget; | ||
| 545 | const LibStub = tapi.LibStub; | ||
| 546 | const LoadCommandIterator = macho.LoadCommandIterator; | ||
| 547 | const MachO = @import("../MachO.zig"); | ||
| 548 | const Tbd = tapi.Tbd; |
src/link/MachO/Object.zig+76-79| ... | @@ -2,31 +2,6 @@ | ... | @@ -2,31 +2,6 @@ |
| 2 | //! Each Object is fully loaded into memory for easier | 2 | //! Each Object is fully loaded into memory for easier |
| 3 | //! access into different data within. | 3 | //! access into different data within. |
| 4 | 4 | ||
| 5 | const Object = @This(); | ||
| 6 | |||
| 7 | const std = @import("std"); | ||
| 8 | const build_options = @import("build_options"); | ||
| 9 | const assert = std.debug.assert; | ||
| 10 | const dwarf = std.dwarf; | ||
| 11 | const eh_frame = @import("eh_frame.zig"); | ||
| 12 | const fs = std.fs; | ||
| 13 | const io = std.io; | ||
| 14 | const log = std.log.scoped(.link); | ||
| 15 | const macho = std.macho; | ||
| 16 | const math = std.math; | ||
| 17 | const mem = std.mem; | ||
| 18 | const sort = std.sort; | ||
| 19 | const trace = @import("../../tracy.zig").trace; | ||
| 20 | |||
| 21 | const Allocator = mem.Allocator; | ||
| 22 | const Atom = @import("Atom.zig"); | ||
| 23 | const DwarfInfo = @import("DwarfInfo.zig"); | ||
| 24 | const LoadCommandIterator = macho.LoadCommandIterator; | ||
| 25 | const MachO = @import("../MachO.zig"); | ||
| 26 | const SymbolWithLoc = MachO.SymbolWithLoc; | ||
| 27 | const UnwindInfo = @import("UnwindInfo.zig"); | ||
| 28 | const Zld = @import("zld.zig").Zld; | ||
| 29 | |||
| 30 | name: []const u8, | 5 | name: []const u8, |
| 31 | mtime: u64, | 6 | mtime: u64, |
| 32 | contents: []align(@alignOf(u64)) const u8, | 7 | contents: []align(@alignOf(u64)) const u8, |
| ... | @@ -359,25 +334,25 @@ fn sectionLessThanByAddress(ctx: void, lhs: SortedSection, rhs: SortedSection) b | ... | @@ -359,25 +334,25 @@ fn sectionLessThanByAddress(ctx: void, lhs: SortedSection, rhs: SortedSection) b |
| 359 | return lhs.header.addr < rhs.header.addr; | 334 | return lhs.header.addr < rhs.header.addr; |
| 360 | } | 335 | } |
| 361 | 336 | ||
| 362 | pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u32) !void { | 337 | pub fn splitIntoAtoms(self: *Object, macho_file: *MachO, object_id: u32) !void { |
| 363 | log.debug("splitting object({d}, {s}) into atoms", .{ object_id, self.name }); | 338 | log.debug("splitting object({d}, {s}) into atoms", .{ object_id, self.name }); |
| 364 | 339 | ||
| 365 | try self.splitRegularSections(zld, object_id); | 340 | try self.splitRegularSections(macho_file, object_id); |
| 366 | try self.parseEhFrameSection(zld, object_id); | 341 | try self.parseEhFrameSection(macho_file, object_id); |
| 367 | try self.parseUnwindInfo(zld, object_id); | 342 | try self.parseUnwindInfo(macho_file, object_id); |
| 368 | try self.parseDataInCode(zld.gpa); | 343 | try self.parseDataInCode(macho_file.base.allocator); |
| 369 | } | 344 | } |
| 370 | 345 | ||
| 371 | /// Splits input regular sections into Atoms. | 346 | /// Splits input regular sections into Atoms. |
| 372 | /// If the Object was compiled with `MH_SUBSECTIONS_VIA_SYMBOLS`, splits section | 347 | /// If the Object was compiled with `MH_SUBSECTIONS_VIA_SYMBOLS`, splits section |
| 373 | /// into subsections where each subsection then represents an Atom. | 348 | /// into subsections where each subsection then represents an Atom. |
| 374 | pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void { | 349 | pub fn splitRegularSections(self: *Object, macho_file: *MachO, object_id: u32) !void { |
| 375 | const gpa = zld.gpa; | 350 | const gpa = macho_file.base.allocator; |
| 376 | 351 | ||
| 377 | const sections = self.getSourceSections(); | 352 | const sections = self.getSourceSections(); |
| 378 | for (sections, 0..) |sect, id| { | 353 | for (sections, 0..) |sect, id| { |
| 379 | if (sect.isDebug()) continue; | 354 | if (sect.isDebug()) continue; |
| 380 | const out_sect_id = (try Atom.getOutputSection(zld, sect)) orelse { | 355 | const out_sect_id = (try Atom.getOutputSection(macho_file, sect)) orelse { |
| 381 | log.debug(" unhandled section '{s},{s}'", .{ sect.segName(), sect.sectName() }); | 356 | log.debug(" unhandled section '{s},{s}'", .{ sect.segName(), sect.sectName() }); |
| 382 | continue; | 357 | continue; |
| 383 | }; | 358 | }; |
| ... | @@ -397,13 +372,13 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void { | ... | @@ -397,13 +372,13 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void { |
| 397 | if (self.in_symtab == null) { | 372 | if (self.in_symtab == null) { |
| 398 | for (sections, 0..) |sect, id| { | 373 | for (sections, 0..) |sect, id| { |
| 399 | if (sect.isDebug()) continue; | 374 | if (sect.isDebug()) continue; |
| 400 | const out_sect_id = (try Atom.getOutputSection(zld, sect)) orelse continue; | 375 | const out_sect_id = (try Atom.getOutputSection(macho_file, sect)) orelse continue; |
| 401 | if (sect.size == 0) continue; | 376 | if (sect.size == 0) continue; |
| 402 | 377 | ||
| 403 | const sect_id = @as(u8, @intCast(id)); | 378 | const sect_id = @as(u8, @intCast(id)); |
| 404 | const sym_index = self.getSectionAliasSymbolIndex(sect_id); | 379 | const sym_index = self.getSectionAliasSymbolIndex(sect_id); |
| 405 | const atom_index = try self.createAtomFromSubsection( | 380 | const atom_index = try self.createAtomFromSubsection( |
| 406 | zld, | 381 | macho_file, |
| 407 | object_id, | 382 | object_id, |
| 408 | sym_index, | 383 | sym_index, |
| 409 | sym_index, | 384 | sym_index, |
| ... | @@ -412,7 +387,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void { | ... | @@ -412,7 +387,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void { |
| 412 | sect.@"align", | 387 | sect.@"align", |
| 413 | out_sect_id, | 388 | out_sect_id, |
| 414 | ); | 389 | ); |
| 415 | zld.addAtomToSection(atom_index); | 390 | macho_file.addAtomToSection(atom_index); |
| 416 | } | 391 | } |
| 417 | return; | 392 | return; |
| 418 | } | 393 | } |
| ... | @@ -456,17 +431,17 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void { | ... | @@ -456,17 +431,17 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void { |
| 456 | log.debug("splitting section '{s},{s}' into atoms", .{ sect.segName(), sect.sectName() }); | 431 | log.debug("splitting section '{s},{s}' into atoms", .{ sect.segName(), sect.sectName() }); |
| 457 | 432 | ||
| 458 | // Get output segment/section in the final artifact. | 433 | // Get output segment/section in the final artifact. |
| 459 | const out_sect_id = (try Atom.getOutputSection(zld, sect)) orelse continue; | 434 | const out_sect_id = (try Atom.getOutputSection(macho_file, sect)) orelse continue; |
| 460 | 435 | ||
| 461 | log.debug(" output sect({d}, '{s},{s}')", .{ | 436 | log.debug(" output sect({d}, '{s},{s}')", .{ |
| 462 | out_sect_id + 1, | 437 | out_sect_id + 1, |
| 463 | zld.sections.items(.header)[out_sect_id].segName(), | 438 | macho_file.sections.items(.header)[out_sect_id].segName(), |
| 464 | zld.sections.items(.header)[out_sect_id].sectName(), | 439 | macho_file.sections.items(.header)[out_sect_id].sectName(), |
| 465 | }); | 440 | }); |
| 466 | 441 | ||
| 467 | try self.parseRelocs(gpa, section.id); | 442 | try self.parseRelocs(gpa, section.id); |
| 468 | 443 | ||
| 469 | const cpu_arch = zld.options.target.cpu.arch; | 444 | const cpu_arch = macho_file.base.options.target.cpu.arch; |
| 470 | const sect_loc = filterSymbolsBySection(symtab[sect_sym_index..], sect_id + 1); | 445 | const sect_loc = filterSymbolsBySection(symtab[sect_sym_index..], sect_id + 1); |
| 471 | const sect_start_index = sect_sym_index + sect_loc.index; | 446 | const sect_start_index = sect_sym_index + sect_loc.index; |
| 472 | 447 | ||
| ... | @@ -482,7 +457,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void { | ... | @@ -482,7 +457,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void { |
| 482 | const sym_index = self.getSectionAliasSymbolIndex(sect_id); | 457 | const sym_index = self.getSectionAliasSymbolIndex(sect_id); |
| 483 | const atom_size = first_sym.n_value - sect.addr; | 458 | const atom_size = first_sym.n_value - sect.addr; |
| 484 | const atom_index = try self.createAtomFromSubsection( | 459 | const atom_index = try self.createAtomFromSubsection( |
| 485 | zld, | 460 | macho_file, |
| 486 | object_id, | 461 | object_id, |
| 487 | sym_index, | 462 | sym_index, |
| 488 | sym_index, | 463 | sym_index, |
| ... | @@ -492,9 +467,9 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void { | ... | @@ -492,9 +467,9 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void { |
| 492 | out_sect_id, | 467 | out_sect_id, |
| 493 | ); | 468 | ); |
| 494 | if (!sect.isZerofill()) { | 469 | if (!sect.isZerofill()) { |
| 495 | try self.cacheRelocs(zld, atom_index); | 470 | try self.cacheRelocs(macho_file, atom_index); |
| 496 | } | 471 | } |
| 497 | zld.addAtomToSection(atom_index); | 472 | macho_file.addAtomToSection(atom_index); |
| 498 | } | 473 | } |
| 499 | 474 | ||
| 500 | var next_sym_index = sect_start_index; | 475 | var next_sym_index = sect_start_index; |
| ... | @@ -518,7 +493,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void { | ... | @@ -518,7 +493,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void { |
| 518 | sect.@"align"; | 493 | sect.@"align"; |
| 519 | 494 | ||
| 520 | const atom_index = try self.createAtomFromSubsection( | 495 | const atom_index = try self.createAtomFromSubsection( |
| 521 | zld, | 496 | macho_file, |
| 522 | object_id, | 497 | object_id, |
| 523 | atom_sym_index, | 498 | atom_sym_index, |
| 524 | atom_sym_index, | 499 | atom_sym_index, |
| ... | @@ -537,14 +512,14 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void { | ... | @@ -537,14 +512,14 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void { |
| 537 | self.atom_by_index_table[alias_index] = atom_index; | 512 | self.atom_by_index_table[alias_index] = atom_index; |
| 538 | } | 513 | } |
| 539 | if (!sect.isZerofill()) { | 514 | if (!sect.isZerofill()) { |
| 540 | try self.cacheRelocs(zld, atom_index); | 515 | try self.cacheRelocs(macho_file, atom_index); |
| 541 | } | 516 | } |
| 542 | zld.addAtomToSection(atom_index); | 517 | macho_file.addAtomToSection(atom_index); |
| 543 | } | 518 | } |
| 544 | } else { | 519 | } else { |
| 545 | const alias_index = self.getSectionAliasSymbolIndex(sect_id); | 520 | const alias_index = self.getSectionAliasSymbolIndex(sect_id); |
| 546 | const atom_index = try self.createAtomFromSubsection( | 521 | const atom_index = try self.createAtomFromSubsection( |
| 547 | zld, | 522 | macho_file, |
| 548 | object_id, | 523 | object_id, |
| 549 | alias_index, | 524 | alias_index, |
| 550 | sect_start_index, | 525 | sect_start_index, |
| ... | @@ -554,16 +529,16 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void { | ... | @@ -554,16 +529,16 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void { |
| 554 | out_sect_id, | 529 | out_sect_id, |
| 555 | ); | 530 | ); |
| 556 | if (!sect.isZerofill()) { | 531 | if (!sect.isZerofill()) { |
| 557 | try self.cacheRelocs(zld, atom_index); | 532 | try self.cacheRelocs(macho_file, atom_index); |
| 558 | } | 533 | } |
| 559 | zld.addAtomToSection(atom_index); | 534 | macho_file.addAtomToSection(atom_index); |
| 560 | } | 535 | } |
| 561 | } | 536 | } |
| 562 | } | 537 | } |
| 563 | 538 | ||
| 564 | fn createAtomFromSubsection( | 539 | fn createAtomFromSubsection( |
| 565 | self: *Object, | 540 | self: *Object, |
| 566 | zld: *Zld, | 541 | macho_file: *MachO, |
| 567 | object_id: u32, | 542 | object_id: u32, |
| 568 | sym_index: u32, | 543 | sym_index: u32, |
| 569 | inner_sym_index: u32, | 544 | inner_sym_index: u32, |
| ... | @@ -572,9 +547,9 @@ fn createAtomFromSubsection( | ... | @@ -572,9 +547,9 @@ fn createAtomFromSubsection( |
| 572 | alignment: u32, | 547 | alignment: u32, |
| 573 | out_sect_id: u8, | 548 | out_sect_id: u8, |
| 574 | ) !Atom.Index { | 549 | ) !Atom.Index { |
| 575 | const gpa = zld.gpa; | 550 | const gpa = macho_file.base.allocator; |
| 576 | const atom_index = try zld.createAtom(sym_index, .{ .size = size, .alignment = alignment }); | 551 | const atom_index = try macho_file.createAtom(sym_index, .{ .size = size, .alignment = alignment }); |
| 577 | const atom = zld.getAtomPtr(atom_index); | 552 | const atom = macho_file.getAtomPtr(atom_index); |
| 578 | atom.inner_sym_index = inner_sym_index; | 553 | atom.inner_sym_index = inner_sym_index; |
| 579 | atom.inner_nsyms_trailing = inner_nsyms_trailing; | 554 | atom.inner_nsyms_trailing = inner_nsyms_trailing; |
| 580 | atom.file = object_id + 1; | 555 | atom.file = object_id + 1; |
| ... | @@ -584,22 +559,22 @@ fn createAtomFromSubsection( | ... | @@ -584,22 +559,22 @@ fn createAtomFromSubsection( |
| 584 | sym_index, | 559 | sym_index, |
| 585 | self.getSymbolName(sym_index), | 560 | self.getSymbolName(sym_index), |
| 586 | out_sect_id + 1, | 561 | out_sect_id + 1, |
| 587 | zld.sections.items(.header)[out_sect_id].segName(), | 562 | macho_file.sections.items(.header)[out_sect_id].segName(), |
| 588 | zld.sections.items(.header)[out_sect_id].sectName(), | 563 | macho_file.sections.items(.header)[out_sect_id].sectName(), |
| 589 | object_id, | 564 | object_id, |
| 590 | }); | 565 | }); |
| 591 | 566 | ||
| 592 | try self.atoms.append(gpa, atom_index); | 567 | try self.atoms.append(gpa, atom_index); |
| 593 | self.atom_by_index_table[sym_index] = atom_index; | 568 | self.atom_by_index_table[sym_index] = atom_index; |
| 594 | 569 | ||
| 595 | var it = Atom.getInnerSymbolsIterator(zld, atom_index); | 570 | var it = Atom.getInnerSymbolsIterator(macho_file, atom_index); |
| 596 | while (it.next()) |sym_loc| { | 571 | while (it.next()) |sym_loc| { |
| 597 | const inner = zld.getSymbolPtr(sym_loc); | 572 | const inner = macho_file.getSymbolPtr(sym_loc); |
| 598 | inner.n_sect = out_sect_id + 1; | 573 | inner.n_sect = out_sect_id + 1; |
| 599 | self.atom_by_index_table[sym_loc.sym_index] = atom_index; | 574 | self.atom_by_index_table[sym_loc.sym_index] = atom_index; |
| 600 | } | 575 | } |
| 601 | 576 | ||
| 602 | const out_sect = zld.sections.items(.header)[out_sect_id]; | 577 | const out_sect = macho_file.sections.items(.header)[out_sect_id]; |
| 603 | if (out_sect.isCode() and | 578 | if (out_sect.isCode() and |
| 604 | mem.eql(u8, "__TEXT", out_sect.segName()) and | 579 | mem.eql(u8, "__TEXT", out_sect.segName()) and |
| 605 | mem.eql(u8, "__text", out_sect.sectName())) | 580 | mem.eql(u8, "__text", out_sect.sectName())) |
| ... | @@ -651,8 +626,8 @@ fn parseRelocs(self: *Object, gpa: Allocator, sect_id: u8) !void { | ... | @@ -651,8 +626,8 @@ fn parseRelocs(self: *Object, gpa: Allocator, sect_id: u8) !void { |
| 651 | self.section_relocs_lookup.items[sect_id] = start; | 626 | self.section_relocs_lookup.items[sect_id] = start; |
| 652 | } | 627 | } |
| 653 | 628 | ||
| 654 | fn cacheRelocs(self: *Object, zld: *Zld, atom_index: Atom.Index) !void { | 629 | fn cacheRelocs(self: *Object, macho_file: *MachO, atom_index: Atom.Index) !void { |
| 655 | const atom = zld.getAtom(atom_index); | 630 | const atom = macho_file.getAtom(atom_index); |
| 656 | 631 | ||
| 657 | const source_sect_id = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: { | 632 | const source_sect_id = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: { |
| 658 | break :blk source_sym.n_sect - 1; | 633 | break :blk source_sym.n_sect - 1; |
| ... | @@ -679,19 +654,19 @@ fn relocGreaterThan(ctx: void, lhs: macho.relocation_info, rhs: macho.relocation | ... | @@ -679,19 +654,19 @@ fn relocGreaterThan(ctx: void, lhs: macho.relocation_info, rhs: macho.relocation |
| 679 | return lhs.r_address > rhs.r_address; | 654 | return lhs.r_address > rhs.r_address; |
| 680 | } | 655 | } |
| 681 | 656 | ||
| 682 | fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void { | 657 | fn parseEhFrameSection(self: *Object, macho_file: *MachO, object_id: u32) !void { |
| 683 | const sect_id = self.eh_frame_sect_id orelse return; | 658 | const sect_id = self.eh_frame_sect_id orelse return; |
| 684 | const sect = self.getSourceSection(sect_id); | 659 | const sect = self.getSourceSection(sect_id); |
| 685 | 660 | ||
| 686 | log.debug("parsing __TEXT,__eh_frame section", .{}); | 661 | log.debug("parsing __TEXT,__eh_frame section", .{}); |
| 687 | 662 | ||
| 688 | const gpa = zld.gpa; | 663 | const gpa = macho_file.base.allocator; |
| 689 | 664 | ||
| 690 | if (zld.eh_frame_section_index == null) { | 665 | if (macho_file.eh_frame_section_index == null) { |
| 691 | zld.eh_frame_section_index = try MachO.initSection(gpa, zld, "__TEXT", "__eh_frame", .{}); | 666 | macho_file.eh_frame_section_index = try macho_file.initSection("__TEXT", "__eh_frame", .{}); |
| 692 | } | 667 | } |
| 693 | 668 | ||
| 694 | const cpu_arch = zld.options.target.cpu.arch; | 669 | const cpu_arch = macho_file.base.options.target.cpu.arch; |
| 695 | try self.parseRelocs(gpa, sect_id); | 670 | try self.parseRelocs(gpa, sect_id); |
| 696 | const relocs = self.getRelocs(sect_id); | 671 | const relocs = self.getRelocs(sect_id); |
| 697 | 672 | ||
| ... | @@ -729,7 +704,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void { | ... | @@ -729,7 +704,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void { |
| 729 | @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)) == .ARM64_RELOC_UNSIGNED) | 704 | @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)) == .ARM64_RELOC_UNSIGNED) |
| 730 | break rel; | 705 | break rel; |
| 731 | } else unreachable; | 706 | } else unreachable; |
| 732 | const target = Atom.parseRelocTarget(zld, .{ | 707 | const target = Atom.parseRelocTarget(macho_file, .{ |
| 733 | .object_id = object_id, | 708 | .object_id = object_id, |
| 734 | .rel = rel, | 709 | .rel = rel, |
| 735 | .code = it.data[offset..], | 710 | .code = it.data[offset..], |
| ... | @@ -744,7 +719,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void { | ... | @@ -744,7 +719,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void { |
| 744 | }); | 719 | }); |
| 745 | const target_sym_index = self.getSymbolByAddress(target_address, null); | 720 | const target_sym_index = self.getSymbolByAddress(target_address, null); |
| 746 | const target = if (self.getGlobal(target_sym_index)) |global_index| | 721 | const target = if (self.getGlobal(target_sym_index)) |global_index| |
| 747 | zld.globals.items[global_index] | 722 | macho_file.globals.items[global_index] |
| 748 | else | 723 | else |
| 749 | SymbolWithLoc{ .sym_index = target_sym_index, .file = object_id + 1 }; | 724 | SymbolWithLoc{ .sym_index = target_sym_index, .file = object_id + 1 }; |
| 750 | break :blk target; | 725 | break :blk target; |
| ... | @@ -770,7 +745,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void { | ... | @@ -770,7 +745,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void { |
| 770 | }; | 745 | }; |
| 771 | log.debug("FDE at offset {x} tracks {s}", .{ | 746 | log.debug("FDE at offset {x} tracks {s}", .{ |
| 772 | offset, | 747 | offset, |
| 773 | zld.getSymbolName(actual_target), | 748 | macho_file.getSymbolName(actual_target), |
| 774 | }); | 749 | }); |
| 775 | try self.eh_frame_records_lookup.putNoClobber(gpa, actual_target, offset); | 750 | try self.eh_frame_records_lookup.putNoClobber(gpa, actual_target, offset); |
| 776 | } | 751 | } |
| ... | @@ -779,19 +754,17 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void { | ... | @@ -779,19 +754,17 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void { |
| 779 | } | 754 | } |
| 780 | } | 755 | } |
| 781 | 756 | ||
| 782 | fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void { | 757 | fn parseUnwindInfo(self: *Object, macho_file: *MachO, object_id: u32) !void { |
| 783 | const gpa = zld.gpa; | 758 | const gpa = macho_file.base.allocator; |
| 784 | const cpu_arch = zld.options.target.cpu.arch; | 759 | const cpu_arch = macho_file.base.options.target.cpu.arch; |
| 785 | const sect_id = self.unwind_info_sect_id orelse { | 760 | const sect_id = self.unwind_info_sect_id orelse { |
| 786 | // If it so happens that the object had `__eh_frame` section defined but no `__compact_unwind`, | 761 | // If it so happens that the object had `__eh_frame` section defined but no `__compact_unwind`, |
| 787 | // we will try fully synthesising unwind info records to somewhat match Apple ld's | 762 | // we will try fully synthesising unwind info records to somewhat match Apple ld's |
| 788 | // approach. However, we will only synthesise DWARF records and nothing more. For this reason, | 763 | // approach. However, we will only synthesise DWARF records and nothing more. For this reason, |
| 789 | // we still create the output `__TEXT,__unwind_info` section. | 764 | // we still create the output `__TEXT,__unwind_info` section. |
| 790 | if (self.hasEhFrameRecords()) { | 765 | if (self.hasEhFrameRecords()) { |
| 791 | if (zld.unwind_info_section_index == null) { | 766 | if (macho_file.unwind_info_section_index == null) { |
| 792 | zld.unwind_info_section_index = try MachO.initSection( | 767 | macho_file.unwind_info_section_index = try macho_file.initSection( |
| 793 | gpa, | ||
| 794 | zld, | ||
| 795 | "__TEXT", | 768 | "__TEXT", |
| 796 | "__unwind_info", | 769 | "__unwind_info", |
| 797 | .{}, | 770 | .{}, |
| ... | @@ -803,8 +776,8 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void { | ... | @@ -803,8 +776,8 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void { |
| 803 | 776 | ||
| 804 | log.debug("parsing unwind info in {s}", .{self.name}); | 777 | log.debug("parsing unwind info in {s}", .{self.name}); |
| 805 | 778 | ||
| 806 | if (zld.unwind_info_section_index == null) { | 779 | if (macho_file.unwind_info_section_index == null) { |
| 807 | zld.unwind_info_section_index = try MachO.initSection(gpa, zld, "__TEXT", "__unwind_info", .{}); | 780 | macho_file.unwind_info_section_index = try macho_file.initSection("__TEXT", "__unwind_info", .{}); |
| 808 | } | 781 | } |
| 809 | 782 | ||
| 810 | const unwind_records = self.getUnwindRecords(); | 783 | const unwind_records = self.getUnwindRecords(); |
| ... | @@ -839,7 +812,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void { | ... | @@ -839,7 +812,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void { |
| 839 | 812 | ||
| 840 | // Find function symbol that this record describes | 813 | // Find function symbol that this record describes |
| 841 | const rel = relocs[rel_pos.start..][rel_pos.len - 1]; | 814 | const rel = relocs[rel_pos.start..][rel_pos.len - 1]; |
| 842 | const target = Atom.parseRelocTarget(zld, .{ | 815 | const target = Atom.parseRelocTarget(macho_file, .{ |
| 843 | .object_id = object_id, | 816 | .object_id = object_id, |
| 844 | .rel = rel, | 817 | .rel = rel, |
| 845 | .code = mem.asBytes(&record), | 818 | .code = mem.asBytes(&record), |
| ... | @@ -863,7 +836,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void { | ... | @@ -863,7 +836,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void { |
| 863 | }; | 836 | }; |
| 864 | log.debug("unwind record {d} tracks {s}", .{ | 837 | log.debug("unwind record {d} tracks {s}", .{ |
| 865 | record_id, | 838 | record_id, |
| 866 | zld.getSymbolName(actual_target), | 839 | macho_file.getSymbolName(actual_target), |
| 867 | }); | 840 | }); |
| 868 | try self.unwind_records_lookup.putNoClobber(gpa, actual_target, @intCast(record_id)); | 841 | try self.unwind_records_lookup.putNoClobber(gpa, actual_target, @intCast(record_id)); |
| 869 | } | 842 | } |
| ... | @@ -1094,3 +1067,27 @@ pub fn getEhFrameRecordsIterator(self: Object) eh_frame.Iterator { | ... | @@ -1094,3 +1067,27 @@ pub fn getEhFrameRecordsIterator(self: Object) eh_frame.Iterator { |
| 1094 | pub fn hasDataInCode(self: Object) bool { | 1067 | pub fn hasDataInCode(self: Object) bool { |
| 1095 | return self.data_in_code.items.len > 0; | 1068 | return self.data_in_code.items.len > 0; |
| 1096 | } | 1069 | } |
| 1070 | |||
| 1071 | const Object = @This(); | ||
| 1072 | |||
| 1073 | const std = @import("std"); | ||
| 1074 | const build_options = @import("build_options"); | ||
| 1075 | const assert = std.debug.assert; | ||
| 1076 | const dwarf = std.dwarf; | ||
| 1077 | const eh_frame = @import("eh_frame.zig"); | ||
| 1078 | const fs = std.fs; | ||
| 1079 | const io = std.io; | ||
| 1080 | const log = std.log.scoped(.link); | ||
| 1081 | const macho = std.macho; | ||
| 1082 | const math = std.math; | ||
| 1083 | const mem = std.mem; | ||
| 1084 | const sort = std.sort; | ||
| 1085 | const trace = @import("../../tracy.zig").trace; | ||
| 1086 | |||
| 1087 | const Allocator = mem.Allocator; | ||
| 1088 | const Atom = @import("Atom.zig"); | ||
| 1089 | const DwarfInfo = @import("DwarfInfo.zig"); | ||
| 1090 | const LoadCommandIterator = macho.LoadCommandIterator; | ||
| 1091 | const MachO = @import("../MachO.zig"); | ||
| 1092 | const SymbolWithLoc = MachO.SymbolWithLoc; | ||
| 1093 | const UnwindInfo = @import("UnwindInfo.zig"); |
src/link/MachO/Trie.zig+242-242| ... | @@ -28,248 +28,6 @@ | ... | @@ -28,248 +28,6 @@ |
| 28 | //! After the optional exported symbol information is a byte of how many edges (0-255) that | 28 | //! After the optional exported symbol information is a byte of how many edges (0-255) that |
| 29 | //! this node has leaving it, followed by each edge. Each edge is a zero terminated UTF8 of | 29 | //! this node has leaving it, followed by each edge. Each edge is a zero terminated UTF8 of |
| 30 | //! the addition chars in the symbol, followed by a uleb128 offset for the node that edge points to. | 30 | //! the addition chars in the symbol, followed by a uleb128 offset for the node that edge points to. |
| 31 | const Trie = @This(); | ||
| 32 | |||
| 33 | const std = @import("std"); | ||
| 34 | const mem = std.mem; | ||
| 35 | const leb = std.leb; | ||
| 36 | const log = std.log.scoped(.link); | ||
| 37 | const macho = std.macho; | ||
| 38 | const testing = std.testing; | ||
| 39 | const assert = std.debug.assert; | ||
| 40 | const Allocator = mem.Allocator; | ||
| 41 | |||
| 42 | pub const Node = struct { | ||
| 43 | base: *Trie, | ||
| 44 | |||
| 45 | /// Terminal info associated with this node. | ||
| 46 | /// If this node is not a terminal node, info is null. | ||
| 47 | terminal_info: ?struct { | ||
| 48 | /// Export flags associated with this exported symbol. | ||
| 49 | export_flags: u64, | ||
| 50 | /// VM address offset wrt to the section this symbol is defined against. | ||
| 51 | vmaddr_offset: u64, | ||
| 52 | } = null, | ||
| 53 | |||
| 54 | /// Offset of this node in the trie output byte stream. | ||
| 55 | trie_offset: ?u64 = null, | ||
| 56 | |||
| 57 | /// List of all edges originating from this node. | ||
| 58 | edges: std.ArrayListUnmanaged(Edge) = .{}, | ||
| 59 | |||
| 60 | node_dirty: bool = true, | ||
| 61 | |||
| 62 | /// Edge connecting to nodes in the trie. | ||
| 63 | pub const Edge = struct { | ||
| 64 | from: *Node, | ||
| 65 | to: *Node, | ||
| 66 | label: []u8, | ||
| 67 | |||
| 68 | fn deinit(self: *Edge, allocator: Allocator) void { | ||
| 69 | self.to.deinit(allocator); | ||
| 70 | allocator.destroy(self.to); | ||
| 71 | allocator.free(self.label); | ||
| 72 | self.from = undefined; | ||
| 73 | self.to = undefined; | ||
| 74 | self.label = undefined; | ||
| 75 | } | ||
| 76 | }; | ||
| 77 | |||
| 78 | fn deinit(self: *Node, allocator: Allocator) void { | ||
| 79 | for (self.edges.items) |*edge| { | ||
| 80 | edge.deinit(allocator); | ||
| 81 | } | ||
| 82 | self.edges.deinit(allocator); | ||
| 83 | } | ||
| 84 | |||
| 85 | /// Inserts a new node starting from `self`. | ||
| 86 | fn put(self: *Node, allocator: Allocator, label: []const u8) !*Node { | ||
| 87 | // Check for match with edges from this node. | ||
| 88 | for (self.edges.items) |*edge| { | ||
| 89 | const match = mem.indexOfDiff(u8, edge.label, label) orelse return edge.to; | ||
| 90 | if (match == 0) continue; | ||
| 91 | if (match == edge.label.len) return edge.to.put(allocator, label[match..]); | ||
| 92 | |||
| 93 | // Found a match, need to splice up nodes. | ||
| 94 | // From: A -> B | ||
| 95 | // To: A -> C -> B | ||
| 96 | const mid = try allocator.create(Node); | ||
| 97 | mid.* = .{ .base = self.base }; | ||
| 98 | var to_label = try allocator.dupe(u8, edge.label[match..]); | ||
| 99 | allocator.free(edge.label); | ||
| 100 | const to_node = edge.to; | ||
| 101 | edge.to = mid; | ||
| 102 | edge.label = try allocator.dupe(u8, label[0..match]); | ||
| 103 | self.base.node_count += 1; | ||
| 104 | |||
| 105 | try mid.edges.append(allocator, .{ | ||
| 106 | .from = mid, | ||
| 107 | .to = to_node, | ||
| 108 | .label = to_label, | ||
| 109 | }); | ||
| 110 | |||
| 111 | return if (match == label.len) mid else mid.put(allocator, label[match..]); | ||
| 112 | } | ||
| 113 | |||
| 114 | // Add a new node. | ||
| 115 | const node = try allocator.create(Node); | ||
| 116 | node.* = .{ .base = self.base }; | ||
| 117 | self.base.node_count += 1; | ||
| 118 | |||
| 119 | try self.edges.append(allocator, .{ | ||
| 120 | .from = self, | ||
| 121 | .to = node, | ||
| 122 | .label = try allocator.dupe(u8, label), | ||
| 123 | }); | ||
| 124 | |||
| 125 | return node; | ||
| 126 | } | ||
| 127 | |||
| 128 | /// Recursively parses the node from the input byte stream. | ||
| 129 | fn read(self: *Node, allocator: Allocator, reader: anytype) Trie.ReadError!usize { | ||
| 130 | self.node_dirty = true; | ||
| 131 | const trie_offset = try reader.context.getPos(); | ||
| 132 | self.trie_offset = trie_offset; | ||
| 133 | |||
| 134 | var nread: usize = 0; | ||
| 135 | |||
| 136 | const node_size = try leb.readULEB128(u64, reader); | ||
| 137 | if (node_size > 0) { | ||
| 138 | const export_flags = try leb.readULEB128(u64, reader); | ||
| 139 | // TODO Parse special flags. | ||
| 140 | assert(export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and | ||
| 141 | export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0); | ||
| 142 | |||
| 143 | const vmaddr_offset = try leb.readULEB128(u64, reader); | ||
| 144 | |||
| 145 | self.terminal_info = .{ | ||
| 146 | .export_flags = export_flags, | ||
| 147 | .vmaddr_offset = vmaddr_offset, | ||
| 148 | }; | ||
| 149 | } | ||
| 150 | |||
| 151 | const nedges = try reader.readByte(); | ||
| 152 | self.base.node_count += nedges; | ||
| 153 | |||
| 154 | nread += (try reader.context.getPos()) - trie_offset; | ||
| 155 | |||
| 156 | var i: usize = 0; | ||
| 157 | while (i < nedges) : (i += 1) { | ||
| 158 | const edge_start_pos = try reader.context.getPos(); | ||
| 159 | |||
| 160 | const label = blk: { | ||
| 161 | var label_buf = std.ArrayList(u8).init(allocator); | ||
| 162 | while (true) { | ||
| 163 | const next = try reader.readByte(); | ||
| 164 | if (next == @as(u8, 0)) | ||
| 165 | break; | ||
| 166 | try label_buf.append(next); | ||
| 167 | } | ||
| 168 | break :blk try label_buf.toOwnedSlice(); | ||
| 169 | }; | ||
| 170 | |||
| 171 | const seek_to = try leb.readULEB128(u64, reader); | ||
| 172 | const return_pos = try reader.context.getPos(); | ||
| 173 | |||
| 174 | nread += return_pos - edge_start_pos; | ||
| 175 | try reader.context.seekTo(seek_to); | ||
| 176 | |||
| 177 | const node = try allocator.create(Node); | ||
| 178 | node.* = .{ .base = self.base }; | ||
| 179 | |||
| 180 | nread += try node.read(allocator, reader); | ||
| 181 | try self.edges.append(allocator, .{ | ||
| 182 | .from = self, | ||
| 183 | .to = node, | ||
| 184 | .label = label, | ||
| 185 | }); | ||
| 186 | try reader.context.seekTo(return_pos); | ||
| 187 | } | ||
| 188 | |||
| 189 | return nread; | ||
| 190 | } | ||
| 191 | |||
| 192 | /// Writes this node to a byte stream. | ||
| 193 | /// The children of this node *are* not written to the byte stream | ||
| 194 | /// recursively. To write all nodes to a byte stream in sequence, | ||
| 195 | /// iterate over `Trie.ordered_nodes` and call this method on each node. | ||
| 196 | /// This is one of the requirements of the MachO. | ||
| 197 | /// Panics if `finalize` was not called before calling this method. | ||
| 198 | fn write(self: Node, writer: anytype) !void { | ||
| 199 | assert(!self.node_dirty); | ||
| 200 | if (self.terminal_info) |info| { | ||
| 201 | // Terminal node info: encode export flags and vmaddr offset of this symbol. | ||
| 202 | var info_buf: [@sizeOf(u64) * 2]u8 = undefined; | ||
| 203 | var info_stream = std.io.fixedBufferStream(&info_buf); | ||
| 204 | // TODO Implement for special flags. | ||
| 205 | assert(info.export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and | ||
| 206 | info.export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0); | ||
| 207 | try leb.writeULEB128(info_stream.writer(), info.export_flags); | ||
| 208 | try leb.writeULEB128(info_stream.writer(), info.vmaddr_offset); | ||
| 209 | |||
| 210 | // Encode the size of the terminal node info. | ||
| 211 | var size_buf: [@sizeOf(u64)]u8 = undefined; | ||
| 212 | var size_stream = std.io.fixedBufferStream(&size_buf); | ||
| 213 | try leb.writeULEB128(size_stream.writer(), info_stream.pos); | ||
| 214 | |||
| 215 | // Now, write them to the output stream. | ||
| 216 | try writer.writeAll(size_buf[0..size_stream.pos]); | ||
| 217 | try writer.writeAll(info_buf[0..info_stream.pos]); | ||
| 218 | } else { | ||
| 219 | // Non-terminal node is delimited by 0 byte. | ||
| 220 | try writer.writeByte(0); | ||
| 221 | } | ||
| 222 | // Write number of edges (max legal number of edges is 256). | ||
| 223 | try writer.writeByte(@as(u8, @intCast(self.edges.items.len))); | ||
| 224 | |||
| 225 | for (self.edges.items) |edge| { | ||
| 226 | // Write edge label and offset to next node in trie. | ||
| 227 | try writer.writeAll(edge.label); | ||
| 228 | try writer.writeByte(0); | ||
| 229 | try leb.writeULEB128(writer, edge.to.trie_offset.?); | ||
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 | const FinalizeResult = struct { | ||
| 234 | /// Current size of this node in bytes. | ||
| 235 | node_size: u64, | ||
| 236 | |||
| 237 | /// True if the trie offset of this node in the output byte stream | ||
| 238 | /// would need updating; false otherwise. | ||
| 239 | updated: bool, | ||
| 240 | }; | ||
| 241 | |||
| 242 | /// Updates offset of this node in the output byte stream. | ||
| 243 | fn finalize(self: *Node, offset_in_trie: u64) !FinalizeResult { | ||
| 244 | var stream = std.io.countingWriter(std.io.null_writer); | ||
| 245 | var writer = stream.writer(); | ||
| 246 | |||
| 247 | var node_size: u64 = 0; | ||
| 248 | if (self.terminal_info) |info| { | ||
| 249 | try leb.writeULEB128(writer, info.export_flags); | ||
| 250 | try leb.writeULEB128(writer, info.vmaddr_offset); | ||
| 251 | try leb.writeULEB128(writer, stream.bytes_written); | ||
| 252 | } else { | ||
| 253 | node_size += 1; // 0x0 for non-terminal nodes | ||
| 254 | } | ||
| 255 | node_size += 1; // 1 byte for edge count | ||
| 256 | |||
| 257 | for (self.edges.items) |edge| { | ||
| 258 | const next_node_offset = edge.to.trie_offset orelse 0; | ||
| 259 | node_size += edge.label.len + 1; | ||
| 260 | try leb.writeULEB128(writer, next_node_offset); | ||
| 261 | } | ||
| 262 | |||
| 263 | const trie_offset = self.trie_offset orelse 0; | ||
| 264 | const updated = offset_in_trie != trie_offset; | ||
| 265 | self.trie_offset = offset_in_trie; | ||
| 266 | self.node_dirty = false; | ||
| 267 | node_size += stream.bytes_written; | ||
| 268 | |||
| 269 | return FinalizeResult{ .node_size = node_size, .updated = updated }; | ||
| 270 | } | ||
| 271 | }; | ||
| 272 | |||
| 273 | /// The root node of the trie. | 31 | /// The root node of the trie. |
| 274 | root: ?*Node = null, | 32 | root: ?*Node = null, |
| 275 | 33 | ||
| ... | @@ -611,3 +369,245 @@ test "ordering bug" { | ... | @@ -611,3 +369,245 @@ test "ordering bug" { |
| 611 | _ = try trie.write(stream.writer()); | 369 | _ = try trie.write(stream.writer()); |
| 612 | try expectEqualHexStrings(&exp_buffer, buffer); | 370 | try expectEqualHexStrings(&exp_buffer, buffer); |
| 613 | } | 371 | } |
| 372 | |||
| 373 | pub const Node = struct { | ||
| 374 | base: *Trie, | ||
| 375 | |||
| 376 | /// Terminal info associated with this node. | ||
| 377 | /// If this node is not a terminal node, info is null. | ||
| 378 | terminal_info: ?struct { | ||
| 379 | /// Export flags associated with this exported symbol. | ||
| 380 | export_flags: u64, | ||
| 381 | /// VM address offset wrt to the section this symbol is defined against. | ||
| 382 | vmaddr_offset: u64, | ||
| 383 | } = null, | ||
| 384 | |||
| 385 | /// Offset of this node in the trie output byte stream. | ||
| 386 | trie_offset: ?u64 = null, | ||
| 387 | |||
| 388 | /// List of all edges originating from this node. | ||
| 389 | edges: std.ArrayListUnmanaged(Edge) = .{}, | ||
| 390 | |||
| 391 | node_dirty: bool = true, | ||
| 392 | |||
| 393 | /// Edge connecting to nodes in the trie. | ||
| 394 | pub const Edge = struct { | ||
| 395 | from: *Node, | ||
| 396 | to: *Node, | ||
| 397 | label: []u8, | ||
| 398 | |||
| 399 | fn deinit(self: *Edge, allocator: Allocator) void { | ||
| 400 | self.to.deinit(allocator); | ||
| 401 | allocator.destroy(self.to); | ||
| 402 | allocator.free(self.label); | ||
| 403 | self.from = undefined; | ||
| 404 | self.to = undefined; | ||
| 405 | self.label = undefined; | ||
| 406 | } | ||
| 407 | }; | ||
| 408 | |||
| 409 | fn deinit(self: *Node, allocator: Allocator) void { | ||
| 410 | for (self.edges.items) |*edge| { | ||
| 411 | edge.deinit(allocator); | ||
| 412 | } | ||
| 413 | self.edges.deinit(allocator); | ||
| 414 | } | ||
| 415 | |||
| 416 | /// Inserts a new node starting from `self`. | ||
| 417 | fn put(self: *Node, allocator: Allocator, label: []const u8) !*Node { | ||
| 418 | // Check for match with edges from this node. | ||
| 419 | for (self.edges.items) |*edge| { | ||
| 420 | const match = mem.indexOfDiff(u8, edge.label, label) orelse return edge.to; | ||
| 421 | if (match == 0) continue; | ||
| 422 | if (match == edge.label.len) return edge.to.put(allocator, label[match..]); | ||
| 423 | |||
| 424 | // Found a match, need to splice up nodes. | ||
| 425 | // From: A -> B | ||
| 426 | // To: A -> C -> B | ||
| 427 | const mid = try allocator.create(Node); | ||
| 428 | mid.* = .{ .base = self.base }; | ||
| 429 | var to_label = try allocator.dupe(u8, edge.label[match..]); | ||
| 430 | allocator.free(edge.label); | ||
| 431 | const to_node = edge.to; | ||
| 432 | edge.to = mid; | ||
| 433 | edge.label = try allocator.dupe(u8, label[0..match]); | ||
| 434 | self.base.node_count += 1; | ||
| 435 | |||
| 436 | try mid.edges.append(allocator, .{ | ||
| 437 | .from = mid, | ||
| 438 | .to = to_node, | ||
| 439 | .label = to_label, | ||
| 440 | }); | ||
| 441 | |||
| 442 | return if (match == label.len) mid else mid.put(allocator, label[match..]); | ||
| 443 | } | ||
| 444 | |||
| 445 | // Add a new node. | ||
| 446 | const node = try allocator.create(Node); | ||
| 447 | node.* = .{ .base = self.base }; | ||
| 448 | self.base.node_count += 1; | ||
| 449 | |||
| 450 | try self.edges.append(allocator, .{ | ||
| 451 | .from = self, | ||
| 452 | .to = node, | ||
| 453 | .label = try allocator.dupe(u8, label), | ||
| 454 | }); | ||
| 455 | |||
| 456 | return node; | ||
| 457 | } | ||
| 458 | |||
| 459 | /// Recursively parses the node from the input byte stream. | ||
| 460 | fn read(self: *Node, allocator: Allocator, reader: anytype) Trie.ReadError!usize { | ||
| 461 | self.node_dirty = true; | ||
| 462 | const trie_offset = try reader.context.getPos(); | ||
| 463 | self.trie_offset = trie_offset; | ||
| 464 | |||
| 465 | var nread: usize = 0; | ||
| 466 | |||
| 467 | const node_size = try leb.readULEB128(u64, reader); | ||
| 468 | if (node_size > 0) { | ||
| 469 | const export_flags = try leb.readULEB128(u64, reader); | ||
| 470 | // TODO Parse special flags. | ||
| 471 | assert(export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and | ||
| 472 | export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0); | ||
| 473 | |||
| 474 | const vmaddr_offset = try leb.readULEB128(u64, reader); | ||
| 475 | |||
| 476 | self.terminal_info = .{ | ||
| 477 | .export_flags = export_flags, | ||
| 478 | .vmaddr_offset = vmaddr_offset, | ||
| 479 | }; | ||
| 480 | } | ||
| 481 | |||
| 482 | const nedges = try reader.readByte(); | ||
| 483 | self.base.node_count += nedges; | ||
| 484 | |||
| 485 | nread += (try reader.context.getPos()) - trie_offset; | ||
| 486 | |||
| 487 | var i: usize = 0; | ||
| 488 | while (i < nedges) : (i += 1) { | ||
| 489 | const edge_start_pos = try reader.context.getPos(); | ||
| 490 | |||
| 491 | const label = blk: { | ||
| 492 | var label_buf = std.ArrayList(u8).init(allocator); | ||
| 493 | while (true) { | ||
| 494 | const next = try reader.readByte(); | ||
| 495 | if (next == @as(u8, 0)) | ||
| 496 | break; | ||
| 497 | try label_buf.append(next); | ||
| 498 | } | ||
| 499 | break :blk try label_buf.toOwnedSlice(); | ||
| 500 | }; | ||
| 501 | |||
| 502 | const seek_to = try leb.readULEB128(u64, reader); | ||
| 503 | const return_pos = try reader.context.getPos(); | ||
| 504 | |||
| 505 | nread += return_pos - edge_start_pos; | ||
| 506 | try reader.context.seekTo(seek_to); | ||
| 507 | |||
| 508 | const node = try allocator.create(Node); | ||
| 509 | node.* = .{ .base = self.base }; | ||
| 510 | |||
| 511 | nread += try node.read(allocator, reader); | ||
| 512 | try self.edges.append(allocator, .{ | ||
| 513 | .from = self, | ||
| 514 | .to = node, | ||
| 515 | .label = label, | ||
| 516 | }); | ||
| 517 | try reader.context.seekTo(return_pos); | ||
| 518 | } | ||
| 519 | |||
| 520 | return nread; | ||
| 521 | } | ||
| 522 | |||
| 523 | /// Writes this node to a byte stream. | ||
| 524 | /// The children of this node *are* not written to the byte stream | ||
| 525 | /// recursively. To write all nodes to a byte stream in sequence, | ||
| 526 | /// iterate over `Trie.ordered_nodes` and call this method on each node. | ||
| 527 | /// This is one of the requirements of the MachO. | ||
| 528 | /// Panics if `finalize` was not called before calling this method. | ||
| 529 | fn write(self: Node, writer: anytype) !void { | ||
| 530 | assert(!self.node_dirty); | ||
| 531 | if (self.terminal_info) |info| { | ||
| 532 | // Terminal node info: encode export flags and vmaddr offset of this symbol. | ||
| 533 | var info_buf: [@sizeOf(u64) * 2]u8 = undefined; | ||
| 534 | var info_stream = std.io.fixedBufferStream(&info_buf); | ||
| 535 | // TODO Implement for special flags. | ||
| 536 | assert(info.export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and | ||
| 537 | info.export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0); | ||
| 538 | try leb.writeULEB128(info_stream.writer(), info.export_flags); | ||
| 539 | try leb.writeULEB128(info_stream.writer(), info.vmaddr_offset); | ||
| 540 | |||
| 541 | // Encode the size of the terminal node info. | ||
| 542 | var size_buf: [@sizeOf(u64)]u8 = undefined; | ||
| 543 | var size_stream = std.io.fixedBufferStream(&size_buf); | ||
| 544 | try leb.writeULEB128(size_stream.writer(), info_stream.pos); | ||
| 545 | |||
| 546 | // Now, write them to the output stream. | ||
| 547 | try writer.writeAll(size_buf[0..size_stream.pos]); | ||
| 548 | try writer.writeAll(info_buf[0..info_stream.pos]); | ||
| 549 | } else { | ||
| 550 | // Non-terminal node is delimited by 0 byte. | ||
| 551 | try writer.writeByte(0); | ||
| 552 | } | ||
| 553 | // Write number of edges (max legal number of edges is 256). | ||
| 554 | try writer.writeByte(@as(u8, @intCast(self.edges.items.len))); | ||
| 555 | |||
| 556 | for (self.edges.items) |edge| { | ||
| 557 | // Write edge label and offset to next node in trie. | ||
| 558 | try writer.writeAll(edge.label); | ||
| 559 | try writer.writeByte(0); | ||
| 560 | try leb.writeULEB128(writer, edge.to.trie_offset.?); | ||
| 561 | } | ||
| 562 | } | ||
| 563 | |||
| 564 | const FinalizeResult = struct { | ||
| 565 | /// Current size of this node in bytes. | ||
| 566 | node_size: u64, | ||
| 567 | |||
| 568 | /// True if the trie offset of this node in the output byte stream | ||
| 569 | /// would need updating; false otherwise. | ||
| 570 | updated: bool, | ||
| 571 | }; | ||
| 572 | |||
| 573 | /// Updates offset of this node in the output byte stream. | ||
| 574 | fn finalize(self: *Node, offset_in_trie: u64) !FinalizeResult { | ||
| 575 | var stream = std.io.countingWriter(std.io.null_writer); | ||
| 576 | var writer = stream.writer(); | ||
| 577 | |||
| 578 | var node_size: u64 = 0; | ||
| 579 | if (self.terminal_info) |info| { | ||
| 580 | try leb.writeULEB128(writer, info.export_flags); | ||
| 581 | try leb.writeULEB128(writer, info.vmaddr_offset); | ||
| 582 | try leb.writeULEB128(writer, stream.bytes_written); | ||
| 583 | } else { | ||
| 584 | node_size += 1; // 0x0 for non-terminal nodes | ||
| 585 | } | ||
| 586 | node_size += 1; // 1 byte for edge count | ||
| 587 | |||
| 588 | for (self.edges.items) |edge| { | ||
| 589 | const next_node_offset = edge.to.trie_offset orelse 0; | ||
| 590 | node_size += edge.label.len + 1; | ||
| 591 | try leb.writeULEB128(writer, next_node_offset); | ||
| 592 | } | ||
| 593 | |||
| 594 | const trie_offset = self.trie_offset orelse 0; | ||
| 595 | const updated = offset_in_trie != trie_offset; | ||
| 596 | self.trie_offset = offset_in_trie; | ||
| 597 | self.node_dirty = false; | ||
| 598 | node_size += stream.bytes_written; | ||
| 599 | |||
| 600 | return FinalizeResult{ .node_size = node_size, .updated = updated }; | ||
| 601 | } | ||
| 602 | }; | ||
| 603 | |||
| 604 | const Trie = @This(); | ||
| 605 | |||
| 606 | const std = @import("std"); | ||
| 607 | const mem = std.mem; | ||
| 608 | const leb = std.leb; | ||
| 609 | const log = std.log.scoped(.link); | ||
| 610 | const macho = std.macho; | ||
| 611 | const testing = std.testing; | ||
| 612 | const assert = std.debug.assert; | ||
| 613 | const Allocator = mem.Allocator; |
src/link/MachO/UnwindInfo.zig+69-71| ... | @@ -1,25 +1,3 @@ | ... | @@ -1,25 +1,3 @@ |
| 1 | const UnwindInfo = @This(); | ||
| 2 | |||
| 3 | const std = @import("std"); | ||
| 4 | const assert = std.debug.assert; | ||
| 5 | const eh_frame = @import("eh_frame.zig"); | ||
| 6 | const fs = std.fs; | ||
| 7 | const leb = std.leb; | ||
| 8 | const log = std.log.scoped(.unwind_info); | ||
| 9 | const macho = std.macho; | ||
| 10 | const math = std.math; | ||
| 11 | const mem = std.mem; | ||
| 12 | const trace = @import("../../tracy.zig").trace; | ||
| 13 | |||
| 14 | const Allocator = mem.Allocator; | ||
| 15 | const Atom = @import("Atom.zig"); | ||
| 16 | const AtomIndex = @import("zld.zig").AtomIndex; | ||
| 17 | const EhFrameRecord = eh_frame.EhFrameRecord; | ||
| 18 | const MachO = @import("../MachO.zig"); | ||
| 19 | const Object = @import("Object.zig"); | ||
| 20 | const SymbolWithLoc = MachO.SymbolWithLoc; | ||
| 21 | const Zld = @import("zld.zig").Zld; | ||
| 22 | |||
| 23 | gpa: Allocator, | 1 | gpa: Allocator, |
| 24 | 2 | ||
| 25 | /// List of all unwind records gathered from all objects and sorted | 3 | /// List of all unwind records gathered from all objects and sorted |
| ... | @@ -203,28 +181,28 @@ pub fn deinit(info: *UnwindInfo) void { | ... | @@ -203,28 +181,28 @@ pub fn deinit(info: *UnwindInfo) void { |
| 203 | info.lsdas_lookup.deinit(info.gpa); | 181 | info.lsdas_lookup.deinit(info.gpa); |
| 204 | } | 182 | } |
| 205 | 183 | ||
| 206 | pub fn scanRelocs(zld: *Zld) !void { | 184 | pub fn scanRelocs(macho_file: *MachO) !void { |
| 207 | if (zld.unwind_info_section_index == null) return; | 185 | if (macho_file.unwind_info_section_index == null) return; |
| 208 | 186 | ||
| 209 | const cpu_arch = zld.options.target.cpu.arch; | 187 | const cpu_arch = macho_file.base.options.target.cpu.arch; |
| 210 | for (zld.objects.items, 0..) |*object, object_id| { | 188 | for (macho_file.objects.items, 0..) |*object, object_id| { |
| 211 | const unwind_records = object.getUnwindRecords(); | 189 | const unwind_records = object.getUnwindRecords(); |
| 212 | for (object.exec_atoms.items) |atom_index| { | 190 | for (object.exec_atoms.items) |atom_index| { |
| 213 | var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index); | 191 | var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index); |
| 214 | while (inner_syms_it.next()) |sym| { | 192 | while (inner_syms_it.next()) |sym| { |
| 215 | const record_id = object.unwind_records_lookup.get(sym) orelse continue; | 193 | const record_id = object.unwind_records_lookup.get(sym) orelse continue; |
| 216 | if (object.unwind_relocs_lookup[record_id].dead) continue; | 194 | if (object.unwind_relocs_lookup[record_id].dead) continue; |
| 217 | const record = unwind_records[record_id]; | 195 | const record = unwind_records[record_id]; |
| 218 | if (!UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) { | 196 | if (!UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) { |
| 219 | if (getPersonalityFunctionReloc(zld, @as(u32, @intCast(object_id)), record_id)) |rel| { | 197 | if (getPersonalityFunctionReloc(macho_file, @as(u32, @intCast(object_id)), record_id)) |rel| { |
| 220 | // Personality function; add GOT pointer. | 198 | // Personality function; add GOT pointer. |
| 221 | const target = Atom.parseRelocTarget(zld, .{ | 199 | const target = Atom.parseRelocTarget(macho_file, .{ |
| 222 | .object_id = @as(u32, @intCast(object_id)), | 200 | .object_id = @as(u32, @intCast(object_id)), |
| 223 | .rel = rel, | 201 | .rel = rel, |
| 224 | .code = mem.asBytes(&record), | 202 | .code = mem.asBytes(&record), |
| 225 | .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))), | 203 | .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))), |
| 226 | }); | 204 | }); |
| 227 | try zld.addGotEntry(target); | 205 | try macho_file.addGotEntry(target); |
| 228 | } | 206 | } |
| 229 | } | 207 | } |
| 230 | } | 208 | } |
| ... | @@ -232,10 +210,10 @@ pub fn scanRelocs(zld: *Zld) !void { | ... | @@ -232,10 +210,10 @@ pub fn scanRelocs(zld: *Zld) !void { |
| 232 | } | 210 | } |
| 233 | } | 211 | } |
| 234 | 212 | ||
| 235 | pub fn collect(info: *UnwindInfo, zld: *Zld) !void { | 213 | pub fn collect(info: *UnwindInfo, macho_file: *MachO) !void { |
| 236 | if (zld.unwind_info_section_index == null) return; | 214 | if (macho_file.unwind_info_section_index == null) return; |
| 237 | 215 | ||
| 238 | const cpu_arch = zld.options.target.cpu.arch; | 216 | const cpu_arch = macho_file.base.options.target.cpu.arch; |
| 239 | 217 | ||
| 240 | var records = std.ArrayList(macho.compact_unwind_entry).init(info.gpa); | 218 | var records = std.ArrayList(macho.compact_unwind_entry).init(info.gpa); |
| 241 | defer records.deinit(); | 219 | defer records.deinit(); |
| ... | @@ -244,7 +222,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void { | ... | @@ -244,7 +222,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void { |
| 244 | defer sym_indexes.deinit(); | 222 | defer sym_indexes.deinit(); |
| 245 | 223 | ||
| 246 | // TODO handle dead stripping | 224 | // TODO handle dead stripping |
| 247 | for (zld.objects.items, 0..) |*object, object_id| { | 225 | for (macho_file.objects.items, 0..) |*object, object_id| { |
| 248 | log.debug("collecting unwind records in {s} ({d})", .{ object.name, object_id }); | 226 | log.debug("collecting unwind records in {s} ({d})", .{ object.name, object_id }); |
| 249 | const unwind_records = object.getUnwindRecords(); | 227 | const unwind_records = object.getUnwindRecords(); |
| 250 | 228 | ||
| ... | @@ -254,7 +232,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void { | ... | @@ -254,7 +232,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void { |
| 254 | try sym_indexes.ensureUnusedCapacity(object.exec_atoms.items.len); | 232 | try sym_indexes.ensureUnusedCapacity(object.exec_atoms.items.len); |
| 255 | 233 | ||
| 256 | for (object.exec_atoms.items) |atom_index| { | 234 | for (object.exec_atoms.items) |atom_index| { |
| 257 | var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index); | 235 | var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index); |
| 258 | var prev_symbol: ?SymbolWithLoc = null; | 236 | var prev_symbol: ?SymbolWithLoc = null; |
| 259 | while (inner_syms_it.next()) |symbol| { | 237 | while (inner_syms_it.next()) |symbol| { |
| 260 | var record = if (object.unwind_records_lookup.get(symbol)) |record_id| blk: { | 238 | var record = if (object.unwind_records_lookup.get(symbol)) |record_id| blk: { |
| ... | @@ -262,14 +240,14 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void { | ... | @@ -262,14 +240,14 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void { |
| 262 | var record = unwind_records[record_id]; | 240 | var record = unwind_records[record_id]; |
| 263 | 241 | ||
| 264 | if (UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) { | 242 | if (UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) { |
| 265 | try info.collectPersonalityFromDwarf(zld, @as(u32, @intCast(object_id)), symbol, &record); | 243 | try info.collectPersonalityFromDwarf(macho_file, @as(u32, @intCast(object_id)), symbol, &record); |
| 266 | } else { | 244 | } else { |
| 267 | if (getPersonalityFunctionReloc( | 245 | if (getPersonalityFunctionReloc( |
| 268 | zld, | 246 | macho_file, |
| 269 | @as(u32, @intCast(object_id)), | 247 | @as(u32, @intCast(object_id)), |
| 270 | record_id, | 248 | record_id, |
| 271 | )) |rel| { | 249 | )) |rel| { |
| 272 | const target = Atom.parseRelocTarget(zld, .{ | 250 | const target = Atom.parseRelocTarget(macho_file, .{ |
| 273 | .object_id = @as(u32, @intCast(object_id)), | 251 | .object_id = @as(u32, @intCast(object_id)), |
| 274 | .rel = rel, | 252 | .rel = rel, |
| 275 | .code = mem.asBytes(&record), | 253 | .code = mem.asBytes(&record), |
| ... | @@ -286,8 +264,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void { | ... | @@ -286,8 +264,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void { |
| 286 | UnwindEncoding.setPersonalityIndex(&record.compactUnwindEncoding, personality_index + 1); | 264 | UnwindEncoding.setPersonalityIndex(&record.compactUnwindEncoding, personality_index + 1); |
| 287 | } | 265 | } |
| 288 | 266 | ||
| 289 | if (getLsdaReloc(zld, @as(u32, @intCast(object_id)), record_id)) |rel| { | 267 | if (getLsdaReloc(macho_file, @as(u32, @intCast(object_id)), record_id)) |rel| { |
| 290 | const target = Atom.parseRelocTarget(zld, .{ | 268 | const target = Atom.parseRelocTarget(macho_file, .{ |
| 291 | .object_id = @as(u32, @intCast(object_id)), | 269 | .object_id = @as(u32, @intCast(object_id)), |
| 292 | .rel = rel, | 270 | .rel = rel, |
| 293 | .code = mem.asBytes(&record), | 271 | .code = mem.asBytes(&record), |
| ... | @@ -298,7 +276,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void { | ... | @@ -298,7 +276,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void { |
| 298 | } | 276 | } |
| 299 | break :blk record; | 277 | break :blk record; |
| 300 | } else blk: { | 278 | } else blk: { |
| 301 | const sym = zld.getSymbol(symbol); | 279 | const sym = macho_file.getSymbol(symbol); |
| 302 | if (sym.n_desc == MachO.N_DEAD) continue; | 280 | if (sym.n_desc == MachO.N_DEAD) continue; |
| 303 | if (prev_symbol) |prev_sym| { | 281 | if (prev_symbol) |prev_sym| { |
| 304 | const prev_addr = object.getSourceSymbol(prev_sym.sym_index).?.n_value; | 282 | const prev_addr = object.getSourceSymbol(prev_sym.sym_index).?.n_value; |
| ... | @@ -310,7 +288,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void { | ... | @@ -310,7 +288,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void { |
| 310 | if (object.eh_frame_records_lookup.get(symbol)) |fde_offset| { | 288 | if (object.eh_frame_records_lookup.get(symbol)) |fde_offset| { |
| 311 | if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue; | 289 | if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue; |
| 312 | var record = nullRecord(); | 290 | var record = nullRecord(); |
| 313 | try info.collectPersonalityFromDwarf(zld, @as(u32, @intCast(object_id)), symbol, &record); | 291 | try info.collectPersonalityFromDwarf(macho_file, @as(u32, @intCast(object_id)), symbol, &record); |
| 314 | switch (cpu_arch) { | 292 | switch (cpu_arch) { |
| 315 | .aarch64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_ARM64_MODE.DWARF), | 293 | .aarch64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_ARM64_MODE.DWARF), |
| 316 | .x86_64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_X86_64_MODE.DWARF), | 294 | .x86_64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_X86_64_MODE.DWARF), |
| ... | @@ -323,8 +301,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void { | ... | @@ -323,8 +301,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void { |
| 323 | break :blk nullRecord(); | 301 | break :blk nullRecord(); |
| 324 | }; | 302 | }; |
| 325 | 303 | ||
| 326 | const atom = zld.getAtom(atom_index); | 304 | const atom = macho_file.getAtom(atom_index); |
| 327 | const sym = zld.getSymbol(symbol); | 305 | const sym = macho_file.getSymbol(symbol); |
| 328 | assert(sym.n_desc != MachO.N_DEAD); | 306 | assert(sym.n_desc != MachO.N_DEAD); |
| 329 | const size = if (inner_syms_it.next()) |next_sym| blk: { | 307 | const size = if (inner_syms_it.next()) |next_sym| blk: { |
| 330 | // All this trouble to account for symbol aliases. | 308 | // All this trouble to account for symbol aliases. |
| ... | @@ -336,8 +314,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void { | ... | @@ -336,8 +314,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void { |
| 336 | const curr_addr = object.getSourceSymbol(symbol.sym_index).?.n_value; | 314 | const curr_addr = object.getSourceSymbol(symbol.sym_index).?.n_value; |
| 337 | const next_addr = object.getSourceSymbol(next_sym.sym_index).?.n_value; | 315 | const next_addr = object.getSourceSymbol(next_sym.sym_index).?.n_value; |
| 338 | if (next_addr > curr_addr) break :blk next_addr - curr_addr; | 316 | if (next_addr > curr_addr) break :blk next_addr - curr_addr; |
| 339 | break :blk zld.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value; | 317 | break :blk macho_file.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value; |
| 340 | } else zld.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value; | 318 | } else macho_file.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value; |
| 341 | record.rangeStart = sym.n_value; | 319 | record.rangeStart = sym.n_value; |
| 342 | record.rangeLength = @as(u32, @intCast(size)); | 320 | record.rangeLength = @as(u32, @intCast(size)); |
| 343 | 321 | ||
| ... | @@ -518,23 +496,23 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void { | ... | @@ -518,23 +496,23 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void { |
| 518 | 496 | ||
| 519 | fn collectPersonalityFromDwarf( | 497 | fn collectPersonalityFromDwarf( |
| 520 | info: *UnwindInfo, | 498 | info: *UnwindInfo, |
| 521 | zld: *Zld, | 499 | macho_file: *MachO, |
| 522 | object_id: u32, | 500 | object_id: u32, |
| 523 | sym_loc: SymbolWithLoc, | 501 | sym_loc: SymbolWithLoc, |
| 524 | record: *macho.compact_unwind_entry, | 502 | record: *macho.compact_unwind_entry, |
| 525 | ) !void { | 503 | ) !void { |
| 526 | const object = &zld.objects.items[object_id]; | 504 | const object = &macho_file.objects.items[object_id]; |
| 527 | var it = object.getEhFrameRecordsIterator(); | 505 | var it = object.getEhFrameRecordsIterator(); |
| 528 | const fde_offset = object.eh_frame_records_lookup.get(sym_loc).?; | 506 | const fde_offset = object.eh_frame_records_lookup.get(sym_loc).?; |
| 529 | it.seekTo(fde_offset); | 507 | it.seekTo(fde_offset); |
| 530 | const fde = (try it.next()).?; | 508 | const fde = (try it.next()).?; |
| 531 | const cie_ptr = fde.getCiePointerSource(object_id, zld, fde_offset); | 509 | const cie_ptr = fde.getCiePointerSource(object_id, macho_file, fde_offset); |
| 532 | const cie_offset = fde_offset + 4 - cie_ptr; | 510 | const cie_offset = fde_offset + 4 - cie_ptr; |
| 533 | it.seekTo(cie_offset); | 511 | it.seekTo(cie_offset); |
| 534 | const cie = (try it.next()).?; | 512 | const cie = (try it.next()).?; |
| 535 | 513 | ||
| 536 | if (cie.getPersonalityPointerReloc( | 514 | if (cie.getPersonalityPointerReloc( |
| 537 | zld, | 515 | macho_file, |
| 538 | @as(u32, @intCast(object_id)), | 516 | @as(u32, @intCast(object_id)), |
| 539 | cie_offset, | 517 | cie_offset, |
| 540 | )) |target| { | 518 | )) |target| { |
| ... | @@ -550,9 +528,9 @@ fn collectPersonalityFromDwarf( | ... | @@ -550,9 +528,9 @@ fn collectPersonalityFromDwarf( |
| 550 | } | 528 | } |
| 551 | } | 529 | } |
| 552 | 530 | ||
| 553 | pub fn calcSectionSize(info: UnwindInfo, zld: *Zld) !void { | 531 | pub fn calcSectionSize(info: UnwindInfo, macho_file: *MachO) !void { |
| 554 | const sect_id = zld.unwind_info_section_index orelse return; | 532 | const sect_id = macho_file.unwind_info_section_index orelse return; |
| 555 | const sect = &zld.sections.items(.header)[sect_id]; | 533 | const sect = &macho_file.sections.items(.header)[sect_id]; |
| 556 | sect.@"align" = 2; | 534 | sect.@"align" = 2; |
| 557 | sect.size = info.calcRequiredSize(); | 535 | sect.size = info.calcRequiredSize(); |
| 558 | } | 536 | } |
| ... | @@ -569,23 +547,23 @@ fn calcRequiredSize(info: UnwindInfo) usize { | ... | @@ -569,23 +547,23 @@ fn calcRequiredSize(info: UnwindInfo) usize { |
| 569 | return total_size; | 547 | return total_size; |
| 570 | } | 548 | } |
| 571 | 549 | ||
| 572 | pub fn write(info: *UnwindInfo, zld: *Zld) !void { | 550 | pub fn write(info: *UnwindInfo, macho_file: *MachO) !void { |
| 573 | const sect_id = zld.unwind_info_section_index orelse return; | 551 | const sect_id = macho_file.unwind_info_section_index orelse return; |
| 574 | const sect = &zld.sections.items(.header)[sect_id]; | 552 | const sect = &macho_file.sections.items(.header)[sect_id]; |
| 575 | const seg_id = zld.sections.items(.segment_index)[sect_id]; | 553 | const seg_id = macho_file.sections.items(.segment_index)[sect_id]; |
| 576 | const seg = zld.segments.items[seg_id]; | 554 | const seg = macho_file.segments.items[seg_id]; |
| 577 | 555 | ||
| 578 | const text_sect_id = zld.text_section_index.?; | 556 | const text_sect_id = macho_file.text_section_index.?; |
| 579 | const text_sect = zld.sections.items(.header)[text_sect_id]; | 557 | const text_sect = macho_file.sections.items(.header)[text_sect_id]; |
| 580 | 558 | ||
| 581 | var personalities: [max_personalities]u32 = undefined; | 559 | var personalities: [max_personalities]u32 = undefined; |
| 582 | const cpu_arch = zld.options.target.cpu.arch; | 560 | const cpu_arch = macho_file.base.options.target.cpu.arch; |
| 583 | 561 | ||
| 584 | log.debug("Personalities:", .{}); | 562 | log.debug("Personalities:", .{}); |
| 585 | for (info.personalities[0..info.personalities_count], 0..) |target, i| { | 563 | for (info.personalities[0..info.personalities_count], 0..) |target, i| { |
| 586 | const addr = zld.getGotEntryAddress(target).?; | 564 | const addr = macho_file.getGotEntryAddress(target).?; |
| 587 | personalities[i] = @as(u32, @intCast(addr - seg.vmaddr)); | 565 | personalities[i] = @as(u32, @intCast(addr - seg.vmaddr)); |
| 588 | log.debug(" {d}: 0x{x} ({s})", .{ i, personalities[i], zld.getSymbolName(target) }); | 566 | log.debug(" {d}: 0x{x} ({s})", .{ i, personalities[i], macho_file.getSymbolName(target) }); |
| 589 | } | 567 | } |
| 590 | 568 | ||
| 591 | for (info.records.items) |*rec| { | 569 | for (info.records.items) |*rec| { |
| ... | @@ -599,7 +577,7 @@ pub fn write(info: *UnwindInfo, zld: *Zld) !void { | ... | @@ -599,7 +577,7 @@ pub fn write(info: *UnwindInfo, zld: *Zld) !void { |
| 599 | if (rec.compactUnwindEncoding > 0 and !UnwindEncoding.isDwarf(rec.compactUnwindEncoding, cpu_arch)) { | 577 | if (rec.compactUnwindEncoding > 0 and !UnwindEncoding.isDwarf(rec.compactUnwindEncoding, cpu_arch)) { |
| 600 | const lsda_target = @as(SymbolWithLoc, @bitCast(rec.lsda)); | 578 | const lsda_target = @as(SymbolWithLoc, @bitCast(rec.lsda)); |
| 601 | if (lsda_target.getFile()) |_| { | 579 | if (lsda_target.getFile()) |_| { |
| 602 | const sym = zld.getSymbol(lsda_target); | 580 | const sym = macho_file.getSymbol(lsda_target); |
| 603 | rec.lsda = sym.n_value - seg.vmaddr; | 581 | rec.lsda = sym.n_value - seg.vmaddr; |
| 604 | } | 582 | } |
| 605 | } | 583 | } |
| ... | @@ -689,11 +667,11 @@ pub fn write(info: *UnwindInfo, zld: *Zld) !void { | ... | @@ -689,11 +667,11 @@ pub fn write(info: *UnwindInfo, zld: *Zld) !void { |
| 689 | @memset(buffer.items[offset..], 0); | 667 | @memset(buffer.items[offset..], 0); |
| 690 | } | 668 | } |
| 691 | 669 | ||
| 692 | try zld.file.pwriteAll(buffer.items, sect.offset); | 670 | try macho_file.base.file.?.pwriteAll(buffer.items, sect.offset); |
| 693 | } | 671 | } |
| 694 | 672 | ||
| 695 | fn getRelocs(zld: *Zld, object_id: u32, record_id: usize) []const macho.relocation_info { | 673 | fn getRelocs(macho_file: *MachO, object_id: u32, record_id: usize) []const macho.relocation_info { |
| 696 | const object = &zld.objects.items[object_id]; | 674 | const object = &macho_file.objects.items[object_id]; |
| 697 | assert(object.hasUnwindRecords()); | 675 | assert(object.hasUnwindRecords()); |
| 698 | const rel_pos = object.unwind_relocs_lookup[record_id].reloc; | 676 | const rel_pos = object.unwind_relocs_lookup[record_id].reloc; |
| 699 | const relocs = object.getRelocs(object.unwind_info_sect_id.?); | 677 | const relocs = object.getRelocs(object.unwind_info_sect_id.?); |
| ... | @@ -707,11 +685,11 @@ fn isPersonalityFunction(record_id: usize, rel: macho.relocation_info) bool { | ... | @@ -707,11 +685,11 @@ fn isPersonalityFunction(record_id: usize, rel: macho.relocation_info) bool { |
| 707 | } | 685 | } |
| 708 | 686 | ||
| 709 | pub fn getPersonalityFunctionReloc( | 687 | pub fn getPersonalityFunctionReloc( |
| 710 | zld: *Zld, | 688 | macho_file: *MachO, |
| 711 | object_id: u32, | 689 | object_id: u32, |
| 712 | record_id: usize, | 690 | record_id: usize, |
| 713 | ) ?macho.relocation_info { | 691 | ) ?macho.relocation_info { |
| 714 | const relocs = getRelocs(zld, object_id, record_id); | 692 | const relocs = getRelocs(macho_file, object_id, record_id); |
| 715 | for (relocs) |rel| { | 693 | for (relocs) |rel| { |
| 716 | if (isPersonalityFunction(record_id, rel)) return rel; | 694 | if (isPersonalityFunction(record_id, rel)) return rel; |
| 717 | } | 695 | } |
| ... | @@ -735,8 +713,8 @@ fn isLsda(record_id: usize, rel: macho.relocation_info) bool { | ... | @@ -735,8 +713,8 @@ fn isLsda(record_id: usize, rel: macho.relocation_info) bool { |
| 735 | return rel_offset == 24; | 713 | return rel_offset == 24; |
| 736 | } | 714 | } |
| 737 | 715 | ||
| 738 | pub fn getLsdaReloc(zld: *Zld, object_id: u32, record_id: usize) ?macho.relocation_info { | 716 | pub fn getLsdaReloc(macho_file: *MachO, object_id: u32, record_id: usize) ?macho.relocation_info { |
| 739 | const relocs = getRelocs(zld, object_id, record_id); | 717 | const relocs = getRelocs(macho_file, object_id, record_id); |
| 740 | for (relocs) |rel| { | 718 | for (relocs) |rel| { |
| 741 | if (isLsda(record_id, rel)) return rel; | 719 | if (isLsda(record_id, rel)) return rel; |
| 742 | } | 720 | } |
| ... | @@ -828,3 +806,23 @@ pub const UnwindEncoding = struct { | ... | @@ -828,3 +806,23 @@ pub const UnwindEncoding = struct { |
| 828 | enc.* |= offset; | 806 | enc.* |= offset; |
| 829 | } | 807 | } |
| 830 | }; | 808 | }; |
| 809 | |||
| 810 | const UnwindInfo = @This(); | ||
| 811 | |||
| 812 | const std = @import("std"); | ||
| 813 | const assert = std.debug.assert; | ||
| 814 | const eh_frame = @import("eh_frame.zig"); | ||
| 815 | const fs = std.fs; | ||
| 816 | const leb = std.leb; | ||
| 817 | const log = std.log.scoped(.unwind_info); | ||
| 818 | const macho = std.macho; | ||
| 819 | const math = std.math; | ||
| 820 | const mem = std.mem; | ||
| 821 | const trace = @import("../../tracy.zig").trace; | ||
| 822 | |||
| 823 | const Allocator = mem.Allocator; | ||
| 824 | const Atom = @import("Atom.zig"); | ||
| 825 | const EhFrameRecord = eh_frame.EhFrameRecord; | ||
| 826 | const MachO = @import("../MachO.zig"); | ||
| 827 | const Object = @import("Object.zig"); | ||
| 828 | const SymbolWithLoc = MachO.SymbolWithLoc; |
src/link/MachO/dead_strip.zig+125-126| ... | @@ -1,89 +1,72 @@ | ... | @@ -1,89 +1,72 @@ |
| 1 | //! An algorithm for dead stripping of unreferenced Atoms. | 1 | //! An algorithm for dead stripping of unreferenced Atoms. |
| 2 | 2 | ||
| 3 | const std = @import("std"); | 3 | pub fn gcAtoms(macho_file: *MachO) !void { |
| 4 | const assert = std.debug.assert; | 4 | const gpa = macho_file.base.allocator; |
| 5 | const eh_frame = @import("eh_frame.zig"); | ||
| 6 | const log = std.log.scoped(.dead_strip); | ||
| 7 | const macho = std.macho; | ||
| 8 | const math = std.math; | ||
| 9 | const mem = std.mem; | ||
| 10 | |||
| 11 | const Allocator = mem.Allocator; | ||
| 12 | const Atom = @import("Atom.zig"); | ||
| 13 | const MachO = @import("../MachO.zig"); | ||
| 14 | const SymbolWithLoc = MachO.SymbolWithLoc; | ||
| 15 | const UnwindInfo = @import("UnwindInfo.zig"); | ||
| 16 | const Zld = @import("zld.zig").Zld; | ||
| 17 | |||
| 18 | const AtomTable = std.AutoHashMap(Atom.Index, void); | ||
| 19 | |||
| 20 | pub fn gcAtoms(zld: *Zld) !void { | ||
| 21 | const gpa = zld.gpa; | ||
| 22 | 5 | ||
| 23 | var arena = std.heap.ArenaAllocator.init(gpa); | 6 | var arena = std.heap.ArenaAllocator.init(gpa); |
| 24 | defer arena.deinit(); | 7 | defer arena.deinit(); |
| 25 | 8 | ||
| 26 | var roots = AtomTable.init(arena.allocator()); | 9 | var roots = AtomTable.init(arena.allocator()); |
| 27 | try roots.ensureUnusedCapacity(@as(u32, @intCast(zld.globals.items.len))); | 10 | try roots.ensureUnusedCapacity(@as(u32, @intCast(macho_file.globals.items.len))); |
| 28 | 11 | ||
| 29 | var alive = AtomTable.init(arena.allocator()); | 12 | var alive = AtomTable.init(arena.allocator()); |
| 30 | try alive.ensureTotalCapacity(@as(u32, @intCast(zld.atoms.items.len))); | 13 | try alive.ensureTotalCapacity(@as(u32, @intCast(macho_file.atoms.items.len))); |
| 31 | 14 | ||
| 32 | try collectRoots(zld, &roots); | 15 | try collectRoots(macho_file, &roots); |
| 33 | try mark(zld, roots, &alive); | 16 | try mark(macho_file, roots, &alive); |
| 34 | prune(zld, alive); | 17 | prune(macho_file, alive); |
| 35 | } | 18 | } |
| 36 | 19 | ||
| 37 | fn addRoot(zld: *Zld, roots: *AtomTable, file: u32, sym_loc: SymbolWithLoc) !void { | 20 | fn addRoot(macho_file: *MachO, roots: *AtomTable, file: u32, sym_loc: SymbolWithLoc) !void { |
| 38 | const sym = zld.getSymbol(sym_loc); | 21 | const sym = macho_file.getSymbol(sym_loc); |
| 39 | assert(!sym.undf()); | 22 | assert(!sym.undf()); |
| 40 | const object = &zld.objects.items[file]; | 23 | const object = &macho_file.objects.items[file]; |
| 41 | const atom_index = object.getAtomIndexForSymbol(sym_loc.sym_index).?; // panic here means fatal error | 24 | const atom_index = object.getAtomIndexForSymbol(sym_loc.sym_index).?; // panic here means fatal error |
| 42 | log.debug("root(ATOM({d}, %{d}, {d}))", .{ | 25 | log.debug("root(ATOM({d}, %{d}, {d}))", .{ |
| 43 | atom_index, | 26 | atom_index, |
| 44 | zld.getAtom(atom_index).sym_index, | 27 | macho_file.getAtom(atom_index).sym_index, |
| 45 | file, | 28 | file, |
| 46 | }); | 29 | }); |
| 47 | _ = try roots.getOrPut(atom_index); | 30 | _ = try roots.getOrPut(atom_index); |
| 48 | } | 31 | } |
| 49 | 32 | ||
| 50 | fn collectRoots(zld: *Zld, roots: *AtomTable) !void { | 33 | fn collectRoots(macho_file: *MachO, roots: *AtomTable) !void { |
| 51 | log.debug("collecting roots", .{}); | 34 | log.debug("collecting roots", .{}); |
| 52 | 35 | ||
| 53 | switch (zld.options.output_mode) { | 36 | switch (macho_file.base.options.output_mode) { |
| 54 | .Exe => { | 37 | .Exe => { |
| 55 | // Add entrypoint as GC root | 38 | // Add entrypoint as GC root |
| 56 | const global: SymbolWithLoc = zld.getEntryPoint(); | 39 | const global: SymbolWithLoc = macho_file.getEntryPoint(); |
| 57 | if (global.getFile()) |file| { | 40 | if (global.getFile()) |file| { |
| 58 | try addRoot(zld, roots, file, global); | 41 | try addRoot(macho_file, roots, file, global); |
| 59 | } else { | 42 | } else { |
| 60 | assert(zld.getSymbol(global).undf()); // Stub as our entrypoint is in a dylib. | 43 | assert(macho_file.getSymbol(global).undf()); // Stub as our entrypoint is in a dylib. |
| 61 | } | 44 | } |
| 62 | }, | 45 | }, |
| 63 | else => |other| { | 46 | else => |other| { |
| 64 | assert(other == .Lib); | 47 | assert(other == .Lib); |
| 65 | // Add exports as GC roots | 48 | // Add exports as GC roots |
| 66 | for (zld.globals.items) |global| { | 49 | for (macho_file.globals.items) |global| { |
| 67 | const sym = zld.getSymbol(global); | 50 | const sym = macho_file.getSymbol(global); |
| 68 | if (sym.undf()) continue; | 51 | if (sym.undf()) continue; |
| 69 | 52 | ||
| 70 | if (global.getFile()) |file| { | 53 | if (global.getFile()) |file| { |
| 71 | try addRoot(zld, roots, file, global); | 54 | try addRoot(macho_file, roots, file, global); |
| 72 | } | 55 | } |
| 73 | } | 56 | } |
| 74 | }, | 57 | }, |
| 75 | } | 58 | } |
| 76 | 59 | ||
| 77 | // Add all symbols force-defined by the user. | 60 | // Add all symbols force-defined by the user. |
| 78 | for (zld.options.force_undefined_symbols.keys()) |sym_name| { | 61 | for (macho_file.base.options.force_undefined_symbols.keys()) |sym_name| { |
| 79 | const global_index = zld.resolver.get(sym_name).?; | 62 | const global_index = macho_file.resolver.get(sym_name).?; |
| 80 | const global = zld.globals.items[global_index]; | 63 | const global = macho_file.globals.items[global_index]; |
| 81 | const sym = zld.getSymbol(global); | 64 | const sym = macho_file.getSymbol(global); |
| 82 | assert(!sym.undf()); | 65 | assert(!sym.undf()); |
| 83 | try addRoot(zld, roots, global.getFile().?, global); | 66 | try addRoot(macho_file, roots, global.getFile().?, global); |
| 84 | } | 67 | } |
| 85 | 68 | ||
| 86 | for (zld.objects.items) |object| { | 69 | for (macho_file.objects.items) |object| { |
| 87 | const has_subsections = object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0; | 70 | const has_subsections = object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0; |
| 88 | 71 | ||
| 89 | for (object.atoms.items) |atom_index| { | 72 | for (object.atoms.items) |atom_index| { |
| ... | @@ -92,7 +75,7 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void { | ... | @@ -92,7 +75,7 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void { |
| 92 | // as a root. | 75 | // as a root. |
| 93 | if (!has_subsections) break :blk true; | 76 | if (!has_subsections) break :blk true; |
| 94 | 77 | ||
| 95 | const atom = zld.getAtom(atom_index); | 78 | const atom = macho_file.getAtom(atom_index); |
| 96 | const sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym| | 79 | const sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym| |
| 97 | source_sym.n_sect - 1 | 80 | source_sym.n_sect - 1 |
| 98 | else sect_id: { | 81 | else sect_id: { |
| ... | @@ -115,39 +98,39 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void { | ... | @@ -115,39 +98,39 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void { |
| 115 | 98 | ||
| 116 | log.debug("root(ATOM({d}, %{d}, {?d}))", .{ | 99 | log.debug("root(ATOM({d}, %{d}, {?d}))", .{ |
| 117 | atom_index, | 100 | atom_index, |
| 118 | zld.getAtom(atom_index).sym_index, | 101 | macho_file.getAtom(atom_index).sym_index, |
| 119 | zld.getAtom(atom_index).getFile(), | 102 | macho_file.getAtom(atom_index).getFile(), |
| 120 | }); | 103 | }); |
| 121 | } | 104 | } |
| 122 | } | 105 | } |
| 123 | } | 106 | } |
| 124 | } | 107 | } |
| 125 | 108 | ||
| 126 | fn markLive(zld: *Zld, atom_index: Atom.Index, alive: *AtomTable) void { | 109 | fn markLive(macho_file: *MachO, atom_index: Atom.Index, alive: *AtomTable) void { |
| 127 | if (alive.contains(atom_index)) return; | 110 | if (alive.contains(atom_index)) return; |
| 128 | 111 | ||
| 129 | const atom = zld.getAtom(atom_index); | 112 | const atom = macho_file.getAtom(atom_index); |
| 130 | const sym_loc = atom.getSymbolWithLoc(); | 113 | const sym_loc = atom.getSymbolWithLoc(); |
| 131 | 114 | ||
| 132 | log.debug("mark(ATOM({d}, %{d}, {?d}))", .{ atom_index, sym_loc.sym_index, sym_loc.getFile() }); | 115 | log.debug("mark(ATOM({d}, %{d}, {?d}))", .{ atom_index, sym_loc.sym_index, sym_loc.getFile() }); |
| 133 | 116 | ||
| 134 | alive.putAssumeCapacityNoClobber(atom_index, {}); | 117 | alive.putAssumeCapacityNoClobber(atom_index, {}); |
| 135 | 118 | ||
| 136 | const cpu_arch = zld.options.target.cpu.arch; | 119 | const cpu_arch = macho_file.options.target.cpu.arch; |
| 137 | 120 | ||
| 138 | const sym = zld.getSymbol(atom.getSymbolWithLoc()); | 121 | const sym = macho_file.getSymbol(atom.getSymbolWithLoc()); |
| 139 | const header = zld.sections.items(.header)[sym.n_sect - 1]; | 122 | const header = macho_file.sections.items(.header)[sym.n_sect - 1]; |
| 140 | if (header.isZerofill()) return; | 123 | if (header.isZerofill()) return; |
| 141 | 124 | ||
| 142 | const code = Atom.getAtomCode(zld, atom_index); | 125 | const code = Atom.getAtomCode(macho_file, atom_index); |
| 143 | const relocs = Atom.getAtomRelocs(zld, atom_index); | 126 | const relocs = Atom.getAtomRelocs(macho_file, atom_index); |
| 144 | const ctx = Atom.getRelocContext(zld, atom_index); | 127 | const ctx = Atom.getRelocContext(macho_file, atom_index); |
| 145 | 128 | ||
| 146 | for (relocs) |rel| { | 129 | for (relocs) |rel| { |
| 147 | const target = switch (cpu_arch) { | 130 | const target = switch (cpu_arch) { |
| 148 | .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) { | 131 | .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) { |
| 149 | .ARM64_RELOC_ADDEND => continue, | 132 | .ARM64_RELOC_ADDEND => continue, |
| 150 | else => Atom.parseRelocTarget(zld, .{ | 133 | else => Atom.parseRelocTarget(macho_file, .{ |
| 151 | .object_id = atom.getFile().?, | 134 | .object_id = atom.getFile().?, |
| 152 | .rel = rel, | 135 | .rel = rel, |
| 153 | .code = code, | 136 | .code = code, |
| ... | @@ -155,7 +138,7 @@ fn markLive(zld: *Zld, atom_index: Atom.Index, alive: *AtomTable) void { | ... | @@ -155,7 +138,7 @@ fn markLive(zld: *Zld, atom_index: Atom.Index, alive: *AtomTable) void { |
| 155 | .base_addr = ctx.base_addr, | 138 | .base_addr = ctx.base_addr, |
| 156 | }), | 139 | }), |
| 157 | }, | 140 | }, |
| 158 | .x86_64 => Atom.parseRelocTarget(zld, .{ | 141 | .x86_64 => Atom.parseRelocTarget(macho_file, .{ |
| 159 | .object_id = atom.getFile().?, | 142 | .object_id = atom.getFile().?, |
| 160 | .rel = rel, | 143 | .rel = rel, |
| 161 | .code = code, | 144 | .code = code, |
| ... | @@ -164,50 +147,50 @@ fn markLive(zld: *Zld, atom_index: Atom.Index, alive: *AtomTable) void { | ... | @@ -164,50 +147,50 @@ fn markLive(zld: *Zld, atom_index: Atom.Index, alive: *AtomTable) void { |
| 164 | }), | 147 | }), |
| 165 | else => unreachable, | 148 | else => unreachable, |
| 166 | }; | 149 | }; |
| 167 | const target_sym = zld.getSymbol(target); | 150 | const target_sym = macho_file.getSymbol(target); |
| 168 | 151 | ||
| 169 | if (target_sym.undf()) continue; | 152 | if (target_sym.undf()) continue; |
| 170 | if (target.getFile() == null) { | 153 | if (target.getFile() == null) { |
| 171 | const target_sym_name = zld.getSymbolName(target); | 154 | const target_sym_name = macho_file.getSymbolName(target); |
| 172 | if (mem.eql(u8, "__mh_execute_header", target_sym_name)) continue; | 155 | if (mem.eql(u8, "__mh_execute_header", target_sym_name)) continue; |
| 173 | if (mem.eql(u8, "___dso_handle", target_sym_name)) continue; | 156 | if (mem.eql(u8, "___dso_handle", target_sym_name)) continue; |
| 174 | 157 | ||
| 175 | unreachable; // referenced symbol not found | 158 | unreachable; // referenced symbol not found |
| 176 | } | 159 | } |
| 177 | 160 | ||
| 178 | const object = zld.objects.items[target.getFile().?]; | 161 | const object = macho_file.objects.items[target.getFile().?]; |
| 179 | const target_atom_index = object.getAtomIndexForSymbol(target.sym_index).?; | 162 | const target_atom_index = object.getAtomIndexForSymbol(target.sym_index).?; |
| 180 | log.debug(" following ATOM({d}, %{d}, {?d})", .{ | 163 | log.debug(" following ATOM({d}, %{d}, {?d})", .{ |
| 181 | target_atom_index, | 164 | target_atom_index, |
| 182 | zld.getAtom(target_atom_index).sym_index, | 165 | macho_file.getAtom(target_atom_index).sym_index, |
| 183 | zld.getAtom(target_atom_index).getFile(), | 166 | macho_file.getAtom(target_atom_index).getFile(), |
| 184 | }); | 167 | }); |
| 185 | 168 | ||
| 186 | markLive(zld, target_atom_index, alive); | 169 | markLive(macho_file, target_atom_index, alive); |
| 187 | } | 170 | } |
| 188 | } | 171 | } |
| 189 | 172 | ||
| 190 | fn refersLive(zld: *Zld, atom_index: Atom.Index, alive: AtomTable) bool { | 173 | fn refersLive(macho_file: *MachO, atom_index: Atom.Index, alive: AtomTable) bool { |
| 191 | const atom = zld.getAtom(atom_index); | 174 | const atom = macho_file.getAtom(atom_index); |
| 192 | const sym_loc = atom.getSymbolWithLoc(); | 175 | const sym_loc = atom.getSymbolWithLoc(); |
| 193 | 176 | ||
| 194 | log.debug("refersLive(ATOM({d}, %{d}, {?d}))", .{ atom_index, sym_loc.sym_index, sym_loc.getFile() }); | 177 | log.debug("refersLive(ATOM({d}, %{d}, {?d}))", .{ atom_index, sym_loc.sym_index, sym_loc.getFile() }); |
| 195 | 178 | ||
| 196 | const cpu_arch = zld.options.target.cpu.arch; | 179 | const cpu_arch = macho_file.base.options.target.cpu.arch; |
| 197 | 180 | ||
| 198 | const sym = zld.getSymbol(sym_loc); | 181 | const sym = macho_file.getSymbol(sym_loc); |
| 199 | const header = zld.sections.items(.header)[sym.n_sect - 1]; | 182 | const header = macho_file.sections.items(.header)[sym.n_sect - 1]; |
| 200 | assert(!header.isZerofill()); | 183 | assert(!header.isZerofill()); |
| 201 | 184 | ||
| 202 | const code = Atom.getAtomCode(zld, atom_index); | 185 | const code = Atom.getAtomCode(macho_file, atom_index); |
| 203 | const relocs = Atom.getAtomRelocs(zld, atom_index); | 186 | const relocs = Atom.getAtomRelocs(macho_file, atom_index); |
| 204 | const ctx = Atom.getRelocContext(zld, atom_index); | 187 | const ctx = Atom.getRelocContext(macho_file, atom_index); |
| 205 | 188 | ||
| 206 | for (relocs) |rel| { | 189 | for (relocs) |rel| { |
| 207 | const target = switch (cpu_arch) { | 190 | const target = switch (cpu_arch) { |
| 208 | .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) { | 191 | .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) { |
| 209 | .ARM64_RELOC_ADDEND => continue, | 192 | .ARM64_RELOC_ADDEND => continue, |
| 210 | else => Atom.parseRelocTarget(zld, .{ | 193 | else => Atom.parseRelocTarget(macho_file, .{ |
| 211 | .object_id = atom.getFile().?, | 194 | .object_id = atom.getFile().?, |
| 212 | .rel = rel, | 195 | .rel = rel, |
| 213 | .code = code, | 196 | .code = code, |
| ... | @@ -215,7 +198,7 @@ fn refersLive(zld: *Zld, atom_index: Atom.Index, alive: AtomTable) bool { | ... | @@ -215,7 +198,7 @@ fn refersLive(zld: *Zld, atom_index: Atom.Index, alive: AtomTable) bool { |
| 215 | .base_addr = ctx.base_addr, | 198 | .base_addr = ctx.base_addr, |
| 216 | }), | 199 | }), |
| 217 | }, | 200 | }, |
| 218 | .x86_64 => Atom.parseRelocTarget(zld, .{ | 201 | .x86_64 => Atom.parseRelocTarget(macho_file, .{ |
| 219 | .object_id = atom.getFile().?, | 202 | .object_id = atom.getFile().?, |
| 220 | .rel = rel, | 203 | .rel = rel, |
| 221 | .code = code, | 204 | .code = code, |
| ... | @@ -225,16 +208,16 @@ fn refersLive(zld: *Zld, atom_index: Atom.Index, alive: AtomTable) bool { | ... | @@ -225,16 +208,16 @@ fn refersLive(zld: *Zld, atom_index: Atom.Index, alive: AtomTable) bool { |
| 225 | else => unreachable, | 208 | else => unreachable, |
| 226 | }; | 209 | }; |
| 227 | 210 | ||
| 228 | const object = zld.objects.items[target.getFile().?]; | 211 | const object = macho_file.objects.items[target.getFile().?]; |
| 229 | const target_atom_index = object.getAtomIndexForSymbol(target.sym_index) orelse { | 212 | const target_atom_index = object.getAtomIndexForSymbol(target.sym_index) orelse { |
| 230 | log.debug("atom for symbol '{s}' not found; skipping...", .{zld.getSymbolName(target)}); | 213 | log.debug("atom for symbol '{s}' not found; skipping...", .{macho_file.getSymbolName(target)}); |
| 231 | continue; | 214 | continue; |
| 232 | }; | 215 | }; |
| 233 | if (alive.contains(target_atom_index)) { | 216 | if (alive.contains(target_atom_index)) { |
| 234 | log.debug(" refers live ATOM({d}, %{d}, {?d})", .{ | 217 | log.debug(" refers live ATOM({d}, %{d}, {?d})", .{ |
| 235 | target_atom_index, | 218 | target_atom_index, |
| 236 | zld.getAtom(target_atom_index).sym_index, | 219 | macho_file.getAtom(target_atom_index).sym_index, |
| 237 | zld.getAtom(target_atom_index).getFile(), | 220 | macho_file.getAtom(target_atom_index).getFile(), |
| 238 | }); | 221 | }); |
| 239 | return true; | 222 | return true; |
| 240 | } | 223 | } |
| ... | @@ -243,21 +226,21 @@ fn refersLive(zld: *Zld, atom_index: Atom.Index, alive: AtomTable) bool { | ... | @@ -243,21 +226,21 @@ fn refersLive(zld: *Zld, atom_index: Atom.Index, alive: AtomTable) bool { |
| 243 | return false; | 226 | return false; |
| 244 | } | 227 | } |
| 245 | 228 | ||
| 246 | fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void { | 229 | fn mark(macho_file: *MachO, roots: AtomTable, alive: *AtomTable) !void { |
| 247 | var it = roots.keyIterator(); | 230 | var it = roots.keyIterator(); |
| 248 | while (it.next()) |root| { | 231 | while (it.next()) |root| { |
| 249 | markLive(zld, root.*, alive); | 232 | markLive(macho_file, root.*, alive); |
| 250 | } | 233 | } |
| 251 | 234 | ||
| 252 | var loop: bool = true; | 235 | var loop: bool = true; |
| 253 | while (loop) { | 236 | while (loop) { |
| 254 | loop = false; | 237 | loop = false; |
| 255 | 238 | ||
| 256 | for (zld.objects.items) |object| { | 239 | for (macho_file.objects.items) |object| { |
| 257 | for (object.atoms.items) |atom_index| { | 240 | for (object.atoms.items) |atom_index| { |
| 258 | if (alive.contains(atom_index)) continue; | 241 | if (alive.contains(atom_index)) continue; |
| 259 | 242 | ||
| 260 | const atom = zld.getAtom(atom_index); | 243 | const atom = macho_file.getAtom(atom_index); |
| 261 | const sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym| | 244 | const sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym| |
| 262 | source_sym.n_sect - 1 | 245 | source_sym.n_sect - 1 |
| 263 | else blk: { | 246 | else blk: { |
| ... | @@ -268,8 +251,8 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void { | ... | @@ -268,8 +251,8 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void { |
| 268 | const source_sect = object.getSourceSection(sect_id); | 251 | const source_sect = object.getSourceSection(sect_id); |
| 269 | 252 | ||
| 270 | if (source_sect.isDontDeadStripIfReferencesLive()) { | 253 | if (source_sect.isDontDeadStripIfReferencesLive()) { |
| 271 | if (refersLive(zld, atom_index, alive.*)) { | 254 | if (refersLive(macho_file, atom_index, alive.*)) { |
| 272 | markLive(zld, atom_index, alive); | 255 | markLive(macho_file, atom_index, alive); |
| 273 | loop = true; | 256 | loop = true; |
| 274 | } | 257 | } |
| 275 | } | 258 | } |
| ... | @@ -277,26 +260,26 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void { | ... | @@ -277,26 +260,26 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void { |
| 277 | } | 260 | } |
| 278 | } | 261 | } |
| 279 | 262 | ||
| 280 | for (zld.objects.items, 0..) |_, object_id| { | 263 | for (macho_file.objects.items, 0..) |_, object_id| { |
| 281 | // Traverse unwind and eh_frame records noting if the source symbol has been marked, and if so, | 264 | // Traverse unwind and eh_frame records noting if the source symbol has been marked, and if so, |
| 282 | // marking all references as live. | 265 | // marking all references as live. |
| 283 | try markUnwindRecords(zld, @as(u32, @intCast(object_id)), alive); | 266 | try markUnwindRecords(macho_file, @as(u32, @intCast(object_id)), alive); |
| 284 | } | 267 | } |
| 285 | } | 268 | } |
| 286 | 269 | ||
| 287 | fn markUnwindRecords(zld: *Zld, object_id: u32, alive: *AtomTable) !void { | 270 | fn markUnwindRecords(macho_file: *MachO, object_id: u32, alive: *AtomTable) !void { |
| 288 | const object = &zld.objects.items[object_id]; | 271 | const object = &macho_file.objects.items[object_id]; |
| 289 | const cpu_arch = zld.options.target.cpu.arch; | 272 | const cpu_arch = macho_file.base.options.target.cpu.arch; |
| 290 | 273 | ||
| 291 | const unwind_records = object.getUnwindRecords(); | 274 | const unwind_records = object.getUnwindRecords(); |
| 292 | 275 | ||
| 293 | for (object.exec_atoms.items) |atom_index| { | 276 | for (object.exec_atoms.items) |atom_index| { |
| 294 | var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index); | 277 | var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index); |
| 295 | 278 | ||
| 296 | if (!object.hasUnwindRecords()) { | 279 | if (!object.hasUnwindRecords()) { |
| 297 | if (alive.contains(atom_index)) { | 280 | if (alive.contains(atom_index)) { |
| 298 | // Mark references live and continue. | 281 | // Mark references live and continue. |
| 299 | try markEhFrameRecords(zld, object_id, atom_index, alive); | 282 | try markEhFrameRecords(macho_file, object_id, atom_index, alive); |
| 300 | } else { | 283 | } else { |
| 301 | while (inner_syms_it.next()) |sym| { | 284 | while (inner_syms_it.next()) |sym| { |
| 302 | if (object.eh_frame_records_lookup.get(sym)) |fde_offset| { | 285 | if (object.eh_frame_records_lookup.get(sym)) |fde_offset| { |
| ... | @@ -322,51 +305,51 @@ fn markUnwindRecords(zld: *Zld, object_id: u32, alive: *AtomTable) !void { | ... | @@ -322,51 +305,51 @@ fn markUnwindRecords(zld: *Zld, object_id: u32, alive: *AtomTable) !void { |
| 322 | 305 | ||
| 323 | const record = unwind_records[record_id]; | 306 | const record = unwind_records[record_id]; |
| 324 | if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) { | 307 | if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) { |
| 325 | try markEhFrameRecords(zld, object_id, atom_index, alive); | 308 | try markEhFrameRecords(macho_file, object_id, atom_index, alive); |
| 326 | } else { | 309 | } else { |
| 327 | if (UnwindInfo.getPersonalityFunctionReloc(zld, object_id, record_id)) |rel| { | 310 | if (UnwindInfo.getPersonalityFunctionReloc(macho_file, object_id, record_id)) |rel| { |
| 328 | const target = Atom.parseRelocTarget(zld, .{ | 311 | const target = Atom.parseRelocTarget(macho_file, .{ |
| 329 | .object_id = object_id, | 312 | .object_id = object_id, |
| 330 | .rel = rel, | 313 | .rel = rel, |
| 331 | .code = mem.asBytes(&record), | 314 | .code = mem.asBytes(&record), |
| 332 | .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))), | 315 | .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))), |
| 333 | }); | 316 | }); |
| 334 | const target_sym = zld.getSymbol(target); | 317 | const target_sym = macho_file.getSymbol(target); |
| 335 | if (!target_sym.undf()) { | 318 | if (!target_sym.undf()) { |
| 336 | const target_object = zld.objects.items[target.getFile().?]; | 319 | const target_object = macho_file.objects.items[target.getFile().?]; |
| 337 | const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?; | 320 | const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?; |
| 338 | markLive(zld, target_atom_index, alive); | 321 | markLive(macho_file, target_atom_index, alive); |
| 339 | } | 322 | } |
| 340 | } | 323 | } |
| 341 | 324 | ||
| 342 | if (UnwindInfo.getLsdaReloc(zld, object_id, record_id)) |rel| { | 325 | if (UnwindInfo.getLsdaReloc(macho_file, object_id, record_id)) |rel| { |
| 343 | const target = Atom.parseRelocTarget(zld, .{ | 326 | const target = Atom.parseRelocTarget(macho_file, .{ |
| 344 | .object_id = object_id, | 327 | .object_id = object_id, |
| 345 | .rel = rel, | 328 | .rel = rel, |
| 346 | .code = mem.asBytes(&record), | 329 | .code = mem.asBytes(&record), |
| 347 | .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))), | 330 | .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))), |
| 348 | }); | 331 | }); |
| 349 | const target_object = zld.objects.items[target.getFile().?]; | 332 | const target_object = macho_file.objects.items[target.getFile().?]; |
| 350 | const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?; | 333 | const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?; |
| 351 | markLive(zld, target_atom_index, alive); | 334 | markLive(macho_file, target_atom_index, alive); |
| 352 | } | 335 | } |
| 353 | } | 336 | } |
| 354 | } | 337 | } |
| 355 | } | 338 | } |
| 356 | } | 339 | } |
| 357 | 340 | ||
| 358 | fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: Atom.Index, alive: *AtomTable) !void { | 341 | fn markEhFrameRecords(macho_file: *MachO, object_id: u32, atom_index: Atom.Index, alive: *AtomTable) !void { |
| 359 | const cpu_arch = zld.options.target.cpu.arch; | 342 | const cpu_arch = macho_file.base.options.target.cpu.arch; |
| 360 | const object = &zld.objects.items[object_id]; | 343 | const object = &macho_file.objects.items[object_id]; |
| 361 | var it = object.getEhFrameRecordsIterator(); | 344 | var it = object.getEhFrameRecordsIterator(); |
| 362 | var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index); | 345 | var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index); |
| 363 | 346 | ||
| 364 | while (inner_syms_it.next()) |sym| { | 347 | while (inner_syms_it.next()) |sym| { |
| 365 | const fde_offset = object.eh_frame_records_lookup.get(sym) orelse continue; // Continue in case we hit a temp symbol alias | 348 | const fde_offset = object.eh_frame_records_lookup.get(sym) orelse continue; // Continue in case we hit a temp symbol alias |
| 366 | it.seekTo(fde_offset); | 349 | it.seekTo(fde_offset); |
| 367 | const fde = (try it.next()).?; | 350 | const fde = (try it.next()).?; |
| 368 | 351 | ||
| 369 | const cie_ptr = fde.getCiePointerSource(object_id, zld, fde_offset); | 352 | const cie_ptr = fde.getCiePointerSource(object_id, macho_file, fde_offset); |
| 370 | const cie_offset = fde_offset + 4 - cie_ptr; | 353 | const cie_offset = fde_offset + 4 - cie_ptr; |
| 371 | it.seekTo(cie_offset); | 354 | it.seekTo(cie_offset); |
| 372 | const cie = (try it.next()).?; | 355 | const cie = (try it.next()).?; |
| ... | @@ -374,20 +357,20 @@ fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: Atom.Index, alive: | ... | @@ -374,20 +357,20 @@ fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: Atom.Index, alive: |
| 374 | switch (cpu_arch) { | 357 | switch (cpu_arch) { |
| 375 | .aarch64 => { | 358 | .aarch64 => { |
| 376 | // Mark FDE references which should include any referenced LSDA record | 359 | // Mark FDE references which should include any referenced LSDA record |
| 377 | const relocs = eh_frame.getRelocs(zld, object_id, fde_offset); | 360 | const relocs = eh_frame.getRelocs(macho_file, object_id, fde_offset); |
| 378 | for (relocs) |rel| { | 361 | for (relocs) |rel| { |
| 379 | const target = Atom.parseRelocTarget(zld, .{ | 362 | const target = Atom.parseRelocTarget(macho_file, .{ |
| 380 | .object_id = object_id, | 363 | .object_id = object_id, |
| 381 | .rel = rel, | 364 | .rel = rel, |
| 382 | .code = fde.data, | 365 | .code = fde.data, |
| 383 | .base_offset = @as(i32, @intCast(fde_offset)) + 4, | 366 | .base_offset = @as(i32, @intCast(fde_offset)) + 4, |
| 384 | }); | 367 | }); |
| 385 | const target_sym = zld.getSymbol(target); | 368 | const target_sym = macho_file.getSymbol(target); |
| 386 | if (!target_sym.undf()) blk: { | 369 | if (!target_sym.undf()) blk: { |
| 387 | const target_object = zld.objects.items[target.getFile().?]; | 370 | const target_object = macho_file.objects.items[target.getFile().?]; |
| 388 | const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index) orelse | 371 | const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index) orelse |
| 389 | break :blk; | 372 | break :blk; |
| 390 | markLive(zld, target_atom_index, alive); | 373 | markLive(macho_file, target_atom_index, alive); |
| 391 | } | 374 | } |
| 392 | } | 375 | } |
| 393 | }, | 376 | }, |
| ... | @@ -401,7 +384,7 @@ fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: Atom.Index, alive: | ... | @@ -401,7 +384,7 @@ fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: Atom.Index, alive: |
| 401 | // Mark LSDA record as live | 384 | // Mark LSDA record as live |
| 402 | const sym_index = object.getSymbolByAddress(lsda_address, null); | 385 | const sym_index = object.getSymbolByAddress(lsda_address, null); |
| 403 | const target_atom_index = object.getAtomIndexForSymbol(sym_index).?; | 386 | const target_atom_index = object.getAtomIndexForSymbol(sym_index).?; |
| 404 | markLive(zld, target_atom_index, alive); | 387 | markLive(macho_file, target_atom_index, alive); |
| 405 | } | 388 | } |
| 406 | }, | 389 | }, |
| 407 | else => unreachable, | 390 | else => unreachable, |
| ... | @@ -409,20 +392,20 @@ fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: Atom.Index, alive: | ... | @@ -409,20 +392,20 @@ fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: Atom.Index, alive: |
| 409 | 392 | ||
| 410 | // Mark CIE references which should include any referenced personalities | 393 | // Mark CIE references which should include any referenced personalities |
| 411 | // that are defined locally. | 394 | // that are defined locally. |
| 412 | if (cie.getPersonalityPointerReloc(zld, object_id, cie_offset)) |target| { | 395 | if (cie.getPersonalityPointerReloc(macho_file, object_id, cie_offset)) |target| { |
| 413 | const target_sym = zld.getSymbol(target); | 396 | const target_sym = macho_file.getSymbol(target); |
| 414 | if (!target_sym.undf()) { | 397 | if (!target_sym.undf()) { |
| 415 | const target_object = zld.objects.items[target.getFile().?]; | 398 | const target_object = macho_file.objects.items[target.getFile().?]; |
| 416 | const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?; | 399 | const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?; |
| 417 | markLive(zld, target_atom_index, alive); | 400 | markLive(macho_file, target_atom_index, alive); |
| 418 | } | 401 | } |
| 419 | } | 402 | } |
| 420 | } | 403 | } |
| 421 | } | 404 | } |
| 422 | 405 | ||
| 423 | fn prune(zld: *Zld, alive: AtomTable) void { | 406 | fn prune(macho_file: *MachO, alive: AtomTable) void { |
| 424 | log.debug("pruning dead atoms", .{}); | 407 | log.debug("pruning dead atoms", .{}); |
| 425 | for (zld.objects.items) |*object| { | 408 | for (macho_file.objects.items) |*object| { |
| 426 | var i: usize = 0; | 409 | var i: usize = 0; |
| 427 | while (i < object.atoms.items.len) { | 410 | while (i < object.atoms.items.len) { |
| 428 | const atom_index = object.atoms.items[i]; | 411 | const atom_index = object.atoms.items[i]; |
| ... | @@ -431,7 +414,7 @@ fn prune(zld: *Zld, alive: AtomTable) void { | ... | @@ -431,7 +414,7 @@ fn prune(zld: *Zld, alive: AtomTable) void { |
| 431 | continue; | 414 | continue; |
| 432 | } | 415 | } |
| 433 | 416 | ||
| 434 | const atom = zld.getAtom(atom_index); | 417 | const atom = macho_file.getAtom(atom_index); |
| 435 | const sym_loc = atom.getSymbolWithLoc(); | 418 | const sym_loc = atom.getSymbolWithLoc(); |
| 436 | 419 | ||
| 437 | log.debug("prune(ATOM({d}, %{d}, {?d}))", .{ | 420 | log.debug("prune(ATOM({d}, %{d}, {?d}))", .{ |
| ... | @@ -439,15 +422,15 @@ fn prune(zld: *Zld, alive: AtomTable) void { | ... | @@ -439,15 +422,15 @@ fn prune(zld: *Zld, alive: AtomTable) void { |
| 439 | sym_loc.sym_index, | 422 | sym_loc.sym_index, |
| 440 | sym_loc.getFile(), | 423 | sym_loc.getFile(), |
| 441 | }); | 424 | }); |
| 442 | log.debug(" {s} in {s}", .{ zld.getSymbolName(sym_loc), object.name }); | 425 | log.debug(" {s} in {s}", .{ macho_file.getSymbolName(sym_loc), object.name }); |
| 443 | 426 | ||
| 444 | const sym = zld.getSymbolPtr(sym_loc); | 427 | const sym = macho_file.getSymbolPtr(sym_loc); |
| 445 | const sect_id = sym.n_sect - 1; | 428 | const sect_id = sym.n_sect - 1; |
| 446 | var section = zld.sections.get(sect_id); | 429 | var section = macho_file.sections.get(sect_id); |
| 447 | section.header.size -= atom.size; | 430 | section.header.size -= atom.size; |
| 448 | 431 | ||
| 449 | if (atom.prev_index) |prev_index| { | 432 | if (atom.prev_index) |prev_index| { |
| 450 | const prev = zld.getAtomPtr(prev_index); | 433 | const prev = macho_file.getAtomPtr(prev_index); |
| 451 | prev.next_index = atom.next_index; | 434 | prev.next_index = atom.next_index; |
| 452 | } else { | 435 | } else { |
| 453 | if (atom.next_index) |next_index| { | 436 | if (atom.next_index) |next_index| { |
| ... | @@ -455,7 +438,7 @@ fn prune(zld: *Zld, alive: AtomTable) void { | ... | @@ -455,7 +438,7 @@ fn prune(zld: *Zld, alive: AtomTable) void { |
| 455 | } | 438 | } |
| 456 | } | 439 | } |
| 457 | if (atom.next_index) |next_index| { | 440 | if (atom.next_index) |next_index| { |
| 458 | const next = zld.getAtomPtr(next_index); | 441 | const next = macho_file.getAtomPtr(next_index); |
| 459 | next.prev_index = atom.prev_index; | 442 | next.prev_index = atom.prev_index; |
| 460 | } else { | 443 | } else { |
| 461 | if (atom.prev_index) |prev_index| { | 444 | if (atom.prev_index) |prev_index| { |
| ... | @@ -467,21 +450,37 @@ fn prune(zld: *Zld, alive: AtomTable) void { | ... | @@ -467,21 +450,37 @@ fn prune(zld: *Zld, alive: AtomTable) void { |
| 467 | } | 450 | } |
| 468 | } | 451 | } |
| 469 | 452 | ||
| 470 | zld.sections.set(sect_id, section); | 453 | macho_file.sections.set(sect_id, section); |
| 471 | _ = object.atoms.swapRemove(i); | 454 | _ = object.atoms.swapRemove(i); |
| 472 | 455 | ||
| 473 | sym.n_desc = MachO.N_DEAD; | 456 | sym.n_desc = MachO.N_DEAD; |
| 474 | 457 | ||
| 475 | var inner_sym_it = Atom.getInnerSymbolsIterator(zld, atom_index); | 458 | var inner_sym_it = Atom.getInnerSymbolsIterator(macho_file, atom_index); |
| 476 | while (inner_sym_it.next()) |inner| { | 459 | while (inner_sym_it.next()) |inner| { |
| 477 | const inner_sym = zld.getSymbolPtr(inner); | 460 | const inner_sym = macho_file.getSymbolPtr(inner); |
| 478 | inner_sym.n_desc = MachO.N_DEAD; | 461 | inner_sym.n_desc = MachO.N_DEAD; |
| 479 | } | 462 | } |
| 480 | 463 | ||
| 481 | if (Atom.getSectionAlias(zld, atom_index)) |alias| { | 464 | if (Atom.getSectionAlias(macho_file, atom_index)) |alias| { |
| 482 | const alias_sym = zld.getSymbolPtr(alias); | 465 | const alias_sym = macho_file.getSymbolPtr(alias); |
| 483 | alias_sym.n_desc = MachO.N_DEAD; | 466 | alias_sym.n_desc = MachO.N_DEAD; |
| 484 | } | 467 | } |
| 485 | } | 468 | } |
| 486 | } | 469 | } |
| 487 | } | 470 | } |
| 471 | |||
| 472 | const std = @import("std"); | ||
| 473 | const assert = std.debug.assert; | ||
| 474 | const eh_frame = @import("eh_frame.zig"); | ||
| 475 | const log = std.log.scoped(.dead_strip); | ||
| 476 | const macho = std.macho; | ||
| 477 | const math = std.math; | ||
| 478 | const mem = std.mem; | ||
| 479 | |||
| 480 | const Allocator = mem.Allocator; | ||
| 481 | const Atom = @import("Atom.zig"); | ||
| 482 | const MachO = @import("../MachO.zig"); | ||
| 483 | const SymbolWithLoc = MachO.SymbolWithLoc; | ||
| 484 | const UnwindInfo = @import("UnwindInfo.zig"); | ||
| 485 | |||
| 486 | const AtomTable = std.AutoHashMap(Atom.Index, void); |
src/link/MachO/dyld_info/Rebase.zig+11-11| ... | @@ -1,14 +1,3 @@ | ... | @@ -1,14 +1,3 @@ |
| 1 | const Rebase = @This(); | ||
| 2 | |||
| 3 | const std = @import("std"); | ||
| 4 | const assert = std.debug.assert; | ||
| 5 | const leb = std.leb; | ||
| 6 | const log = std.log.scoped(.dyld_info); | ||
| 7 | const macho = std.macho; | ||
| 8 | const testing = std.testing; | ||
| 9 | |||
| 10 | const Allocator = std.mem.Allocator; | ||
| 11 | |||
| 12 | entries: std.ArrayListUnmanaged(Entry) = .{}, | 1 | entries: std.ArrayListUnmanaged(Entry) = .{}, |
| 13 | buffer: std.ArrayListUnmanaged(u8) = .{}, | 2 | buffer: std.ArrayListUnmanaged(u8) = .{}, |
| 14 | 3 | ||
| ... | @@ -572,3 +561,14 @@ test "rebase - composite" { | ... | @@ -572,3 +561,14 @@ test "rebase - composite" { |
| 572 | macho.REBASE_OPCODE_DONE, | 561 | macho.REBASE_OPCODE_DONE, |
| 573 | }, rebase.buffer.items); | 562 | }, rebase.buffer.items); |
| 574 | } | 563 | } |
| 564 | |||
| 565 | const Rebase = @This(); | ||
| 566 | |||
| 567 | const std = @import("std"); | ||
| 568 | const assert = std.debug.assert; | ||
| 569 | const leb = std.leb; | ||
| 570 | const log = std.log.scoped(.dyld_info); | ||
| 571 | const macho = std.macho; | ||
| 572 | const testing = std.testing; | ||
| 573 | |||
| 574 | const Allocator = std.mem.Allocator; |
src/link/MachO/dyld_info/bind.zig+9-9| ... | @@ -1,12 +1,3 @@ | ... | @@ -1,12 +1,3 @@ |
| 1 | const std = @import("std"); | ||
| 2 | const assert = std.debug.assert; | ||
| 3 | const leb = std.leb; | ||
| 4 | const log = std.log.scoped(.dyld_info); | ||
| 5 | const macho = std.macho; | ||
| 6 | const testing = std.testing; | ||
| 7 | |||
| 8 | const Allocator = std.mem.Allocator; | ||
| 9 | |||
| 10 | pub fn Bind(comptime Ctx: type, comptime Target: type) type { | 1 | pub fn Bind(comptime Ctx: type, comptime Target: type) type { |
| 11 | return struct { | 2 | return struct { |
| 12 | entries: std.ArrayListUnmanaged(Entry) = .{}, | 3 | entries: std.ArrayListUnmanaged(Entry) = .{}, |
| ... | @@ -738,3 +729,12 @@ test "lazy bind" { | ... | @@ -738,3 +729,12 @@ test "lazy bind" { |
| 738 | macho.BIND_OPCODE_DONE, | 729 | macho.BIND_OPCODE_DONE, |
| 739 | }, bind.buffer.items); | 730 | }, bind.buffer.items); |
| 740 | } | 731 | } |
| 732 | |||
| 733 | const std = @import("std"); | ||
| 734 | const assert = std.debug.assert; | ||
| 735 | const leb = std.leb; | ||
| 736 | const log = std.log.scoped(.dyld_info); | ||
| 737 | const macho = std.macho; | ||
| 738 | const testing = std.testing; | ||
| 739 | |||
| 740 | const Allocator = std.mem.Allocator; |
src/link/MachO/eh_frame.zig+63-64| ... | @@ -1,68 +1,52 @@ | ... | @@ -1,68 +1,52 @@ |
| 1 | const std = @import("std"); | 1 | pub fn scanRelocs(macho_file: *MachO) !void { |
| 2 | const assert = std.debug.assert; | 2 | const gpa = macho_file.base.allocator; |
| 3 | const macho = std.macho; | ||
| 4 | const math = std.math; | ||
| 5 | const mem = std.mem; | ||
| 6 | const leb = std.leb; | ||
| 7 | const log = std.log.scoped(.eh_frame); | ||
| 8 | |||
| 9 | const Allocator = mem.Allocator; | ||
| 10 | const Atom = @import("Atom.zig"); | ||
| 11 | const MachO = @import("../MachO.zig"); | ||
| 12 | const Relocation = @import("Relocation.zig"); | ||
| 13 | const SymbolWithLoc = MachO.SymbolWithLoc; | ||
| 14 | const UnwindInfo = @import("UnwindInfo.zig"); | ||
| 15 | const Zld = @import("zld.zig").Zld; | ||
| 16 | |||
| 17 | pub fn scanRelocs(zld: *Zld) !void { | ||
| 18 | const gpa = zld.gpa; | ||
| 19 | 3 | ||
| 20 | for (zld.objects.items, 0..) |*object, object_id| { | 4 | for (macho_file.objects.items, 0..) |*object, object_id| { |
| 21 | var cies = std.AutoHashMap(u32, void).init(gpa); | 5 | var cies = std.AutoHashMap(u32, void).init(gpa); |
| 22 | defer cies.deinit(); | 6 | defer cies.deinit(); |
| 23 | 7 | ||
| 24 | var it = object.getEhFrameRecordsIterator(); | 8 | var it = object.getEhFrameRecordsIterator(); |
| 25 | 9 | ||
| 26 | for (object.exec_atoms.items) |atom_index| { | 10 | for (object.exec_atoms.items) |atom_index| { |
| 27 | var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index); | 11 | var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index); |
| 28 | while (inner_syms_it.next()) |sym| { | 12 | while (inner_syms_it.next()) |sym| { |
| 29 | const fde_offset = object.eh_frame_records_lookup.get(sym) orelse continue; | 13 | const fde_offset = object.eh_frame_records_lookup.get(sym) orelse continue; |
| 30 | if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue; | 14 | if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue; |
| 31 | it.seekTo(fde_offset); | 15 | it.seekTo(fde_offset); |
| 32 | const fde = (try it.next()).?; | 16 | const fde = (try it.next()).?; |
| 33 | 17 | ||
| 34 | const cie_ptr = fde.getCiePointerSource(@intCast(object_id), zld, fde_offset); | 18 | const cie_ptr = fde.getCiePointerSource(@intCast(object_id), macho_file, fde_offset); |
| 35 | const cie_offset = fde_offset + 4 - cie_ptr; | 19 | const cie_offset = fde_offset + 4 - cie_ptr; |
| 36 | 20 | ||
| 37 | if (!cies.contains(cie_offset)) { | 21 | if (!cies.contains(cie_offset)) { |
| 38 | try cies.putNoClobber(cie_offset, {}); | 22 | try cies.putNoClobber(cie_offset, {}); |
| 39 | it.seekTo(cie_offset); | 23 | it.seekTo(cie_offset); |
| 40 | const cie = (try it.next()).?; | 24 | const cie = (try it.next()).?; |
| 41 | try cie.scanRelocs(zld, @as(u32, @intCast(object_id)), cie_offset); | 25 | try cie.scanRelocs(macho_file, @as(u32, @intCast(object_id)), cie_offset); |
| 42 | } | 26 | } |
| 43 | } | 27 | } |
| 44 | } | 28 | } |
| 45 | } | 29 | } |
| 46 | } | 30 | } |
| 47 | 31 | ||
| 48 | pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void { | 32 | pub fn calcSectionSize(macho_file: *MachO, unwind_info: *const UnwindInfo) !void { |
| 49 | const sect_id = zld.eh_frame_section_index orelse return; | 33 | const sect_id = macho_file.eh_frame_section_index orelse return; |
| 50 | const sect = &zld.sections.items(.header)[sect_id]; | 34 | const sect = &macho_file.sections.items(.header)[sect_id]; |
| 51 | sect.@"align" = 3; | 35 | sect.@"align" = 3; |
| 52 | sect.size = 0; | 36 | sect.size = 0; |
| 53 | 37 | ||
| 54 | const cpu_arch = zld.options.target.cpu.arch; | 38 | const cpu_arch = macho_file.base.options.target.cpu.arch; |
| 55 | const gpa = zld.gpa; | 39 | const gpa = macho_file.base.allocator; |
| 56 | var size: u32 = 0; | 40 | var size: u32 = 0; |
| 57 | 41 | ||
| 58 | for (zld.objects.items, 0..) |*object, object_id| { | 42 | for (macho_file.objects.items, 0..) |*object, object_id| { |
| 59 | var cies = std.AutoHashMap(u32, u32).init(gpa); | 43 | var cies = std.AutoHashMap(u32, u32).init(gpa); |
| 60 | defer cies.deinit(); | 44 | defer cies.deinit(); |
| 61 | 45 | ||
| 62 | var eh_it = object.getEhFrameRecordsIterator(); | 46 | var eh_it = object.getEhFrameRecordsIterator(); |
| 63 | 47 | ||
| 64 | for (object.exec_atoms.items) |atom_index| { | 48 | for (object.exec_atoms.items) |atom_index| { |
| 65 | var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index); | 49 | var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index); |
| 66 | while (inner_syms_it.next()) |sym| { | 50 | while (inner_syms_it.next()) |sym| { |
| 67 | const fde_record_offset = object.eh_frame_records_lookup.get(sym) orelse continue; | 51 | const fde_record_offset = object.eh_frame_records_lookup.get(sym) orelse continue; |
| 68 | if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue; | 52 | if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue; |
| ... | @@ -77,7 +61,7 @@ pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void { | ... | @@ -77,7 +61,7 @@ pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void { |
| 77 | eh_it.seekTo(fde_record_offset); | 61 | eh_it.seekTo(fde_record_offset); |
| 78 | const source_fde_record = (try eh_it.next()).?; | 62 | const source_fde_record = (try eh_it.next()).?; |
| 79 | 63 | ||
| 80 | const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), zld, fde_record_offset); | 64 | const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), macho_file, fde_record_offset); |
| 81 | const cie_offset = fde_record_offset + 4 - cie_ptr; | 65 | const cie_offset = fde_record_offset + 4 - cie_ptr; |
| 82 | 66 | ||
| 83 | const gop = try cies.getOrPut(cie_offset); | 67 | const gop = try cies.getOrPut(cie_offset); |
| ... | @@ -96,14 +80,14 @@ pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void { | ... | @@ -96,14 +80,14 @@ pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void { |
| 96 | } | 80 | } |
| 97 | } | 81 | } |
| 98 | 82 | ||
| 99 | pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void { | 83 | pub fn write(macho_file: *MachO, unwind_info: *UnwindInfo) !void { |
| 100 | const sect_id = zld.eh_frame_section_index orelse return; | 84 | const sect_id = macho_file.eh_frame_section_index orelse return; |
| 101 | const sect = zld.sections.items(.header)[sect_id]; | 85 | const sect = macho_file.sections.items(.header)[sect_id]; |
| 102 | const seg_id = zld.sections.items(.segment_index)[sect_id]; | 86 | const seg_id = macho_file.sections.items(.segment_index)[sect_id]; |
| 103 | const seg = zld.segments.items[seg_id]; | 87 | const seg = macho_file.segments.items[seg_id]; |
| 104 | 88 | ||
| 105 | const cpu_arch = zld.options.target.cpu.arch; | 89 | const cpu_arch = macho_file.base.options.target.cpu.arch; |
| 106 | const gpa = zld.gpa; | 90 | const gpa = macho_file.base.allocator; |
| 107 | 91 | ||
| 108 | var eh_records = std.AutoArrayHashMap(u32, EhFrameRecord(true)).init(gpa); | 92 | var eh_records = std.AutoArrayHashMap(u32, EhFrameRecord(true)).init(gpa); |
| 109 | defer { | 93 | defer { |
| ... | @@ -115,7 +99,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void { | ... | @@ -115,7 +99,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void { |
| 115 | 99 | ||
| 116 | var eh_frame_offset: u32 = 0; | 100 | var eh_frame_offset: u32 = 0; |
| 117 | 101 | ||
| 118 | for (zld.objects.items, 0..) |*object, object_id| { | 102 | for (macho_file.objects.items, 0..) |*object, object_id| { |
| 119 | try eh_records.ensureUnusedCapacity(2 * @as(u32, @intCast(object.exec_atoms.items.len))); | 103 | try eh_records.ensureUnusedCapacity(2 * @as(u32, @intCast(object.exec_atoms.items.len))); |
| 120 | 104 | ||
| 121 | var cies = std.AutoHashMap(u32, u32).init(gpa); | 105 | var cies = std.AutoHashMap(u32, u32).init(gpa); |
| ... | @@ -124,7 +108,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void { | ... | @@ -124,7 +108,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void { |
| 124 | var eh_it = object.getEhFrameRecordsIterator(); | 108 | var eh_it = object.getEhFrameRecordsIterator(); |
| 125 | 109 | ||
| 126 | for (object.exec_atoms.items) |atom_index| { | 110 | for (object.exec_atoms.items) |atom_index| { |
| 127 | var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index); | 111 | var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index); |
| 128 | while (inner_syms_it.next()) |target| { | 112 | while (inner_syms_it.next()) |target| { |
| 129 | const fde_record_offset = object.eh_frame_records_lookup.get(target) orelse continue; | 113 | const fde_record_offset = object.eh_frame_records_lookup.get(target) orelse continue; |
| 130 | if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue; | 114 | if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue; |
| ... | @@ -139,7 +123,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void { | ... | @@ -139,7 +123,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void { |
| 139 | eh_it.seekTo(fde_record_offset); | 123 | eh_it.seekTo(fde_record_offset); |
| 140 | const source_fde_record = (try eh_it.next()).?; | 124 | const source_fde_record = (try eh_it.next()).?; |
| 141 | 125 | ||
| 142 | const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), zld, fde_record_offset); | 126 | const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), macho_file, fde_record_offset); |
| 143 | const cie_offset = fde_record_offset + 4 - cie_ptr; | 127 | const cie_offset = fde_record_offset + 4 - cie_ptr; |
| 144 | 128 | ||
| 145 | const gop = try cies.getOrPut(cie_offset); | 129 | const gop = try cies.getOrPut(cie_offset); |
| ... | @@ -147,7 +131,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void { | ... | @@ -147,7 +131,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void { |
| 147 | eh_it.seekTo(cie_offset); | 131 | eh_it.seekTo(cie_offset); |
| 148 | const source_cie_record = (try eh_it.next()).?; | 132 | const source_cie_record = (try eh_it.next()).?; |
| 149 | var cie_record = try source_cie_record.toOwned(gpa); | 133 | var cie_record = try source_cie_record.toOwned(gpa); |
| 150 | try cie_record.relocate(zld, @as(u32, @intCast(object_id)), .{ | 134 | try cie_record.relocate(macho_file, @as(u32, @intCast(object_id)), .{ |
| 151 | .source_offset = cie_offset, | 135 | .source_offset = cie_offset, |
| 152 | .out_offset = eh_frame_offset, | 136 | .out_offset = eh_frame_offset, |
| 153 | .sect_addr = sect.addr, | 137 | .sect_addr = sect.addr, |
| ... | @@ -158,7 +142,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void { | ... | @@ -158,7 +142,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void { |
| 158 | } | 142 | } |
| 159 | 143 | ||
| 160 | var fde_record = try source_fde_record.toOwned(gpa); | 144 | var fde_record = try source_fde_record.toOwned(gpa); |
| 161 | try fde_record.relocate(zld, @as(u32, @intCast(object_id)), .{ | 145 | try fde_record.relocate(macho_file, @as(u32, @intCast(object_id)), .{ |
| 162 | .source_offset = fde_record_offset, | 146 | .source_offset = fde_record_offset, |
| 163 | .out_offset = eh_frame_offset, | 147 | .out_offset = eh_frame_offset, |
| 164 | .sect_addr = sect.addr, | 148 | .sect_addr = sect.addr, |
| ... | @@ -169,7 +153,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void { | ... | @@ -169,7 +153,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void { |
| 169 | .aarch64 => {}, // relocs take care of LSDA pointers | 153 | .aarch64 => {}, // relocs take care of LSDA pointers |
| 170 | .x86_64 => { | 154 | .x86_64 => { |
| 171 | // We need to relocate target symbol address ourselves. | 155 | // We need to relocate target symbol address ourselves. |
| 172 | const atom_sym = zld.getSymbol(target); | 156 | const atom_sym = macho_file.getSymbol(target); |
| 173 | try fde_record.setTargetSymbolAddress(atom_sym.n_value, .{ | 157 | try fde_record.setTargetSymbolAddress(atom_sym.n_value, .{ |
| 174 | .base_addr = sect.addr, | 158 | .base_addr = sect.addr, |
| 175 | .base_offset = eh_frame_offset, | 159 | .base_offset = eh_frame_offset, |
| ... | @@ -229,7 +213,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void { | ... | @@ -229,7 +213,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void { |
| 229 | try buffer.appendSlice(record.data); | 213 | try buffer.appendSlice(record.data); |
| 230 | } | 214 | } |
| 231 | 215 | ||
| 232 | try zld.file.pwriteAll(buffer.items, sect.offset); | 216 | try macho_file.base.file.?.pwriteAll(buffer.items, sect.offset); |
| 233 | } | 217 | } |
| 234 | const EhFrameRecordTag = enum { cie, fde }; | 218 | const EhFrameRecordTag = enum { cie, fde }; |
| 235 | 219 | ||
| ... | @@ -261,12 +245,12 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type { | ... | @@ -261,12 +245,12 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type { |
| 261 | 245 | ||
| 262 | pub fn scanRelocs( | 246 | pub fn scanRelocs( |
| 263 | rec: Record, | 247 | rec: Record, |
| 264 | zld: *Zld, | 248 | macho_file: *MachO, |
| 265 | object_id: u32, | 249 | object_id: u32, |
| 266 | source_offset: u32, | 250 | source_offset: u32, |
| 267 | ) !void { | 251 | ) !void { |
| 268 | if (rec.getPersonalityPointerReloc(zld, object_id, source_offset)) |target| { | 252 | if (rec.getPersonalityPointerReloc(macho_file, object_id, source_offset)) |target| { |
| 269 | try zld.addGotEntry(target); | 253 | try macho_file.addGotEntry(target); |
| 270 | } | 254 | } |
| 271 | } | 255 | } |
| 272 | 256 | ||
| ... | @@ -290,12 +274,12 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type { | ... | @@ -290,12 +274,12 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type { |
| 290 | 274 | ||
| 291 | pub fn getPersonalityPointerReloc( | 275 | pub fn getPersonalityPointerReloc( |
| 292 | rec: Record, | 276 | rec: Record, |
| 293 | zld: *Zld, | 277 | macho_file: *MachO, |
| 294 | object_id: u32, | 278 | object_id: u32, |
| 295 | source_offset: u32, | 279 | source_offset: u32, |
| 296 | ) ?SymbolWithLoc { | 280 | ) ?SymbolWithLoc { |
| 297 | const cpu_arch = zld.options.target.cpu.arch; | 281 | const cpu_arch = macho_file.base.options.target.cpu.arch; |
| 298 | const relocs = getRelocs(zld, object_id, source_offset); | 282 | const relocs = getRelocs(macho_file, object_id, source_offset); |
| 299 | for (relocs) |rel| { | 283 | for (relocs) |rel| { |
| 300 | switch (cpu_arch) { | 284 | switch (cpu_arch) { |
| 301 | .aarch64 => { | 285 | .aarch64 => { |
| ... | @@ -317,7 +301,7 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type { | ... | @@ -317,7 +301,7 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type { |
| 317 | }, | 301 | }, |
| 318 | else => unreachable, | 302 | else => unreachable, |
| 319 | } | 303 | } |
| 320 | const target = Atom.parseRelocTarget(zld, .{ | 304 | const target = Atom.parseRelocTarget(macho_file, .{ |
| 321 | .object_id = object_id, | 305 | .object_id = object_id, |
| 322 | .rel = rel, | 306 | .rel = rel, |
| 323 | .code = rec.data, | 307 | .code = rec.data, |
| ... | @@ -328,18 +312,18 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type { | ... | @@ -328,18 +312,18 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type { |
| 328 | return null; | 312 | return null; |
| 329 | } | 313 | } |
| 330 | 314 | ||
| 331 | pub fn relocate(rec: *Record, zld: *Zld, object_id: u32, ctx: struct { | 315 | pub fn relocate(rec: *Record, macho_file: *MachO, object_id: u32, ctx: struct { |
| 332 | source_offset: u32, | 316 | source_offset: u32, |
| 333 | out_offset: u32, | 317 | out_offset: u32, |
| 334 | sect_addr: u64, | 318 | sect_addr: u64, |
| 335 | }) !void { | 319 | }) !void { |
| 336 | comptime assert(is_mutable); | 320 | comptime assert(is_mutable); |
| 337 | 321 | ||
| 338 | const cpu_arch = zld.options.target.cpu.arch; | 322 | const cpu_arch = macho_file.base.options.target.cpu.arch; |
| 339 | const relocs = getRelocs(zld, object_id, ctx.source_offset); | 323 | const relocs = getRelocs(macho_file, object_id, ctx.source_offset); |
| 340 | 324 | ||
| 341 | for (relocs) |rel| { | 325 | for (relocs) |rel| { |
| 342 | const target = Atom.parseRelocTarget(zld, .{ | 326 | const target = Atom.parseRelocTarget(macho_file, .{ |
| 343 | .object_id = object_id, | 327 | .object_id = object_id, |
| 344 | .rel = rel, | 328 | .rel = rel, |
| 345 | .code = rec.data, | 329 | .code = rec.data, |
| ... | @@ -356,14 +340,14 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type { | ... | @@ -356,14 +340,14 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type { |
| 356 | // Address of the __eh_frame in the source object file | 340 | // Address of the __eh_frame in the source object file |
| 357 | }, | 341 | }, |
| 358 | .ARM64_RELOC_POINTER_TO_GOT => { | 342 | .ARM64_RELOC_POINTER_TO_GOT => { |
| 359 | const target_addr = zld.getGotEntryAddress(target).?; | 343 | const target_addr = macho_file.getGotEntryAddress(target).?; |
| 360 | const result = math.cast(i32, @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr))) orelse | 344 | const result = math.cast(i32, @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr))) orelse |
| 361 | return error.Overflow; | 345 | return error.Overflow; |
| 362 | mem.writeIntLittle(i32, rec.data[rel_offset..][0..4], result); | 346 | mem.writeIntLittle(i32, rec.data[rel_offset..][0..4], result); |
| 363 | }, | 347 | }, |
| 364 | .ARM64_RELOC_UNSIGNED => { | 348 | .ARM64_RELOC_UNSIGNED => { |
| 365 | assert(rel.r_extern == 1); | 349 | assert(rel.r_extern == 1); |
| 366 | const target_addr = try Atom.getRelocTargetAddress(zld, target, false); | 350 | const target_addr = try Atom.getRelocTargetAddress(macho_file, target, false); |
| 367 | const result = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr)); | 351 | const result = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr)); |
| 368 | mem.writeIntLittle(i64, rec.data[rel_offset..][0..8], @as(i64, @intCast(result))); | 352 | mem.writeIntLittle(i64, rec.data[rel_offset..][0..8], @as(i64, @intCast(result))); |
| 369 | }, | 353 | }, |
| ... | @@ -374,7 +358,7 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type { | ... | @@ -374,7 +358,7 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type { |
| 374 | const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type)); | 358 | const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type)); |
| 375 | switch (rel_type) { | 359 | switch (rel_type) { |
| 376 | .X86_64_RELOC_GOT => { | 360 | .X86_64_RELOC_GOT => { |
| 377 | const target_addr = zld.getGotEntryAddress(target).?; | 361 | const target_addr = macho_file.getGotEntryAddress(target).?; |
| 378 | const addend = mem.readIntLittle(i32, rec.data[rel_offset..][0..4]); | 362 | const addend = mem.readIntLittle(i32, rec.data[rel_offset..][0..4]); |
| 379 | const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend)); | 363 | const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend)); |
| 380 | const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0); | 364 | const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0); |
| ... | @@ -388,20 +372,20 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type { | ... | @@ -388,20 +372,20 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type { |
| 388 | } | 372 | } |
| 389 | } | 373 | } |
| 390 | 374 | ||
| 391 | pub fn getCiePointerSource(rec: Record, object_id: u32, zld: *Zld, offset: u32) u32 { | 375 | pub fn getCiePointerSource(rec: Record, object_id: u32, macho_file: *MachO, offset: u32) u32 { |
| 392 | assert(rec.tag == .fde); | 376 | assert(rec.tag == .fde); |
| 393 | const cpu_arch = zld.options.target.cpu.arch; | 377 | const cpu_arch = macho_file.base.options.target.cpu.arch; |
| 394 | const addend = mem.readIntLittle(u32, rec.data[0..4]); | 378 | const addend = mem.readIntLittle(u32, rec.data[0..4]); |
| 395 | switch (cpu_arch) { | 379 | switch (cpu_arch) { |
| 396 | .aarch64 => { | 380 | .aarch64 => { |
| 397 | const relocs = getRelocs(zld, object_id, offset); | 381 | const relocs = getRelocs(macho_file, object_id, offset); |
| 398 | const maybe_rel = for (relocs) |rel| { | 382 | const maybe_rel = for (relocs) |rel| { |
| 399 | if (rel.r_address - @as(i32, @intCast(offset)) == 4 and | 383 | if (rel.r_address - @as(i32, @intCast(offset)) == 4 and |
| 400 | @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)) == .ARM64_RELOC_SUBTRACTOR) | 384 | @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)) == .ARM64_RELOC_SUBTRACTOR) |
| 401 | break rel; | 385 | break rel; |
| 402 | } else null; | 386 | } else null; |
| 403 | const rel = maybe_rel orelse return addend; | 387 | const rel = maybe_rel orelse return addend; |
| 404 | const object = &zld.objects.items[object_id]; | 388 | const object = &macho_file.objects.items[object_id]; |
| 405 | const target_addr = object.in_symtab.?[rel.r_symbolnum].n_value; | 389 | const target_addr = object.in_symtab.?[rel.r_symbolnum].n_value; |
| 406 | const sect = object.getSourceSection(object.eh_frame_sect_id.?); | 390 | const sect = object.getSourceSection(object.eh_frame_sect_id.?); |
| 407 | return @intCast(sect.addr + offset - target_addr + addend); | 391 | return @intCast(sect.addr + offset - target_addr + addend); |
| ... | @@ -583,8 +567,8 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type { | ... | @@ -583,8 +567,8 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type { |
| 583 | }; | 567 | }; |
| 584 | } | 568 | } |
| 585 | 569 | ||
| 586 | pub fn getRelocs(zld: *Zld, object_id: u32, source_offset: u32) []const macho.relocation_info { | 570 | pub fn getRelocs(macho_file: *MachO, object_id: u32, source_offset: u32) []const macho.relocation_info { |
| 587 | const object = &zld.objects.items[object_id]; | 571 | const object = &macho_file.objects.items[object_id]; |
| 588 | assert(object.hasEhFrameRecords()); | 572 | assert(object.hasEhFrameRecords()); |
| 589 | const urel = object.eh_frame_relocs_lookup.get(source_offset) orelse | 573 | const urel = object.eh_frame_relocs_lookup.get(source_offset) orelse |
| 590 | return &[0]macho.relocation_info{}; | 574 | return &[0]macho.relocation_info{}; |
| ... | @@ -650,3 +634,18 @@ pub const EH_PE = struct { | ... | @@ -650,3 +634,18 @@ pub const EH_PE = struct { |
| 650 | pub const indirect = 0x80; | 634 | pub const indirect = 0x80; |
| 651 | pub const omit = 0xFF; | 635 | pub const omit = 0xFF; |
| 652 | }; | 636 | }; |
| 637 | |||
| 638 | const std = @import("std"); | ||
| 639 | const assert = std.debug.assert; | ||
| 640 | const macho = std.macho; | ||
| 641 | const math = std.math; | ||
| 642 | const mem = std.mem; | ||
| 643 | const leb = std.leb; | ||
| 644 | const log = std.log.scoped(.eh_frame); | ||
| 645 | |||
| 646 | const Allocator = mem.Allocator; | ||
| 647 | const Atom = @import("Atom.zig"); | ||
| 648 | const MachO = @import("../MachO.zig"); | ||
| 649 | const Relocation = @import("Relocation.zig"); | ||
| 650 | const SymbolWithLoc = MachO.SymbolWithLoc; | ||
| 651 | const UnwindInfo = @import("UnwindInfo.zig"); |
src/link/MachO/fat.zig+6-6| ... | @@ -1,9 +1,3 @@ | ... | @@ -1,9 +1,3 @@ |
| 1 | const std = @import("std"); | ||
| 2 | const assert = std.debug.assert; | ||
| 3 | const log = std.log.scoped(.archive); | ||
| 4 | const macho = std.macho; | ||
| 5 | const mem = std.mem; | ||
| 6 | |||
| 7 | pub fn isFatLibrary(file: std.fs.File) bool { | 1 | pub fn isFatLibrary(file: std.fs.File) bool { |
| 8 | const reader = file.reader(); | 2 | const reader = file.reader(); |
| 9 | const hdr = reader.readStructBig(macho.fat_header) catch return false; | 3 | const hdr = reader.readStructBig(macho.fat_header) catch return false; |
| ... | @@ -38,3 +32,9 @@ pub fn parseArchs(file: std.fs.File, buffer: *[2]Arch) ![]const Arch { | ... | @@ -38,3 +32,9 @@ pub fn parseArchs(file: std.fs.File, buffer: *[2]Arch) ![]const Arch { |
| 38 | 32 | ||
| 39 | return buffer[0..count]; | 33 | return buffer[0..count]; |
| 40 | } | 34 | } |
| 35 | |||
| 36 | const std = @import("std"); | ||
| 37 | const assert = std.debug.assert; | ||
| 38 | const log = std.log.scoped(.archive); | ||
| 39 | const macho = std.macho; | ||
| 40 | const mem = std.mem; |
src/link/MachO/hasher.zig+9-9| ... | @@ -1,12 +1,3 @@ | ... | @@ -1,12 +1,3 @@ |
| 1 | const std = @import("std"); | ||
| 2 | const assert = std.debug.assert; | ||
| 3 | const fs = std.fs; | ||
| 4 | const mem = std.mem; | ||
| 5 | |||
| 6 | const Allocator = mem.Allocator; | ||
| 7 | const ThreadPool = std.Thread.Pool; | ||
| 8 | const WaitGroup = std.Thread.WaitGroup; | ||
| 9 | |||
| 10 | pub fn ParallelHasher(comptime Hasher: type) type { | 1 | pub fn ParallelHasher(comptime Hasher: type) type { |
| 11 | const hash_size = Hasher.digest_length; | 2 | const hash_size = Hasher.digest_length; |
| 12 | 3 | ||
| ... | @@ -69,3 +60,12 @@ pub fn ParallelHasher(comptime Hasher: type) type { | ... | @@ -69,3 +60,12 @@ pub fn ParallelHasher(comptime Hasher: type) type { |
| 69 | const Self = @This(); | 60 | const Self = @This(); |
| 70 | }; | 61 | }; |
| 71 | } | 62 | } |
| 63 | |||
| 64 | const std = @import("std"); | ||
| 65 | const assert = std.debug.assert; | ||
| 66 | const fs = std.fs; | ||
| 67 | const mem = std.mem; | ||
| 68 | |||
| 69 | const Allocator = mem.Allocator; | ||
| 70 | const ThreadPool = std.Thread.Pool; | ||
| 71 | const WaitGroup = std.Thread.WaitGroup; |
src/link/MachO/load_commands.zig+10-10| ... | @@ -1,13 +1,3 @@ | ... | @@ -1,13 +1,3 @@ |
| 1 | const std = @import("std"); | ||
| 2 | const assert = std.debug.assert; | ||
| 3 | const link = @import("../../link.zig"); | ||
| 4 | const log = std.log.scoped(.link); | ||
| 5 | const macho = std.macho; | ||
| 6 | const mem = std.mem; | ||
| 7 | |||
| 8 | const Allocator = mem.Allocator; | ||
| 9 | const Dylib = @import("Dylib.zig"); | ||
| 10 | |||
| 11 | /// Default implicit entrypoint symbol name. | 1 | /// Default implicit entrypoint symbol name. |
| 12 | pub const default_entry_point: []const u8 = "_main"; | 2 | pub const default_entry_point: []const u8 = "_main"; |
| 13 | 3 | ||
| ... | @@ -374,3 +364,13 @@ test "parseSdkVersion" { | ... | @@ -374,3 +364,13 @@ test "parseSdkVersion" { |
| 374 | 364 | ||
| 375 | try expect(parseSdkVersion("11") == null); | 365 | try expect(parseSdkVersion("11") == null); |
| 376 | } | 366 | } |
| 367 | |||
| 368 | const std = @import("std"); | ||
| 369 | const assert = std.debug.assert; | ||
| 370 | const link = @import("../../link.zig"); | ||
| 371 | const log = std.log.scoped(.link); | ||
| 372 | const macho = std.macho; | ||
| 373 | const mem = std.mem; | ||
| 374 | |||
| 375 | const Allocator = mem.Allocator; | ||
| 376 | const Dylib = @import("Dylib.zig"); |
src/link/MachO/stubs.zig+5-5| ... | @@ -1,8 +1,3 @@ | ... | @@ -1,8 +1,3 @@ |
| 1 | const std = @import("std"); | ||
| 2 | const aarch64 = @import("../../arch/aarch64/bits.zig"); | ||
| 3 | |||
| 4 | const Relocation = @import("Relocation.zig"); | ||
| 5 | |||
| 6 | pub inline fn stubHelperPreambleSize(cpu_arch: std.Target.Cpu.Arch) u8 { | 1 | pub inline fn stubHelperPreambleSize(cpu_arch: std.Target.Cpu.Arch) u8 { |
| 7 | return switch (cpu_arch) { | 2 | return switch (cpu_arch) { |
| 8 | .x86_64 => 15, | 3 | .x86_64 => 15, |
| ... | @@ -167,3 +162,8 @@ pub fn writeStubCode(args: struct { | ... | @@ -167,3 +162,8 @@ pub fn writeStubCode(args: struct { |
| 167 | else => unreachable, | 162 | else => unreachable, |
| 168 | } | 163 | } |
| 169 | } | 164 | } |
| 165 | |||
| 166 | const std = @import("std"); | ||
| 167 | const aarch64 = @import("../../arch/aarch64/bits.zig"); | ||
| 168 | |||
| 169 | const Relocation = @import("Relocation.zig"); |
src/link/MachO/thunks.zig+84-85| ... | @@ -5,22 +5,6 @@ | ... | @@ -5,22 +5,6 @@ |
| 5 | //! The algorithm works pessimistically and assumes that any reference to an Atom in | 5 | //! The algorithm works pessimistically and assumes that any reference to an Atom in |
| 6 | //! another output section is out of range. | 6 | //! another output section is out of range. |
| 7 | 7 | ||
| 8 | const std = @import("std"); | ||
| 9 | const assert = std.debug.assert; | ||
| 10 | const log = std.log.scoped(.thunks); | ||
| 11 | const macho = std.macho; | ||
| 12 | const math = std.math; | ||
| 13 | const mem = std.mem; | ||
| 14 | |||
| 15 | const aarch64 = @import("../../arch/aarch64/bits.zig"); | ||
| 16 | |||
| 17 | const Allocator = mem.Allocator; | ||
| 18 | const Atom = @import("Atom.zig"); | ||
| 19 | const MachO = @import("../MachO.zig"); | ||
| 20 | const Relocation = @import("Relocation.zig"); | ||
| 21 | const SymbolWithLoc = MachO.SymbolWithLoc; | ||
| 22 | const Zld = @import("zld.zig").Zld; | ||
| 23 | |||
| 24 | /// Branch instruction has 26 bits immediate but 4 byte aligned. | 8 | /// Branch instruction has 26 bits immediate but 4 byte aligned. |
| 25 | const jump_bits = @bitSizeOf(i28); | 9 | const jump_bits = @bitSizeOf(i28); |
| 26 | 10 | ||
| ... | @@ -74,18 +58,18 @@ pub const Thunk = struct { | ... | @@ -74,18 +58,18 @@ pub const Thunk = struct { |
| 74 | return @alignOf(u32); | 58 | return @alignOf(u32); |
| 75 | } | 59 | } |
| 76 | 60 | ||
| 77 | pub fn getTrampoline(self: Thunk, zld: *Zld, tag: Tag, target: SymbolWithLoc) ?SymbolWithLoc { | 61 | pub fn getTrampoline(self: Thunk, macho_file: *MachO, tag: Tag, target: SymbolWithLoc) ?SymbolWithLoc { |
| 78 | const atom_index = self.lookup.get(.{ .tag = tag, .target = target }) orelse return null; | 62 | const atom_index = self.lookup.get(.{ .tag = tag, .target = target }) orelse return null; |
| 79 | return zld.getAtom(atom_index).getSymbolWithLoc(); | 63 | return macho_file.getAtom(atom_index).getSymbolWithLoc(); |
| 80 | } | 64 | } |
| 81 | }; | 65 | }; |
| 82 | 66 | ||
| 83 | pub fn createThunks(zld: *Zld, sect_id: u8) !void { | 67 | pub fn createThunks(macho_file: *MachO, sect_id: u8) !void { |
| 84 | const header = &zld.sections.items(.header)[sect_id]; | 68 | const header = &macho_file.sections.items(.header)[sect_id]; |
| 85 | if (header.size == 0) return; | 69 | if (header.size == 0) return; |
| 86 | 70 | ||
| 87 | const gpa = zld.gpa; | 71 | const gpa = macho_file.base.allocator; |
| 88 | const first_atom_index = zld.sections.items(.first_atom_index)[sect_id].?; | 72 | const first_atom_index = macho_file.sections.items(.first_atom_index)[sect_id].?; |
| 89 | 73 | ||
| 90 | header.size = 0; | 74 | header.size = 0; |
| 91 | header.@"align" = 0; | 75 | header.@"align" = 0; |
| ... | @@ -95,8 +79,8 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void { | ... | @@ -95,8 +79,8 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void { |
| 95 | { | 79 | { |
| 96 | var atom_index = first_atom_index; | 80 | var atom_index = first_atom_index; |
| 97 | while (true) { | 81 | while (true) { |
| 98 | const atom = zld.getAtom(atom_index); | 82 | const atom = macho_file.getAtom(atom_index); |
| 99 | const sym = zld.getSymbolPtr(atom.getSymbolWithLoc()); | 83 | const sym = macho_file.getSymbolPtr(atom.getSymbolWithLoc()); |
| 100 | sym.n_value = 0; | 84 | sym.n_value = 0; |
| 101 | atom_count += 1; | 85 | atom_count += 1; |
| 102 | 86 | ||
| ... | @@ -115,24 +99,24 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void { | ... | @@ -115,24 +99,24 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void { |
| 115 | var offset: u64 = 0; | 99 | var offset: u64 = 0; |
| 116 | 100 | ||
| 117 | while (true) { | 101 | while (true) { |
| 118 | const group_start_atom = zld.getAtom(group_start); | 102 | const group_start_atom = macho_file.getAtom(group_start); |
| 119 | log.debug("GROUP START at {d}", .{group_start}); | 103 | log.debug("GROUP START at {d}", .{group_start}); |
| 120 | 104 | ||
| 121 | while (true) { | 105 | while (true) { |
| 122 | const atom = zld.getAtom(group_end); | 106 | const atom = macho_file.getAtom(group_end); |
| 123 | offset = mem.alignForward(u64, offset, try math.powi(u32, 2, atom.alignment)); | 107 | offset = mem.alignForward(u64, offset, try math.powi(u32, 2, atom.alignment)); |
| 124 | 108 | ||
| 125 | const sym = zld.getSymbolPtr(atom.getSymbolWithLoc()); | 109 | const sym = macho_file.getSymbolPtr(atom.getSymbolWithLoc()); |
| 126 | sym.n_value = offset; | 110 | sym.n_value = offset; |
| 127 | offset += atom.size; | 111 | offset += atom.size; |
| 128 | 112 | ||
| 129 | zld.logAtom(group_end, log); | 113 | macho_file.logAtom(group_end, log); |
| 130 | 114 | ||
| 131 | header.@"align" = @max(header.@"align", atom.alignment); | 115 | header.@"align" = @max(header.@"align", atom.alignment); |
| 132 | 116 | ||
| 133 | allocated.putAssumeCapacityNoClobber(group_end, {}); | 117 | allocated.putAssumeCapacityNoClobber(group_end, {}); |
| 134 | 118 | ||
| 135 | const group_start_sym = zld.getSymbol(group_start_atom.getSymbolWithLoc()); | 119 | const group_start_sym = macho_file.getSymbol(group_start_atom.getSymbolWithLoc()); |
| 136 | if (offset - group_start_sym.n_value >= max_allowed_distance) break; | 120 | if (offset - group_start_sym.n_value >= max_allowed_distance) break; |
| 137 | 121 | ||
| 138 | if (atom.next_index) |next_index| { | 122 | if (atom.next_index) |next_index| { |
| ... | @@ -142,15 +126,15 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void { | ... | @@ -142,15 +126,15 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void { |
| 142 | log.debug("GROUP END at {d}", .{group_end}); | 126 | log.debug("GROUP END at {d}", .{group_end}); |
| 143 | 127 | ||
| 144 | // Insert thunk at group_end | 128 | // Insert thunk at group_end |
| 145 | const thunk_index = @as(u32, @intCast(zld.thunks.items.len)); | 129 | const thunk_index = @as(u32, @intCast(macho_file.thunks.items.len)); |
| 146 | try zld.thunks.append(gpa, .{ .start_index = undefined, .len = 0 }); | 130 | try macho_file.thunks.append(gpa, .{ .start_index = undefined, .len = 0 }); |
| 147 | 131 | ||
| 148 | // Scan relocs in the group and create trampolines for any unreachable callsite. | 132 | // Scan relocs in the group and create trampolines for any unreachable callsite. |
| 149 | var atom_index = group_start; | 133 | var atom_index = group_start; |
| 150 | while (true) { | 134 | while (true) { |
| 151 | const atom = zld.getAtom(atom_index); | 135 | const atom = macho_file.getAtom(atom_index); |
| 152 | try scanRelocs( | 136 | try scanRelocs( |
| 153 | zld, | 137 | macho_file, |
| 154 | atom_index, | 138 | atom_index, |
| 155 | allocated, | 139 | allocated, |
| 156 | thunk_index, | 140 | thunk_index, |
| ... | @@ -165,19 +149,19 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void { | ... | @@ -165,19 +149,19 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void { |
| 165 | } | 149 | } |
| 166 | 150 | ||
| 167 | offset = mem.alignForward(u64, offset, Thunk.getAlignment()); | 151 | offset = mem.alignForward(u64, offset, Thunk.getAlignment()); |
| 168 | allocateThunk(zld, thunk_index, offset, header); | 152 | allocateThunk(macho_file, thunk_index, offset, header); |
| 169 | offset += zld.thunks.items[thunk_index].getSize(); | 153 | offset += macho_file.thunks.items[thunk_index].getSize(); |
| 170 | 154 | ||
| 171 | const thunk = zld.thunks.items[thunk_index]; | 155 | const thunk = macho_file.thunks.items[thunk_index]; |
| 172 | if (thunk.len == 0) { | 156 | if (thunk.len == 0) { |
| 173 | const group_end_atom = zld.getAtom(group_end); | 157 | const group_end_atom = macho_file.getAtom(group_end); |
| 174 | if (group_end_atom.next_index) |next_index| { | 158 | if (group_end_atom.next_index) |next_index| { |
| 175 | group_start = next_index; | 159 | group_start = next_index; |
| 176 | group_end = next_index; | 160 | group_end = next_index; |
| 177 | } else break; | 161 | } else break; |
| 178 | } else { | 162 | } else { |
| 179 | const thunk_end_atom_index = thunk.getEndAtomIndex(); | 163 | const thunk_end_atom_index = thunk.getEndAtomIndex(); |
| 180 | const thunk_end_atom = zld.getAtom(thunk_end_atom_index); | 164 | const thunk_end_atom = macho_file.getAtom(thunk_end_atom_index); |
| 181 | if (thunk_end_atom.next_index) |next_index| { | 165 | if (thunk_end_atom.next_index) |next_index| { |
| 182 | group_start = next_index; | 166 | group_start = next_index; |
| 183 | group_end = next_index; | 167 | group_end = next_index; |
| ... | @@ -189,12 +173,12 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void { | ... | @@ -189,12 +173,12 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void { |
| 189 | } | 173 | } |
| 190 | 174 | ||
| 191 | fn allocateThunk( | 175 | fn allocateThunk( |
| 192 | zld: *Zld, | 176 | macho_file: *MachO, |
| 193 | thunk_index: Thunk.Index, | 177 | thunk_index: Thunk.Index, |
| 194 | base_offset: u64, | 178 | base_offset: u64, |
| 195 | header: *macho.section_64, | 179 | header: *macho.section_64, |
| 196 | ) void { | 180 | ) void { |
| 197 | const thunk = zld.thunks.items[thunk_index]; | 181 | const thunk = macho_file.thunks.items[thunk_index]; |
| 198 | if (thunk.len == 0) return; | 182 | if (thunk.len == 0) return; |
| 199 | 183 | ||
| 200 | const first_atom_index = thunk.getStartAtomIndex(); | 184 | const first_atom_index = thunk.getStartAtomIndex(); |
| ... | @@ -203,14 +187,14 @@ fn allocateThunk( | ... | @@ -203,14 +187,14 @@ fn allocateThunk( |
| 203 | var atom_index = first_atom_index; | 187 | var atom_index = first_atom_index; |
| 204 | var offset = base_offset; | 188 | var offset = base_offset; |
| 205 | while (true) { | 189 | while (true) { |
| 206 | const atom = zld.getAtom(atom_index); | 190 | const atom = macho_file.getAtom(atom_index); |
| 207 | offset = mem.alignForward(u64, offset, Thunk.getAlignment()); | 191 | offset = mem.alignForward(u64, offset, Thunk.getAlignment()); |
| 208 | 192 | ||
| 209 | const sym = zld.getSymbolPtr(atom.getSymbolWithLoc()); | 193 | const sym = macho_file.getSymbolPtr(atom.getSymbolWithLoc()); |
| 210 | sym.n_value = offset; | 194 | sym.n_value = offset; |
| 211 | offset += atom.size; | 195 | offset += atom.size; |
| 212 | 196 | ||
| 213 | zld.logAtom(atom_index, log); | 197 | macho_file.logAtom(atom_index, log); |
| 214 | 198 | ||
| 215 | header.@"align" = @max(header.@"align", atom.alignment); | 199 | header.@"align" = @max(header.@"align", atom.alignment); |
| 216 | 200 | ||
| ... | @@ -223,69 +207,69 @@ fn allocateThunk( | ... | @@ -223,69 +207,69 @@ fn allocateThunk( |
| 223 | } | 207 | } |
| 224 | 208 | ||
| 225 | fn scanRelocs( | 209 | fn scanRelocs( |
| 226 | zld: *Zld, | 210 | macho_file: *MachO, |
| 227 | atom_index: Atom.Index, | 211 | atom_index: Atom.Index, |
| 228 | allocated: std.AutoHashMap(Atom.Index, void), | 212 | allocated: std.AutoHashMap(Atom.Index, void), |
| 229 | thunk_index: Thunk.Index, | 213 | thunk_index: Thunk.Index, |
| 230 | group_end: Atom.Index, | 214 | group_end: Atom.Index, |
| 231 | ) !void { | 215 | ) !void { |
| 232 | const atom = zld.getAtom(atom_index); | 216 | const atom = macho_file.getAtom(atom_index); |
| 233 | const object = zld.objects.items[atom.getFile().?]; | 217 | const object = macho_file.objects.items[atom.getFile().?]; |
| 234 | 218 | ||
| 235 | const base_offset = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: { | 219 | const base_offset = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: { |
| 236 | const source_sect = object.getSourceSection(source_sym.n_sect - 1); | 220 | const source_sect = object.getSourceSection(source_sym.n_sect - 1); |
| 237 | break :blk @as(i32, @intCast(source_sym.n_value - source_sect.addr)); | 221 | break :blk @as(i32, @intCast(source_sym.n_value - source_sect.addr)); |
| 238 | } else 0; | 222 | } else 0; |
| 239 | 223 | ||
| 240 | const code = Atom.getAtomCode(zld, atom_index); | 224 | const code = Atom.getAtomCode(macho_file, atom_index); |
| 241 | const relocs = Atom.getAtomRelocs(zld, atom_index); | 225 | const relocs = Atom.getAtomRelocs(macho_file, atom_index); |
| 242 | const ctx = Atom.getRelocContext(zld, atom_index); | 226 | const ctx = Atom.getRelocContext(macho_file, atom_index); |
| 243 | 227 | ||
| 244 | for (relocs) |rel| { | 228 | for (relocs) |rel| { |
| 245 | if (!relocNeedsThunk(rel)) continue; | 229 | if (!relocNeedsThunk(rel)) continue; |
| 246 | 230 | ||
| 247 | const target = Atom.parseRelocTarget(zld, .{ | 231 | const target = Atom.parseRelocTarget(macho_file, .{ |
| 248 | .object_id = atom.getFile().?, | 232 | .object_id = atom.getFile().?, |
| 249 | .rel = rel, | 233 | .rel = rel, |
| 250 | .code = code, | 234 | .code = code, |
| 251 | .base_offset = ctx.base_offset, | 235 | .base_offset = ctx.base_offset, |
| 252 | .base_addr = ctx.base_addr, | 236 | .base_addr = ctx.base_addr, |
| 253 | }); | 237 | }); |
| 254 | if (isReachable(zld, atom_index, rel, base_offset, target, allocated)) continue; | 238 | if (isReachable(macho_file, atom_index, rel, base_offset, target, allocated)) continue; |
| 255 | 239 | ||
| 256 | log.debug("{x}: source = {s}@{x}, target = {s}@{x} unreachable", .{ | 240 | log.debug("{x}: source = {s}@{x}, target = {s}@{x} unreachable", .{ |
| 257 | rel.r_address - base_offset, | 241 | rel.r_address - base_offset, |
| 258 | zld.getSymbolName(atom.getSymbolWithLoc()), | 242 | macho_file.getSymbolName(atom.getSymbolWithLoc()), |
| 259 | zld.getSymbol(atom.getSymbolWithLoc()).n_value, | 243 | macho_file.getSymbol(atom.getSymbolWithLoc()).n_value, |
| 260 | zld.getSymbolName(target), | 244 | macho_file.getSymbolName(target), |
| 261 | zld.getSymbol(target).n_value, | 245 | macho_file.getSymbol(target).n_value, |
| 262 | }); | 246 | }); |
| 263 | 247 | ||
| 264 | const gpa = zld.gpa; | 248 | const gpa = macho_file.base.allocator; |
| 265 | const target_sym = zld.getSymbol(target); | 249 | const target_sym = macho_file.getSymbol(target); |
| 266 | const thunk = &zld.thunks.items[thunk_index]; | 250 | const thunk = &macho_file.thunks.items[thunk_index]; |
| 267 | 251 | ||
| 268 | const tag: Thunk.Tag = if (target_sym.undf()) .stub else .atom; | 252 | const tag: Thunk.Tag = if (target_sym.undf()) .stub else .atom; |
| 269 | const thunk_target: Thunk.Target = .{ .tag = tag, .target = target }; | 253 | const thunk_target: Thunk.Target = .{ .tag = tag, .target = target }; |
| 270 | const gop = try thunk.lookup.getOrPut(gpa, thunk_target); | 254 | const gop = try thunk.lookup.getOrPut(gpa, thunk_target); |
| 271 | if (!gop.found_existing) { | 255 | if (!gop.found_existing) { |
| 272 | gop.value_ptr.* = try pushThunkAtom(zld, thunk, group_end); | 256 | gop.value_ptr.* = try pushThunkAtom(macho_file, thunk, group_end); |
| 273 | try thunk.targets.append(gpa, thunk_target); | 257 | try thunk.targets.append(gpa, thunk_target); |
| 274 | } | 258 | } |
| 275 | 259 | ||
| 276 | try zld.thunk_table.put(gpa, atom_index, thunk_index); | 260 | try macho_file.thunk_table.put(gpa, atom_index, thunk_index); |
| 277 | } | 261 | } |
| 278 | } | 262 | } |
| 279 | 263 | ||
| 280 | fn pushThunkAtom(zld: *Zld, thunk: *Thunk, group_end: Atom.Index) !Atom.Index { | 264 | fn pushThunkAtom(macho_file: *MachO, thunk: *Thunk, group_end: Atom.Index) !Atom.Index { |
| 281 | const thunk_atom_index = try createThunkAtom(zld); | 265 | const thunk_atom_index = try createThunkAtom(macho_file); |
| 282 | 266 | ||
| 283 | const thunk_atom = zld.getAtomPtr(thunk_atom_index); | 267 | const thunk_atom = macho_file.getAtomPtr(thunk_atom_index); |
| 284 | const end_atom_index = if (thunk.len == 0) group_end else thunk.getEndAtomIndex(); | 268 | const end_atom_index = if (thunk.len == 0) group_end else thunk.getEndAtomIndex(); |
| 285 | const end_atom = zld.getAtomPtr(end_atom_index); | 269 | const end_atom = macho_file.getAtomPtr(end_atom_index); |
| 286 | 270 | ||
| 287 | if (end_atom.next_index) |first_after_index| { | 271 | if (end_atom.next_index) |first_after_index| { |
| 288 | const first_after_atom = zld.getAtomPtr(first_after_index); | 272 | const first_after_atom = macho_file.getAtomPtr(first_after_index); |
| 289 | first_after_atom.prev_index = thunk_atom_index; | 273 | first_after_atom.prev_index = thunk_atom_index; |
| 290 | thunk_atom.next_index = first_after_index; | 274 | thunk_atom.next_index = first_after_index; |
| 291 | } | 275 | } |
| ... | @@ -308,58 +292,58 @@ inline fn relocNeedsThunk(rel: macho.relocation_info) bool { | ... | @@ -308,58 +292,58 @@ inline fn relocNeedsThunk(rel: macho.relocation_info) bool { |
| 308 | } | 292 | } |
| 309 | 293 | ||
| 310 | fn isReachable( | 294 | fn isReachable( |
| 311 | zld: *Zld, | 295 | macho_file: *MachO, |
| 312 | atom_index: Atom.Index, | 296 | atom_index: Atom.Index, |
| 313 | rel: macho.relocation_info, | 297 | rel: macho.relocation_info, |
| 314 | base_offset: i32, | 298 | base_offset: i32, |
| 315 | target: SymbolWithLoc, | 299 | target: SymbolWithLoc, |
| 316 | allocated: std.AutoHashMap(Atom.Index, void), | 300 | allocated: std.AutoHashMap(Atom.Index, void), |
| 317 | ) bool { | 301 | ) bool { |
| 318 | if (zld.stubs_table.lookup.contains(target)) return false; | 302 | if (macho_file.stub_table.lookup.contains(target)) return false; |
| 319 | 303 | ||
| 320 | const source_atom = zld.getAtom(atom_index); | 304 | const source_atom = macho_file.getAtom(atom_index); |
| 321 | const source_sym = zld.getSymbol(source_atom.getSymbolWithLoc()); | 305 | const source_sym = macho_file.getSymbol(source_atom.getSymbolWithLoc()); |
| 322 | 306 | ||
| 323 | const target_object = zld.objects.items[target.getFile().?]; | 307 | const target_object = macho_file.objects.items[target.getFile().?]; |
| 324 | const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?; | 308 | const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?; |
| 325 | const target_atom = zld.getAtom(target_atom_index); | 309 | const target_atom = macho_file.getAtom(target_atom_index); |
| 326 | const target_sym = zld.getSymbol(target_atom.getSymbolWithLoc()); | 310 | const target_sym = macho_file.getSymbol(target_atom.getSymbolWithLoc()); |
| 327 | 311 | ||
| 328 | if (source_sym.n_sect != target_sym.n_sect) return false; | 312 | if (source_sym.n_sect != target_sym.n_sect) return false; |
| 329 | 313 | ||
| 330 | if (!allocated.contains(target_atom_index)) return false; | 314 | if (!allocated.contains(target_atom_index)) return false; |
| 331 | 315 | ||
| 332 | const source_addr = source_sym.n_value + @as(u32, @intCast(rel.r_address - base_offset)); | 316 | const source_addr = source_sym.n_value + @as(u32, @intCast(rel.r_address - base_offset)); |
| 333 | const target_addr = if (Atom.relocRequiresGot(zld, rel)) | 317 | const target_addr = if (Atom.relocRequiresGot(macho_file, rel)) |
| 334 | zld.getGotEntryAddress(target).? | 318 | macho_file.getGotEntryAddress(target).? |
| 335 | else | 319 | else |
| 336 | Atom.getRelocTargetAddress(zld, target, false) catch unreachable; | 320 | Atom.getRelocTargetAddress(macho_file, target, false) catch unreachable; |
| 337 | _ = Relocation.calcPcRelativeDisplacementArm64(source_addr, target_addr) catch | 321 | _ = Relocation.calcPcRelativeDisplacementArm64(source_addr, target_addr) catch |
| 338 | return false; | 322 | return false; |
| 339 | 323 | ||
| 340 | return true; | 324 | return true; |
| 341 | } | 325 | } |
| 342 | 326 | ||
| 343 | fn createThunkAtom(zld: *Zld) !Atom.Index { | 327 | fn createThunkAtom(macho_file: *MachO) !Atom.Index { |
| 344 | const sym_index = try zld.allocateSymbol(); | 328 | const sym_index = try macho_file.allocateSymbol(); |
| 345 | const atom_index = try zld.createAtom(sym_index, .{ .size = @sizeOf(u32) * 3, .alignment = 2 }); | 329 | const atom_index = try macho_file.createAtom(sym_index, .{ .size = @sizeOf(u32) * 3, .alignment = 2 }); |
| 346 | const sym = zld.getSymbolPtr(.{ .sym_index = sym_index }); | 330 | const sym = macho_file.getSymbolPtr(.{ .sym_index = sym_index }); |
| 347 | sym.n_type = macho.N_SECT; | 331 | sym.n_type = macho.N_SECT; |
| 348 | sym.n_sect = zld.text_section_index.? + 1; | 332 | sym.n_sect = macho_file.text_section_index.? + 1; |
| 349 | return atom_index; | 333 | return atom_index; |
| 350 | } | 334 | } |
| 351 | 335 | ||
| 352 | pub fn writeThunkCode(zld: *Zld, thunk: *const Thunk, writer: anytype) !void { | 336 | pub fn writeThunkCode(macho_file: *MachO, thunk: *const Thunk, writer: anytype) !void { |
| 353 | const slice = thunk.targets.slice(); | 337 | const slice = thunk.targets.slice(); |
| 354 | for (thunk.getStartAtomIndex()..thunk.getEndAtomIndex(), 0..) |atom_index, target_index| { | 338 | for (thunk.getStartAtomIndex()..thunk.getEndAtomIndex(), 0..) |atom_index, target_index| { |
| 355 | const atom = zld.getAtom(@intCast(atom_index)); | 339 | const atom = macho_file.getAtom(@intCast(atom_index)); |
| 356 | const sym = zld.getSymbol(atom.getSymbolWithLoc()); | 340 | const sym = macho_file.getSymbol(atom.getSymbolWithLoc()); |
| 357 | const source_addr = sym.n_value; | 341 | const source_addr = sym.n_value; |
| 358 | const tag = slice.items(.tag)[target_index]; | 342 | const tag = slice.items(.tag)[target_index]; |
| 359 | const target = slice.items(.target)[target_index]; | 343 | const target = slice.items(.target)[target_index]; |
| 360 | const target_addr = switch (tag) { | 344 | const target_addr = switch (tag) { |
| 361 | .stub => zld.getStubsEntryAddress(target).?, | 345 | .stub => macho_file.getStubsEntryAddress(target).?, |
| 362 | .atom => zld.getSymbol(target).n_value, | 346 | .atom => macho_file.getSymbol(target).n_value, |
| 363 | }; | 347 | }; |
| 364 | const pages = Relocation.calcNumberOfPages(source_addr, target_addr); | 348 | const pages = Relocation.calcNumberOfPages(source_addr, target_addr); |
| 365 | try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32()); | 349 | try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32()); |
| ... | @@ -368,3 +352,18 @@ pub fn writeThunkCode(zld: *Zld, thunk: *const Thunk, writer: anytype) !void { | ... | @@ -368,3 +352,18 @@ pub fn writeThunkCode(zld: *Zld, thunk: *const Thunk, writer: anytype) !void { |
| 368 | try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32()); | 352 | try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32()); |
| 369 | } | 353 | } |
| 370 | } | 354 | } |
| 355 | |||
| 356 | const std = @import("std"); | ||
| 357 | const assert = std.debug.assert; | ||
| 358 | const log = std.log.scoped(.thunks); | ||
| 359 | const macho = std.macho; | ||
| 360 | const math = std.math; | ||
| 361 | const mem = std.mem; | ||
| 362 | |||
| 363 | const aarch64 = @import("../../arch/aarch64/bits.zig"); | ||
| 364 | |||
| 365 | const Allocator = mem.Allocator; | ||
| 366 | const Atom = @import("Atom.zig"); | ||
| 367 | const MachO = @import("../MachO.zig"); | ||
| 368 | const Relocation = @import("Relocation.zig"); | ||
| 369 | const SymbolWithLoc = MachO.SymbolWithLoc; |
src/link/MachO/uuid.zig+9-9| ... | @@ -1,12 +1,3 @@ | ... | @@ -1,12 +1,3 @@ |
| 1 | const std = @import("std"); | ||
| 2 | const fs = std.fs; | ||
| 3 | const mem = std.mem; | ||
| 4 | |||
| 5 | const Allocator = mem.Allocator; | ||
| 6 | const Compilation = @import("../../Compilation.zig"); | ||
| 7 | const Md5 = std.crypto.hash.Md5; | ||
| 8 | const Hasher = @import("hasher.zig").ParallelHasher; | ||
| 9 | |||
| 10 | /// Calculates Md5 hash of each chunk in parallel and then hashes all Md5 hashes to produce | 1 | /// Calculates Md5 hash of each chunk in parallel and then hashes all Md5 hashes to produce |
| 11 | /// the final digest. | 2 | /// the final digest. |
| 12 | /// While this is NOT a correct MD5 hash of the contents, this methodology is used by LLVM/LLD | 3 | /// While this is NOT a correct MD5 hash of the contents, this methodology is used by LLVM/LLD |
| ... | @@ -43,3 +34,12 @@ inline fn conform(out: *[Md5.digest_length]u8) void { | ... | @@ -43,3 +34,12 @@ inline fn conform(out: *[Md5.digest_length]u8) void { |
| 43 | out[6] = (out[6] & 0x0F) | (3 << 4); | 34 | out[6] = (out[6] & 0x0F) | (3 << 4); |
| 44 | out[8] = (out[8] & 0x3F) | 0x80; | 35 | out[8] = (out[8] & 0x3F) | 0x80; |
| 45 | } | 36 | } |
| 37 | |||
| 38 | const std = @import("std"); | ||
| 39 | const fs = std.fs; | ||
| 40 | const mem = std.mem; | ||
| 41 | |||
| 42 | const Allocator = mem.Allocator; | ||
| 43 | const Compilation = @import("../../Compilation.zig"); | ||
| 44 | const Md5 = std.crypto.hash.Md5; | ||
| 45 | const Hasher = @import("hasher.zig").ParallelHasher; |
src/link/MachO/zld.zig+726-2704| ... | @@ -1,2584 +1,8 @@ | ... | @@ -1,2584 +1,8 @@ |
| 1 | const std = @import("std"); | 1 | pub fn linkWithZld( |
| 2 | const build_options = @import("build_options"); | 2 | macho_file: *MachO, |
| 3 | const assert = std.debug.assert; | 3 | comp: *Compilation, |
| 4 | const dwarf = std.dwarf; | 4 | prog_node: *std.Progress.Node, |
| 5 | const fs = std.fs; | 5 | ) link.File.FlushError!void { |
| 6 | const log = std.log.scoped(.link); | ||
| 7 | const macho = std.macho; | ||
| 8 | const math = std.math; | ||
| 9 | const mem = std.mem; | ||
| 10 | |||
| 11 | const aarch64 = @import("../../arch/aarch64/bits.zig"); | ||
| 12 | const calcUuid = @import("uuid.zig").calcUuid; | ||
| 13 | const dead_strip = @import("dead_strip.zig"); | ||
| 14 | const eh_frame = @import("eh_frame.zig"); | ||
| 15 | const fat = @import("fat.zig"); | ||
| 16 | const link = @import("../../link.zig"); | ||
| 17 | const load_commands = @import("load_commands.zig"); | ||
| 18 | const stubs = @import("stubs.zig"); | ||
| 19 | const thunks = @import("thunks.zig"); | ||
| 20 | const trace = @import("../../tracy.zig").trace; | ||
| 21 | |||
| 22 | const Allocator = mem.Allocator; | ||
| 23 | const Archive = @import("Archive.zig"); | ||
| 24 | const Atom = @import("Atom.zig"); | ||
| 25 | const Cache = std.Build.Cache; | ||
| 26 | const CodeSignature = @import("CodeSignature.zig"); | ||
| 27 | const Compilation = @import("../../Compilation.zig"); | ||
| 28 | const DwarfInfo = @import("DwarfInfo.zig"); | ||
| 29 | const Dylib = @import("Dylib.zig"); | ||
| 30 | const MachO = @import("../MachO.zig"); | ||
| 31 | const Md5 = std.crypto.hash.Md5; | ||
| 32 | const LibStub = @import("../tapi.zig").LibStub; | ||
| 33 | const Object = @import("Object.zig"); | ||
| 34 | const Section = MachO.Section; | ||
| 35 | const StringTable = @import("../strtab.zig").StringTable; | ||
| 36 | const SymbolWithLoc = MachO.SymbolWithLoc; | ||
| 37 | const TableSection = @import("../table_section.zig").TableSection; | ||
| 38 | const Trie = @import("Trie.zig"); | ||
| 39 | const UnwindInfo = @import("UnwindInfo.zig"); | ||
| 40 | |||
| 41 | const Bind = @import("dyld_info/bind.zig").Bind(*const Zld, SymbolWithLoc); | ||
| 42 | const LazyBind = @import("dyld_info/bind.zig").LazyBind(*const Zld, SymbolWithLoc); | ||
| 43 | const Rebase = @import("dyld_info/Rebase.zig"); | ||
| 44 | |||
| 45 | pub const Zld = struct { | ||
| 46 | gpa: Allocator, | ||
| 47 | file: fs.File, | ||
| 48 | options: *const link.Options, | ||
| 49 | |||
| 50 | dyld_info_cmd: macho.dyld_info_command = .{}, | ||
| 51 | symtab_cmd: macho.symtab_command = .{}, | ||
| 52 | dysymtab_cmd: macho.dysymtab_command = .{}, | ||
| 53 | function_starts_cmd: macho.linkedit_data_command = .{ .cmd = .FUNCTION_STARTS }, | ||
| 54 | data_in_code_cmd: macho.linkedit_data_command = .{ .cmd = .DATA_IN_CODE }, | ||
| 55 | uuid_cmd: macho.uuid_command = .{ | ||
| 56 | .uuid = [_]u8{0} ** 16, | ||
| 57 | }, | ||
| 58 | codesig_cmd: macho.linkedit_data_command = .{ .cmd = .CODE_SIGNATURE }, | ||
| 59 | |||
| 60 | objects: std.ArrayListUnmanaged(Object) = .{}, | ||
| 61 | archives: std.ArrayListUnmanaged(Archive) = .{}, | ||
| 62 | dylibs: std.ArrayListUnmanaged(Dylib) = .{}, | ||
| 63 | dylibs_map: std.StringHashMapUnmanaged(u16) = .{}, | ||
| 64 | referenced_dylibs: std.AutoArrayHashMapUnmanaged(u16, void) = .{}, | ||
| 65 | |||
| 66 | segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{}, | ||
| 67 | sections: std.MultiArrayList(Section) = .{}, | ||
| 68 | |||
| 69 | pagezero_segment_cmd_index: ?u8 = null, | ||
| 70 | header_segment_cmd_index: ?u8 = null, | ||
| 71 | text_segment_cmd_index: ?u8 = null, | ||
| 72 | data_const_segment_cmd_index: ?u8 = null, | ||
| 73 | data_segment_cmd_index: ?u8 = null, | ||
| 74 | linkedit_segment_cmd_index: ?u8 = null, | ||
| 75 | |||
| 76 | text_section_index: ?u8 = null, | ||
| 77 | data_const_section_index: ?u8 = null, | ||
| 78 | data_section_index: ?u8 = null, | ||
| 79 | bss_section_index: ?u8 = null, | ||
| 80 | thread_vars_section_index: ?u8 = null, | ||
| 81 | thread_data_section_index: ?u8 = null, | ||
| 82 | thread_bss_section_index: ?u8 = null, | ||
| 83 | eh_frame_section_index: ?u8 = null, | ||
| 84 | unwind_info_section_index: ?u8 = null, | ||
| 85 | got_section_index: ?u8 = null, | ||
| 86 | tlv_ptr_section_index: ?u8 = null, | ||
| 87 | stubs_section_index: ?u8 = null, | ||
| 88 | stub_helper_section_index: ?u8 = null, | ||
| 89 | la_symbol_ptr_section_index: ?u8 = null, | ||
| 90 | |||
| 91 | locals: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | ||
| 92 | globals: std.ArrayListUnmanaged(SymbolWithLoc) = .{}, | ||
| 93 | resolver: std.StringHashMapUnmanaged(u32) = .{}, | ||
| 94 | unresolved: std.AutoArrayHashMapUnmanaged(u32, void) = .{}, | ||
| 95 | |||
| 96 | locals_free_list: std.ArrayListUnmanaged(u32) = .{}, | ||
| 97 | globals_free_list: std.ArrayListUnmanaged(u32) = .{}, | ||
| 98 | |||
| 99 | entry_index: ?u32 = null, | ||
| 100 | dyld_stub_binder_index: ?u32 = null, | ||
| 101 | dyld_private_atom_index: ?Atom.Index = null, | ||
| 102 | |||
| 103 | strtab: StringTable(.strtab) = .{}, | ||
| 104 | |||
| 105 | tlv_ptr_table: TableSection(SymbolWithLoc) = .{}, | ||
| 106 | got_table: TableSection(SymbolWithLoc) = .{}, | ||
| 107 | stubs_table: TableSection(SymbolWithLoc) = .{}, | ||
| 108 | |||
| 109 | thunk_table: std.AutoHashMapUnmanaged(Atom.Index, thunks.Thunk.Index) = .{}, | ||
| 110 | thunks: std.ArrayListUnmanaged(thunks.Thunk) = .{}, | ||
| 111 | |||
| 112 | atoms: std.ArrayListUnmanaged(Atom) = .{}, | ||
| 113 | |||
| 114 | pub fn addAtomToSection(self: *Zld, atom_index: Atom.Index) void { | ||
| 115 | const atom = self.getAtomPtr(atom_index); | ||
| 116 | const sym = self.getSymbol(atom.getSymbolWithLoc()); | ||
| 117 | var section = self.sections.get(sym.n_sect - 1); | ||
| 118 | if (section.header.size > 0) { | ||
| 119 | const last_atom = self.getAtomPtr(section.last_atom_index.?); | ||
| 120 | last_atom.next_index = atom_index; | ||
| 121 | atom.prev_index = section.last_atom_index; | ||
| 122 | } else { | ||
| 123 | section.first_atom_index = atom_index; | ||
| 124 | } | ||
| 125 | section.last_atom_index = atom_index; | ||
| 126 | section.header.size += atom.size; | ||
| 127 | self.sections.set(sym.n_sect - 1, section); | ||
| 128 | } | ||
| 129 | |||
| 130 | const CreateAtomOpts = struct { | ||
| 131 | size: u64 = 0, | ||
| 132 | alignment: u32 = 0, | ||
| 133 | }; | ||
| 134 | |||
| 135 | pub fn createAtom(self: *Zld, sym_index: u32, opts: CreateAtomOpts) !Atom.Index { | ||
| 136 | const gpa = self.gpa; | ||
| 137 | const index = @as(Atom.Index, @intCast(self.atoms.items.len)); | ||
| 138 | const atom = try self.atoms.addOne(gpa); | ||
| 139 | atom.* = .{}; | ||
| 140 | atom.sym_index = sym_index; | ||
| 141 | atom.size = opts.size; | ||
| 142 | atom.alignment = opts.alignment; | ||
| 143 | log.debug("creating ATOM(%{d}) at index {d}", .{ sym_index, index }); | ||
| 144 | return index; | ||
| 145 | } | ||
| 146 | |||
| 147 | fn createDyldPrivateAtom(self: *Zld) !void { | ||
| 148 | const sym_index = try self.allocateSymbol(); | ||
| 149 | const atom_index = try self.createAtom(sym_index, .{ .size = @sizeOf(u64), .alignment = 3 }); | ||
| 150 | const sym = self.getSymbolPtr(.{ .sym_index = sym_index }); | ||
| 151 | sym.n_type = macho.N_SECT; | ||
| 152 | |||
| 153 | if (self.data_section_index == null) { | ||
| 154 | self.data_section_index = try MachO.initSection(self.gpa, self, "__DATA", "__data", .{}); | ||
| 155 | } | ||
| 156 | sym.n_sect = self.data_section_index.? + 1; | ||
| 157 | self.dyld_private_atom_index = atom_index; | ||
| 158 | |||
| 159 | self.addAtomToSection(atom_index); | ||
| 160 | } | ||
| 161 | |||
| 162 | fn createTentativeDefAtoms(self: *Zld) !void { | ||
| 163 | const gpa = self.gpa; | ||
| 164 | |||
| 165 | for (self.globals.items) |global| { | ||
| 166 | const sym = self.getSymbolPtr(global); | ||
| 167 | if (!sym.tentative()) continue; | ||
| 168 | if (sym.n_desc == MachO.N_DEAD) continue; | ||
| 169 | |||
| 170 | log.debug("creating tentative definition for ATOM(%{d}, '{s}') in object({?})", .{ | ||
| 171 | global.sym_index, self.getSymbolName(global), global.file, | ||
| 172 | }); | ||
| 173 | |||
| 174 | // Convert any tentative definition into a regular symbol and allocate | ||
| 175 | // text blocks for each tentative definition. | ||
| 176 | const size = sym.n_value; | ||
| 177 | const alignment = (sym.n_desc >> 8) & 0x0f; | ||
| 178 | |||
| 179 | if (self.bss_section_index == null) { | ||
| 180 | self.bss_section_index = try MachO.initSection(gpa, self, "__DATA", "__bss", .{ | ||
| 181 | .flags = macho.S_ZEROFILL, | ||
| 182 | }); | ||
| 183 | } | ||
| 184 | |||
| 185 | sym.* = .{ | ||
| 186 | .n_strx = sym.n_strx, | ||
| 187 | .n_type = macho.N_SECT | macho.N_EXT, | ||
| 188 | .n_sect = self.bss_section_index.? + 1, | ||
| 189 | .n_desc = 0, | ||
| 190 | .n_value = 0, | ||
| 191 | }; | ||
| 192 | |||
| 193 | const atom_index = try self.createAtom(global.sym_index, .{ | ||
| 194 | .size = size, | ||
| 195 | .alignment = alignment, | ||
| 196 | }); | ||
| 197 | const atom = self.getAtomPtr(atom_index); | ||
| 198 | atom.file = global.file; | ||
| 199 | |||
| 200 | self.addAtomToSection(atom_index); | ||
| 201 | |||
| 202 | assert(global.getFile() != null); | ||
| 203 | const object = &self.objects.items[global.getFile().?]; | ||
| 204 | try object.atoms.append(gpa, atom_index); | ||
| 205 | object.atom_by_index_table[global.sym_index] = atom_index; | ||
| 206 | } | ||
| 207 | } | ||
| 208 | |||
| 209 | fn addUndefined(self: *Zld, name: []const u8) !u32 { | ||
| 210 | const gop = try self.getOrPutGlobalPtr(name); | ||
| 211 | const global_index = self.getGlobalIndex(name).?; | ||
| 212 | |||
| 213 | if (gop.found_existing) return global_index; | ||
| 214 | |||
| 215 | const sym_index = try self.allocateSymbol(); | ||
| 216 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index }; | ||
| 217 | gop.value_ptr.* = sym_loc; | ||
| 218 | |||
| 219 | const sym = self.getSymbolPtr(sym_loc); | ||
| 220 | sym.n_strx = try self.strtab.insert(self.gpa, name); | ||
| 221 | sym.n_type = macho.N_UNDF; | ||
| 222 | |||
| 223 | try self.unresolved.putNoClobber(self.gpa, global_index, {}); | ||
| 224 | |||
| 225 | return global_index; | ||
| 226 | } | ||
| 227 | |||
| 228 | fn resolveSymbols(self: *Zld) !void { | ||
| 229 | // We add the specified entrypoint as the first unresolved symbols so that | ||
| 230 | // we search for it in libraries should there be no object files specified | ||
| 231 | // on the linker line. | ||
| 232 | if (self.options.output_mode == .Exe) { | ||
| 233 | const entry_name = self.options.entry orelse load_commands.default_entry_point; | ||
| 234 | _ = try self.addUndefined(entry_name); | ||
| 235 | } | ||
| 236 | |||
| 237 | // Force resolution of any symbols requested by the user. | ||
| 238 | for (self.options.force_undefined_symbols.keys()) |sym_name| { | ||
| 239 | _ = try self.addUndefined(sym_name); | ||
| 240 | } | ||
| 241 | |||
| 242 | for (self.objects.items, 0..) |_, object_id| { | ||
| 243 | try self.resolveSymbolsInObject(@as(u32, @intCast(object_id))); | ||
| 244 | } | ||
| 245 | |||
| 246 | try self.resolveSymbolsInArchives(); | ||
| 247 | |||
| 248 | // Finally, force resolution of dyld_stub_binder if there are imports | ||
| 249 | // requested. | ||
| 250 | if (self.unresolved.count() > 0) { | ||
| 251 | self.dyld_stub_binder_index = try self.addUndefined("dyld_stub_binder"); | ||
| 252 | } | ||
| 253 | |||
| 254 | try self.resolveSymbolsInDylibs(); | ||
| 255 | |||
| 256 | try self.createMhExecuteHeaderSymbol(); | ||
| 257 | try self.createDsoHandleSymbol(); | ||
| 258 | try self.resolveSymbolsAtLoading(); | ||
| 259 | } | ||
| 260 | |||
| 261 | fn resolveGlobalSymbol(self: *Zld, current: SymbolWithLoc) !void { | ||
| 262 | const gpa = self.gpa; | ||
| 263 | const sym = self.getSymbol(current); | ||
| 264 | const sym_name = self.getSymbolName(current); | ||
| 265 | |||
| 266 | const gop = try self.getOrPutGlobalPtr(sym_name); | ||
| 267 | if (!gop.found_existing) { | ||
| 268 | gop.value_ptr.* = current; | ||
| 269 | if (sym.undf() and !sym.tentative()) { | ||
| 270 | try self.unresolved.putNoClobber(gpa, self.getGlobalIndex(sym_name).?, {}); | ||
| 271 | } | ||
| 272 | return; | ||
| 273 | } | ||
| 274 | const global_index = self.getGlobalIndex(sym_name).?; | ||
| 275 | const global = gop.value_ptr.*; | ||
| 276 | const global_sym = self.getSymbol(global); | ||
| 277 | |||
| 278 | // Cases to consider: sym vs global_sym | ||
| 279 | // 1. strong(sym) and strong(global_sym) => error | ||
| 280 | // 2. strong(sym) and weak(global_sym) => sym | ||
| 281 | // 3. strong(sym) and tentative(global_sym) => sym | ||
| 282 | // 4. strong(sym) and undf(global_sym) => sym | ||
| 283 | // 5. weak(sym) and strong(global_sym) => global_sym | ||
| 284 | // 6. weak(sym) and tentative(global_sym) => sym | ||
| 285 | // 7. weak(sym) and undf(global_sym) => sym | ||
| 286 | // 8. tentative(sym) and strong(global_sym) => global_sym | ||
| 287 | // 9. tentative(sym) and weak(global_sym) => global_sym | ||
| 288 | // 10. tentative(sym) and tentative(global_sym) => pick larger | ||
| 289 | // 11. tentative(sym) and undf(global_sym) => sym | ||
| 290 | // 12. undf(sym) and * => global_sym | ||
| 291 | // | ||
| 292 | // Reduces to: | ||
| 293 | // 1. strong(sym) and strong(global_sym) => error | ||
| 294 | // 2. * and strong(global_sym) => global_sym | ||
| 295 | // 3. weak(sym) and weak(global_sym) => global_sym | ||
| 296 | // 4. tentative(sym) and tentative(global_sym) => pick larger | ||
| 297 | // 5. undf(sym) and * => global_sym | ||
| 298 | // 6. else => sym | ||
| 299 | |||
| 300 | const sym_is_strong = sym.sect() and !(sym.weakDef() or sym.pext()); | ||
| 301 | const global_is_strong = global_sym.sect() and !(global_sym.weakDef() or global_sym.pext()); | ||
| 302 | const sym_is_weak = sym.sect() and (sym.weakDef() or sym.pext()); | ||
| 303 | const global_is_weak = global_sym.sect() and (global_sym.weakDef() or global_sym.pext()); | ||
| 304 | |||
| 305 | if (sym_is_strong and global_is_strong) { | ||
| 306 | log.err("symbol '{s}' defined multiple times", .{sym_name}); | ||
| 307 | if (global.getFile()) |file| { | ||
| 308 | log.err(" first definition in '{s}'", .{self.objects.items[file].name}); | ||
| 309 | } | ||
| 310 | if (current.getFile()) |file| { | ||
| 311 | log.err(" next definition in '{s}'", .{self.objects.items[file].name}); | ||
| 312 | } | ||
| 313 | return error.MultipleSymbolDefinitions; | ||
| 314 | } | ||
| 315 | |||
| 316 | if (current.getFile()) |file| { | ||
| 317 | const object = &self.objects.items[file]; | ||
| 318 | object.globals_lookup[current.sym_index] = global_index; | ||
| 319 | } | ||
| 320 | |||
| 321 | if (global_is_strong) return; | ||
| 322 | if (sym_is_weak and global_is_weak) return; | ||
| 323 | if (sym.tentative() and global_sym.tentative()) { | ||
| 324 | if (global_sym.n_value >= sym.n_value) return; | ||
| 325 | } | ||
| 326 | if (sym.undf() and !sym.tentative()) return; | ||
| 327 | |||
| 328 | if (global.getFile()) |file| { | ||
| 329 | const global_object = &self.objects.items[file]; | ||
| 330 | global_object.globals_lookup[global.sym_index] = global_index; | ||
| 331 | } | ||
| 332 | _ = self.unresolved.swapRemove(global_index); | ||
| 333 | |||
| 334 | gop.value_ptr.* = current; | ||
| 335 | } | ||
| 336 | |||
| 337 | fn resolveSymbolsInObject(self: *Zld, object_id: u32) !void { | ||
| 338 | const object = &self.objects.items[object_id]; | ||
| 339 | const in_symtab = object.in_symtab orelse return; | ||
| 340 | |||
| 341 | log.debug("resolving symbols in '{s}'", .{object.name}); | ||
| 342 | |||
| 343 | var sym_index: u32 = 0; | ||
| 344 | while (sym_index < in_symtab.len) : (sym_index += 1) { | ||
| 345 | const sym = &object.symtab[sym_index]; | ||
| 346 | const sym_name = object.getSymbolName(sym_index); | ||
| 347 | |||
| 348 | if (sym.stab()) { | ||
| 349 | log.err("unhandled symbol type: stab", .{}); | ||
| 350 | log.err(" symbol '{s}'", .{sym_name}); | ||
| 351 | log.err(" first definition in '{s}'", .{object.name}); | ||
| 352 | return error.UnhandledSymbolType; | ||
| 353 | } | ||
| 354 | |||
| 355 | if (sym.indr()) { | ||
| 356 | log.err("unhandled symbol type: indirect", .{}); | ||
| 357 | log.err(" symbol '{s}'", .{sym_name}); | ||
| 358 | log.err(" first definition in '{s}'", .{object.name}); | ||
| 359 | return error.UnhandledSymbolType; | ||
| 360 | } | ||
| 361 | |||
| 362 | if (sym.abs()) { | ||
| 363 | log.err("unhandled symbol type: absolute", .{}); | ||
| 364 | log.err(" symbol '{s}'", .{sym_name}); | ||
| 365 | log.err(" first definition in '{s}'", .{object.name}); | ||
| 366 | return error.UnhandledSymbolType; | ||
| 367 | } | ||
| 368 | |||
| 369 | if (sym.sect() and !sym.ext()) { | ||
| 370 | log.debug("symbol '{s}' local to object {s}; skipping...", .{ | ||
| 371 | sym_name, | ||
| 372 | object.name, | ||
| 373 | }); | ||
| 374 | continue; | ||
| 375 | } | ||
| 376 | |||
| 377 | try self.resolveGlobalSymbol(.{ .sym_index = sym_index, .file = object_id + 1 }); | ||
| 378 | } | ||
| 379 | } | ||
| 380 | |||
| 381 | fn resolveSymbolsInArchives(self: *Zld) !void { | ||
| 382 | if (self.archives.items.len == 0) return; | ||
| 383 | |||
| 384 | const gpa = self.gpa; | ||
| 385 | |||
| 386 | var next_sym: usize = 0; | ||
| 387 | loop: while (next_sym < self.unresolved.count()) { | ||
| 388 | const global = self.globals.items[self.unresolved.keys()[next_sym]]; | ||
| 389 | const sym_name = self.getSymbolName(global); | ||
| 390 | |||
| 391 | for (self.archives.items) |archive| { | ||
| 392 | // Check if the entry exists in a static archive. | ||
| 393 | const offsets = archive.toc.get(sym_name) orelse { | ||
| 394 | // No hit. | ||
| 395 | continue; | ||
| 396 | }; | ||
| 397 | assert(offsets.items.len > 0); | ||
| 398 | |||
| 399 | const object_id = @as(u16, @intCast(self.objects.items.len)); | ||
| 400 | const object = try archive.parseObject(gpa, offsets.items[0]); | ||
| 401 | try self.objects.append(gpa, object); | ||
| 402 | try self.resolveSymbolsInObject(object_id); | ||
| 403 | |||
| 404 | continue :loop; | ||
| 405 | } | ||
| 406 | |||
| 407 | next_sym += 1; | ||
| 408 | } | ||
| 409 | } | ||
| 410 | |||
| 411 | fn resolveSymbolsInDylibs(self: *Zld) !void { | ||
| 412 | if (self.dylibs.items.len == 0) return; | ||
| 413 | |||
| 414 | var next_sym: usize = 0; | ||
| 415 | loop: while (next_sym < self.unresolved.count()) { | ||
| 416 | const global_index = self.unresolved.keys()[next_sym]; | ||
| 417 | const global = self.globals.items[global_index]; | ||
| 418 | const sym = self.getSymbolPtr(global); | ||
| 419 | const sym_name = self.getSymbolName(global); | ||
| 420 | |||
| 421 | for (self.dylibs.items, 0..) |dylib, id| { | ||
| 422 | if (!dylib.symbols.contains(sym_name)) continue; | ||
| 423 | |||
| 424 | const dylib_id = @as(u16, @intCast(id)); | ||
| 425 | if (!self.referenced_dylibs.contains(dylib_id)) { | ||
| 426 | try self.referenced_dylibs.putNoClobber(self.gpa, dylib_id, {}); | ||
| 427 | } | ||
| 428 | |||
| 429 | const ordinal = self.referenced_dylibs.getIndex(dylib_id) orelse unreachable; | ||
| 430 | sym.n_type |= macho.N_EXT; | ||
| 431 | sym.n_desc = @as(u16, @intCast(ordinal + 1)) * macho.N_SYMBOL_RESOLVER; | ||
| 432 | |||
| 433 | if (dylib.weak) { | ||
| 434 | sym.n_desc |= macho.N_WEAK_REF; | ||
| 435 | } | ||
| 436 | |||
| 437 | assert(self.unresolved.swapRemove(global_index)); | ||
| 438 | continue :loop; | ||
| 439 | } | ||
| 440 | |||
| 441 | next_sym += 1; | ||
| 442 | } | ||
| 443 | } | ||
| 444 | |||
| 445 | fn resolveSymbolsAtLoading(self: *Zld) !void { | ||
| 446 | const is_lib = self.options.output_mode == .Lib; | ||
| 447 | const is_dyn_lib = self.options.link_mode == .Dynamic and is_lib; | ||
| 448 | const allow_undef = is_dyn_lib and (self.options.allow_shlib_undefined orelse false); | ||
| 449 | |||
| 450 | var next_sym: usize = 0; | ||
| 451 | while (next_sym < self.unresolved.count()) { | ||
| 452 | const global_index = self.unresolved.keys()[next_sym]; | ||
| 453 | const global = self.globals.items[global_index]; | ||
| 454 | const sym = self.getSymbolPtr(global); | ||
| 455 | |||
| 456 | if (sym.discarded()) { | ||
| 457 | sym.* = .{ | ||
| 458 | .n_strx = 0, | ||
| 459 | .n_type = macho.N_UNDF, | ||
| 460 | .n_sect = 0, | ||
| 461 | .n_desc = 0, | ||
| 462 | .n_value = 0, | ||
| 463 | }; | ||
| 464 | _ = self.unresolved.swapRemove(global_index); | ||
| 465 | continue; | ||
| 466 | } else if (allow_undef) { | ||
| 467 | const n_desc = @as( | ||
| 468 | u16, | ||
| 469 | @bitCast(macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP * @as(i16, @intCast(macho.N_SYMBOL_RESOLVER))), | ||
| 470 | ); | ||
| 471 | sym.n_type = macho.N_EXT; | ||
| 472 | sym.n_desc = n_desc; | ||
| 473 | _ = self.unresolved.swapRemove(global_index); | ||
| 474 | continue; | ||
| 475 | } | ||
| 476 | |||
| 477 | next_sym += 1; | ||
| 478 | } | ||
| 479 | } | ||
| 480 | |||
| 481 | fn createMhExecuteHeaderSymbol(self: *Zld) !void { | ||
| 482 | if (self.options.output_mode != .Exe) return; | ||
| 483 | |||
| 484 | const gpa = self.gpa; | ||
| 485 | const sym_index = try self.allocateSymbol(); | ||
| 486 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index }; | ||
| 487 | const sym = self.getSymbolPtr(sym_loc); | ||
| 488 | sym.* = .{ | ||
| 489 | .n_strx = try self.strtab.insert(gpa, "__mh_execute_header"), | ||
| 490 | .n_type = macho.N_SECT | macho.N_EXT, | ||
| 491 | .n_sect = 0, | ||
| 492 | .n_desc = macho.REFERENCED_DYNAMICALLY, | ||
| 493 | .n_value = 0, | ||
| 494 | }; | ||
| 495 | |||
| 496 | const gop = try self.getOrPutGlobalPtr("__mh_execute_header"); | ||
| 497 | if (gop.found_existing) { | ||
| 498 | const global = gop.value_ptr.*; | ||
| 499 | if (global.getFile()) |file| { | ||
| 500 | const global_object = &self.objects.items[file]; | ||
| 501 | global_object.globals_lookup[global.sym_index] = self.getGlobalIndex("__mh_execute_header").?; | ||
| 502 | } | ||
| 503 | } | ||
| 504 | gop.value_ptr.* = sym_loc; | ||
| 505 | } | ||
| 506 | |||
| 507 | fn createDsoHandleSymbol(self: *Zld) !void { | ||
| 508 | const global = self.getGlobalPtr("___dso_handle") orelse return; | ||
| 509 | if (!self.getSymbol(global.*).undf()) return; | ||
| 510 | |||
| 511 | const sym_index = try self.allocateSymbol(); | ||
| 512 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index }; | ||
| 513 | const sym = self.getSymbolPtr(sym_loc); | ||
| 514 | sym.* = .{ | ||
| 515 | .n_strx = try self.strtab.insert(self.gpa, "___dso_handle"), | ||
| 516 | .n_type = macho.N_SECT | macho.N_EXT, | ||
| 517 | .n_sect = 0, | ||
| 518 | .n_desc = macho.N_WEAK_DEF, | ||
| 519 | .n_value = 0, | ||
| 520 | }; | ||
| 521 | const global_index = self.getGlobalIndex("___dso_handle").?; | ||
| 522 | if (global.getFile()) |file| { | ||
| 523 | const global_object = &self.objects.items[file]; | ||
| 524 | global_object.globals_lookup[global.sym_index] = global_index; | ||
| 525 | } | ||
| 526 | global.* = sym_loc; | ||
| 527 | _ = self.unresolved.swapRemove(global_index); | ||
| 528 | } | ||
| 529 | |||
| 530 | pub fn deinit(self: *Zld) void { | ||
| 531 | const gpa = self.gpa; | ||
| 532 | |||
| 533 | self.tlv_ptr_table.deinit(gpa); | ||
| 534 | self.got_table.deinit(gpa); | ||
| 535 | self.stubs_table.deinit(gpa); | ||
| 536 | self.thunk_table.deinit(gpa); | ||
| 537 | |||
| 538 | for (self.thunks.items) |*thunk| { | ||
| 539 | thunk.deinit(gpa); | ||
| 540 | } | ||
| 541 | self.thunks.deinit(gpa); | ||
| 542 | |||
| 543 | self.strtab.deinit(gpa); | ||
| 544 | self.locals.deinit(gpa); | ||
| 545 | self.globals.deinit(gpa); | ||
| 546 | self.resolver.deinit(gpa); | ||
| 547 | self.unresolved.deinit(gpa); | ||
| 548 | self.locals_free_list.deinit(gpa); | ||
| 549 | self.globals_free_list.deinit(gpa); | ||
| 550 | |||
| 551 | for (self.objects.items) |*object| { | ||
| 552 | object.deinit(gpa); | ||
| 553 | } | ||
| 554 | self.objects.deinit(gpa); | ||
| 555 | for (self.archives.items) |*archive| { | ||
| 556 | archive.deinit(gpa); | ||
| 557 | } | ||
| 558 | self.archives.deinit(gpa); | ||
| 559 | for (self.dylibs.items) |*dylib| { | ||
| 560 | dylib.deinit(gpa); | ||
| 561 | } | ||
| 562 | self.dylibs.deinit(gpa); | ||
| 563 | self.dylibs_map.deinit(gpa); | ||
| 564 | self.referenced_dylibs.deinit(gpa); | ||
| 565 | |||
| 566 | self.segments.deinit(gpa); | ||
| 567 | self.sections.deinit(gpa); | ||
| 568 | self.atoms.deinit(gpa); | ||
| 569 | } | ||
| 570 | |||
| 571 | fn createSegments(self: *Zld) !void { | ||
| 572 | const pagezero_vmsize = self.options.pagezero_size orelse MachO.default_pagezero_vmsize; | ||
| 573 | const page_size = MachO.getPageSize(self.options.target.cpu.arch); | ||
| 574 | const aligned_pagezero_vmsize = mem.alignBackward(u64, pagezero_vmsize, page_size); | ||
| 575 | if (self.options.output_mode != .Lib and aligned_pagezero_vmsize > 0) { | ||
| 576 | if (aligned_pagezero_vmsize != pagezero_vmsize) { | ||
| 577 | log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_vmsize}); | ||
| 578 | log.warn(" rounding down to 0x{x}", .{aligned_pagezero_vmsize}); | ||
| 579 | } | ||
| 580 | self.pagezero_segment_cmd_index = @intCast(self.segments.items.len); | ||
| 581 | try self.segments.append(self.gpa, .{ | ||
| 582 | .cmdsize = @sizeOf(macho.segment_command_64), | ||
| 583 | .segname = makeStaticString("__PAGEZERO"), | ||
| 584 | .vmsize = aligned_pagezero_vmsize, | ||
| 585 | }); | ||
| 586 | } | ||
| 587 | |||
| 588 | // __TEXT segment is non-optional | ||
| 589 | { | ||
| 590 | const protection = MachO.getSegmentMemoryProtection("__TEXT"); | ||
| 591 | self.text_segment_cmd_index = @intCast(self.segments.items.len); | ||
| 592 | self.header_segment_cmd_index = self.text_segment_cmd_index.?; | ||
| 593 | try self.segments.append(self.gpa, .{ | ||
| 594 | .cmdsize = @sizeOf(macho.segment_command_64), | ||
| 595 | .segname = makeStaticString("__TEXT"), | ||
| 596 | .maxprot = protection, | ||
| 597 | .initprot = protection, | ||
| 598 | }); | ||
| 599 | } | ||
| 600 | |||
| 601 | for (self.sections.items(.header), 0..) |header, sect_id| { | ||
| 602 | if (header.size == 0) continue; // empty section | ||
| 603 | |||
| 604 | const segname = header.segName(); | ||
| 605 | const segment_id = self.getSegmentByName(segname) orelse blk: { | ||
| 606 | log.debug("creating segment '{s}'", .{segname}); | ||
| 607 | const segment_id = @as(u8, @intCast(self.segments.items.len)); | ||
| 608 | const protection = MachO.getSegmentMemoryProtection(segname); | ||
| 609 | try self.segments.append(self.gpa, .{ | ||
| 610 | .cmdsize = @sizeOf(macho.segment_command_64), | ||
| 611 | .segname = makeStaticString(segname), | ||
| 612 | .maxprot = protection, | ||
| 613 | .initprot = protection, | ||
| 614 | }); | ||
| 615 | break :blk segment_id; | ||
| 616 | }; | ||
| 617 | const segment = &self.segments.items[segment_id]; | ||
| 618 | segment.cmdsize += @sizeOf(macho.section_64); | ||
| 619 | segment.nsects += 1; | ||
| 620 | self.sections.items(.segment_index)[sect_id] = segment_id; | ||
| 621 | } | ||
| 622 | |||
| 623 | if (self.getSegmentByName("__DATA_CONST")) |index| { | ||
| 624 | self.data_const_segment_cmd_index = index; | ||
| 625 | } | ||
| 626 | |||
| 627 | if (self.getSegmentByName("__DATA")) |index| { | ||
| 628 | self.data_segment_cmd_index = index; | ||
| 629 | } | ||
| 630 | |||
| 631 | // __LINKEDIT always comes last | ||
| 632 | { | ||
| 633 | const protection = MachO.getSegmentMemoryProtection("__LINKEDIT"); | ||
| 634 | self.linkedit_segment_cmd_index = @intCast(self.segments.items.len); | ||
| 635 | try self.segments.append(self.gpa, .{ | ||
| 636 | .cmdsize = @sizeOf(macho.segment_command_64), | ||
| 637 | .segname = makeStaticString("__LINKEDIT"), | ||
| 638 | .maxprot = protection, | ||
| 639 | .initprot = protection, | ||
| 640 | }); | ||
| 641 | } | ||
| 642 | } | ||
| 643 | |||
| 644 | pub fn allocateSymbol(self: *Zld) !u32 { | ||
| 645 | try self.locals.ensureUnusedCapacity(self.gpa, 1); | ||
| 646 | log.debug(" (allocating symbol index {d})", .{self.locals.items.len}); | ||
| 647 | const index = @as(u32, @intCast(self.locals.items.len)); | ||
| 648 | _ = self.locals.addOneAssumeCapacity(); | ||
| 649 | self.locals.items[index] = .{ | ||
| 650 | .n_strx = 0, | ||
| 651 | .n_type = 0, | ||
| 652 | .n_sect = 0, | ||
| 653 | .n_desc = 0, | ||
| 654 | .n_value = 0, | ||
| 655 | }; | ||
| 656 | return index; | ||
| 657 | } | ||
| 658 | |||
| 659 | fn allocateGlobal(self: *Zld) !u32 { | ||
| 660 | try self.globals.ensureUnusedCapacity(self.gpa, 1); | ||
| 661 | |||
| 662 | const index = blk: { | ||
| 663 | if (self.globals_free_list.popOrNull()) |index| { | ||
| 664 | log.debug(" (reusing global index {d})", .{index}); | ||
| 665 | break :blk index; | ||
| 666 | } else { | ||
| 667 | log.debug(" (allocating symbol index {d})", .{self.globals.items.len}); | ||
| 668 | const index = @as(u32, @intCast(self.globals.items.len)); | ||
| 669 | _ = self.globals.addOneAssumeCapacity(); | ||
| 670 | break :blk index; | ||
| 671 | } | ||
| 672 | }; | ||
| 673 | |||
| 674 | self.globals.items[index] = .{ .sym_index = 0 }; | ||
| 675 | |||
| 676 | return index; | ||
| 677 | } | ||
| 678 | |||
| 679 | pub fn addGotEntry(self: *Zld, target: SymbolWithLoc) !void { | ||
| 680 | if (self.got_table.lookup.contains(target)) return; | ||
| 681 | _ = try self.got_table.allocateEntry(self.gpa, target); | ||
| 682 | if (self.got_section_index == null) { | ||
| 683 | self.got_section_index = try MachO.initSection(self.gpa, self, "__DATA_CONST", "__got", .{ | ||
| 684 | .flags = macho.S_NON_LAZY_SYMBOL_POINTERS, | ||
| 685 | }); | ||
| 686 | } | ||
| 687 | } | ||
| 688 | |||
| 689 | pub fn addTlvPtrEntry(self: *Zld, target: SymbolWithLoc) !void { | ||
| 690 | if (self.tlv_ptr_table.lookup.contains(target)) return; | ||
| 691 | _ = try self.tlv_ptr_table.allocateEntry(self.gpa, target); | ||
| 692 | if (self.tlv_ptr_section_index == null) { | ||
| 693 | self.tlv_ptr_section_index = try MachO.initSection(self.gpa, self, "__DATA", "__thread_ptrs", .{ | ||
| 694 | .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS, | ||
| 695 | }); | ||
| 696 | } | ||
| 697 | } | ||
| 698 | |||
| 699 | pub fn addStubEntry(self: *Zld, target: SymbolWithLoc) !void { | ||
| 700 | if (self.stubs_table.lookup.contains(target)) return; | ||
| 701 | _ = try self.stubs_table.allocateEntry(self.gpa, target); | ||
| 702 | if (self.stubs_section_index == null) { | ||
| 703 | self.stubs_section_index = try MachO.initSection(self.gpa, self, "__TEXT", "__stubs", .{ | ||
| 704 | .flags = macho.S_SYMBOL_STUBS | | ||
| 705 | macho.S_ATTR_PURE_INSTRUCTIONS | | ||
| 706 | macho.S_ATTR_SOME_INSTRUCTIONS, | ||
| 707 | .reserved2 = stubs.stubSize(self.options.target.cpu.arch), | ||
| 708 | }); | ||
| 709 | self.stub_helper_section_index = try MachO.initSection(self.gpa, self, "__TEXT", "__stub_helper", .{ | ||
| 710 | .flags = macho.S_REGULAR | | ||
| 711 | macho.S_ATTR_PURE_INSTRUCTIONS | | ||
| 712 | macho.S_ATTR_SOME_INSTRUCTIONS, | ||
| 713 | }); | ||
| 714 | self.la_symbol_ptr_section_index = try MachO.initSection(self.gpa, self, "__DATA", "__la_symbol_ptr", .{ | ||
| 715 | .flags = macho.S_LAZY_SYMBOL_POINTERS, | ||
| 716 | }); | ||
| 717 | } | ||
| 718 | } | ||
| 719 | |||
| 720 | fn writeAtoms(self: *Zld) !void { | ||
| 721 | const gpa = self.gpa; | ||
| 722 | const slice = self.sections.slice(); | ||
| 723 | |||
| 724 | for (slice.items(.first_atom_index), 0..) |first_atom_index, sect_id| { | ||
| 725 | const header = slice.items(.header)[sect_id]; | ||
| 726 | if (header.isZerofill()) continue; | ||
| 727 | |||
| 728 | var atom_index = first_atom_index orelse continue; | ||
| 729 | |||
| 730 | var buffer = try gpa.alloc(u8, math.cast(usize, header.size) orelse return error.Overflow); | ||
| 731 | defer gpa.free(buffer); | ||
| 732 | @memset(buffer, 0); // TODO with NOPs | ||
| 733 | |||
| 734 | log.debug("writing atoms in {s},{s}", .{ header.segName(), header.sectName() }); | ||
| 735 | |||
| 736 | while (true) { | ||
| 737 | const atom = self.getAtom(atom_index); | ||
| 738 | if (atom.getFile()) |file| { | ||
| 739 | const this_sym = self.getSymbol(atom.getSymbolWithLoc()); | ||
| 740 | const padding_size: usize = if (atom.next_index) |next_index| blk: { | ||
| 741 | const next_sym = self.getSymbol(self.getAtom(next_index).getSymbolWithLoc()); | ||
| 742 | const size = next_sym.n_value - (this_sym.n_value + atom.size); | ||
| 743 | break :blk math.cast(usize, size) orelse return error.Overflow; | ||
| 744 | } else 0; | ||
| 745 | |||
| 746 | log.debug(" (adding ATOM(%{d}, '{s}') from object({d}) to buffer)", .{ | ||
| 747 | atom.sym_index, | ||
| 748 | self.getSymbolName(atom.getSymbolWithLoc()), | ||
| 749 | file, | ||
| 750 | }); | ||
| 751 | if (padding_size > 0) { | ||
| 752 | log.debug(" (with padding {x})", .{padding_size}); | ||
| 753 | } | ||
| 754 | |||
| 755 | const offset = this_sym.n_value - header.addr; | ||
| 756 | log.debug(" (at offset 0x{x})", .{offset}); | ||
| 757 | |||
| 758 | const code = Atom.getAtomCode(self, atom_index); | ||
| 759 | const relocs = Atom.getAtomRelocs(self, atom_index); | ||
| 760 | const size = math.cast(usize, atom.size) orelse return error.Overflow; | ||
| 761 | @memcpy(buffer[offset .. offset + size], code); | ||
| 762 | try Atom.resolveRelocs( | ||
| 763 | self, | ||
| 764 | atom_index, | ||
| 765 | buffer[offset..][0..size], | ||
| 766 | relocs, | ||
| 767 | ); | ||
| 768 | } | ||
| 769 | |||
| 770 | if (atom.next_index) |next_index| { | ||
| 771 | atom_index = next_index; | ||
| 772 | } else break; | ||
| 773 | } | ||
| 774 | |||
| 775 | log.debug(" (writing at file offset 0x{x})", .{header.offset}); | ||
| 776 | try self.file.pwriteAll(buffer, header.offset); | ||
| 777 | } | ||
| 778 | } | ||
| 779 | |||
| 780 | fn writeDyldPrivateAtom(self: *Zld) !void { | ||
| 781 | const atom_index = self.dyld_private_atom_index orelse return; | ||
| 782 | const atom = self.getAtom(atom_index); | ||
| 783 | const sym = self.getSymbol(atom.getSymbolWithLoc()); | ||
| 784 | const sect_id = self.data_section_index.?; | ||
| 785 | const header = self.sections.items(.header)[sect_id]; | ||
| 786 | const offset = sym.n_value - header.addr + header.offset; | ||
| 787 | log.debug("writing __dyld_private at offset 0x{x}", .{offset}); | ||
| 788 | const buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64); | ||
| 789 | try self.file.pwriteAll(&buffer, offset); | ||
| 790 | } | ||
| 791 | |||
| 792 | fn writeThunks(self: *Zld) !void { | ||
| 793 | assert(self.requiresThunks()); | ||
| 794 | const gpa = self.gpa; | ||
| 795 | |||
| 796 | const sect_id = self.text_section_index orelse return; | ||
| 797 | const header = self.sections.items(.header)[sect_id]; | ||
| 798 | |||
| 799 | for (self.thunks.items, 0..) |*thunk, i| { | ||
| 800 | if (thunk.getSize() == 0) continue; | ||
| 801 | var buffer = try std.ArrayList(u8).initCapacity(gpa, thunk.getSize()); | ||
| 802 | defer buffer.deinit(); | ||
| 803 | try thunks.writeThunkCode(self, thunk, buffer.writer()); | ||
| 804 | const thunk_atom = self.getAtom(thunk.getStartAtomIndex()); | ||
| 805 | const thunk_sym = self.getSymbol(thunk_atom.getSymbolWithLoc()); | ||
| 806 | const offset = thunk_sym.n_value - header.addr + header.offset; | ||
| 807 | log.debug("writing thunk({d}) at offset 0x{x}", .{ i, offset }); | ||
| 808 | try self.file.pwriteAll(buffer.items, offset); | ||
| 809 | } | ||
| 810 | } | ||
| 811 | |||
| 812 | fn writePointerEntries(self: *Zld, sect_id: u8, table: anytype) !void { | ||
| 813 | const header = self.sections.items(.header)[sect_id]; | ||
| 814 | var buffer = try std.ArrayList(u8).initCapacity(self.gpa, header.size); | ||
| 815 | defer buffer.deinit(); | ||
| 816 | for (table.entries.items) |entry| { | ||
| 817 | const sym = self.getSymbol(entry); | ||
| 818 | buffer.writer().writeIntLittle(u64, sym.n_value) catch unreachable; | ||
| 819 | } | ||
| 820 | log.debug("writing __DATA_CONST,__got contents at file offset 0x{x}", .{header.offset}); | ||
| 821 | try self.file.pwriteAll(buffer.items, header.offset); | ||
| 822 | } | ||
| 823 | |||
| 824 | fn writeStubs(self: *Zld) !void { | ||
| 825 | const gpa = self.gpa; | ||
| 826 | const cpu_arch = self.options.target.cpu.arch; | ||
| 827 | const stubs_header = self.sections.items(.header)[self.stubs_section_index.?]; | ||
| 828 | const la_symbol_ptr_header = self.sections.items(.header)[self.la_symbol_ptr_section_index.?]; | ||
| 829 | |||
| 830 | var buffer = try std.ArrayList(u8).initCapacity(gpa, stubs_header.size); | ||
| 831 | defer buffer.deinit(); | ||
| 832 | |||
| 833 | for (0..self.stubs_table.count()) |index| { | ||
| 834 | try stubs.writeStubCode(.{ | ||
| 835 | .cpu_arch = cpu_arch, | ||
| 836 | .source_addr = stubs_header.addr + stubs.stubSize(cpu_arch) * index, | ||
| 837 | .target_addr = la_symbol_ptr_header.addr + index * @sizeOf(u64), | ||
| 838 | }, buffer.writer()); | ||
| 839 | } | ||
| 840 | |||
| 841 | log.debug("writing __TEXT,__stubs contents at file offset 0x{x}", .{stubs_header.offset}); | ||
| 842 | try self.file.pwriteAll(buffer.items, stubs_header.offset); | ||
| 843 | } | ||
| 844 | |||
| 845 | fn writeStubHelpers(self: *Zld) !void { | ||
| 846 | const gpa = self.gpa; | ||
| 847 | const cpu_arch = self.options.target.cpu.arch; | ||
| 848 | const stub_helper_header = self.sections.items(.header)[self.stub_helper_section_index.?]; | ||
| 849 | |||
| 850 | var buffer = try std.ArrayList(u8).initCapacity(gpa, stub_helper_header.size); | ||
| 851 | defer buffer.deinit(); | ||
| 852 | |||
| 853 | { | ||
| 854 | const dyld_private_addr = blk: { | ||
| 855 | const atom = self.getAtom(self.dyld_private_atom_index.?); | ||
| 856 | const sym = self.getSymbol(atom.getSymbolWithLoc()); | ||
| 857 | break :blk sym.n_value; | ||
| 858 | }; | ||
| 859 | const dyld_stub_binder_got_addr = blk: { | ||
| 860 | const sym_loc = self.globals.items[self.dyld_stub_binder_index.?]; | ||
| 861 | break :blk self.getGotEntryAddress(sym_loc).?; | ||
| 862 | }; | ||
| 863 | try stubs.writeStubHelperPreambleCode(.{ | ||
| 864 | .cpu_arch = cpu_arch, | ||
| 865 | .source_addr = stub_helper_header.addr, | ||
| 866 | .dyld_private_addr = dyld_private_addr, | ||
| 867 | .dyld_stub_binder_got_addr = dyld_stub_binder_got_addr, | ||
| 868 | }, buffer.writer()); | ||
| 869 | } | ||
| 870 | |||
| 871 | for (0..self.stubs_table.count()) |index| { | ||
| 872 | const source_addr = stub_helper_header.addr + stubs.stubHelperPreambleSize(cpu_arch) + | ||
| 873 | stubs.stubHelperSize(cpu_arch) * index; | ||
| 874 | try stubs.writeStubHelperCode(.{ | ||
| 875 | .cpu_arch = cpu_arch, | ||
| 876 | .source_addr = source_addr, | ||
| 877 | .target_addr = stub_helper_header.addr, | ||
| 878 | }, buffer.writer()); | ||
| 879 | } | ||
| 880 | |||
| 881 | log.debug("writing __TEXT,__stub_helper contents at file offset 0x{x}", .{ | ||
| 882 | stub_helper_header.offset, | ||
| 883 | }); | ||
| 884 | try self.file.pwriteAll(buffer.items, stub_helper_header.offset); | ||
| 885 | } | ||
| 886 | |||
| 887 | fn writeLaSymbolPtrs(self: *Zld) !void { | ||
| 888 | const gpa = self.gpa; | ||
| 889 | const cpu_arch = self.options.target.cpu.arch; | ||
| 890 | const la_symbol_ptr_header = self.sections.items(.header)[self.la_symbol_ptr_section_index.?]; | ||
| 891 | const stub_helper_header = self.sections.items(.header)[self.stub_helper_section_index.?]; | ||
| 892 | |||
| 893 | var buffer = try std.ArrayList(u8).initCapacity(gpa, la_symbol_ptr_header.size); | ||
| 894 | defer buffer.deinit(); | ||
| 895 | |||
| 896 | for (0..self.stubs_table.count()) |index| { | ||
| 897 | const target_addr = stub_helper_header.addr + stubs.stubHelperPreambleSize(cpu_arch) + | ||
| 898 | stubs.stubHelperSize(cpu_arch) * index; | ||
| 899 | buffer.writer().writeIntLittle(u64, target_addr) catch unreachable; | ||
| 900 | } | ||
| 901 | |||
| 902 | log.debug("writing __DATA,__la_symbol_ptr contents at file offset 0x{x}", .{ | ||
| 903 | la_symbol_ptr_header.offset, | ||
| 904 | }); | ||
| 905 | try self.file.pwriteAll(buffer.items, la_symbol_ptr_header.offset); | ||
| 906 | } | ||
| 907 | |||
| 908 | fn pruneAndSortSections(self: *Zld) !void { | ||
| 909 | const Entry = struct { | ||
| 910 | index: u8, | ||
| 911 | |||
| 912 | pub fn lessThan(zld: *Zld, lhs: @This(), rhs: @This()) bool { | ||
| 913 | const lhs_header = zld.sections.items(.header)[lhs.index]; | ||
| 914 | const rhs_header = zld.sections.items(.header)[rhs.index]; | ||
| 915 | return MachO.getSectionPrecedence(lhs_header) < MachO.getSectionPrecedence(rhs_header); | ||
| 916 | } | ||
| 917 | }; | ||
| 918 | |||
| 919 | const gpa = self.gpa; | ||
| 920 | |||
| 921 | var entries = try std.ArrayList(Entry).initCapacity(gpa, self.sections.slice().len); | ||
| 922 | defer entries.deinit(); | ||
| 923 | |||
| 924 | for (0..self.sections.slice().len) |index| { | ||
| 925 | const section = self.sections.get(index); | ||
| 926 | if (section.header.size == 0) { | ||
| 927 | log.debug("pruning section {s},{s} {?d}", .{ | ||
| 928 | section.header.segName(), | ||
| 929 | section.header.sectName(), | ||
| 930 | section.first_atom_index, | ||
| 931 | }); | ||
| 932 | for (&[_]*?u8{ | ||
| 933 | &self.text_section_index, | ||
| 934 | &self.data_const_section_index, | ||
| 935 | &self.data_section_index, | ||
| 936 | &self.bss_section_index, | ||
| 937 | &self.thread_vars_section_index, | ||
| 938 | &self.thread_data_section_index, | ||
| 939 | &self.thread_bss_section_index, | ||
| 940 | &self.eh_frame_section_index, | ||
| 941 | &self.unwind_info_section_index, | ||
| 942 | &self.got_section_index, | ||
| 943 | &self.tlv_ptr_section_index, | ||
| 944 | &self.stubs_section_index, | ||
| 945 | &self.stub_helper_section_index, | ||
| 946 | &self.la_symbol_ptr_section_index, | ||
| 947 | }) |maybe_index| { | ||
| 948 | if (maybe_index.* != null and maybe_index.*.? == index) { | ||
| 949 | maybe_index.* = null; | ||
| 950 | } | ||
| 951 | } | ||
| 952 | continue; | ||
| 953 | } | ||
| 954 | entries.appendAssumeCapacity(.{ .index = @intCast(index) }); | ||
| 955 | } | ||
| 956 | |||
| 957 | mem.sort(Entry, entries.items, self, Entry.lessThan); | ||
| 958 | |||
| 959 | var slice = self.sections.toOwnedSlice(); | ||
| 960 | defer slice.deinit(gpa); | ||
| 961 | |||
| 962 | const backlinks = try gpa.alloc(u8, slice.len); | ||
| 963 | defer gpa.free(backlinks); | ||
| 964 | for (entries.items, 0..) |entry, i| { | ||
| 965 | backlinks[entry.index] = @as(u8, @intCast(i)); | ||
| 966 | } | ||
| 967 | |||
| 968 | try self.sections.ensureTotalCapacity(gpa, entries.items.len); | ||
| 969 | for (entries.items) |entry| { | ||
| 970 | self.sections.appendAssumeCapacity(slice.get(entry.index)); | ||
| 971 | } | ||
| 972 | |||
| 973 | for (&[_]*?u8{ | ||
| 974 | &self.text_section_index, | ||
| 975 | &self.data_const_section_index, | ||
| 976 | &self.data_section_index, | ||
| 977 | &self.bss_section_index, | ||
| 978 | &self.thread_vars_section_index, | ||
| 979 | &self.thread_data_section_index, | ||
| 980 | &self.thread_bss_section_index, | ||
| 981 | &self.eh_frame_section_index, | ||
| 982 | &self.unwind_info_section_index, | ||
| 983 | &self.got_section_index, | ||
| 984 | &self.tlv_ptr_section_index, | ||
| 985 | &self.stubs_section_index, | ||
| 986 | &self.stub_helper_section_index, | ||
| 987 | &self.la_symbol_ptr_section_index, | ||
| 988 | }) |maybe_index| { | ||
| 989 | if (maybe_index.*) |*index| { | ||
| 990 | index.* = backlinks[index.*]; | ||
| 991 | } | ||
| 992 | } | ||
| 993 | } | ||
| 994 | |||
| 995 | fn calcSectionSizes(self: *Zld) !void { | ||
| 996 | const slice = self.sections.slice(); | ||
| 997 | for (slice.items(.header), 0..) |*header, sect_id| { | ||
| 998 | if (header.size == 0) continue; | ||
| 999 | if (self.text_section_index) |txt| { | ||
| 1000 | if (txt == sect_id and self.requiresThunks()) continue; | ||
| 1001 | } | ||
| 1002 | |||
| 1003 | var atom_index = slice.items(.first_atom_index)[sect_id] orelse continue; | ||
| 1004 | |||
| 1005 | header.size = 0; | ||
| 1006 | header.@"align" = 0; | ||
| 1007 | |||
| 1008 | while (true) { | ||
| 1009 | const atom = self.getAtom(atom_index); | ||
| 1010 | const atom_alignment = try math.powi(u32, 2, atom.alignment); | ||
| 1011 | const atom_offset = mem.alignForward(u64, header.size, atom_alignment); | ||
| 1012 | const padding = atom_offset - header.size; | ||
| 1013 | |||
| 1014 | const sym = self.getSymbolPtr(atom.getSymbolWithLoc()); | ||
| 1015 | sym.n_value = atom_offset; | ||
| 1016 | |||
| 1017 | header.size += padding + atom.size; | ||
| 1018 | header.@"align" = @max(header.@"align", atom.alignment); | ||
| 1019 | |||
| 1020 | if (atom.next_index) |next_index| { | ||
| 1021 | atom_index = next_index; | ||
| 1022 | } else break; | ||
| 1023 | } | ||
| 1024 | } | ||
| 1025 | |||
| 1026 | if (self.text_section_index != null and self.requiresThunks()) { | ||
| 1027 | // Create jump/branch range extenders if needed. | ||
| 1028 | try thunks.createThunks(self, self.text_section_index.?); | ||
| 1029 | } | ||
| 1030 | |||
| 1031 | // Update offsets of all symbols contained within each Atom. | ||
| 1032 | // We need to do this since our unwind info synthesiser relies on | ||
| 1033 | // traversing the symbols when synthesising unwind info and DWARF CFI records. | ||
| 1034 | for (slice.items(.first_atom_index)) |first_atom_index| { | ||
| 1035 | var atom_index = first_atom_index orelse continue; | ||
| 1036 | |||
| 1037 | while (true) { | ||
| 1038 | const atom = self.getAtom(atom_index); | ||
| 1039 | const sym = self.getSymbol(atom.getSymbolWithLoc()); | ||
| 1040 | |||
| 1041 | if (atom.getFile() != null) { | ||
| 1042 | // Update each symbol contained within the atom | ||
| 1043 | var it = Atom.getInnerSymbolsIterator(self, atom_index); | ||
| 1044 | while (it.next()) |sym_loc| { | ||
| 1045 | const inner_sym = self.getSymbolPtr(sym_loc); | ||
| 1046 | inner_sym.n_value = sym.n_value + Atom.calcInnerSymbolOffset( | ||
| 1047 | self, | ||
| 1048 | atom_index, | ||
| 1049 | sym_loc.sym_index, | ||
| 1050 | ); | ||
| 1051 | } | ||
| 1052 | |||
| 1053 | // If there is a section alias, update it now too | ||
| 1054 | if (Atom.getSectionAlias(self, atom_index)) |sym_loc| { | ||
| 1055 | const alias = self.getSymbolPtr(sym_loc); | ||
| 1056 | alias.n_value = sym.n_value; | ||
| 1057 | } | ||
| 1058 | } | ||
| 1059 | |||
| 1060 | if (atom.next_index) |next_index| { | ||
| 1061 | atom_index = next_index; | ||
| 1062 | } else break; | ||
| 1063 | } | ||
| 1064 | } | ||
| 1065 | |||
| 1066 | if (self.got_section_index) |sect_id| { | ||
| 1067 | const header = &self.sections.items(.header)[sect_id]; | ||
| 1068 | header.size = self.got_table.count() * @sizeOf(u64); | ||
| 1069 | header.@"align" = 3; | ||
| 1070 | } | ||
| 1071 | |||
| 1072 | if (self.tlv_ptr_section_index) |sect_id| { | ||
| 1073 | const header = &self.sections.items(.header)[sect_id]; | ||
| 1074 | header.size = self.tlv_ptr_table.count() * @sizeOf(u64); | ||
| 1075 | header.@"align" = 3; | ||
| 1076 | } | ||
| 1077 | |||
| 1078 | const cpu_arch = self.options.target.cpu.arch; | ||
| 1079 | |||
| 1080 | if (self.stubs_section_index) |sect_id| { | ||
| 1081 | const header = &self.sections.items(.header)[sect_id]; | ||
| 1082 | header.size = self.stubs_table.count() * stubs.stubSize(cpu_arch); | ||
| 1083 | header.@"align" = stubs.stubAlignment(cpu_arch); | ||
| 1084 | } | ||
| 1085 | |||
| 1086 | if (self.stub_helper_section_index) |sect_id| { | ||
| 1087 | const header = &self.sections.items(.header)[sect_id]; | ||
| 1088 | header.size = self.stubs_table.count() * stubs.stubHelperSize(cpu_arch) + | ||
| 1089 | stubs.stubHelperPreambleSize(cpu_arch); | ||
| 1090 | header.@"align" = stubs.stubAlignment(cpu_arch); | ||
| 1091 | } | ||
| 1092 | |||
| 1093 | if (self.la_symbol_ptr_section_index) |sect_id| { | ||
| 1094 | const header = &self.sections.items(.header)[sect_id]; | ||
| 1095 | header.size = self.stubs_table.count() * @sizeOf(u64); | ||
| 1096 | header.@"align" = 3; | ||
| 1097 | } | ||
| 1098 | } | ||
| 1099 | |||
| 1100 | fn allocateSegments(self: *Zld) !void { | ||
| 1101 | for (self.segments.items, 0..) |*segment, segment_index| { | ||
| 1102 | const is_text_segment = mem.eql(u8, segment.segName(), "__TEXT"); | ||
| 1103 | const base_size = if (is_text_segment) try load_commands.calcMinHeaderPad(self.gpa, self.options, .{ | ||
| 1104 | .segments = self.segments.items, | ||
| 1105 | .dylibs = self.dylibs.items, | ||
| 1106 | .referenced_dylibs = self.referenced_dylibs.keys(), | ||
| 1107 | }) else 0; | ||
| 1108 | try self.allocateSegment(@as(u8, @intCast(segment_index)), base_size); | ||
| 1109 | } | ||
| 1110 | } | ||
| 1111 | |||
| 1112 | fn getSegmentAllocBase(self: Zld, segment_index: u8) struct { vmaddr: u64, fileoff: u64 } { | ||
| 1113 | if (segment_index > 0) { | ||
| 1114 | const prev_segment = self.segments.items[segment_index - 1]; | ||
| 1115 | return .{ | ||
| 1116 | .vmaddr = prev_segment.vmaddr + prev_segment.vmsize, | ||
| 1117 | .fileoff = prev_segment.fileoff + prev_segment.filesize, | ||
| 1118 | }; | ||
| 1119 | } | ||
| 1120 | return .{ .vmaddr = 0, .fileoff = 0 }; | ||
| 1121 | } | ||
| 1122 | |||
| 1123 | fn allocateSegment(self: *Zld, segment_index: u8, init_size: u64) !void { | ||
| 1124 | const segment = &self.segments.items[segment_index]; | ||
| 1125 | |||
| 1126 | if (mem.eql(u8, segment.segName(), "__PAGEZERO")) return; // allocated upon creation | ||
| 1127 | |||
| 1128 | const base = self.getSegmentAllocBase(segment_index); | ||
| 1129 | segment.vmaddr = base.vmaddr; | ||
| 1130 | segment.fileoff = base.fileoff; | ||
| 1131 | segment.filesize = init_size; | ||
| 1132 | segment.vmsize = init_size; | ||
| 1133 | |||
| 1134 | // Allocate the sections according to their alignment at the beginning of the segment. | ||
| 1135 | const indexes = self.getSectionIndexes(segment_index); | ||
| 1136 | var start = init_size; | ||
| 1137 | |||
| 1138 | const slice = self.sections.slice(); | ||
| 1139 | for (slice.items(.header)[indexes.start..indexes.end], 0..) |*header, sect_id| { | ||
| 1140 | const alignment = try math.powi(u32, 2, header.@"align"); | ||
| 1141 | const start_aligned = mem.alignForward(u64, start, alignment); | ||
| 1142 | const n_sect = @as(u8, @intCast(indexes.start + sect_id + 1)); | ||
| 1143 | |||
| 1144 | header.offset = if (header.isZerofill()) | ||
| 1145 | 0 | ||
| 1146 | else | ||
| 1147 | @as(u32, @intCast(segment.fileoff + start_aligned)); | ||
| 1148 | header.addr = segment.vmaddr + start_aligned; | ||
| 1149 | |||
| 1150 | if (slice.items(.first_atom_index)[indexes.start + sect_id]) |first_atom_index| { | ||
| 1151 | var atom_index = first_atom_index; | ||
| 1152 | |||
| 1153 | log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{ | ||
| 1154 | n_sect, | ||
| 1155 | header.segName(), | ||
| 1156 | header.sectName(), | ||
| 1157 | }); | ||
| 1158 | |||
| 1159 | while (true) { | ||
| 1160 | const atom = self.getAtom(atom_index); | ||
| 1161 | const sym = self.getSymbolPtr(atom.getSymbolWithLoc()); | ||
| 1162 | sym.n_value += header.addr; | ||
| 1163 | sym.n_sect = n_sect; | ||
| 1164 | |||
| 1165 | log.debug(" ATOM(%{d}, '{s}') @{x}", .{ | ||
| 1166 | atom.sym_index, | ||
| 1167 | self.getSymbolName(atom.getSymbolWithLoc()), | ||
| 1168 | sym.n_value, | ||
| 1169 | }); | ||
| 1170 | |||
| 1171 | if (atom.getFile() != null) { | ||
| 1172 | // Update each symbol contained within the atom | ||
| 1173 | var it = Atom.getInnerSymbolsIterator(self, atom_index); | ||
| 1174 | while (it.next()) |sym_loc| { | ||
| 1175 | const inner_sym = self.getSymbolPtr(sym_loc); | ||
| 1176 | inner_sym.n_value = sym.n_value + Atom.calcInnerSymbolOffset( | ||
| 1177 | self, | ||
| 1178 | atom_index, | ||
| 1179 | sym_loc.sym_index, | ||
| 1180 | ); | ||
| 1181 | inner_sym.n_sect = n_sect; | ||
| 1182 | } | ||
| 1183 | |||
| 1184 | // If there is a section alias, update it now too | ||
| 1185 | if (Atom.getSectionAlias(self, atom_index)) |sym_loc| { | ||
| 1186 | const alias = self.getSymbolPtr(sym_loc); | ||
| 1187 | alias.n_value = sym.n_value; | ||
| 1188 | alias.n_sect = n_sect; | ||
| 1189 | } | ||
| 1190 | } | ||
| 1191 | |||
| 1192 | if (atom.next_index) |next_index| { | ||
| 1193 | atom_index = next_index; | ||
| 1194 | } else break; | ||
| 1195 | } | ||
| 1196 | } | ||
| 1197 | |||
| 1198 | start = start_aligned + header.size; | ||
| 1199 | |||
| 1200 | if (!header.isZerofill()) { | ||
| 1201 | segment.filesize = start; | ||
| 1202 | } | ||
| 1203 | segment.vmsize = start; | ||
| 1204 | } | ||
| 1205 | |||
| 1206 | const page_size = MachO.getPageSize(self.options.target.cpu.arch); | ||
| 1207 | segment.filesize = mem.alignForward(u64, segment.filesize, page_size); | ||
| 1208 | segment.vmsize = mem.alignForward(u64, segment.vmsize, page_size); | ||
| 1209 | } | ||
| 1210 | |||
| 1211 | fn writeLinkeditSegmentData(self: *Zld) !void { | ||
| 1212 | const page_size = MachO.getPageSize(self.options.target.cpu.arch); | ||
| 1213 | const seg = self.getLinkeditSegmentPtr(); | ||
| 1214 | seg.filesize = 0; | ||
| 1215 | seg.vmsize = 0; | ||
| 1216 | |||
| 1217 | for (self.segments.items, 0..) |segment, id| { | ||
| 1218 | if (self.linkedit_segment_cmd_index.? == @as(u8, @intCast(id))) continue; | ||
| 1219 | if (seg.vmaddr < segment.vmaddr + segment.vmsize) { | ||
| 1220 | seg.vmaddr = mem.alignForward(u64, segment.vmaddr + segment.vmsize, page_size); | ||
| 1221 | } | ||
| 1222 | if (seg.fileoff < segment.fileoff + segment.filesize) { | ||
| 1223 | seg.fileoff = mem.alignForward(u64, segment.fileoff + segment.filesize, page_size); | ||
| 1224 | } | ||
| 1225 | } | ||
| 1226 | try self.writeDyldInfoData(); | ||
| 1227 | try self.writeFunctionStarts(); | ||
| 1228 | try self.writeDataInCode(); | ||
| 1229 | try self.writeSymtabs(); | ||
| 1230 | |||
| 1231 | seg.vmsize = mem.alignForward(u64, seg.filesize, page_size); | ||
| 1232 | } | ||
| 1233 | |||
| 1234 | fn collectRebaseData(self: *Zld, rebase: *Rebase) !void { | ||
| 1235 | log.debug("collecting rebase data", .{}); | ||
| 1236 | |||
| 1237 | // First, unpack GOT entries | ||
| 1238 | if (self.got_section_index) |sect_id| { | ||
| 1239 | try MachO.collectRebaseDataFromTableSection(self.gpa, self, sect_id, rebase, self.got_table); | ||
| 1240 | } | ||
| 1241 | |||
| 1242 | // Next, unpack __la_symbol_ptr entries | ||
| 1243 | if (self.la_symbol_ptr_section_index) |sect_id| { | ||
| 1244 | try MachO.collectRebaseDataFromTableSection(self.gpa, self, sect_id, rebase, self.stubs_table); | ||
| 1245 | } | ||
| 1246 | |||
| 1247 | // Finally, unpack the rest. | ||
| 1248 | const cpu_arch = self.options.target.cpu.arch; | ||
| 1249 | for (self.objects.items) |*object| { | ||
| 1250 | for (object.atoms.items) |atom_index| { | ||
| 1251 | const atom = self.getAtom(atom_index); | ||
| 1252 | const sym = self.getSymbol(atom.getSymbolWithLoc()); | ||
| 1253 | if (sym.n_desc == MachO.N_DEAD) continue; | ||
| 1254 | |||
| 1255 | const sect_id = sym.n_sect - 1; | ||
| 1256 | const section = self.sections.items(.header)[sect_id]; | ||
| 1257 | const segment_id = self.sections.items(.segment_index)[sect_id]; | ||
| 1258 | const segment = self.segments.items[segment_id]; | ||
| 1259 | if (segment.maxprot & macho.PROT.WRITE == 0) continue; | ||
| 1260 | switch (section.type()) { | ||
| 1261 | macho.S_LITERAL_POINTERS, | ||
| 1262 | macho.S_REGULAR, | ||
| 1263 | macho.S_MOD_INIT_FUNC_POINTERS, | ||
| 1264 | macho.S_MOD_TERM_FUNC_POINTERS, | ||
| 1265 | => {}, | ||
| 1266 | else => continue, | ||
| 1267 | } | ||
| 1268 | |||
| 1269 | log.debug(" ATOM({d}, %{d}, '{s}')", .{ | ||
| 1270 | atom_index, | ||
| 1271 | atom.sym_index, | ||
| 1272 | self.getSymbolName(atom.getSymbolWithLoc()), | ||
| 1273 | }); | ||
| 1274 | |||
| 1275 | const code = Atom.getAtomCode(self, atom_index); | ||
| 1276 | const relocs = Atom.getAtomRelocs(self, atom_index); | ||
| 1277 | const ctx = Atom.getRelocContext(self, atom_index); | ||
| 1278 | |||
| 1279 | for (relocs) |rel| { | ||
| 1280 | switch (cpu_arch) { | ||
| 1281 | .aarch64 => { | ||
| 1282 | const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)); | ||
| 1283 | if (rel_type != .ARM64_RELOC_UNSIGNED) continue; | ||
| 1284 | if (rel.r_length != 3) continue; | ||
| 1285 | }, | ||
| 1286 | .x86_64 => { | ||
| 1287 | const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type)); | ||
| 1288 | if (rel_type != .X86_64_RELOC_UNSIGNED) continue; | ||
| 1289 | if (rel.r_length != 3) continue; | ||
| 1290 | }, | ||
| 1291 | else => unreachable, | ||
| 1292 | } | ||
| 1293 | const target = Atom.parseRelocTarget(self, .{ | ||
| 1294 | .object_id = atom.getFile().?, | ||
| 1295 | .rel = rel, | ||
| 1296 | .code = code, | ||
| 1297 | .base_offset = ctx.base_offset, | ||
| 1298 | .base_addr = ctx.base_addr, | ||
| 1299 | }); | ||
| 1300 | const target_sym = self.getSymbol(target); | ||
| 1301 | if (target_sym.undf()) continue; | ||
| 1302 | |||
| 1303 | const base_offset = @as(i32, @intCast(sym.n_value - segment.vmaddr)); | ||
| 1304 | const rel_offset = rel.r_address - ctx.base_offset; | ||
| 1305 | const offset = @as(u64, @intCast(base_offset + rel_offset)); | ||
| 1306 | log.debug(" | rebase at {x}", .{offset}); | ||
| 1307 | |||
| 1308 | try rebase.entries.append(self.gpa, .{ | ||
| 1309 | .offset = offset, | ||
| 1310 | .segment_id = segment_id, | ||
| 1311 | }); | ||
| 1312 | } | ||
| 1313 | } | ||
| 1314 | } | ||
| 1315 | |||
| 1316 | try rebase.finalize(self.gpa); | ||
| 1317 | } | ||
| 1318 | |||
| 1319 | fn collectBindData( | ||
| 1320 | self: *Zld, | ||
| 1321 | bind: *Bind, | ||
| 1322 | ) !void { | ||
| 1323 | log.debug("collecting bind data", .{}); | ||
| 1324 | |||
| 1325 | // First, unpack GOT section | ||
| 1326 | if (self.got_section_index) |sect_id| { | ||
| 1327 | try MachO.collectBindDataFromTableSection(self.gpa, self, sect_id, bind, self.got_table); | ||
| 1328 | } | ||
| 1329 | |||
| 1330 | // Next, unpack TLV pointers section | ||
| 1331 | if (self.tlv_ptr_section_index) |sect_id| { | ||
| 1332 | try MachO.collectBindDataFromTableSection(self.gpa, self, sect_id, bind, self.tlv_ptr_table); | ||
| 1333 | } | ||
| 1334 | |||
| 1335 | // Finally, unpack the rest. | ||
| 1336 | const cpu_arch = self.options.target.cpu.arch; | ||
| 1337 | for (self.objects.items) |*object| { | ||
| 1338 | for (object.atoms.items) |atom_index| { | ||
| 1339 | const atom = self.getAtom(atom_index); | ||
| 1340 | const sym = self.getSymbol(atom.getSymbolWithLoc()); | ||
| 1341 | if (sym.n_desc == MachO.N_DEAD) continue; | ||
| 1342 | |||
| 1343 | const sect_id = sym.n_sect - 1; | ||
| 1344 | const section = self.sections.items(.header)[sect_id]; | ||
| 1345 | const segment_id = self.sections.items(.segment_index)[sect_id]; | ||
| 1346 | const segment = self.segments.items[segment_id]; | ||
| 1347 | if (segment.maxprot & macho.PROT.WRITE == 0) continue; | ||
| 1348 | switch (section.type()) { | ||
| 1349 | macho.S_LITERAL_POINTERS, | ||
| 1350 | macho.S_REGULAR, | ||
| 1351 | macho.S_MOD_INIT_FUNC_POINTERS, | ||
| 1352 | macho.S_MOD_TERM_FUNC_POINTERS, | ||
| 1353 | => {}, | ||
| 1354 | else => continue, | ||
| 1355 | } | ||
| 1356 | |||
| 1357 | log.debug(" ATOM({d}, %{d}, '{s}')", .{ | ||
| 1358 | atom_index, | ||
| 1359 | atom.sym_index, | ||
| 1360 | self.getSymbolName(atom.getSymbolWithLoc()), | ||
| 1361 | }); | ||
| 1362 | |||
| 1363 | const code = Atom.getAtomCode(self, atom_index); | ||
| 1364 | const relocs = Atom.getAtomRelocs(self, atom_index); | ||
| 1365 | const ctx = Atom.getRelocContext(self, atom_index); | ||
| 1366 | |||
| 1367 | for (relocs) |rel| { | ||
| 1368 | switch (cpu_arch) { | ||
| 1369 | .aarch64 => { | ||
| 1370 | const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)); | ||
| 1371 | if (rel_type != .ARM64_RELOC_UNSIGNED) continue; | ||
| 1372 | if (rel.r_length != 3) continue; | ||
| 1373 | }, | ||
| 1374 | .x86_64 => { | ||
| 1375 | const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type)); | ||
| 1376 | if (rel_type != .X86_64_RELOC_UNSIGNED) continue; | ||
| 1377 | if (rel.r_length != 3) continue; | ||
| 1378 | }, | ||
| 1379 | else => unreachable, | ||
| 1380 | } | ||
| 1381 | |||
| 1382 | const global = Atom.parseRelocTarget(self, .{ | ||
| 1383 | .object_id = atom.getFile().?, | ||
| 1384 | .rel = rel, | ||
| 1385 | .code = code, | ||
| 1386 | .base_offset = ctx.base_offset, | ||
| 1387 | .base_addr = ctx.base_addr, | ||
| 1388 | }); | ||
| 1389 | const bind_sym_name = self.getSymbolName(global); | ||
| 1390 | const bind_sym = self.getSymbol(global); | ||
| 1391 | if (!bind_sym.undf()) continue; | ||
| 1392 | |||
| 1393 | const base_offset = sym.n_value - segment.vmaddr; | ||
| 1394 | const rel_offset = @as(u32, @intCast(rel.r_address - ctx.base_offset)); | ||
| 1395 | const offset = @as(u64, @intCast(base_offset + rel_offset)); | ||
| 1396 | const addend = mem.readIntLittle(i64, code[rel_offset..][0..8]); | ||
| 1397 | |||
| 1398 | const dylib_ordinal = @divTrunc(@as(i16, @bitCast(bind_sym.n_desc)), macho.N_SYMBOL_RESOLVER); | ||
| 1399 | log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{ | ||
| 1400 | base_offset, | ||
| 1401 | bind_sym_name, | ||
| 1402 | dylib_ordinal, | ||
| 1403 | }); | ||
| 1404 | log.debug(" | with addend {x}", .{addend}); | ||
| 1405 | if (bind_sym.weakRef()) { | ||
| 1406 | log.debug(" | marking as weak ref ", .{}); | ||
| 1407 | } | ||
| 1408 | try bind.entries.append(self.gpa, .{ | ||
| 1409 | .target = global, | ||
| 1410 | .offset = offset, | ||
| 1411 | .segment_id = segment_id, | ||
| 1412 | .addend = addend, | ||
| 1413 | }); | ||
| 1414 | } | ||
| 1415 | } | ||
| 1416 | } | ||
| 1417 | |||
| 1418 | try bind.finalize(self.gpa, self); | ||
| 1419 | } | ||
| 1420 | |||
| 1421 | fn collectLazyBindData(self: *Zld, lazy_bind: *LazyBind) !void { | ||
| 1422 | const sect_id = self.la_symbol_ptr_section_index orelse return; | ||
| 1423 | try MachO.collectBindDataFromTableSection(self.gpa, self, sect_id, lazy_bind, self.stubs_table); | ||
| 1424 | try lazy_bind.finalize(self.gpa, self); | ||
| 1425 | } | ||
| 1426 | |||
| 1427 | fn collectExportData(self: *Zld, trie: *Trie) !void { | ||
| 1428 | const gpa = self.gpa; | ||
| 1429 | |||
| 1430 | // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER. | ||
| 1431 | log.debug("collecting export data", .{}); | ||
| 1432 | |||
| 1433 | const exec_segment = self.segments.items[self.header_segment_cmd_index.?]; | ||
| 1434 | const base_address = exec_segment.vmaddr; | ||
| 1435 | |||
| 1436 | for (self.globals.items) |global| { | ||
| 1437 | const sym = self.getSymbol(global); | ||
| 1438 | if (sym.undf()) continue; | ||
| 1439 | if (sym.n_desc == MachO.N_DEAD) continue; | ||
| 1440 | |||
| 1441 | const sym_name = self.getSymbolName(global); | ||
| 1442 | log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value }); | ||
| 1443 | try trie.put(gpa, .{ | ||
| 1444 | .name = sym_name, | ||
| 1445 | .vmaddr_offset = sym.n_value - base_address, | ||
| 1446 | .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR, | ||
| 1447 | }); | ||
| 1448 | } | ||
| 1449 | |||
| 1450 | try trie.finalize(gpa); | ||
| 1451 | } | ||
| 1452 | |||
| 1453 | fn writeDyldInfoData(self: *Zld) !void { | ||
| 1454 | const gpa = self.gpa; | ||
| 1455 | |||
| 1456 | var rebase = Rebase{}; | ||
| 1457 | defer rebase.deinit(gpa); | ||
| 1458 | try self.collectRebaseData(&rebase); | ||
| 1459 | |||
| 1460 | var bind = Bind{}; | ||
| 1461 | defer bind.deinit(gpa); | ||
| 1462 | try self.collectBindData(&bind); | ||
| 1463 | |||
| 1464 | var lazy_bind = LazyBind{}; | ||
| 1465 | defer lazy_bind.deinit(gpa); | ||
| 1466 | try self.collectLazyBindData(&lazy_bind); | ||
| 1467 | |||
| 1468 | var trie = Trie{}; | ||
| 1469 | defer trie.deinit(gpa); | ||
| 1470 | try trie.init(gpa); | ||
| 1471 | try self.collectExportData(&trie); | ||
| 1472 | |||
| 1473 | const link_seg = self.getLinkeditSegmentPtr(); | ||
| 1474 | assert(mem.isAlignedGeneric(u64, link_seg.fileoff, @alignOf(u64))); | ||
| 1475 | const rebase_off = link_seg.fileoff; | ||
| 1476 | const rebase_size = rebase.size(); | ||
| 1477 | const rebase_size_aligned = mem.alignForward(u64, rebase_size, @alignOf(u64)); | ||
| 1478 | log.debug("writing rebase info from 0x{x} to 0x{x}", .{ rebase_off, rebase_off + rebase_size_aligned }); | ||
| 1479 | |||
| 1480 | const bind_off = rebase_off + rebase_size_aligned; | ||
| 1481 | const bind_size = bind.size(); | ||
| 1482 | const bind_size_aligned = mem.alignForward(u64, bind_size, @alignOf(u64)); | ||
| 1483 | log.debug("writing bind info from 0x{x} to 0x{x}", .{ bind_off, bind_off + bind_size_aligned }); | ||
| 1484 | |||
| 1485 | const lazy_bind_off = bind_off + bind_size_aligned; | ||
| 1486 | const lazy_bind_size = lazy_bind.size(); | ||
| 1487 | const lazy_bind_size_aligned = mem.alignForward(u64, lazy_bind_size, @alignOf(u64)); | ||
| 1488 | log.debug("writing lazy bind info from 0x{x} to 0x{x}", .{ | ||
| 1489 | lazy_bind_off, | ||
| 1490 | lazy_bind_off + lazy_bind_size_aligned, | ||
| 1491 | }); | ||
| 1492 | |||
| 1493 | const export_off = lazy_bind_off + lazy_bind_size_aligned; | ||
| 1494 | const export_size = trie.size; | ||
| 1495 | const export_size_aligned = mem.alignForward(u64, export_size, @alignOf(u64)); | ||
| 1496 | log.debug("writing export trie from 0x{x} to 0x{x}", .{ export_off, export_off + export_size_aligned }); | ||
| 1497 | |||
| 1498 | const needed_size = math.cast(usize, export_off + export_size_aligned - rebase_off) orelse | ||
| 1499 | return error.Overflow; | ||
| 1500 | link_seg.filesize = needed_size; | ||
| 1501 | assert(mem.isAlignedGeneric(u64, link_seg.fileoff + link_seg.filesize, @alignOf(u64))); | ||
| 1502 | |||
| 1503 | var buffer = try gpa.alloc(u8, needed_size); | ||
| 1504 | defer gpa.free(buffer); | ||
| 1505 | @memset(buffer, 0); | ||
| 1506 | |||
| 1507 | var stream = std.io.fixedBufferStream(buffer); | ||
| 1508 | const writer = stream.writer(); | ||
| 1509 | |||
| 1510 | try rebase.write(writer); | ||
| 1511 | try stream.seekTo(bind_off - rebase_off); | ||
| 1512 | |||
| 1513 | try bind.write(writer); | ||
| 1514 | try stream.seekTo(lazy_bind_off - rebase_off); | ||
| 1515 | |||
| 1516 | try lazy_bind.write(writer); | ||
| 1517 | try stream.seekTo(export_off - rebase_off); | ||
| 1518 | |||
| 1519 | _ = try trie.write(writer); | ||
| 1520 | |||
| 1521 | log.debug("writing dyld info from 0x{x} to 0x{x}", .{ | ||
| 1522 | rebase_off, | ||
| 1523 | rebase_off + needed_size, | ||
| 1524 | }); | ||
| 1525 | |||
| 1526 | try self.file.pwriteAll(buffer, rebase_off); | ||
| 1527 | try MachO.populateLazyBindOffsetsInStubHelper( | ||
| 1528 | self, | ||
| 1529 | self.options.target.cpu.arch, | ||
| 1530 | self.file, | ||
| 1531 | lazy_bind, | ||
| 1532 | ); | ||
| 1533 | |||
| 1534 | self.dyld_info_cmd.rebase_off = @as(u32, @intCast(rebase_off)); | ||
| 1535 | self.dyld_info_cmd.rebase_size = @as(u32, @intCast(rebase_size_aligned)); | ||
| 1536 | self.dyld_info_cmd.bind_off = @as(u32, @intCast(bind_off)); | ||
| 1537 | self.dyld_info_cmd.bind_size = @as(u32, @intCast(bind_size_aligned)); | ||
| 1538 | self.dyld_info_cmd.lazy_bind_off = @as(u32, @intCast(lazy_bind_off)); | ||
| 1539 | self.dyld_info_cmd.lazy_bind_size = @as(u32, @intCast(lazy_bind_size_aligned)); | ||
| 1540 | self.dyld_info_cmd.export_off = @as(u32, @intCast(export_off)); | ||
| 1541 | self.dyld_info_cmd.export_size = @as(u32, @intCast(export_size_aligned)); | ||
| 1542 | } | ||
| 1543 | |||
| 1544 | const asc_u64 = std.sort.asc(u64); | ||
| 1545 | |||
| 1546 | fn addSymbolToFunctionStarts(self: *Zld, sym_loc: SymbolWithLoc, addresses: *std.ArrayList(u64)) !void { | ||
| 1547 | const sym = self.getSymbol(sym_loc); | ||
| 1548 | if (sym.n_strx == 0) return; | ||
| 1549 | if (sym.n_desc == MachO.N_DEAD) return; | ||
| 1550 | if (self.symbolIsTemp(sym_loc)) return; | ||
| 1551 | try addresses.append(sym.n_value); | ||
| 1552 | } | ||
| 1553 | |||
| 1554 | fn writeFunctionStarts(self: *Zld) !void { | ||
| 1555 | const gpa = self.gpa; | ||
| 1556 | const seg = self.segments.items[self.header_segment_cmd_index.?]; | ||
| 1557 | |||
| 1558 | // We need to sort by address first | ||
| 1559 | var addresses = std.ArrayList(u64).init(gpa); | ||
| 1560 | defer addresses.deinit(); | ||
| 1561 | |||
| 1562 | for (self.objects.items) |object| { | ||
| 1563 | for (object.exec_atoms.items) |atom_index| { | ||
| 1564 | const atom = self.getAtom(atom_index); | ||
| 1565 | const sym_loc = atom.getSymbolWithLoc(); | ||
| 1566 | try self.addSymbolToFunctionStarts(sym_loc, &addresses); | ||
| 1567 | |||
| 1568 | var it = Atom.getInnerSymbolsIterator(self, atom_index); | ||
| 1569 | while (it.next()) |inner_sym_loc| { | ||
| 1570 | try self.addSymbolToFunctionStarts(inner_sym_loc, &addresses); | ||
| 1571 | } | ||
| 1572 | } | ||
| 1573 | } | ||
| 1574 | |||
| 1575 | mem.sort(u64, addresses.items, {}, asc_u64); | ||
| 1576 | |||
| 1577 | var offsets = std.ArrayList(u32).init(gpa); | ||
| 1578 | defer offsets.deinit(); | ||
| 1579 | try offsets.ensureTotalCapacityPrecise(addresses.items.len); | ||
| 1580 | |||
| 1581 | var last_off: u32 = 0; | ||
| 1582 | for (addresses.items) |addr| { | ||
| 1583 | const offset = @as(u32, @intCast(addr - seg.vmaddr)); | ||
| 1584 | const diff = offset - last_off; | ||
| 1585 | |||
| 1586 | if (diff == 0) continue; | ||
| 1587 | |||
| 1588 | offsets.appendAssumeCapacity(diff); | ||
| 1589 | last_off = offset; | ||
| 1590 | } | ||
| 1591 | |||
| 1592 | var buffer = std.ArrayList(u8).init(gpa); | ||
| 1593 | defer buffer.deinit(); | ||
| 1594 | |||
| 1595 | const max_size = @as(usize, @intCast(offsets.items.len * @sizeOf(u64))); | ||
| 1596 | try buffer.ensureTotalCapacity(max_size); | ||
| 1597 | |||
| 1598 | for (offsets.items) |offset| { | ||
| 1599 | try std.leb.writeULEB128(buffer.writer(), offset); | ||
| 1600 | } | ||
| 1601 | |||
| 1602 | const link_seg = self.getLinkeditSegmentPtr(); | ||
| 1603 | const offset = link_seg.fileoff + link_seg.filesize; | ||
| 1604 | assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64))); | ||
| 1605 | const needed_size = buffer.items.len; | ||
| 1606 | const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64)); | ||
| 1607 | const padding = math.cast(usize, needed_size_aligned - needed_size) orelse return error.Overflow; | ||
| 1608 | if (padding > 0) { | ||
| 1609 | try buffer.ensureUnusedCapacity(padding); | ||
| 1610 | buffer.appendNTimesAssumeCapacity(0, padding); | ||
| 1611 | } | ||
| 1612 | link_seg.filesize = offset + needed_size_aligned - link_seg.fileoff; | ||
| 1613 | |||
| 1614 | log.debug("writing function starts info from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned }); | ||
| 1615 | |||
| 1616 | try self.file.pwriteAll(buffer.items, offset); | ||
| 1617 | |||
| 1618 | self.function_starts_cmd.dataoff = @as(u32, @intCast(offset)); | ||
| 1619 | self.function_starts_cmd.datasize = @as(u32, @intCast(needed_size_aligned)); | ||
| 1620 | } | ||
| 1621 | |||
| 1622 | fn filterDataInCode( | ||
| 1623 | dices: []const macho.data_in_code_entry, | ||
| 1624 | start_addr: u64, | ||
| 1625 | end_addr: u64, | ||
| 1626 | ) []const macho.data_in_code_entry { | ||
| 1627 | const Predicate = struct { | ||
| 1628 | addr: u64, | ||
| 1629 | |||
| 1630 | pub fn predicate(self: @This(), dice: macho.data_in_code_entry) bool { | ||
| 1631 | return dice.offset >= self.addr; | ||
| 1632 | } | ||
| 1633 | }; | ||
| 1634 | |||
| 1635 | const start = MachO.lsearch(macho.data_in_code_entry, dices, Predicate{ .addr = start_addr }); | ||
| 1636 | const end = MachO.lsearch(macho.data_in_code_entry, dices[start..], Predicate{ .addr = end_addr }) + start; | ||
| 1637 | |||
| 1638 | return dices[start..end]; | ||
| 1639 | } | ||
| 1640 | |||
| 1641 | fn writeDataInCode(self: *Zld) !void { | ||
| 1642 | var out_dice = std.ArrayList(macho.data_in_code_entry).init(self.gpa); | ||
| 1643 | defer out_dice.deinit(); | ||
| 1644 | |||
| 1645 | const text_sect_id = self.text_section_index orelse return; | ||
| 1646 | const text_sect_header = self.sections.items(.header)[text_sect_id]; | ||
| 1647 | |||
| 1648 | for (self.objects.items) |object| { | ||
| 1649 | if (!object.hasDataInCode()) continue; | ||
| 1650 | const dice = object.data_in_code.items; | ||
| 1651 | try out_dice.ensureUnusedCapacity(dice.len); | ||
| 1652 | |||
| 1653 | for (object.exec_atoms.items) |atom_index| { | ||
| 1654 | const atom = self.getAtom(atom_index); | ||
| 1655 | const sym = self.getSymbol(atom.getSymbolWithLoc()); | ||
| 1656 | if (sym.n_desc == MachO.N_DEAD) continue; | ||
| 1657 | |||
| 1658 | const source_addr = if (object.getSourceSymbol(atom.sym_index)) |source_sym| | ||
| 1659 | source_sym.n_value | ||
| 1660 | else blk: { | ||
| 1661 | const nbase = @as(u32, @intCast(object.in_symtab.?.len)); | ||
| 1662 | const source_sect_id = @as(u8, @intCast(atom.sym_index - nbase)); | ||
| 1663 | break :blk object.getSourceSection(source_sect_id).addr; | ||
| 1664 | }; | ||
| 1665 | const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size); | ||
| 1666 | const base = math.cast(u32, sym.n_value - text_sect_header.addr + text_sect_header.offset) orelse | ||
| 1667 | return error.Overflow; | ||
| 1668 | |||
| 1669 | for (filtered_dice) |single| { | ||
| 1670 | const offset = math.cast(u32, single.offset - source_addr + base) orelse | ||
| 1671 | return error.Overflow; | ||
| 1672 | out_dice.appendAssumeCapacity(.{ | ||
| 1673 | .offset = offset, | ||
| 1674 | .length = single.length, | ||
| 1675 | .kind = single.kind, | ||
| 1676 | }); | ||
| 1677 | } | ||
| 1678 | } | ||
| 1679 | } | ||
| 1680 | |||
| 1681 | const seg = self.getLinkeditSegmentPtr(); | ||
| 1682 | const offset = seg.fileoff + seg.filesize; | ||
| 1683 | assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64))); | ||
| 1684 | const needed_size = out_dice.items.len * @sizeOf(macho.data_in_code_entry); | ||
| 1685 | const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64)); | ||
| 1686 | seg.filesize = offset + needed_size_aligned - seg.fileoff; | ||
| 1687 | |||
| 1688 | const buffer = try self.gpa.alloc(u8, math.cast(usize, needed_size_aligned) orelse return error.Overflow); | ||
| 1689 | defer self.gpa.free(buffer); | ||
| 1690 | { | ||
| 1691 | const src = mem.sliceAsBytes(out_dice.items); | ||
| 1692 | @memcpy(buffer[0..src.len], src); | ||
| 1693 | @memset(buffer[src.len..], 0); | ||
| 1694 | } | ||
| 1695 | |||
| 1696 | log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned }); | ||
| 1697 | |||
| 1698 | try self.file.pwriteAll(buffer, offset); | ||
| 1699 | |||
| 1700 | self.data_in_code_cmd.dataoff = @as(u32, @intCast(offset)); | ||
| 1701 | self.data_in_code_cmd.datasize = @as(u32, @intCast(needed_size_aligned)); | ||
| 1702 | } | ||
| 1703 | |||
| 1704 | fn writeSymtabs(self: *Zld) !void { | ||
| 1705 | var ctx = try self.writeSymtab(); | ||
| 1706 | defer ctx.imports_table.deinit(); | ||
| 1707 | try self.writeDysymtab(ctx); | ||
| 1708 | try self.writeStrtab(); | ||
| 1709 | } | ||
| 1710 | |||
| 1711 | fn addLocalToSymtab(self: *Zld, sym_loc: SymbolWithLoc, locals: *std.ArrayList(macho.nlist_64)) !void { | ||
| 1712 | const sym = self.getSymbol(sym_loc); | ||
| 1713 | if (sym.n_strx == 0) return; // no name, skip | ||
| 1714 | if (sym.n_desc == MachO.N_DEAD) return; // garbage-collected, skip | ||
| 1715 | if (sym.ext()) return; // an export lands in its own symtab section, skip | ||
| 1716 | if (self.symbolIsTemp(sym_loc)) return; // local temp symbol, skip | ||
| 1717 | |||
| 1718 | var out_sym = sym; | ||
| 1719 | out_sym.n_strx = try self.strtab.insert(self.gpa, self.getSymbolName(sym_loc)); | ||
| 1720 | try locals.append(out_sym); | ||
| 1721 | } | ||
| 1722 | |||
| 1723 | fn writeSymtab(self: *Zld) !SymtabCtx { | ||
| 1724 | const gpa = self.gpa; | ||
| 1725 | |||
| 1726 | var locals = std.ArrayList(macho.nlist_64).init(gpa); | ||
| 1727 | defer locals.deinit(); | ||
| 1728 | |||
| 1729 | for (self.objects.items) |object| { | ||
| 1730 | for (object.atoms.items) |atom_index| { | ||
| 1731 | const atom = self.getAtom(atom_index); | ||
| 1732 | const sym_loc = atom.getSymbolWithLoc(); | ||
| 1733 | try self.addLocalToSymtab(sym_loc, &locals); | ||
| 1734 | |||
| 1735 | var it = Atom.getInnerSymbolsIterator(self, atom_index); | ||
| 1736 | while (it.next()) |inner_sym_loc| { | ||
| 1737 | try self.addLocalToSymtab(inner_sym_loc, &locals); | ||
| 1738 | } | ||
| 1739 | } | ||
| 1740 | } | ||
| 1741 | |||
| 1742 | var exports = std.ArrayList(macho.nlist_64).init(gpa); | ||
| 1743 | defer exports.deinit(); | ||
| 1744 | |||
| 1745 | for (self.globals.items) |global| { | ||
| 1746 | const sym = self.getSymbol(global); | ||
| 1747 | if (sym.undf()) continue; // import, skip | ||
| 1748 | if (sym.n_desc == MachO.N_DEAD) continue; | ||
| 1749 | |||
| 1750 | var out_sym = sym; | ||
| 1751 | out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global)); | ||
| 1752 | try exports.append(out_sym); | ||
| 1753 | } | ||
| 1754 | |||
| 1755 | var imports = std.ArrayList(macho.nlist_64).init(gpa); | ||
| 1756 | defer imports.deinit(); | ||
| 1757 | |||
| 1758 | var imports_table = std.AutoHashMap(SymbolWithLoc, u32).init(gpa); | ||
| 1759 | |||
| 1760 | for (self.globals.items) |global| { | ||
| 1761 | const sym = self.getSymbol(global); | ||
| 1762 | if (!sym.undf()) continue; // not an import, skip | ||
| 1763 | if (sym.n_desc == MachO.N_DEAD) continue; | ||
| 1764 | |||
| 1765 | const new_index = @as(u32, @intCast(imports.items.len)); | ||
| 1766 | var out_sym = sym; | ||
| 1767 | out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global)); | ||
| 1768 | try imports.append(out_sym); | ||
| 1769 | try imports_table.putNoClobber(global, new_index); | ||
| 1770 | } | ||
| 1771 | |||
| 1772 | // We generate stabs last in order to ensure that the strtab always has debug info | ||
| 1773 | // strings trailing | ||
| 1774 | if (!self.options.strip) { | ||
| 1775 | for (self.objects.items) |object| { | ||
| 1776 | try self.generateSymbolStabs(object, &locals); | ||
| 1777 | } | ||
| 1778 | } | ||
| 1779 | |||
| 1780 | const nlocals = @as(u32, @intCast(locals.items.len)); | ||
| 1781 | const nexports = @as(u32, @intCast(exports.items.len)); | ||
| 1782 | const nimports = @as(u32, @intCast(imports.items.len)); | ||
| 1783 | const nsyms = nlocals + nexports + nimports; | ||
| 1784 | |||
| 1785 | const seg = self.getLinkeditSegmentPtr(); | ||
| 1786 | const offset = seg.fileoff + seg.filesize; | ||
| 1787 | assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64))); | ||
| 1788 | const needed_size = nsyms * @sizeOf(macho.nlist_64); | ||
| 1789 | seg.filesize = offset + needed_size - seg.fileoff; | ||
| 1790 | assert(mem.isAlignedGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64))); | ||
| 1791 | |||
| 1792 | var buffer = std.ArrayList(u8).init(gpa); | ||
| 1793 | defer buffer.deinit(); | ||
| 1794 | try buffer.ensureTotalCapacityPrecise(needed_size); | ||
| 1795 | buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(locals.items)); | ||
| 1796 | buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(exports.items)); | ||
| 1797 | buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(imports.items)); | ||
| 1798 | |||
| 1799 | log.debug("writing symtab from 0x{x} to 0x{x}", .{ offset, offset + needed_size }); | ||
| 1800 | try self.file.pwriteAll(buffer.items, offset); | ||
| 1801 | |||
| 1802 | self.symtab_cmd.symoff = @as(u32, @intCast(offset)); | ||
| 1803 | self.symtab_cmd.nsyms = nsyms; | ||
| 1804 | |||
| 1805 | return SymtabCtx{ | ||
| 1806 | .nlocalsym = nlocals, | ||
| 1807 | .nextdefsym = nexports, | ||
| 1808 | .nundefsym = nimports, | ||
| 1809 | .imports_table = imports_table, | ||
| 1810 | }; | ||
| 1811 | } | ||
| 1812 | |||
| 1813 | fn writeStrtab(self: *Zld) !void { | ||
| 1814 | const seg = self.getLinkeditSegmentPtr(); | ||
| 1815 | const offset = seg.fileoff + seg.filesize; | ||
| 1816 | assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64))); | ||
| 1817 | const needed_size = self.strtab.buffer.items.len; | ||
| 1818 | const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64)); | ||
| 1819 | seg.filesize = offset + needed_size_aligned - seg.fileoff; | ||
| 1820 | |||
| 1821 | log.debug("writing string table from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned }); | ||
| 1822 | |||
| 1823 | const buffer = try self.gpa.alloc(u8, math.cast(usize, needed_size_aligned) orelse return error.Overflow); | ||
| 1824 | defer self.gpa.free(buffer); | ||
| 1825 | @memcpy(buffer[0..self.strtab.buffer.items.len], self.strtab.buffer.items); | ||
| 1826 | @memset(buffer[self.strtab.buffer.items.len..], 0); | ||
| 1827 | |||
| 1828 | try self.file.pwriteAll(buffer, offset); | ||
| 1829 | |||
| 1830 | self.symtab_cmd.stroff = @as(u32, @intCast(offset)); | ||
| 1831 | self.symtab_cmd.strsize = @as(u32, @intCast(needed_size_aligned)); | ||
| 1832 | } | ||
| 1833 | |||
| 1834 | const SymtabCtx = struct { | ||
| 1835 | nlocalsym: u32, | ||
| 1836 | nextdefsym: u32, | ||
| 1837 | nundefsym: u32, | ||
| 1838 | imports_table: std.AutoHashMap(SymbolWithLoc, u32), | ||
| 1839 | }; | ||
| 1840 | |||
| 1841 | fn writeDysymtab(self: *Zld, ctx: SymtabCtx) !void { | ||
| 1842 | const gpa = self.gpa; | ||
| 1843 | const nstubs = @as(u32, @intCast(self.stubs_table.lookup.count())); | ||
| 1844 | const ngot_entries = @as(u32, @intCast(self.got_table.lookup.count())); | ||
| 1845 | const nindirectsyms = nstubs * 2 + ngot_entries; | ||
| 1846 | const iextdefsym = ctx.nlocalsym; | ||
| 1847 | const iundefsym = iextdefsym + ctx.nextdefsym; | ||
| 1848 | |||
| 1849 | const seg = self.getLinkeditSegmentPtr(); | ||
| 1850 | const offset = seg.fileoff + seg.filesize; | ||
| 1851 | assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64))); | ||
| 1852 | const needed_size = nindirectsyms * @sizeOf(u32); | ||
| 1853 | const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64)); | ||
| 1854 | seg.filesize = offset + needed_size_aligned - seg.fileoff; | ||
| 1855 | |||
| 1856 | log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned }); | ||
| 1857 | |||
| 1858 | var buf = std.ArrayList(u8).init(gpa); | ||
| 1859 | defer buf.deinit(); | ||
| 1860 | try buf.ensureTotalCapacityPrecise(math.cast(usize, needed_size_aligned) orelse return error.Overflow); | ||
| 1861 | const writer = buf.writer(); | ||
| 1862 | |||
| 1863 | if (self.stubs_section_index) |sect_id| { | ||
| 1864 | const header = &self.sections.items(.header)[sect_id]; | ||
| 1865 | header.reserved1 = 0; | ||
| 1866 | for (self.stubs_table.entries.items) |entry| { | ||
| 1867 | if (!self.stubs_table.lookup.contains(entry)) continue; | ||
| 1868 | const target_sym = self.getSymbol(entry); | ||
| 1869 | assert(target_sym.undf()); | ||
| 1870 | try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry).?); | ||
| 1871 | } | ||
| 1872 | } | ||
| 1873 | |||
| 1874 | if (self.got_section_index) |sect_id| { | ||
| 1875 | const header = &self.sections.items(.header)[sect_id]; | ||
| 1876 | header.reserved1 = nstubs; | ||
| 1877 | for (self.got_table.entries.items) |entry| { | ||
| 1878 | if (!self.got_table.lookup.contains(entry)) continue; | ||
| 1879 | const target_sym = self.getSymbol(entry); | ||
| 1880 | if (target_sym.undf()) { | ||
| 1881 | try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry).?); | ||
| 1882 | } else { | ||
| 1883 | try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL); | ||
| 1884 | } | ||
| 1885 | } | ||
| 1886 | } | ||
| 1887 | |||
| 1888 | if (self.la_symbol_ptr_section_index) |sect_id| { | ||
| 1889 | const header = &self.sections.items(.header)[sect_id]; | ||
| 1890 | header.reserved1 = nstubs + ngot_entries; | ||
| 1891 | for (self.stubs_table.entries.items) |entry| { | ||
| 1892 | if (!self.stubs_table.lookup.contains(entry)) continue; | ||
| 1893 | const target_sym = self.getSymbol(entry); | ||
| 1894 | assert(target_sym.undf()); | ||
| 1895 | try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry).?); | ||
| 1896 | } | ||
| 1897 | } | ||
| 1898 | |||
| 1899 | const padding = math.cast(usize, needed_size_aligned - needed_size) orelse return error.Overflow; | ||
| 1900 | if (padding > 0) { | ||
| 1901 | buf.appendNTimesAssumeCapacity(0, padding); | ||
| 1902 | } | ||
| 1903 | |||
| 1904 | assert(buf.items.len == needed_size_aligned); | ||
| 1905 | try self.file.pwriteAll(buf.items, offset); | ||
| 1906 | |||
| 1907 | self.dysymtab_cmd.nlocalsym = ctx.nlocalsym; | ||
| 1908 | self.dysymtab_cmd.iextdefsym = iextdefsym; | ||
| 1909 | self.dysymtab_cmd.nextdefsym = ctx.nextdefsym; | ||
| 1910 | self.dysymtab_cmd.iundefsym = iundefsym; | ||
| 1911 | self.dysymtab_cmd.nundefsym = ctx.nundefsym; | ||
| 1912 | self.dysymtab_cmd.indirectsymoff = @as(u32, @intCast(offset)); | ||
| 1913 | self.dysymtab_cmd.nindirectsyms = nindirectsyms; | ||
| 1914 | } | ||
| 1915 | |||
| 1916 | fn writeUuid(self: *Zld, comp: *const Compilation, uuid_cmd_offset: u32, has_codesig: bool) !void { | ||
| 1917 | const file_size = if (!has_codesig) blk: { | ||
| 1918 | const seg = self.getLinkeditSegmentPtr(); | ||
| 1919 | break :blk seg.fileoff + seg.filesize; | ||
| 1920 | } else self.codesig_cmd.dataoff; | ||
| 1921 | try calcUuid(comp, self.file, file_size, &self.uuid_cmd.uuid); | ||
| 1922 | const offset = uuid_cmd_offset + @sizeOf(macho.load_command); | ||
| 1923 | try self.file.pwriteAll(&self.uuid_cmd.uuid, offset); | ||
| 1924 | } | ||
| 1925 | |||
| 1926 | fn writeCodeSignaturePadding(self: *Zld, code_sig: *CodeSignature) !void { | ||
| 1927 | const seg = self.getLinkeditSegmentPtr(); | ||
| 1928 | // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file | ||
| 1929 | // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271 | ||
| 1930 | const offset = mem.alignForward(u64, seg.fileoff + seg.filesize, 16); | ||
| 1931 | const needed_size = code_sig.estimateSize(offset); | ||
| 1932 | seg.filesize = offset + needed_size - seg.fileoff; | ||
| 1933 | seg.vmsize = mem.alignForward(u64, seg.filesize, MachO.getPageSize(self.options.target.cpu.arch)); | ||
| 1934 | log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ offset, offset + needed_size }); | ||
| 1935 | // Pad out the space. We need to do this to calculate valid hashes for everything in the file | ||
| 1936 | // except for code signature data. | ||
| 1937 | try self.file.pwriteAll(&[_]u8{0}, offset + needed_size - 1); | ||
| 1938 | |||
| 1939 | self.codesig_cmd.dataoff = @as(u32, @intCast(offset)); | ||
| 1940 | self.codesig_cmd.datasize = @as(u32, @intCast(needed_size)); | ||
| 1941 | } | ||
| 1942 | |||
| 1943 | fn writeCodeSignature(self: *Zld, comp: *const Compilation, code_sig: *CodeSignature) !void { | ||
| 1944 | const seg_id = self.header_segment_cmd_index.?; | ||
| 1945 | const seg = self.segments.items[seg_id]; | ||
| 1946 | |||
| 1947 | var buffer = std.ArrayList(u8).init(self.gpa); | ||
| 1948 | defer buffer.deinit(); | ||
| 1949 | try buffer.ensureTotalCapacityPrecise(code_sig.size()); | ||
| 1950 | try code_sig.writeAdhocSignature(comp, .{ | ||
| 1951 | .file = self.file, | ||
| 1952 | .exec_seg_base = seg.fileoff, | ||
| 1953 | .exec_seg_limit = seg.filesize, | ||
| 1954 | .file_size = self.codesig_cmd.dataoff, | ||
| 1955 | .output_mode = self.options.output_mode, | ||
| 1956 | }, buffer.writer()); | ||
| 1957 | assert(buffer.items.len == code_sig.size()); | ||
| 1958 | |||
| 1959 | log.debug("writing code signature from 0x{x} to 0x{x}", .{ | ||
| 1960 | self.codesig_cmd.dataoff, | ||
| 1961 | self.codesig_cmd.dataoff + buffer.items.len, | ||
| 1962 | }); | ||
| 1963 | |||
| 1964 | try self.file.pwriteAll(buffer.items, self.codesig_cmd.dataoff); | ||
| 1965 | } | ||
| 1966 | |||
| 1967 | /// Writes Mach-O file header. | ||
| 1968 | fn writeHeader(self: *Zld, ncmds: u32, sizeofcmds: u32) !void { | ||
| 1969 | var header: macho.mach_header_64 = .{}; | ||
| 1970 | header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL; | ||
| 1971 | |||
| 1972 | switch (self.options.target.cpu.arch) { | ||
| 1973 | .aarch64 => { | ||
| 1974 | header.cputype = macho.CPU_TYPE_ARM64; | ||
| 1975 | header.cpusubtype = macho.CPU_SUBTYPE_ARM_ALL; | ||
| 1976 | }, | ||
| 1977 | .x86_64 => { | ||
| 1978 | header.cputype = macho.CPU_TYPE_X86_64; | ||
| 1979 | header.cpusubtype = macho.CPU_SUBTYPE_X86_64_ALL; | ||
| 1980 | }, | ||
| 1981 | else => return error.UnsupportedCpuArchitecture, | ||
| 1982 | } | ||
| 1983 | |||
| 1984 | switch (self.options.output_mode) { | ||
| 1985 | .Exe => { | ||
| 1986 | header.filetype = macho.MH_EXECUTE; | ||
| 1987 | }, | ||
| 1988 | .Lib => { | ||
| 1989 | // By this point, it can only be a dylib. | ||
| 1990 | header.filetype = macho.MH_DYLIB; | ||
| 1991 | header.flags |= macho.MH_NO_REEXPORTED_DYLIBS; | ||
| 1992 | }, | ||
| 1993 | else => unreachable, | ||
| 1994 | } | ||
| 1995 | |||
| 1996 | if (self.thread_vars_section_index) |sect_id| { | ||
| 1997 | header.flags |= macho.MH_HAS_TLV_DESCRIPTORS; | ||
| 1998 | if (self.sections.items(.header)[sect_id].size > 0) { | ||
| 1999 | header.flags |= macho.MH_HAS_TLV_DESCRIPTORS; | ||
| 2000 | } | ||
| 2001 | } | ||
| 2002 | |||
| 2003 | header.ncmds = ncmds; | ||
| 2004 | header.sizeofcmds = sizeofcmds; | ||
| 2005 | |||
| 2006 | log.debug("writing Mach-O header {}", .{header}); | ||
| 2007 | |||
| 2008 | try self.file.pwriteAll(mem.asBytes(&header), 0); | ||
| 2009 | } | ||
| 2010 | |||
| 2011 | pub fn makeStaticString(bytes: []const u8) [16]u8 { | ||
| 2012 | var buf = [_]u8{0} ** 16; | ||
| 2013 | @memcpy(buf[0..bytes.len], bytes); | ||
| 2014 | return buf; | ||
| 2015 | } | ||
| 2016 | |||
| 2017 | pub fn getAtomPtr(self: *Zld, atom_index: Atom.Index) *Atom { | ||
| 2018 | assert(atom_index < self.atoms.items.len); | ||
| 2019 | return &self.atoms.items[atom_index]; | ||
| 2020 | } | ||
| 2021 | |||
| 2022 | pub fn getAtom(self: Zld, atom_index: Atom.Index) Atom { | ||
| 2023 | assert(atom_index < self.atoms.items.len); | ||
| 2024 | return self.atoms.items[atom_index]; | ||
| 2025 | } | ||
| 2026 | |||
| 2027 | fn getSegmentByName(self: Zld, segname: []const u8) ?u8 { | ||
| 2028 | for (self.segments.items, 0..) |seg, i| { | ||
| 2029 | if (mem.eql(u8, segname, seg.segName())) return @as(u8, @intCast(i)); | ||
| 2030 | } else return null; | ||
| 2031 | } | ||
| 2032 | |||
| 2033 | pub fn getSegment(self: Zld, sect_id: u8) macho.segment_command_64 { | ||
| 2034 | const index = self.sections.items(.segment_index)[sect_id]; | ||
| 2035 | return self.segments.items[index]; | ||
| 2036 | } | ||
| 2037 | |||
| 2038 | pub fn getSegmentPtr(self: *Zld, sect_id: u8) *macho.segment_command_64 { | ||
| 2039 | const index = self.sections.items(.segment_index)[sect_id]; | ||
| 2040 | return &self.segments.items[index]; | ||
| 2041 | } | ||
| 2042 | |||
| 2043 | pub fn getLinkeditSegmentPtr(self: *Zld) *macho.segment_command_64 { | ||
| 2044 | assert(self.segments.items.len > 0); | ||
| 2045 | const seg = &self.segments.items[self.segments.items.len - 1]; | ||
| 2046 | assert(mem.eql(u8, seg.segName(), "__LINKEDIT")); | ||
| 2047 | return seg; | ||
| 2048 | } | ||
| 2049 | |||
| 2050 | pub fn getSectionByName(self: Zld, segname: []const u8, sectname: []const u8) ?u8 { | ||
| 2051 | // TODO investigate caching with a hashmap | ||
| 2052 | for (self.sections.items(.header), 0..) |header, i| { | ||
| 2053 | if (mem.eql(u8, header.segName(), segname) and mem.eql(u8, header.sectName(), sectname)) | ||
| 2054 | return @as(u8, @intCast(i)); | ||
| 2055 | } else return null; | ||
| 2056 | } | ||
| 2057 | |||
| 2058 | pub fn getSectionIndexes(self: Zld, segment_index: u8) struct { start: u8, end: u8 } { | ||
| 2059 | var start: u8 = 0; | ||
| 2060 | const nsects = for (self.segments.items, 0..) |seg, i| { | ||
| 2061 | if (i == segment_index) break @as(u8, @intCast(seg.nsects)); | ||
| 2062 | start += @as(u8, @intCast(seg.nsects)); | ||
| 2063 | } else 0; | ||
| 2064 | return .{ .start = start, .end = start + nsects }; | ||
| 2065 | } | ||
| 2066 | |||
| 2067 | pub fn symbolIsTemp(self: *Zld, sym_with_loc: SymbolWithLoc) bool { | ||
| 2068 | const sym = self.getSymbol(sym_with_loc); | ||
| 2069 | if (!sym.sect()) return false; | ||
| 2070 | if (sym.ext()) return false; | ||
| 2071 | const sym_name = self.getSymbolName(sym_with_loc); | ||
| 2072 | return mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L"); | ||
| 2073 | } | ||
| 2074 | |||
| 2075 | /// Returns pointer-to-symbol described by `sym_with_loc` descriptor. | ||
| 2076 | pub fn getSymbolPtr(self: *Zld, sym_with_loc: SymbolWithLoc) *macho.nlist_64 { | ||
| 2077 | if (sym_with_loc.getFile()) |file| { | ||
| 2078 | const object = &self.objects.items[file]; | ||
| 2079 | return &object.symtab[sym_with_loc.sym_index]; | ||
| 2080 | } else { | ||
| 2081 | return &self.locals.items[sym_with_loc.sym_index]; | ||
| 2082 | } | ||
| 2083 | } | ||
| 2084 | |||
| 2085 | /// Returns symbol described by `sym_with_loc` descriptor. | ||
| 2086 | pub fn getSymbol(self: *const Zld, sym_with_loc: SymbolWithLoc) macho.nlist_64 { | ||
| 2087 | if (sym_with_loc.getFile()) |file| { | ||
| 2088 | const object = &self.objects.items[file]; | ||
| 2089 | return object.symtab[sym_with_loc.sym_index]; | ||
| 2090 | } else { | ||
| 2091 | return self.locals.items[sym_with_loc.sym_index]; | ||
| 2092 | } | ||
| 2093 | } | ||
| 2094 | |||
| 2095 | /// Returns name of the symbol described by `sym_with_loc` descriptor. | ||
| 2096 | pub fn getSymbolName(self: *const Zld, sym_with_loc: SymbolWithLoc) []const u8 { | ||
| 2097 | if (sym_with_loc.getFile()) |file| { | ||
| 2098 | const object = self.objects.items[file]; | ||
| 2099 | return object.getSymbolName(sym_with_loc.sym_index); | ||
| 2100 | } else { | ||
| 2101 | const sym = self.locals.items[sym_with_loc.sym_index]; | ||
| 2102 | return self.strtab.get(sym.n_strx).?; | ||
| 2103 | } | ||
| 2104 | } | ||
| 2105 | |||
| 2106 | pub fn getGlobalIndex(self: *const Zld, name: []const u8) ?u32 { | ||
| 2107 | return self.resolver.get(name); | ||
| 2108 | } | ||
| 2109 | |||
| 2110 | pub fn getGlobalPtr(self: *Zld, name: []const u8) ?*SymbolWithLoc { | ||
| 2111 | const global_index = self.resolver.get(name) orelse return null; | ||
| 2112 | return &self.globals.items[global_index]; | ||
| 2113 | } | ||
| 2114 | |||
| 2115 | pub fn getGlobal(self: *const Zld, name: []const u8) ?SymbolWithLoc { | ||
| 2116 | const global_index = self.resolver.get(name) orelse return null; | ||
| 2117 | return self.globals.items[global_index]; | ||
| 2118 | } | ||
| 2119 | |||
| 2120 | const GetOrPutGlobalPtrResult = struct { | ||
| 2121 | found_existing: bool, | ||
| 2122 | value_ptr: *SymbolWithLoc, | ||
| 2123 | }; | ||
| 2124 | |||
| 2125 | pub fn getOrPutGlobalPtr(self: *Zld, name: []const u8) !GetOrPutGlobalPtrResult { | ||
| 2126 | if (self.getGlobalPtr(name)) |ptr| { | ||
| 2127 | return GetOrPutGlobalPtrResult{ .found_existing = true, .value_ptr = ptr }; | ||
| 2128 | } | ||
| 2129 | const global_index = try self.allocateGlobal(); | ||
| 2130 | const global_name = try self.gpa.dupe(u8, name); | ||
| 2131 | _ = try self.resolver.put(self.gpa, global_name, global_index); | ||
| 2132 | const ptr = &self.globals.items[global_index]; | ||
| 2133 | return GetOrPutGlobalPtrResult{ .found_existing = false, .value_ptr = ptr }; | ||
| 2134 | } | ||
| 2135 | |||
| 2136 | pub fn getGotEntryAddress(self: *Zld, sym_with_loc: SymbolWithLoc) ?u64 { | ||
| 2137 | const index = self.got_table.lookup.get(sym_with_loc) orelse return null; | ||
| 2138 | const header = self.sections.items(.header)[self.got_section_index.?]; | ||
| 2139 | return header.addr + @sizeOf(u64) * index; | ||
| 2140 | } | ||
| 2141 | |||
| 2142 | pub fn getTlvPtrEntryAddress(self: *Zld, sym_with_loc: SymbolWithLoc) ?u64 { | ||
| 2143 | const index = self.tlv_ptr_table.lookup.get(sym_with_loc) orelse return null; | ||
| 2144 | const header = self.sections.items(.header)[self.tlv_ptr_section_index.?]; | ||
| 2145 | return header.addr + @sizeOf(u64) * index; | ||
| 2146 | } | ||
| 2147 | |||
| 2148 | pub fn getStubsEntryAddress(self: *Zld, sym_with_loc: SymbolWithLoc) ?u64 { | ||
| 2149 | const index = self.stubs_table.lookup.get(sym_with_loc) orelse return null; | ||
| 2150 | const header = self.sections.items(.header)[self.stubs_section_index.?]; | ||
| 2151 | return header.addr + stubs.stubSize(self.options.target.cpu.arch) * index; | ||
| 2152 | } | ||
| 2153 | |||
| 2154 | /// Returns symbol location corresponding to the set entrypoint. | ||
| 2155 | /// Asserts output mode is executable. | ||
| 2156 | pub fn getEntryPoint(self: Zld) SymbolWithLoc { | ||
| 2157 | assert(self.options.output_mode == .Exe); | ||
| 2158 | const global_index = self.entry_index.?; | ||
| 2159 | return self.globals.items[global_index]; | ||
| 2160 | } | ||
| 2161 | |||
| 2162 | inline fn requiresThunks(self: Zld) bool { | ||
| 2163 | return self.options.target.cpu.arch == .aarch64; | ||
| 2164 | } | ||
| 2165 | |||
| 2166 | pub fn generateSymbolStabs(self: *Zld, object: Object, locals: *std.ArrayList(macho.nlist_64)) !void { | ||
| 2167 | log.debug("generating stabs for '{s}'", .{object.name}); | ||
| 2168 | |||
| 2169 | const gpa = self.gpa; | ||
| 2170 | var debug_info = object.parseDwarfInfo(); | ||
| 2171 | |||
| 2172 | var lookup = DwarfInfo.AbbrevLookupTable.init(gpa); | ||
| 2173 | defer lookup.deinit(); | ||
| 2174 | try lookup.ensureUnusedCapacity(std.math.maxInt(u8)); | ||
| 2175 | |||
| 2176 | // We assume there is only one CU. | ||
| 2177 | var cu_it = debug_info.getCompileUnitIterator(); | ||
| 2178 | const compile_unit = while (try cu_it.next()) |cu| { | ||
| 2179 | const offset = math.cast(usize, cu.cuh.debug_abbrev_offset) orelse return error.Overflow; | ||
| 2180 | try debug_info.genAbbrevLookupByKind(offset, &lookup); | ||
| 2181 | break cu; | ||
| 2182 | } else { | ||
| 2183 | log.debug("no compile unit found in debug info in {s}; skipping", .{object.name}); | ||
| 2184 | return; | ||
| 2185 | }; | ||
| 2186 | |||
| 2187 | var abbrev_it = compile_unit.getAbbrevEntryIterator(debug_info); | ||
| 2188 | const cu_entry: DwarfInfo.AbbrevEntry = while (try abbrev_it.next(lookup)) |entry| switch (entry.tag) { | ||
| 2189 | dwarf.TAG.compile_unit => break entry, | ||
| 2190 | else => continue, | ||
| 2191 | } else { | ||
| 2192 | log.debug("missing DWARF_TAG_compile_unit tag in {s}; skipping", .{object.name}); | ||
| 2193 | return; | ||
| 2194 | }; | ||
| 2195 | |||
| 2196 | var maybe_tu_name: ?[]const u8 = null; | ||
| 2197 | var maybe_tu_comp_dir: ?[]const u8 = null; | ||
| 2198 | var attr_it = cu_entry.getAttributeIterator(debug_info, compile_unit.cuh); | ||
| 2199 | |||
| 2200 | while (try attr_it.next()) |attr| switch (attr.name) { | ||
| 2201 | dwarf.AT.comp_dir => maybe_tu_comp_dir = attr.getString(debug_info, compile_unit.cuh) orelse continue, | ||
| 2202 | dwarf.AT.name => maybe_tu_name = attr.getString(debug_info, compile_unit.cuh) orelse continue, | ||
| 2203 | else => continue, | ||
| 2204 | }; | ||
| 2205 | |||
| 2206 | if (maybe_tu_name == null or maybe_tu_comp_dir == null) { | ||
| 2207 | log.debug("missing DWARF_AT_comp_dir and DWARF_AT_name attributes {s}; skipping", .{object.name}); | ||
| 2208 | return; | ||
| 2209 | } | ||
| 2210 | |||
| 2211 | const tu_name = maybe_tu_name.?; | ||
| 2212 | const tu_comp_dir = maybe_tu_comp_dir.?; | ||
| 2213 | |||
| 2214 | // Open scope | ||
| 2215 | try locals.ensureUnusedCapacity(3); | ||
| 2216 | locals.appendAssumeCapacity(.{ | ||
| 2217 | .n_strx = try self.strtab.insert(gpa, tu_comp_dir), | ||
| 2218 | .n_type = macho.N_SO, | ||
| 2219 | .n_sect = 0, | ||
| 2220 | .n_desc = 0, | ||
| 2221 | .n_value = 0, | ||
| 2222 | }); | ||
| 2223 | locals.appendAssumeCapacity(.{ | ||
| 2224 | .n_strx = try self.strtab.insert(gpa, tu_name), | ||
| 2225 | .n_type = macho.N_SO, | ||
| 2226 | .n_sect = 0, | ||
| 2227 | .n_desc = 0, | ||
| 2228 | .n_value = 0, | ||
| 2229 | }); | ||
| 2230 | locals.appendAssumeCapacity(.{ | ||
| 2231 | .n_strx = try self.strtab.insert(gpa, object.name), | ||
| 2232 | .n_type = macho.N_OSO, | ||
| 2233 | .n_sect = 0, | ||
| 2234 | .n_desc = 1, | ||
| 2235 | .n_value = object.mtime, | ||
| 2236 | }); | ||
| 2237 | |||
| 2238 | var stabs_buf: [4]macho.nlist_64 = undefined; | ||
| 2239 | |||
| 2240 | var name_lookup: ?DwarfInfo.SubprogramLookupByName = if (object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS == 0) blk: { | ||
| 2241 | var name_lookup = DwarfInfo.SubprogramLookupByName.init(gpa); | ||
| 2242 | errdefer name_lookup.deinit(); | ||
| 2243 | try name_lookup.ensureUnusedCapacity(@as(u32, @intCast(object.atoms.items.len))); | ||
| 2244 | try debug_info.genSubprogramLookupByName(compile_unit, lookup, &name_lookup); | ||
| 2245 | break :blk name_lookup; | ||
| 2246 | } else null; | ||
| 2247 | defer if (name_lookup) |*nl| nl.deinit(); | ||
| 2248 | |||
| 2249 | for (object.atoms.items) |atom_index| { | ||
| 2250 | const atom = self.getAtom(atom_index); | ||
| 2251 | const stabs = try self.generateSymbolStabsForSymbol( | ||
| 2252 | atom_index, | ||
| 2253 | atom.getSymbolWithLoc(), | ||
| 2254 | name_lookup, | ||
| 2255 | &stabs_buf, | ||
| 2256 | ); | ||
| 2257 | try locals.appendSlice(stabs); | ||
| 2258 | |||
| 2259 | var it = Atom.getInnerSymbolsIterator(self, atom_index); | ||
| 2260 | while (it.next()) |sym_loc| { | ||
| 2261 | const contained_stabs = try self.generateSymbolStabsForSymbol( | ||
| 2262 | atom_index, | ||
| 2263 | sym_loc, | ||
| 2264 | name_lookup, | ||
| 2265 | &stabs_buf, | ||
| 2266 | ); | ||
| 2267 | try locals.appendSlice(contained_stabs); | ||
| 2268 | } | ||
| 2269 | } | ||
| 2270 | |||
| 2271 | // Close scope | ||
| 2272 | try locals.append(.{ | ||
| 2273 | .n_strx = 0, | ||
| 2274 | .n_type = macho.N_SO, | ||
| 2275 | .n_sect = 0, | ||
| 2276 | .n_desc = 0, | ||
| 2277 | .n_value = 0, | ||
| 2278 | }); | ||
| 2279 | } | ||
| 2280 | |||
| 2281 | fn generateSymbolStabsForSymbol( | ||
| 2282 | self: *Zld, | ||
| 2283 | atom_index: Atom.Index, | ||
| 2284 | sym_loc: SymbolWithLoc, | ||
| 2285 | lookup: ?DwarfInfo.SubprogramLookupByName, | ||
| 2286 | buf: *[4]macho.nlist_64, | ||
| 2287 | ) ![]const macho.nlist_64 { | ||
| 2288 | const gpa = self.gpa; | ||
| 2289 | const object = self.objects.items[sym_loc.getFile().?]; | ||
| 2290 | const sym = self.getSymbol(sym_loc); | ||
| 2291 | const sym_name = self.getSymbolName(sym_loc); | ||
| 2292 | const header = self.sections.items(.header)[sym.n_sect - 1]; | ||
| 2293 | |||
| 2294 | if (sym.n_strx == 0) return buf[0..0]; | ||
| 2295 | if (self.symbolIsTemp(sym_loc)) return buf[0..0]; | ||
| 2296 | |||
| 2297 | if (!header.isCode()) { | ||
| 2298 | // Since we are not dealing with machine code, it's either a global or a static depending | ||
| 2299 | // on the linkage scope. | ||
| 2300 | if (sym.sect() and sym.ext()) { | ||
| 2301 | // Global gets an N_GSYM stab type. | ||
| 2302 | buf[0] = .{ | ||
| 2303 | .n_strx = try self.strtab.insert(gpa, sym_name), | ||
| 2304 | .n_type = macho.N_GSYM, | ||
| 2305 | .n_sect = sym.n_sect, | ||
| 2306 | .n_desc = 0, | ||
| 2307 | .n_value = 0, | ||
| 2308 | }; | ||
| 2309 | } else { | ||
| 2310 | // Local static gets an N_STSYM stab type. | ||
| 2311 | buf[0] = .{ | ||
| 2312 | .n_strx = try self.strtab.insert(gpa, sym_name), | ||
| 2313 | .n_type = macho.N_STSYM, | ||
| 2314 | .n_sect = sym.n_sect, | ||
| 2315 | .n_desc = 0, | ||
| 2316 | .n_value = sym.n_value, | ||
| 2317 | }; | ||
| 2318 | } | ||
| 2319 | return buf[0..1]; | ||
| 2320 | } | ||
| 2321 | |||
| 2322 | const size: u64 = size: { | ||
| 2323 | if (object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0) { | ||
| 2324 | break :size self.getAtom(atom_index).size; | ||
| 2325 | } | ||
| 2326 | |||
| 2327 | // Since we don't have subsections to work with, we need to infer the size of each function | ||
| 2328 | // the slow way by scanning the debug info for matching symbol names and extracting | ||
| 2329 | // the symbol's DWARF_AT_low_pc and DWARF_AT_high_pc values. | ||
| 2330 | const source_sym = object.getSourceSymbol(sym_loc.sym_index) orelse return buf[0..0]; | ||
| 2331 | const subprogram = lookup.?.get(sym_name[1..]) orelse return buf[0..0]; | ||
| 2332 | |||
| 2333 | if (subprogram.addr <= source_sym.n_value and source_sym.n_value < subprogram.addr + subprogram.size) { | ||
| 2334 | break :size subprogram.size; | ||
| 2335 | } else { | ||
| 2336 | log.debug("no stab found for {s}", .{sym_name}); | ||
| 2337 | return buf[0..0]; | ||
| 2338 | } | ||
| 2339 | }; | ||
| 2340 | |||
| 2341 | buf[0] = .{ | ||
| 2342 | .n_strx = 0, | ||
| 2343 | .n_type = macho.N_BNSYM, | ||
| 2344 | .n_sect = sym.n_sect, | ||
| 2345 | .n_desc = 0, | ||
| 2346 | .n_value = sym.n_value, | ||
| 2347 | }; | ||
| 2348 | buf[1] = .{ | ||
| 2349 | .n_strx = try self.strtab.insert(gpa, sym_name), | ||
| 2350 | .n_type = macho.N_FUN, | ||
| 2351 | .n_sect = sym.n_sect, | ||
| 2352 | .n_desc = 0, | ||
| 2353 | .n_value = sym.n_value, | ||
| 2354 | }; | ||
| 2355 | buf[2] = .{ | ||
| 2356 | .n_strx = 0, | ||
| 2357 | .n_type = macho.N_FUN, | ||
| 2358 | .n_sect = 0, | ||
| 2359 | .n_desc = 0, | ||
| 2360 | .n_value = size, | ||
| 2361 | }; | ||
| 2362 | buf[3] = .{ | ||
| 2363 | .n_strx = 0, | ||
| 2364 | .n_type = macho.N_ENSYM, | ||
| 2365 | .n_sect = sym.n_sect, | ||
| 2366 | .n_desc = 0, | ||
| 2367 | .n_value = size, | ||
| 2368 | }; | ||
| 2369 | |||
| 2370 | return buf; | ||
| 2371 | } | ||
| 2372 | |||
| 2373 | fn logSegments(self: *Zld) void { | ||
| 2374 | log.debug("segments:", .{}); | ||
| 2375 | for (self.segments.items, 0..) |segment, i| { | ||
| 2376 | log.debug(" segment({d}): {s} @{x} ({x}), sizeof({x})", .{ | ||
| 2377 | i, | ||
| 2378 | segment.segName(), | ||
| 2379 | segment.fileoff, | ||
| 2380 | segment.vmaddr, | ||
| 2381 | segment.vmsize, | ||
| 2382 | }); | ||
| 2383 | } | ||
| 2384 | } | ||
| 2385 | |||
| 2386 | fn logSections(self: *Zld) void { | ||
| 2387 | log.debug("sections:", .{}); | ||
| 2388 | for (self.sections.items(.header), 0..) |header, i| { | ||
| 2389 | log.debug(" sect({d}): {s},{s} @{x} ({x}), sizeof({x})", .{ | ||
| 2390 | i + 1, | ||
| 2391 | header.segName(), | ||
| 2392 | header.sectName(), | ||
| 2393 | header.offset, | ||
| 2394 | header.addr, | ||
| 2395 | header.size, | ||
| 2396 | }); | ||
| 2397 | } | ||
| 2398 | } | ||
| 2399 | |||
| 2400 | fn logSymAttributes(sym: macho.nlist_64, buf: []u8) []const u8 { | ||
| 2401 | if (sym.sect()) { | ||
| 2402 | buf[0] = 's'; | ||
| 2403 | } | ||
| 2404 | if (sym.ext()) { | ||
| 2405 | if (sym.weakDef() or sym.pext()) { | ||
| 2406 | buf[1] = 'w'; | ||
| 2407 | } else { | ||
| 2408 | buf[1] = 'e'; | ||
| 2409 | } | ||
| 2410 | } | ||
| 2411 | if (sym.tentative()) { | ||
| 2412 | buf[2] = 't'; | ||
| 2413 | } | ||
| 2414 | if (sym.undf()) { | ||
| 2415 | buf[3] = 'u'; | ||
| 2416 | } | ||
| 2417 | return buf[0..]; | ||
| 2418 | } | ||
| 2419 | |||
| 2420 | fn logSymtab(self: *Zld) void { | ||
| 2421 | var buf: [4]u8 = undefined; | ||
| 2422 | |||
| 2423 | const scoped_log = std.log.scoped(.symtab); | ||
| 2424 | |||
| 2425 | scoped_log.debug("locals:", .{}); | ||
| 2426 | for (self.objects.items, 0..) |object, id| { | ||
| 2427 | scoped_log.debug(" object({d}): {s}", .{ id, object.name }); | ||
| 2428 | if (object.in_symtab == null) continue; | ||
| 2429 | for (object.symtab, 0..) |sym, sym_id| { | ||
| 2430 | @memset(&buf, '_'); | ||
| 2431 | scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s}", .{ | ||
| 2432 | sym_id, | ||
| 2433 | object.getSymbolName(@as(u32, @intCast(sym_id))), | ||
| 2434 | sym.n_value, | ||
| 2435 | sym.n_sect, | ||
| 2436 | logSymAttributes(sym, &buf), | ||
| 2437 | }); | ||
| 2438 | } | ||
| 2439 | } | ||
| 2440 | scoped_log.debug(" object(-1)", .{}); | ||
| 2441 | for (self.locals.items, 0..) |sym, sym_id| { | ||
| 2442 | if (sym.undf()) continue; | ||
| 2443 | scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s}", .{ | ||
| 2444 | sym_id, | ||
| 2445 | self.strtab.get(sym.n_strx).?, | ||
| 2446 | sym.n_value, | ||
| 2447 | sym.n_sect, | ||
| 2448 | logSymAttributes(sym, &buf), | ||
| 2449 | }); | ||
| 2450 | } | ||
| 2451 | |||
| 2452 | scoped_log.debug("exports:", .{}); | ||
| 2453 | for (self.globals.items, 0..) |global, i| { | ||
| 2454 | const sym = self.getSymbol(global); | ||
| 2455 | if (sym.undf()) continue; | ||
| 2456 | if (sym.n_desc == MachO.N_DEAD) continue; | ||
| 2457 | scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s} (def in object({?}))", .{ | ||
| 2458 | i, | ||
| 2459 | self.getSymbolName(global), | ||
| 2460 | sym.n_value, | ||
| 2461 | sym.n_sect, | ||
| 2462 | logSymAttributes(sym, &buf), | ||
| 2463 | global.file, | ||
| 2464 | }); | ||
| 2465 | } | ||
| 2466 | |||
| 2467 | scoped_log.debug("imports:", .{}); | ||
| 2468 | for (self.globals.items, 0..) |global, i| { | ||
| 2469 | const sym = self.getSymbol(global); | ||
| 2470 | if (!sym.undf()) continue; | ||
| 2471 | if (sym.n_desc == MachO.N_DEAD) continue; | ||
| 2472 | const ord = @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER); | ||
| 2473 | scoped_log.debug(" %{d}: {s} @{x} in ord({d}), {s}", .{ | ||
| 2474 | i, | ||
| 2475 | self.getSymbolName(global), | ||
| 2476 | sym.n_value, | ||
| 2477 | ord, | ||
| 2478 | logSymAttributes(sym, &buf), | ||
| 2479 | }); | ||
| 2480 | } | ||
| 2481 | |||
| 2482 | scoped_log.debug("GOT entries:", .{}); | ||
| 2483 | scoped_log.debug("{}", .{self.got_table}); | ||
| 2484 | |||
| 2485 | scoped_log.debug("TLV pointers:", .{}); | ||
| 2486 | scoped_log.debug("{}", .{self.tlv_ptr_table}); | ||
| 2487 | |||
| 2488 | scoped_log.debug("stubs entries:", .{}); | ||
| 2489 | scoped_log.debug("{}", .{self.stubs_table}); | ||
| 2490 | |||
| 2491 | scoped_log.debug("thunks:", .{}); | ||
| 2492 | for (self.thunks.items, 0..) |thunk, i| { | ||
| 2493 | scoped_log.debug(" thunk({d})", .{i}); | ||
| 2494 | const slice = thunk.targets.slice(); | ||
| 2495 | for (slice.items(.tag), slice.items(.target), 0..) |tag, target, j| { | ||
| 2496 | const atom_index = @as(u32, @intCast(thunk.getStartAtomIndex() + j)); | ||
| 2497 | const atom = self.getAtom(atom_index); | ||
| 2498 | const atom_sym = self.getSymbol(atom.getSymbolWithLoc()); | ||
| 2499 | const target_addr = switch (tag) { | ||
| 2500 | .stub => self.getStubsEntryAddress(target).?, | ||
| 2501 | .atom => self.getSymbol(target).n_value, | ||
| 2502 | }; | ||
| 2503 | scoped_log.debug(" {d}@{x} => {s}({s}@{x})", .{ | ||
| 2504 | j, | ||
| 2505 | atom_sym.n_value, | ||
| 2506 | @tagName(tag), | ||
| 2507 | self.getSymbolName(target), | ||
| 2508 | target_addr, | ||
| 2509 | }); | ||
| 2510 | } | ||
| 2511 | } | ||
| 2512 | } | ||
| 2513 | |||
| 2514 | fn logAtoms(self: *Zld) void { | ||
| 2515 | log.debug("atoms:", .{}); | ||
| 2516 | const slice = self.sections.slice(); | ||
| 2517 | for (slice.items(.first_atom_index), 0..) |first_atom_index, sect_id| { | ||
| 2518 | var atom_index = first_atom_index orelse continue; | ||
| 2519 | const header = slice.items(.header)[sect_id]; | ||
| 2520 | |||
| 2521 | log.debug("{s},{s}", .{ header.segName(), header.sectName() }); | ||
| 2522 | |||
| 2523 | while (true) { | ||
| 2524 | const atom = self.getAtom(atom_index); | ||
| 2525 | self.logAtom(atom_index, log); | ||
| 2526 | |||
| 2527 | if (atom.next_index) |next_index| { | ||
| 2528 | atom_index = next_index; | ||
| 2529 | } else break; | ||
| 2530 | } | ||
| 2531 | } | ||
| 2532 | } | ||
| 2533 | |||
| 2534 | pub fn logAtom(self: *Zld, atom_index: Atom.Index, logger: anytype) void { | ||
| 2535 | if (!build_options.enable_logging) return; | ||
| 2536 | |||
| 2537 | const atom = self.getAtom(atom_index); | ||
| 2538 | const sym = self.getSymbol(atom.getSymbolWithLoc()); | ||
| 2539 | const sym_name = self.getSymbolName(atom.getSymbolWithLoc()); | ||
| 2540 | logger.debug(" ATOM({d}, %{d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({?}) in sect({d})", .{ | ||
| 2541 | atom_index, | ||
| 2542 | atom.sym_index, | ||
| 2543 | sym_name, | ||
| 2544 | sym.n_value, | ||
| 2545 | atom.size, | ||
| 2546 | atom.alignment, | ||
| 2547 | atom.getFile(), | ||
| 2548 | sym.n_sect, | ||
| 2549 | }); | ||
| 2550 | |||
| 2551 | if (atom.getFile() != null) { | ||
| 2552 | var it = Atom.getInnerSymbolsIterator(self, atom_index); | ||
| 2553 | while (it.next()) |sym_loc| { | ||
| 2554 | const inner = self.getSymbol(sym_loc); | ||
| 2555 | const inner_name = self.getSymbolName(sym_loc); | ||
| 2556 | const offset = Atom.calcInnerSymbolOffset(self, atom_index, sym_loc.sym_index); | ||
| 2557 | |||
| 2558 | logger.debug(" (%{d}, '{s}') @ {x} ({x})", .{ | ||
| 2559 | sym_loc.sym_index, | ||
| 2560 | inner_name, | ||
| 2561 | inner.n_value, | ||
| 2562 | offset, | ||
| 2563 | }); | ||
| 2564 | } | ||
| 2565 | |||
| 2566 | if (Atom.getSectionAlias(self, atom_index)) |sym_loc| { | ||
| 2567 | const alias = self.getSymbol(sym_loc); | ||
| 2568 | const alias_name = self.getSymbolName(sym_loc); | ||
| 2569 | |||
| 2570 | logger.debug(" (%{d}, '{s}') @ {x} ({x})", .{ | ||
| 2571 | sym_loc.sym_index, | ||
| 2572 | alias_name, | ||
| 2573 | alias.n_value, | ||
| 2574 | 0, | ||
| 2575 | }); | ||
| 2576 | } | ||
| 2577 | } | ||
| 2578 | } | ||
| 2579 | }; | ||
| 2580 | |||
| 2581 | pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { | ||
| 2582 | const tracy = trace(@src()); | 6 | const tracy = trace(@src()); |
| 2583 | defer tracy.end(); | 7 | defer tracy.end(); |
| 2584 | 8 | ||
| ... | @@ -2611,8 +35,6 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr | ... | @@ -2611,8 +35,6 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr |
| 2611 | defer sub_prog_node.end(); | 35 | defer sub_prog_node.end(); |
| 2612 | 36 | ||
| 2613 | const cpu_arch = target.cpu.arch; | 37 | const cpu_arch = target.cpu.arch; |
| 2614 | const os_tag = target.os.tag; | ||
| 2615 | const abi = target.abi; | ||
| 2616 | const is_lib = options.output_mode == .Lib; | 38 | const is_lib = options.output_mode == .Lib; |
| 2617 | const is_dyn_lib = options.link_mode == .Dynamic and is_lib; | 39 | const is_dyn_lib = options.link_mode == .Dynamic and is_lib; |
| 2618 | const is_exe_or_dyn_lib = is_dyn_lib or options.output_mode == .Exe; | 40 | const is_exe_or_dyn_lib = is_dyn_lib or options.output_mode == .Exe; |
| ... | @@ -2730,29 +152,26 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr | ... | @@ -2730,29 +152,26 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr |
| 2730 | } else { | 152 | } else { |
| 2731 | const sub_path = options.emit.?.sub_path; | 153 | const sub_path = options.emit.?.sub_path; |
| 2732 | 154 | ||
| 155 | const old_file = macho_file.base.file; // TODO is this needed at all? | ||
| 156 | defer macho_file.base.file = old_file; | ||
| 157 | |||
| 2733 | const file = try directory.handle.createFile(sub_path, .{ | 158 | const file = try directory.handle.createFile(sub_path, .{ |
| 2734 | .truncate = true, | 159 | .truncate = true, |
| 2735 | .read = true, | 160 | .read = true, |
| 2736 | .mode = link.determineMode(options.*), | 161 | .mode = link.determineMode(options.*), |
| 2737 | }); | 162 | }); |
| 2738 | defer file.close(); | 163 | defer file.close(); |
| 2739 | 164 | macho_file.base.file = file; | |
| 2740 | var zld = Zld{ | ||
| 2741 | .gpa = gpa, | ||
| 2742 | .file = file, | ||
| 2743 | .options = options, | ||
| 2744 | }; | ||
| 2745 | defer zld.deinit(); | ||
| 2746 | 165 | ||
| 2747 | // Index 0 is always a null symbol. | 166 | // Index 0 is always a null symbol. |
| 2748 | try zld.locals.append(gpa, .{ | 167 | try macho_file.locals.append(gpa, .{ |
| 2749 | .n_strx = 0, | 168 | .n_strx = 0, |
| 2750 | .n_type = 0, | 169 | .n_type = 0, |
| 2751 | .n_sect = 0, | 170 | .n_sect = 0, |
| 2752 | .n_desc = 0, | 171 | .n_desc = 0, |
| 2753 | .n_value = 0, | 172 | .n_value = 0, |
| 2754 | }); | 173 | }); |
| 2755 | try zld.strtab.buffer.append(gpa, 0); | 174 | try macho_file.strtab.buffer.append(gpa, 0); |
| 2756 | 175 | ||
| 2757 | // Positional arguments to the linker such as object files and static archives. | 176 | // Positional arguments to the linker such as object files and static archives. |
| 2758 | var positionals = std.ArrayList(Compilation.LinkObject).init(arena); | 177 | var positionals = std.ArrayList(Compilation.LinkObject).init(arena); |
| ... | @@ -2930,9 +349,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr | ... | @@ -2930,9 +349,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr |
| 2930 | const in_file = try std.fs.cwd().openFile(obj.path, .{}); | 349 | const in_file = try std.fs.cwd().openFile(obj.path, .{}); |
| 2931 | defer in_file.close(); | 350 | defer in_file.close(); |
| 2932 | 351 | ||
| 2933 | MachO.parsePositional( | 352 | macho_file.parsePositional( |
| 2934 | &zld, | ||
| 2935 | gpa, | ||
| 2936 | in_file, | 353 | in_file, |
| 2937 | obj.path, | 354 | obj.path, |
| 2938 | obj.must_link, | 355 | obj.must_link, |
| ... | @@ -2949,9 +366,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr | ... | @@ -2949,9 +366,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr |
| 2949 | const in_file = try std.fs.cwd().openFile(path, .{}); | 366 | const in_file = try std.fs.cwd().openFile(path, .{}); |
| 2950 | defer in_file.close(); | 367 | defer in_file.close(); |
| 2951 | 368 | ||
| 2952 | MachO.parseLibrary( | 369 | macho_file.parseLibrary( |
| 2953 | &zld, | ||
| 2954 | gpa, | ||
| 2955 | in_file, | 370 | in_file, |
| 2956 | path, | 371 | path, |
| 2957 | lib, | 372 | lib, |
| ... | @@ -2965,198 +380,199 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr | ... | @@ -2965,198 +380,199 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr |
| 2965 | }; | 380 | }; |
| 2966 | } | 381 | } |
| 2967 | 382 | ||
| 2968 | MachO.parseDependentLibs(&zld, gpa, &dependent_libs, options) catch |err| { | 383 | macho_file.parseDependentLibs(&dependent_libs, options) catch |err| { |
| 2969 | // TODO convert to error | 384 | // TODO convert to error |
| 2970 | log.err("parsing dependent libraries failed with err {s}", .{@errorName(err)}); | 385 | log.err("parsing dependent libraries failed with err {s}", .{@errorName(err)}); |
| 2971 | }; | 386 | }; |
| 2972 | 387 | ||
| 2973 | try zld.resolveSymbols(); | 388 | var actions = std.ArrayList(MachO.ResolveAction).init(gpa); |
| 2974 | try macho_file.reportUndefined(&zld); | 389 | defer actions.deinit(); |
| 2975 | 390 | try macho_file.resolveSymbols(&actions); | |
| 2976 | if (options.output_mode == .Exe) { | 391 | try macho_file.reportUndefined(); |
| 2977 | const entry_name = options.entry orelse load_commands.default_entry_point; | ||
| 2978 | const global_index = zld.resolver.get(entry_name).?; // Error was flagged earlier | ||
| 2979 | zld.entry_index = global_index; | ||
| 2980 | } | ||
| 2981 | 392 | ||
| 2982 | for (zld.objects.items, 0..) |*object, object_id| { | 393 | for (macho_file.objects.items, 0..) |*object, object_id| { |
| 2983 | try object.splitIntoAtoms(&zld, @as(u32, @intCast(object_id))); | 394 | try object.splitIntoAtoms(macho_file, @as(u32, @intCast(object_id))); |
| 2984 | } | 395 | } |
| 2985 | 396 | ||
| 2986 | if (gc_sections) { | 397 | if (gc_sections) { |
| 2987 | try dead_strip.gcAtoms(&zld); | 398 | try dead_strip.gcAtoms(macho_file); |
| 2988 | } | 399 | } |
| 2989 | 400 | ||
| 2990 | try zld.createDyldPrivateAtom(); | 401 | try macho_file.createDyldPrivateAtom(); |
| 2991 | try zld.createTentativeDefAtoms(); | 402 | try macho_file.createTentativeDefAtoms(); |
| 2992 | 403 | ||
| 2993 | if (zld.options.output_mode == .Exe) { | 404 | if (macho_file.options.output_mode == .Exe) { |
| 2994 | const global = zld.getEntryPoint(); | 405 | const global = macho_file.getEntryPoint().?; |
| 2995 | if (zld.getSymbol(global).undf()) { | 406 | if (macho_file.getSymbol(global).undf()) { |
| 2996 | // We do one additional check here in case the entry point was found in one of the dylibs. | 407 | // We do one additional check here in case the entry point was found in one of the dylibs. |
| 2997 | // (I actually have no idea what this would imply but it is a possible outcome and so we | 408 | // (I actually have no idea what this would imply but it is a possible outcome and so we |
| 2998 | // support it.) | 409 | // support it.) |
| 2999 | try zld.addStubEntry(global); | 410 | try macho_file.addStubEntry(global); |
| 3000 | } | 411 | } |
| 3001 | } | 412 | } |
| 3002 | 413 | ||
| 3003 | for (zld.objects.items) |object| { | 414 | for (macho_file.objects.items) |object| { |
| 3004 | for (object.atoms.items) |atom_index| { | 415 | for (object.atoms.items) |atom_index| { |
| 3005 | const atom = zld.getAtom(atom_index); | 416 | const atom = macho_file.getAtom(atom_index); |
| 3006 | const sym = zld.getSymbol(atom.getSymbolWithLoc()); | 417 | const sym = macho_file.getSymbol(atom.getSymbolWithLoc()); |
| 3007 | const header = zld.sections.items(.header)[sym.n_sect - 1]; | 418 | const header = macho_file.sections.items(.header)[sym.n_sect - 1]; |
| 3008 | if (header.isZerofill()) continue; | 419 | if (header.isZerofill()) continue; |
| 3009 | 420 | ||
| 3010 | const relocs = Atom.getAtomRelocs(&zld, atom_index); | 421 | const relocs = Atom.getAtomRelocs(macho_file, atom_index); |
| 3011 | try Atom.scanAtomRelocs(&zld, atom_index, relocs); | 422 | try Atom.scanAtomRelocs(macho_file, atom_index, relocs); |
| 3012 | } | 423 | } |
| 3013 | } | 424 | } |
| 3014 | 425 | ||
| 3015 | try eh_frame.scanRelocs(&zld); | 426 | try eh_frame.scanRelocs(macho_file); |
| 3016 | try UnwindInfo.scanRelocs(&zld); | 427 | try UnwindInfo.scanRelocs(macho_file); |
| 3017 | 428 | ||
| 3018 | if (zld.dyld_stub_binder_index) |index| try zld.addGotEntry(zld.globals.items[index]); | 429 | if (macho_file.dyld_stub_binder_index) |index| |
| 430 | try macho_file.addGotEntry(macho_file.globals.items[index]); | ||
| 3019 | 431 | ||
| 3020 | try zld.calcSectionSizes(); | 432 | try macho_file.calcSectionSizes(); |
| 3021 | 433 | ||
| 3022 | var unwind_info = UnwindInfo{ .gpa = zld.gpa }; | 434 | var unwind_info = UnwindInfo{ .gpa = gpa }; |
| 3023 | defer unwind_info.deinit(); | 435 | defer unwind_info.deinit(); |
| 3024 | try unwind_info.collect(&zld); | 436 | try unwind_info.collect(macho_file); |
| 3025 | 437 | ||
| 3026 | try eh_frame.calcSectionSize(&zld, &unwind_info); | 438 | try eh_frame.calcSectionSize(macho_file, &unwind_info); |
| 3027 | try unwind_info.calcSectionSize(&zld); | 439 | try unwind_info.calcSectionSize(macho_file); |
| 3028 | 440 | ||
| 3029 | try zld.pruneAndSortSections(); | 441 | try pruneAndSortSections(macho_file); |
| 3030 | try zld.createSegments(); | 442 | try createSegments(macho_file); |
| 3031 | try zld.allocateSegments(); | 443 | try allocateSegments(macho_file); |
| 3032 | 444 | ||
| 3033 | try MachO.allocateSpecialSymbols(&zld); | 445 | try macho_file.allocateSpecialSymbols(); |
| 3034 | 446 | ||
| 3035 | if (build_options.enable_logging) { | 447 | if (build_options.enable_logging) { |
| 3036 | zld.logSymtab(); | 448 | macho_file.logSymtab(); |
| 3037 | zld.logSegments(); | 449 | macho_file.logSegments(); |
| 3038 | zld.logSections(); | 450 | macho_file.logSections(); |
| 3039 | zld.logAtoms(); | 451 | macho_file.logAtoms(); |
| 3040 | } | 452 | } |
| 3041 | 453 | ||
| 3042 | try zld.writeAtoms(); | 454 | try writeAtoms(macho_file); |
| 3043 | if (zld.requiresThunks()) try zld.writeThunks(); | 455 | if (macho_file.requiresThunks()) try writeThunks(macho_file); |
| 3044 | try zld.writeDyldPrivateAtom(); | 456 | try writeDyldPrivateAtom(macho_file); |
| 3045 | 457 | ||
| 3046 | if (zld.stubs_section_index) |_| { | 458 | if (macho_file.stubs_section_index) |_| { |
| 3047 | try zld.writeStubs(); | 459 | try writeStubs(macho_file); |
| 3048 | try zld.writeStubHelpers(); | 460 | try writeStubHelpers(macho_file); |
| 3049 | try zld.writeLaSymbolPtrs(); | 461 | try writeLaSymbolPtrs(macho_file); |
| 3050 | } | 462 | } |
| 3051 | if (zld.got_section_index) |sect_id| try zld.writePointerEntries(sect_id, &zld.got_table); | 463 | if (macho_file.got_section_index) |sect_id| |
| 3052 | if (zld.tlv_ptr_section_index) |sect_id| try zld.writePointerEntries(sect_id, &zld.tlv_ptr_table); | 464 | try macho_file.writePointerEntries(sect_id, &macho_file.got_table); |
| 465 | if (macho_file.tlv_ptr_section_index) |sect_id| | ||
| 466 | try macho_file.writePointerEntries(sect_id, &macho_file.tlv_ptr_table); | ||
| 3053 | 467 | ||
| 3054 | try eh_frame.write(&zld, &unwind_info); | 468 | try eh_frame.write(macho_file, &unwind_info); |
| 3055 | try unwind_info.write(&zld); | 469 | try unwind_info.write(macho_file); |
| 3056 | try zld.writeLinkeditSegmentData(); | 470 | try macho_file.writeLinkeditSegmentData(); |
| 3057 | 471 | ||
| 3058 | // If the last section of __DATA segment is zerofill section, we need to ensure | 472 | // If the last section of __DATA segment is zerofill section, we need to ensure |
| 3059 | // that the free space between the end of the last non-zerofill section of __DATA | 473 | // that the free space between the end of the last non-zerofill section of __DATA |
| 3060 | // segment and the beginning of __LINKEDIT segment is zerofilled as the loader will | 474 | // segment and the beginning of __LINKEDIT segment is zerofilled as the loader will |
| 3061 | // copy-paste this space into memory for quicker zerofill operation. | 475 | // copy-paste this space into memory for quicker zerofill operation. |
| 3062 | if (zld.data_segment_cmd_index) |data_seg_id| blk: { | 476 | if (macho_file.data_segment_cmd_index) |data_seg_id| blk: { |
| 3063 | var physical_zerofill_start: ?u64 = null; | 477 | var physical_zerofill_start: ?u64 = null; |
| 3064 | const section_indexes = zld.getSectionIndexes(data_seg_id); | 478 | const section_indexes = macho_file.getSectionIndexes(data_seg_id); |
| 3065 | for (zld.sections.items(.header)[section_indexes.start..section_indexes.end]) |header| { | 479 | for (macho_file.sections.items(.header)[section_indexes.start..section_indexes.end]) |header| { |
| 3066 | if (header.isZerofill() and header.size > 0) break; | 480 | if (header.isZerofill() and header.size > 0) break; |
| 3067 | physical_zerofill_start = header.offset + header.size; | 481 | physical_zerofill_start = header.offset + header.size; |
| 3068 | } else break :blk; | 482 | } else break :blk; |
| 3069 | const start = physical_zerofill_start orelse break :blk; | 483 | const start = physical_zerofill_start orelse break :blk; |
| 3070 | const linkedit = zld.getLinkeditSegmentPtr(); | 484 | const linkedit = macho_file.getLinkeditSegmentPtr(); |
| 3071 | const size = math.cast(usize, linkedit.fileoff - start) orelse return error.Overflow; | 485 | const size = math.cast(usize, linkedit.fileoff - start) orelse return error.Overflow; |
| 3072 | if (size > 0) { | 486 | if (size > 0) { |
| 3073 | log.debug("zeroing out zerofill area of length {x} at {x}", .{ size, start }); | 487 | log.debug("zeroing out zerofill area of length {x} at {x}", .{ size, start }); |
| 3074 | var padding = try zld.gpa.alloc(u8, size); | 488 | var padding = try gpa.alloc(u8, size); |
| 3075 | defer zld.gpa.free(padding); | 489 | defer gpa.free(padding); |
| 3076 | @memset(padding, 0); | 490 | @memset(padding, 0); |
| 3077 | try zld.file.pwriteAll(padding, start); | 491 | try macho_file.base.file.?.pwriteAll(padding, start); |
| 3078 | } | 492 | } |
| 3079 | } | 493 | } |
| 3080 | 494 | ||
| 3081 | // Write code signature padding if required | 495 | // Write code signature padding if required |
| 3082 | const requires_codesig = blk: { | 496 | var codesig: ?CodeSignature = if (macho_file.requiresCodeSignature()) blk: { |
| 3083 | if (options.entitlements) |_| break :blk true; | ||
| 3084 | if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) break :blk true; | ||
| 3085 | break :blk false; | ||
| 3086 | }; | ||
| 3087 | var codesig: ?CodeSignature = if (requires_codesig) blk: { | ||
| 3088 | // Preallocate space for the code signature. | 497 | // Preallocate space for the code signature. |
| 3089 | // We need to do this at this stage so that we have the load commands with proper values | 498 | // We need to do this at this stage so that we have the load commands with proper values |
| 3090 | // written out to the file. | 499 | // written out to the file. |
| 3091 | // The most important here is to have the correct vm and filesize of the __LINKEDIT segment | 500 | // The most important here is to have the correct vm and filesize of the __LINKEDIT segment |
| 3092 | // where the code signature goes into. | 501 | // where the code signature goes into. |
| 3093 | var codesig = CodeSignature.init(MachO.getPageSize(zld.options.target.cpu.arch)); | 502 | var codesig = CodeSignature.init(MachO.getPageSize(cpu_arch)); |
| 3094 | codesig.code_directory.ident = fs.path.basename(full_out_path); | 503 | codesig.code_directory.ident = fs.path.basename(full_out_path); |
| 3095 | if (options.entitlements) |path| { | 504 | if (options.entitlements) |path| { |
| 3096 | try codesig.addEntitlements(zld.gpa, path); | 505 | try codesig.addEntitlements(gpa, path); |
| 3097 | } | 506 | } |
| 3098 | try zld.writeCodeSignaturePadding(&codesig); | 507 | try macho_file.writeCodeSignaturePadding(&codesig); |
| 3099 | break :blk codesig; | 508 | break :blk codesig; |
| 3100 | } else null; | 509 | } else null; |
| 3101 | defer if (codesig) |*csig| csig.deinit(zld.gpa); | 510 | defer if (codesig) |*csig| csig.deinit(gpa); |
| 3102 | 511 | ||
| 3103 | // Write load commands | 512 | // Write load commands |
| 3104 | var lc_buffer = std.ArrayList(u8).init(arena); | 513 | var lc_buffer = std.ArrayList(u8).init(arena); |
| 3105 | const lc_writer = lc_buffer.writer(); | 514 | const lc_writer = lc_buffer.writer(); |
| 3106 | 515 | ||
| 3107 | try MachO.writeSegmentHeaders(&zld, lc_writer); | 516 | try macho_file.writeSegmentHeaders(lc_writer); |
| 3108 | try lc_writer.writeStruct(zld.dyld_info_cmd); | 517 | try lc_writer.writeStruct(macho_file.dyld_info_cmd); |
| 3109 | try lc_writer.writeStruct(zld.function_starts_cmd); | 518 | try lc_writer.writeStruct(macho_file.function_starts_cmd); |
| 3110 | try lc_writer.writeStruct(zld.data_in_code_cmd); | 519 | try lc_writer.writeStruct(macho_file.data_in_code_cmd); |
| 3111 | try lc_writer.writeStruct(zld.symtab_cmd); | 520 | try lc_writer.writeStruct(macho_file.symtab_cmd); |
| 3112 | try lc_writer.writeStruct(zld.dysymtab_cmd); | 521 | try lc_writer.writeStruct(macho_file.dysymtab_cmd); |
| 3113 | try load_commands.writeDylinkerLC(lc_writer); | 522 | try load_commands.writeDylinkerLC(lc_writer); |
| 3114 | 523 | ||
| 3115 | if (zld.options.output_mode == .Exe) { | 524 | switch (macho_file.base.options.output_mode) { |
| 3116 | const seg_id = zld.header_segment_cmd_index.?; | 525 | .Exe => blk: { |
| 3117 | const seg = zld.segments.items[seg_id]; | 526 | const seg_id = macho_file.header_segment_cmd_index.?; |
| 3118 | const global = zld.getEntryPoint(); | 527 | const seg = macho_file.segments.items[seg_id]; |
| 3119 | const sym = zld.getSymbol(global); | 528 | const global = macho_file.getEntryPoint() orelse break :blk; |
| 3120 | 529 | const sym = macho_file.getSymbol(global); | |
| 3121 | const addr: u64 = if (sym.undf()) | 530 | |
| 3122 | // In this case, the symbol has been resolved in one of dylibs and so we point | 531 | const addr: u64 = if (sym.undf()) |
| 3123 | // to the stub as its vmaddr value. | 532 | // In this case, the symbol has been resolved in one of dylibs and so we point |
| 3124 | zld.getStubsEntryAddress(global).? | 533 | // to the stub as its vmaddr value. |
| 3125 | else | 534 | macho_file.getStubsEntryAddress(global).? |
| 3126 | sym.n_value; | 535 | else |
| 3127 | 536 | sym.n_value; | |
| 3128 | try lc_writer.writeStruct(macho.entry_point_command{ | 537 | |
| 3129 | .entryoff = @as(u32, @intCast(addr - seg.vmaddr)), | 538 | try lc_writer.writeStruct(macho.entry_point_command{ |
| 3130 | .stacksize = options.stack_size_override orelse 0, | 539 | .entryoff = @as(u32, @intCast(addr - seg.vmaddr)), |
| 3131 | }); | 540 | .stacksize = macho_file.base.options.stack_size_override orelse 0, |
| 3132 | } else { | 541 | }); |
| 3133 | assert(zld.options.output_mode == .Lib); | 542 | }, |
| 3134 | try load_commands.writeDylibIdLC(zld.gpa, zld.options, lc_writer); | 543 | .Lib => if (macho_file.base.options.link_mode == .Dynamic) { |
| 544 | try load_commands.writeDylibIdLC(gpa, &macho_file.base.options, lc_writer); | ||
| 545 | }, | ||
| 546 | else => {}, | ||
| 3135 | } | 547 | } |
| 3136 | 548 | ||
| 3137 | try load_commands.writeRpathLCs(zld.gpa, zld.options, lc_writer); | 549 | try load_commands.writeRpathLCs(gpa, macho_file.base.options, lc_writer); |
| 3138 | try lc_writer.writeStruct(macho.source_version_command{ | 550 | try lc_writer.writeStruct(macho.source_version_command{ |
| 3139 | .version = 0, | 551 | .version = 0, |
| 3140 | }); | 552 | }); |
| 3141 | try load_commands.writeBuildVersionLC(zld.options, lc_writer); | 553 | try load_commands.writeBuildVersionLC(macho_file.base.options, lc_writer); |
| 3142 | 554 | ||
| 3143 | const uuid_cmd_offset = @sizeOf(macho.mach_header_64) + @as(u32, @intCast(lc_buffer.items.len)); | 555 | const uuid_cmd_offset = @sizeOf(macho.mach_header_64) + @as(u32, @intCast(lc_buffer.items.len)); |
| 3144 | try lc_writer.writeStruct(zld.uuid_cmd); | 556 | try lc_writer.writeStruct(macho_file.uuid_cmd); |
| 3145 | 557 | ||
| 3146 | try load_commands.writeLoadDylibLCs(zld.dylibs.items, zld.referenced_dylibs.keys(), lc_writer); | 558 | try load_commands.writeLoadDylibLCs( |
| 559 | macho_file.dylibs.items, | ||
| 560 | macho_file.referenced_dylibs.keys(), | ||
| 561 | lc_writer, | ||
| 562 | ); | ||
| 3147 | 563 | ||
| 3148 | if (requires_codesig) { | 564 | if (codesig != null) { |
| 3149 | try lc_writer.writeStruct(zld.codesig_cmd); | 565 | try lc_writer.writeStruct(macho_file.codesig_cmd); |
| 3150 | } | 566 | } |
| 3151 | 567 | ||
| 3152 | const ncmds = load_commands.calcNumOfLCs(lc_buffer.items); | 568 | const ncmds = load_commands.calcNumOfLCs(lc_buffer.items); |
| 3153 | try zld.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64)); | 569 | try macho_file.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64)); |
| 3154 | try zld.writeHeader(ncmds, @as(u32, @intCast(lc_buffer.items.len))); | 570 | try macho_file.writeHeader(ncmds, @as(u32, @intCast(lc_buffer.items.len))); |
| 3155 | try zld.writeUuid(comp, uuid_cmd_offset, requires_codesig); | 571 | try macho_file.writeUuid(comp, uuid_cmd_offset, codesig != null); |
| 3156 | 572 | ||
| 3157 | if (codesig) |*csig| { | 573 | if (codesig) |*csig| { |
| 3158 | try zld.writeCodeSignature(comp, csig); // code signing always comes last | 574 | try macho_file.writeCodeSignature(comp, csig); // code signing always comes last |
| 3159 | try MachO.invalidateKernelCache(directory.handle, zld.options.emit.?.sub_path); | 575 | try MachO.invalidateKernelCache(directory.handle, macho_file.base.options.emit.?.sub_path); |
| 3160 | } | 576 | } |
| 3161 | } | 577 | } |
| 3162 | 578 | ||
| ... | @@ -3177,3 +593,609 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr | ... | @@ -3177,3 +593,609 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr |
| 3177 | macho_file.base.lock = man.toOwnedLock(); | 593 | macho_file.base.lock = man.toOwnedLock(); |
| 3178 | } | 594 | } |
| 3179 | } | 595 | } |
| 596 | |||
| 597 | fn createSegments(macho_file: *MachO) !void { | ||
| 598 | const gpa = macho_file.base.allocator; | ||
| 599 | const pagezero_vmsize = macho_file.base.options.pagezero_size orelse MachO.default_pagezero_vmsize; | ||
| 600 | const page_size = MachO.getPageSize(macho_file.base.options.target.cpu.arch); | ||
| 601 | const aligned_pagezero_vmsize = mem.alignBackward(u64, pagezero_vmsize, page_size); | ||
| 602 | if (macho_file.base.options.output_mode != .Lib and aligned_pagezero_vmsize > 0) { | ||
| 603 | if (aligned_pagezero_vmsize != pagezero_vmsize) { | ||
| 604 | log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_vmsize}); | ||
| 605 | log.warn(" rounding down to 0x{x}", .{aligned_pagezero_vmsize}); | ||
| 606 | } | ||
| 607 | macho_file.pagezero_segment_cmd_index = @intCast(macho_file.segments.items.len); | ||
| 608 | try macho_file.segments.append(gpa, .{ | ||
| 609 | .cmdsize = @sizeOf(macho.segment_command_64), | ||
| 610 | .segname = MachO.makeStaticString("__PAGEZERO"), | ||
| 611 | .vmsize = aligned_pagezero_vmsize, | ||
| 612 | }); | ||
| 613 | } | ||
| 614 | |||
| 615 | // __TEXT segment is non-optional | ||
| 616 | { | ||
| 617 | const protection = MachO.getSegmentMemoryProtection("__TEXT"); | ||
| 618 | macho_file.text_segment_cmd_index = @intCast(macho_file.segments.items.len); | ||
| 619 | macho_file.header_segment_cmd_index = macho_file.text_segment_cmd_index.?; | ||
| 620 | try macho_file.segments.append(gpa, .{ | ||
| 621 | .cmdsize = @sizeOf(macho.segment_command_64), | ||
| 622 | .segname = MachO.makeStaticString("__TEXT"), | ||
| 623 | .maxprot = protection, | ||
| 624 | .initprot = protection, | ||
| 625 | }); | ||
| 626 | } | ||
| 627 | |||
| 628 | for (macho_file.sections.items(.header), 0..) |header, sect_id| { | ||
| 629 | if (header.size == 0) continue; // empty section | ||
| 630 | |||
| 631 | const segname = header.segName(); | ||
| 632 | const segment_id = macho_file.getSegmentByName(segname) orelse blk: { | ||
| 633 | log.debug("creating segment '{s}'", .{segname}); | ||
| 634 | const segment_id = @as(u8, @intCast(macho_file.segments.items.len)); | ||
| 635 | const protection = MachO.getSegmentMemoryProtection(segname); | ||
| 636 | try macho_file.segments.append(gpa, .{ | ||
| 637 | .cmdsize = @sizeOf(macho.segment_command_64), | ||
| 638 | .segname = MachO.makeStaticString(segname), | ||
| 639 | .maxprot = protection, | ||
| 640 | .initprot = protection, | ||
| 641 | }); | ||
| 642 | break :blk segment_id; | ||
| 643 | }; | ||
| 644 | const segment = &macho_file.segments.items[segment_id]; | ||
| 645 | segment.cmdsize += @sizeOf(macho.section_64); | ||
| 646 | segment.nsects += 1; | ||
| 647 | macho_file.sections.items(.segment_index)[sect_id] = segment_id; | ||
| 648 | } | ||
| 649 | |||
| 650 | if (macho_file.getSegmentByName("__DATA_CONST")) |index| { | ||
| 651 | macho_file.data_const_segment_cmd_index = index; | ||
| 652 | } | ||
| 653 | |||
| 654 | if (macho_file.getSegmentByName("__DATA")) |index| { | ||
| 655 | macho_file.data_segment_cmd_index = index; | ||
| 656 | } | ||
| 657 | |||
| 658 | // __LINKEDIT always comes last | ||
| 659 | { | ||
| 660 | const protection = MachO.getSegmentMemoryProtection("__LINKEDIT"); | ||
| 661 | macho_file.linkedit_segment_cmd_index = @intCast(macho_file.segments.items.len); | ||
| 662 | try macho_file.segments.append(gpa, .{ | ||
| 663 | .cmdsize = @sizeOf(macho.segment_command_64), | ||
| 664 | .segname = MachO.makeStaticString("__LINKEDIT"), | ||
| 665 | .maxprot = protection, | ||
| 666 | .initprot = protection, | ||
| 667 | }); | ||
| 668 | } | ||
| 669 | } | ||
| 670 | |||
| 671 | fn writeAtoms(macho_file: *MachO) !void { | ||
| 672 | const gpa = macho_file.base.allocator; | ||
| 673 | const slice = macho_file.sections.slice(); | ||
| 674 | |||
| 675 | for (slice.items(.first_atom_index), 0..) |first_atom_index, sect_id| { | ||
| 676 | const header = slice.items(.header)[sect_id]; | ||
| 677 | if (header.isZerofill()) continue; | ||
| 678 | |||
| 679 | var atom_index = first_atom_index orelse continue; | ||
| 680 | |||
| 681 | var buffer = try gpa.alloc(u8, math.cast(usize, header.size) orelse return error.Overflow); | ||
| 682 | defer gpa.free(buffer); | ||
| 683 | @memset(buffer, 0); // TODO with NOPs | ||
| 684 | |||
| 685 | log.debug("writing atoms in {s},{s}", .{ header.segName(), header.sectName() }); | ||
| 686 | |||
| 687 | while (true) { | ||
| 688 | const atom = macho_file.getAtom(atom_index); | ||
| 689 | if (atom.getFile()) |file| { | ||
| 690 | const this_sym = macho_file.getSymbol(atom.getSymbolWithLoc()); | ||
| 691 | const padding_size: usize = if (atom.next_index) |next_index| blk: { | ||
| 692 | const next_sym = macho_file.getSymbol(macho_file.getAtom(next_index).getSymbolWithLoc()); | ||
| 693 | const size = next_sym.n_value - (this_sym.n_value + atom.size); | ||
| 694 | break :blk math.cast(usize, size) orelse return error.Overflow; | ||
| 695 | } else 0; | ||
| 696 | |||
| 697 | log.debug(" (adding ATOM(%{d}, '{s}') from object({d}) to buffer)", .{ | ||
| 698 | atom.sym_index, | ||
| 699 | macho_file.getSymbolName(atom.getSymbolWithLoc()), | ||
| 700 | file, | ||
| 701 | }); | ||
| 702 | if (padding_size > 0) { | ||
| 703 | log.debug(" (with padding {x})", .{padding_size}); | ||
| 704 | } | ||
| 705 | |||
| 706 | const offset = this_sym.n_value - header.addr; | ||
| 707 | log.debug(" (at offset 0x{x})", .{offset}); | ||
| 708 | |||
| 709 | const code = Atom.getAtomCode(macho_file, atom_index); | ||
| 710 | const relocs = Atom.getAtomRelocs(macho_file, atom_index); | ||
| 711 | const size = math.cast(usize, atom.size) orelse return error.Overflow; | ||
| 712 | @memcpy(buffer[offset .. offset + size], code); | ||
| 713 | try Atom.resolveRelocs( | ||
| 714 | macho_file, | ||
| 715 | atom_index, | ||
| 716 | buffer[offset..][0..size], | ||
| 717 | relocs, | ||
| 718 | ); | ||
| 719 | } | ||
| 720 | |||
| 721 | if (atom.next_index) |next_index| { | ||
| 722 | atom_index = next_index; | ||
| 723 | } else break; | ||
| 724 | } | ||
| 725 | |||
| 726 | log.debug(" (writing at file offset 0x{x})", .{header.offset}); | ||
| 727 | try macho_file.base.file.?.pwriteAll(buffer, header.offset); | ||
| 728 | } | ||
| 729 | } | ||
| 730 | |||
| 731 | fn writeDyldPrivateAtom(macho_file: *MachO) !void { | ||
| 732 | const atom_index = macho_file.dyld_private_atom_index orelse return; | ||
| 733 | const atom = macho_file.getAtom(atom_index); | ||
| 734 | const sym = macho_file.getSymbol(atom.getSymbolWithLoc()); | ||
| 735 | const sect_id = macho_file.data_section_index.?; | ||
| 736 | const header = macho_file.sections.items(.header)[sect_id]; | ||
| 737 | const offset = sym.n_value - header.addr + header.offset; | ||
| 738 | log.debug("writing __dyld_private at offset 0x{x}", .{offset}); | ||
| 739 | const buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64); | ||
| 740 | try macho_file.base.file.?.pwriteAll(&buffer, offset); | ||
| 741 | } | ||
| 742 | |||
| 743 | fn writeThunks(macho_file: *MachO) !void { | ||
| 744 | assert(macho_file.requiresThunks()); | ||
| 745 | const gpa = macho_file.base.allocator; | ||
| 746 | |||
| 747 | const sect_id = macho_file.text_section_index orelse return; | ||
| 748 | const header = macho_file.sections.items(.header)[sect_id]; | ||
| 749 | |||
| 750 | for (macho_file.thunks.items, 0..) |*thunk, i| { | ||
| 751 | if (thunk.getSize() == 0) continue; | ||
| 752 | var buffer = try std.ArrayList(u8).initCapacity(gpa, thunk.getSize()); | ||
| 753 | defer buffer.deinit(); | ||
| 754 | try thunks.writeThunkCode(macho_file, thunk, buffer.writer()); | ||
| 755 | const thunk_atom = macho_file.getAtom(thunk.getStartAtomIndex()); | ||
| 756 | const thunk_sym = macho_file.getSymbol(thunk_atom.getSymbolWithLoc()); | ||
| 757 | const offset = thunk_sym.n_value - header.addr + header.offset; | ||
| 758 | log.debug("writing thunk({d}) at offset 0x{x}", .{ i, offset }); | ||
| 759 | try macho_file.base.file.?.pwriteAll(buffer.items, offset); | ||
| 760 | } | ||
| 761 | } | ||
| 762 | |||
| 763 | fn writePointerEntries(macho_file: *MachO, sect_id: u8, table: anytype) !void { | ||
| 764 | const gpa = macho_file.base.allocator; | ||
| 765 | const header = macho_file.sections.items(.header)[sect_id]; | ||
| 766 | var buffer = try std.ArrayList(u8).initCapacity(gpa, header.size); | ||
| 767 | defer buffer.deinit(); | ||
| 768 | for (table.entries.items) |entry| { | ||
| 769 | const sym = macho_file.getSymbol(entry); | ||
| 770 | buffer.writer().writeIntLittle(u64, sym.n_value) catch unreachable; | ||
| 771 | } | ||
| 772 | log.debug("writing __DATA_CONST,__got contents at file offset 0x{x}", .{header.offset}); | ||
| 773 | try macho_file.base.file.?.pwriteAll(buffer.items, header.offset); | ||
| 774 | } | ||
| 775 | |||
| 776 | fn writeStubs(macho_file: *MachO) !void { | ||
| 777 | const gpa = macho_file.base.allocator; | ||
| 778 | const cpu_arch = macho_file.base.options.target.cpu.arch; | ||
| 779 | const stubs_header = macho_file.sections.items(.header)[macho_file.stubs_section_index.?]; | ||
| 780 | const la_symbol_ptr_header = macho_file.sections.items(.header)[macho_file.la_symbol_ptr_section_index.?]; | ||
| 781 | |||
| 782 | var buffer = try std.ArrayList(u8).initCapacity(gpa, stubs_header.size); | ||
| 783 | defer buffer.deinit(); | ||
| 784 | |||
| 785 | for (0..macho_file.stub_table.count()) |index| { | ||
| 786 | try stubs.writeStubCode(.{ | ||
| 787 | .cpu_arch = cpu_arch, | ||
| 788 | .source_addr = stubs_header.addr + stubs.stubSize(cpu_arch) * index, | ||
| 789 | .target_addr = la_symbol_ptr_header.addr + index * @sizeOf(u64), | ||
| 790 | }, buffer.writer()); | ||
| 791 | } | ||
| 792 | |||
| 793 | log.debug("writing __TEXT,__stubs contents at file offset 0x{x}", .{stubs_header.offset}); | ||
| 794 | try macho_file.base.file.?.pwriteAll(buffer.items, stubs_header.offset); | ||
| 795 | } | ||
| 796 | |||
| 797 | fn writeStubHelpers(macho_file: *MachO) !void { | ||
| 798 | const gpa = macho_file.base.allocator; | ||
| 799 | const cpu_arch = macho_file.base.options.target.cpu.arch; | ||
| 800 | const stub_helper_header = macho_file.sections.items(.header)[macho_file.stub_helper_section_index.?]; | ||
| 801 | |||
| 802 | var buffer = try std.ArrayList(u8).initCapacity(gpa, stub_helper_header.size); | ||
| 803 | defer buffer.deinit(); | ||
| 804 | |||
| 805 | { | ||
| 806 | const dyld_private_addr = blk: { | ||
| 807 | const atom = macho_file.getAtom(macho_file.dyld_private_atom_index.?); | ||
| 808 | const sym = macho_file.getSymbol(atom.getSymbolWithLoc()); | ||
| 809 | break :blk sym.n_value; | ||
| 810 | }; | ||
| 811 | const dyld_stub_binder_got_addr = blk: { | ||
| 812 | const sym_loc = macho_file.globals.items[macho_file.dyld_stub_binder_index.?]; | ||
| 813 | break :blk macho_file.getGotEntryAddress(sym_loc).?; | ||
| 814 | }; | ||
| 815 | try stubs.writeStubHelperPreambleCode(.{ | ||
| 816 | .cpu_arch = cpu_arch, | ||
| 817 | .source_addr = stub_helper_header.addr, | ||
| 818 | .dyld_private_addr = dyld_private_addr, | ||
| 819 | .dyld_stub_binder_got_addr = dyld_stub_binder_got_addr, | ||
| 820 | }, buffer.writer()); | ||
| 821 | } | ||
| 822 | |||
| 823 | for (0..macho_file.stub_table.count()) |index| { | ||
| 824 | const source_addr = stub_helper_header.addr + stubs.stubHelperPreambleSize(cpu_arch) + | ||
| 825 | stubs.stubHelperSize(cpu_arch) * index; | ||
| 826 | try stubs.writeStubHelperCode(.{ | ||
| 827 | .cpu_arch = cpu_arch, | ||
| 828 | .source_addr = source_addr, | ||
| 829 | .target_addr = stub_helper_header.addr, | ||
| 830 | }, buffer.writer()); | ||
| 831 | } | ||
| 832 | |||
| 833 | log.debug("writing __TEXT,__stub_helper contents at file offset 0x{x}", .{ | ||
| 834 | stub_helper_header.offset, | ||
| 835 | }); | ||
| 836 | try macho_file.base.file.?.pwriteAll(buffer.items, stub_helper_header.offset); | ||
| 837 | } | ||
| 838 | |||
| 839 | fn writeLaSymbolPtrs(macho_file: *MachO) !void { | ||
| 840 | const gpa = macho_file.base.allocator; | ||
| 841 | const cpu_arch = macho_file.base.options.target.cpu.arch; | ||
| 842 | const la_symbol_ptr_header = macho_file.sections.items(.header)[macho_file.la_symbol_ptr_section_index.?]; | ||
| 843 | const stub_helper_header = macho_file.sections.items(.header)[macho_file.stub_helper_section_index.?]; | ||
| 844 | |||
| 845 | var buffer = try std.ArrayList(u8).initCapacity(gpa, la_symbol_ptr_header.size); | ||
| 846 | defer buffer.deinit(); | ||
| 847 | |||
| 848 | for (0..macho_file.stub_table.count()) |index| { | ||
| 849 | const target_addr = stub_helper_header.addr + stubs.stubHelperPreambleSize(cpu_arch) + | ||
| 850 | stubs.stubHelperSize(cpu_arch) * index; | ||
| 851 | buffer.writer().writeIntLittle(u64, target_addr) catch unreachable; | ||
| 852 | } | ||
| 853 | |||
| 854 | log.debug("writing __DATA,__la_symbol_ptr contents at file offset 0x{x}", .{ | ||
| 855 | la_symbol_ptr_header.offset, | ||
| 856 | }); | ||
| 857 | try macho_file.base.file.?.pwriteAll(buffer.items, la_symbol_ptr_header.offset); | ||
| 858 | } | ||
| 859 | |||
| 860 | fn pruneAndSortSections(macho_file: *MachO) !void { | ||
| 861 | const Entry = struct { | ||
| 862 | index: u8, | ||
| 863 | |||
| 864 | pub fn lessThan(ctx: *MachO, lhs: @This(), rhs: @This()) bool { | ||
| 865 | const lhs_header = ctx.sections.items(.header)[lhs.index]; | ||
| 866 | const rhs_header = ctx.sections.items(.header)[rhs.index]; | ||
| 867 | return MachO.getSectionPrecedence(lhs_header) < MachO.getSectionPrecedence(rhs_header); | ||
| 868 | } | ||
| 869 | }; | ||
| 870 | |||
| 871 | const gpa = macho_file.base.allocator; | ||
| 872 | |||
| 873 | var entries = try std.ArrayList(Entry).initCapacity(gpa, macho_file.sections.slice().len); | ||
| 874 | defer entries.deinit(); | ||
| 875 | |||
| 876 | for (0..macho_file.sections.slice().len) |index| { | ||
| 877 | const section = macho_file.sections.get(index); | ||
| 878 | if (section.header.size == 0) { | ||
| 879 | log.debug("pruning section {s},{s} {?d}", .{ | ||
| 880 | section.header.segName(), | ||
| 881 | section.header.sectName(), | ||
| 882 | section.first_atom_index, | ||
| 883 | }); | ||
| 884 | for (&[_]*?u8{ | ||
| 885 | &macho_file.text_section_index, | ||
| 886 | &macho_file.data_const_section_index, | ||
| 887 | &macho_file.data_section_index, | ||
| 888 | &macho_file.bss_section_index, | ||
| 889 | &macho_file.thread_vars_section_index, | ||
| 890 | &macho_file.thread_data_section_index, | ||
| 891 | &macho_file.thread_bss_section_index, | ||
| 892 | &macho_file.eh_frame_section_index, | ||
| 893 | &macho_file.unwind_info_section_index, | ||
| 894 | &macho_file.got_section_index, | ||
| 895 | &macho_file.tlv_ptr_section_index, | ||
| 896 | &macho_file.stubs_section_index, | ||
| 897 | &macho_file.stub_helper_section_index, | ||
| 898 | &macho_file.la_symbol_ptr_section_index, | ||
| 899 | }) |maybe_index| { | ||
| 900 | if (maybe_index.* != null and maybe_index.*.? == index) { | ||
| 901 | maybe_index.* = null; | ||
| 902 | } | ||
| 903 | } | ||
| 904 | continue; | ||
| 905 | } | ||
| 906 | entries.appendAssumeCapacity(.{ .index = @intCast(index) }); | ||
| 907 | } | ||
| 908 | |||
| 909 | mem.sort(Entry, entries.items, macho_file, Entry.lessThan); | ||
| 910 | |||
| 911 | var slice = macho_file.sections.toOwnedSlice(); | ||
| 912 | defer slice.deinit(gpa); | ||
| 913 | |||
| 914 | const backlinks = try gpa.alloc(u8, slice.len); | ||
| 915 | defer gpa.free(backlinks); | ||
| 916 | for (entries.items, 0..) |entry, i| { | ||
| 917 | backlinks[entry.index] = @as(u8, @intCast(i)); | ||
| 918 | } | ||
| 919 | |||
| 920 | try macho_file.sections.ensureTotalCapacity(gpa, entries.items.len); | ||
| 921 | for (entries.items) |entry| { | ||
| 922 | macho_file.sections.appendAssumeCapacity(slice.get(entry.index)); | ||
| 923 | } | ||
| 924 | |||
| 925 | for (&[_]*?u8{ | ||
| 926 | &macho_file.text_section_index, | ||
| 927 | &macho_file.data_const_section_index, | ||
| 928 | &macho_file.data_section_index, | ||
| 929 | &macho_file.bss_section_index, | ||
| 930 | &macho_file.thread_vars_section_index, | ||
| 931 | &macho_file.thread_data_section_index, | ||
| 932 | &macho_file.thread_bss_section_index, | ||
| 933 | &macho_file.eh_frame_section_index, | ||
| 934 | &macho_file.unwind_info_section_index, | ||
| 935 | &macho_file.got_section_index, | ||
| 936 | &macho_file.tlv_ptr_section_index, | ||
| 937 | &macho_file.stubs_section_index, | ||
| 938 | &macho_file.stub_helper_section_index, | ||
| 939 | &macho_file.la_symbol_ptr_section_index, | ||
| 940 | }) |maybe_index| { | ||
| 941 | if (maybe_index.*) |*index| { | ||
| 942 | index.* = backlinks[index.*]; | ||
| 943 | } | ||
| 944 | } | ||
| 945 | } | ||
| 946 | |||
| 947 | fn calcSectionSizes(macho_file: *MachO) !void { | ||
| 948 | const slice = macho_file.sections.slice(); | ||
| 949 | for (slice.items(.header), 0..) |*header, sect_id| { | ||
| 950 | if (header.size == 0) continue; | ||
| 951 | if (macho_file.text_section_index) |txt| { | ||
| 952 | if (txt == sect_id and macho_file.requiresThunks()) continue; | ||
| 953 | } | ||
| 954 | |||
| 955 | var atom_index = slice.items(.first_atom_index)[sect_id] orelse continue; | ||
| 956 | |||
| 957 | header.size = 0; | ||
| 958 | header.@"align" = 0; | ||
| 959 | |||
| 960 | while (true) { | ||
| 961 | const atom = macho_file.getAtom(atom_index); | ||
| 962 | const atom_alignment = try math.powi(u32, 2, atom.alignment); | ||
| 963 | const atom_offset = mem.alignForward(u64, header.size, atom_alignment); | ||
| 964 | const padding = atom_offset - header.size; | ||
| 965 | |||
| 966 | const sym = macho_file.getSymbolPtr(atom.getSymbolWithLoc()); | ||
| 967 | sym.n_value = atom_offset; | ||
| 968 | |||
| 969 | header.size += padding + atom.size; | ||
| 970 | header.@"align" = @max(header.@"align", atom.alignment); | ||
| 971 | |||
| 972 | if (atom.next_index) |next_index| { | ||
| 973 | atom_index = next_index; | ||
| 974 | } else break; | ||
| 975 | } | ||
| 976 | } | ||
| 977 | |||
| 978 | if (macho_file.text_section_index != null and macho_file.requiresThunks()) { | ||
| 979 | // Create jump/branch range extenders if needed. | ||
| 980 | try thunks.createThunks(macho_file, macho_file.text_section_index.?); | ||
| 981 | } | ||
| 982 | |||
| 983 | // Update offsets of all symbols contained within each Atom. | ||
| 984 | // We need to do this since our unwind info synthesiser relies on | ||
| 985 | // traversing the symbols when synthesising unwind info and DWARF CFI records. | ||
| 986 | for (slice.items(.first_atom_index)) |first_atom_index| { | ||
| 987 | var atom_index = first_atom_index orelse continue; | ||
| 988 | |||
| 989 | while (true) { | ||
| 990 | const atom = macho_file.getAtom(atom_index); | ||
| 991 | const sym = macho_file.getSymbol(atom.getSymbolWithLoc()); | ||
| 992 | |||
| 993 | if (atom.getFile() != null) { | ||
| 994 | // Update each symbol contained within the atom | ||
| 995 | var it = Atom.getInnerSymbolsIterator(macho_file, atom_index); | ||
| 996 | while (it.next()) |sym_loc| { | ||
| 997 | const inner_sym = macho_file.getSymbolPtr(sym_loc); | ||
| 998 | inner_sym.n_value = sym.n_value + Atom.calcInnerSymbolOffset( | ||
| 999 | macho_file, | ||
| 1000 | atom_index, | ||
| 1001 | sym_loc.sym_index, | ||
| 1002 | ); | ||
| 1003 | } | ||
| 1004 | |||
| 1005 | // If there is a section alias, update it now too | ||
| 1006 | if (Atom.getSectionAlias(macho_file, atom_index)) |sym_loc| { | ||
| 1007 | const alias = macho_file.getSymbolPtr(sym_loc); | ||
| 1008 | alias.n_value = sym.n_value; | ||
| 1009 | } | ||
| 1010 | } | ||
| 1011 | |||
| 1012 | if (atom.next_index) |next_index| { | ||
| 1013 | atom_index = next_index; | ||
| 1014 | } else break; | ||
| 1015 | } | ||
| 1016 | } | ||
| 1017 | |||
| 1018 | if (macho_file.got_section_index) |sect_id| { | ||
| 1019 | const header = &macho_file.sections.items(.header)[sect_id]; | ||
| 1020 | header.size = macho_file.got_table.count() * @sizeOf(u64); | ||
| 1021 | header.@"align" = 3; | ||
| 1022 | } | ||
| 1023 | |||
| 1024 | if (macho_file.tlv_ptr_section_index) |sect_id| { | ||
| 1025 | const header = &macho_file.sections.items(.header)[sect_id]; | ||
| 1026 | header.size = macho_file.tlv_ptr_table.count() * @sizeOf(u64); | ||
| 1027 | header.@"align" = 3; | ||
| 1028 | } | ||
| 1029 | |||
| 1030 | const cpu_arch = macho_file.base.options.target.cpu.arch; | ||
| 1031 | |||
| 1032 | if (macho_file.stubs_section_index) |sect_id| { | ||
| 1033 | const header = &macho_file.sections.items(.header)[sect_id]; | ||
| 1034 | header.size = macho_file.stub_table.count() * stubs.stubSize(cpu_arch); | ||
| 1035 | header.@"align" = stubs.stubAlignment(cpu_arch); | ||
| 1036 | } | ||
| 1037 | |||
| 1038 | if (macho_file.stub_helper_section_index) |sect_id| { | ||
| 1039 | const header = &macho_file.sections.items(.header)[sect_id]; | ||
| 1040 | header.size = macho_file.stub_table.count() * stubs.stubHelperSize(cpu_arch) + | ||
| 1041 | stubs.stubHelperPreambleSize(cpu_arch); | ||
| 1042 | header.@"align" = stubs.stubAlignment(cpu_arch); | ||
| 1043 | } | ||
| 1044 | |||
| 1045 | if (macho_file.la_symbol_ptr_section_index) |sect_id| { | ||
| 1046 | const header = &macho_file.sections.items(.header)[sect_id]; | ||
| 1047 | header.size = macho_file.stub_table.count() * @sizeOf(u64); | ||
| 1048 | header.@"align" = 3; | ||
| 1049 | } | ||
| 1050 | } | ||
| 1051 | |||
| 1052 | fn allocateSegments(macho_file: *MachO) !void { | ||
| 1053 | const gpa = macho_file.base.allocator; | ||
| 1054 | for (macho_file.segments.items, 0..) |*segment, segment_index| { | ||
| 1055 | const is_text_segment = mem.eql(u8, segment.segName(), "__TEXT"); | ||
| 1056 | const base_size = if (is_text_segment) try load_commands.calcMinHeaderPad(gpa, macho_file.base.options, .{ | ||
| 1057 | .segments = macho_file.segments.items, | ||
| 1058 | .dylibs = macho_file.dylibs.items, | ||
| 1059 | .referenced_dylibs = macho_file.referenced_dylibs.keys(), | ||
| 1060 | }) else 0; | ||
| 1061 | try allocateSegment(macho_file, @as(u8, @intCast(segment_index)), base_size); | ||
| 1062 | } | ||
| 1063 | } | ||
| 1064 | |||
| 1065 | fn getSegmentAllocBase(macho_file: *MachO, segment_index: u8) struct { vmaddr: u64, fileoff: u64 } { | ||
| 1066 | if (segment_index > 0) { | ||
| 1067 | const prev_segment = macho_file.segments.items[segment_index - 1]; | ||
| 1068 | return .{ | ||
| 1069 | .vmaddr = prev_segment.vmaddr + prev_segment.vmsize, | ||
| 1070 | .fileoff = prev_segment.fileoff + prev_segment.filesize, | ||
| 1071 | }; | ||
| 1072 | } | ||
| 1073 | return .{ .vmaddr = 0, .fileoff = 0 }; | ||
| 1074 | } | ||
| 1075 | |||
| 1076 | fn allocateSegment(macho_file: *MachO, segment_index: u8, init_size: u64) !void { | ||
| 1077 | const segment = &macho_file.segments.items[segment_index]; | ||
| 1078 | |||
| 1079 | if (mem.eql(u8, segment.segName(), "__PAGEZERO")) return; // allocated upon creation | ||
| 1080 | |||
| 1081 | const base = getSegmentAllocBase(macho_file, segment_index); | ||
| 1082 | segment.vmaddr = base.vmaddr; | ||
| 1083 | segment.fileoff = base.fileoff; | ||
| 1084 | segment.filesize = init_size; | ||
| 1085 | segment.vmsize = init_size; | ||
| 1086 | |||
| 1087 | // Allocate the sections according to their alignment at the beginning of the segment. | ||
| 1088 | const indexes = macho_file.getSectionIndexes(segment_index); | ||
| 1089 | var start = init_size; | ||
| 1090 | |||
| 1091 | const slice = macho_file.sections.slice(); | ||
| 1092 | for (slice.items(.header)[indexes.start..indexes.end], 0..) |*header, sect_id| { | ||
| 1093 | const alignment = try math.powi(u32, 2, header.@"align"); | ||
| 1094 | const start_aligned = mem.alignForward(u64, start, alignment); | ||
| 1095 | const n_sect = @as(u8, @intCast(indexes.start + sect_id + 1)); | ||
| 1096 | |||
| 1097 | header.offset = if (header.isZerofill()) | ||
| 1098 | 0 | ||
| 1099 | else | ||
| 1100 | @as(u32, @intCast(segment.fileoff + start_aligned)); | ||
| 1101 | header.addr = segment.vmaddr + start_aligned; | ||
| 1102 | |||
| 1103 | if (slice.items(.first_atom_index)[indexes.start + sect_id]) |first_atom_index| { | ||
| 1104 | var atom_index = first_atom_index; | ||
| 1105 | |||
| 1106 | log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{ | ||
| 1107 | n_sect, | ||
| 1108 | header.segName(), | ||
| 1109 | header.sectName(), | ||
| 1110 | }); | ||
| 1111 | |||
| 1112 | while (true) { | ||
| 1113 | const atom = macho_file.getAtom(atom_index); | ||
| 1114 | const sym = macho_file.getSymbolPtr(atom.getSymbolWithLoc()); | ||
| 1115 | sym.n_value += header.addr; | ||
| 1116 | sym.n_sect = n_sect; | ||
| 1117 | |||
| 1118 | log.debug(" ATOM(%{d}, '{s}') @{x}", .{ | ||
| 1119 | atom.sym_index, | ||
| 1120 | macho_file.getSymbolName(atom.getSymbolWithLoc()), | ||
| 1121 | sym.n_value, | ||
| 1122 | }); | ||
| 1123 | |||
| 1124 | if (atom.getFile() != null) { | ||
| 1125 | // Update each symbol contained within the atom | ||
| 1126 | var it = Atom.getInnerSymbolsIterator(macho_file, atom_index); | ||
| 1127 | while (it.next()) |sym_loc| { | ||
| 1128 | const inner_sym = macho_file.getSymbolPtr(sym_loc); | ||
| 1129 | inner_sym.n_value = sym.n_value + Atom.calcInnerSymbolOffset( | ||
| 1130 | macho_file, | ||
| 1131 | atom_index, | ||
| 1132 | sym_loc.sym_index, | ||
| 1133 | ); | ||
| 1134 | inner_sym.n_sect = n_sect; | ||
| 1135 | } | ||
| 1136 | |||
| 1137 | // If there is a section alias, update it now too | ||
| 1138 | if (Atom.getSectionAlias(macho_file, atom_index)) |sym_loc| { | ||
| 1139 | const alias = macho_file.getSymbolPtr(sym_loc); | ||
| 1140 | alias.n_value = sym.n_value; | ||
| 1141 | alias.n_sect = n_sect; | ||
| 1142 | } | ||
| 1143 | } | ||
| 1144 | |||
| 1145 | if (atom.next_index) |next_index| { | ||
| 1146 | atom_index = next_index; | ||
| 1147 | } else break; | ||
| 1148 | } | ||
| 1149 | } | ||
| 1150 | |||
| 1151 | start = start_aligned + header.size; | ||
| 1152 | |||
| 1153 | if (!header.isZerofill()) { | ||
| 1154 | segment.filesize = start; | ||
| 1155 | } | ||
| 1156 | segment.vmsize = start; | ||
| 1157 | } | ||
| 1158 | |||
| 1159 | const page_size = MachO.getPageSize(macho_file.base.options.target.cpu.arch); | ||
| 1160 | segment.filesize = mem.alignForward(u64, segment.filesize, page_size); | ||
| 1161 | segment.vmsize = mem.alignForward(u64, segment.vmsize, page_size); | ||
| 1162 | } | ||
| 1163 | |||
| 1164 | const std = @import("std"); | ||
| 1165 | const build_options = @import("build_options"); | ||
| 1166 | const assert = std.debug.assert; | ||
| 1167 | const dwarf = std.dwarf; | ||
| 1168 | const fs = std.fs; | ||
| 1169 | const log = std.log.scoped(.link); | ||
| 1170 | const macho = std.macho; | ||
| 1171 | const math = std.math; | ||
| 1172 | const mem = std.mem; | ||
| 1173 | |||
| 1174 | const aarch64 = @import("../../arch/aarch64/bits.zig"); | ||
| 1175 | const calcUuid = @import("uuid.zig").calcUuid; | ||
| 1176 | const dead_strip = @import("dead_strip.zig"); | ||
| 1177 | const eh_frame = @import("eh_frame.zig"); | ||
| 1178 | const fat = @import("fat.zig"); | ||
| 1179 | const link = @import("../../link.zig"); | ||
| 1180 | const load_commands = @import("load_commands.zig"); | ||
| 1181 | const stubs = @import("stubs.zig"); | ||
| 1182 | const thunks = @import("thunks.zig"); | ||
| 1183 | const trace = @import("../../tracy.zig").trace; | ||
| 1184 | |||
| 1185 | const Allocator = mem.Allocator; | ||
| 1186 | const Archive = @import("Archive.zig"); | ||
| 1187 | const Atom = @import("Atom.zig"); | ||
| 1188 | const Cache = std.Build.Cache; | ||
| 1189 | const CodeSignature = @import("CodeSignature.zig"); | ||
| 1190 | const Compilation = @import("../../Compilation.zig"); | ||
| 1191 | const Dylib = @import("Dylib.zig"); | ||
| 1192 | const MachO = @import("../MachO.zig"); | ||
| 1193 | const Md5 = std.crypto.hash.Md5; | ||
| 1194 | const LibStub = @import("../tapi.zig").LibStub; | ||
| 1195 | const Object = @import("Object.zig"); | ||
| 1196 | const Section = MachO.Section; | ||
| 1197 | const StringTable = @import("../strtab.zig").StringTable; | ||
| 1198 | const SymbolWithLoc = MachO.SymbolWithLoc; | ||
| 1199 | const TableSection = @import("../table_section.zig").TableSection; | ||
| 1200 | const Trie = @import("Trie.zig"); | ||
| 1201 | const UnwindInfo = @import("UnwindInfo.zig"); |