| ... | @@ -27,6 +27,10 @@ const LoadCommand = union(enum) { | ... | @@ -27,6 +27,10 @@ const LoadCommand = union(enum) { |
| 27 | LinkeditData: macho.linkedit_data_command, | 27 | LinkeditData: macho.linkedit_data_command, |
| 28 | Symtab: macho.symtab_command, | 28 | Symtab: macho.symtab_command, |
| 29 | Dysymtab: macho.dysymtab_command, | 29 | Dysymtab: macho.dysymtab_command, |
| | 30 | DyldInfo: macho.dyld_info_command, |
| | 31 | Dylinker: macho.dylinker_command, |
| | 32 | Dylib: macho.dylib_command, |
| | 33 | EntryPoint: macho.entry_point_command, |
| 30 | | 34 | |
| 31 | pub fn cmdsize(self: LoadCommand) u32 { | 35 | pub fn cmdsize(self: LoadCommand) u32 { |
| 32 | return switch (self) { | 36 | return switch (self) { |
| ... | @@ -34,6 +38,10 @@ const LoadCommand = union(enum) { | ... | @@ -34,6 +38,10 @@ const LoadCommand = union(enum) { |
| 34 | .LinkeditData => |x| x.cmdsize, | 38 | .LinkeditData => |x| x.cmdsize, |
| 35 | .Symtab => |x| x.cmdsize, | 39 | .Symtab => |x| x.cmdsize, |
| 36 | .Dysymtab => |x| x.cmdsize, | 40 | .Dysymtab => |x| x.cmdsize, |
| | 41 | .DyldInfo => |x| x.cmdsize, |
| | 42 | .Dylinker => |x| x.cmdsize, |
| | 43 | .Dylib => |x| x.cmdsize, |
| | 44 | .EntryPoint => |x| x.cmdsize, |
| 37 | }; | 45 | }; |
| 38 | } | 46 | } |
| 39 | | 47 | |
| ... | @@ -43,6 +51,10 @@ const LoadCommand = union(enum) { | ... | @@ -43,6 +51,10 @@ const LoadCommand = union(enum) { |
| 43 | .LinkeditData => |cmd| writeGeneric(cmd, file, offset), | 51 | .LinkeditData => |cmd| writeGeneric(cmd, file, offset), |
| 44 | .Symtab => |cmd| writeGeneric(cmd, file, offset), | 52 | .Symtab => |cmd| writeGeneric(cmd, file, offset), |
| 45 | .Dysymtab => |cmd| writeGeneric(cmd, file, offset), | 53 | .Dysymtab => |cmd| writeGeneric(cmd, file, offset), |
| | 54 | .DyldInfo => |cmd| writeGeneric(cmd, file, offset), |
| | 55 | .Dylinker => |cmd| writeGeneric(cmd, file, offset), |
| | 56 | .Dylib => |cmd| writeGeneric(cmd, file, offset), |
| | 57 | .EntryPoint => |cmd| writeGeneric(cmd, file, offset), |
| 46 | }; | 58 | }; |
| 47 | } | 59 | } |
| 48 | | 60 | |
| ... | @@ -56,24 +68,42 @@ base: File, | ... | @@ -56,24 +68,42 @@ base: File, |
| 56 | | 68 | |
| 57 | /// Table of all load commands | 69 | /// Table of all load commands |
| 58 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, | 70 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, |
| | 71 | /// __PAGEZERO segment |
| | 72 | pagezero_segment_cmd_index: ?u16 = null, |
| | 73 | /// __TEXT segment |
| | 74 | text_segment_cmd_index: ?u16 = null, |
| | 75 | /// __DATA segment |
| | 76 | data_segment_cmd_index: ?u16 = null, |
| | 77 | /// __LINKEDIT segment |
| | 78 | linkedit_segment_cmd_index: ?u16 = null, |
| 59 | segment_cmd_index: ?u16 = null, | 79 | segment_cmd_index: ?u16 = null, |
| | 80 | /// Dyld info |
| | 81 | dyld_info_cmd_index: ?u16 = null, |
| | 82 | /// Symbol table |
| 60 | symtab_cmd_index: ?u16 = null, | 83 | symtab_cmd_index: ?u16 = null, |
| | 84 | /// Dynamic symbol table |
| 61 | dysymtab_cmd_index: ?u16 = null, | 85 | dysymtab_cmd_index: ?u16 = null, |
| | 86 | /// Path to dyld linker |
| | 87 | dylinker_cmd_index: ?u16 = null, |
| | 88 | /// Path to libSystem |
| | 89 | libsystem_cmd_index: ?u16 = null, |
| | 90 | /// Data-in-code section of __LINKEDIT segment |
| 62 | data_in_code_cmd_index: ?u16 = null, | 91 | data_in_code_cmd_index: ?u16 = null, |
| | 92 | /// Address to entry point function |
| | 93 | function_starts_cmd_index: ?u16 = null, |
| | 94 | /// Main/entry point |
| | 95 | /// Specifies offset wrt __TEXT segment start address to the main entry point |
| | 96 | /// of the binary. |
| | 97 | main_cmd_index: ?u16 = null, |
| 63 | | 98 | |
| 64 | /// Table of all sections | 99 | /// Table of all sections |
| 65 | sections: std.ArrayListUnmanaged(macho.section_64) = .{}, | 100 | sections: std.ArrayListUnmanaged(macho.section_64) = .{}, |
| 66 | | 101 | |
| 67 | /// __TEXT segment sections | 102 | /// __TEXT,__text section |
| 68 | text_section_index: ?u16 = null, | 103 | text_section_index: ?u16 = null, |
| 69 | cstring_section_index: ?u16 = null, | | |
| 70 | const_text_section_index: ?u16 = null, | | |
| 71 | stubs_section_index: ?u16 = null, | | |
| 72 | stub_helper_section_index: ?u16 = null, | | |
| 73 | | 104 | |
| 74 | /// __DATA segment sections | 105 | /// __DATA,__got section |
| 75 | got_section_index: ?u16 = null, | 106 | got_section_index: ?u16 = null, |
| 76 | const_data_section_index: ?u16 = null, | | |
| 77 | | 107 | |
| 78 | entry_addr: ?u64 = null, | 108 | entry_addr: ?u64 = null, |
| 79 | | 109 | |
| ... | @@ -734,11 +764,13 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { | ... | @@ -734,11 +764,13 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 734 | .n_desc = 0, | 764 | .n_desc = 0, |
| 735 | .n_value = addr, | 765 | .n_value = addr, |
| 736 | }; | 766 | }; |
| | 767 | self.offset_table.items[decl.link.macho.offset_table_index.?] = addr; |
| 737 | | 768 | |
| 738 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. | 769 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. |
| 739 | const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; | 770 | const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; |
| 740 | try self.updateDeclExports(module, decl, decl_exports); | 771 | try self.updateDeclExports(module, decl, decl_exports); |
| 741 | try self.writeSymbol(decl.link.macho.symbol_table_index.?); | 772 | try self.writeSymbol(decl.link.macho.symbol_table_index.?); |
| | 773 | try self.writeOffsetTableEntry(decl.link.macho.offset_table_index.?); |
| 742 | | 774 | |
| 743 | const text_section = self.sections.items[self.text_section_index.?]; | 775 | const text_section = self.sections.items[self.text_section_index.?]; |
| 744 | const section_offset = symbol.n_value - text_section.addr; | 776 | const section_offset = symbol.n_value - text_section.addr; |
| ... | @@ -778,6 +810,25 @@ pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 { | ... | @@ -778,6 +810,25 @@ pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 { |
| 778 | } | 810 | } |
| 779 | | 811 | |
| 780 | pub fn populateMissingMetadata(self: *MachO) !void { | 812 | pub fn populateMissingMetadata(self: *MachO) !void { |
| | 813 | if (self.pagezero_segment_cmd_index == null) { |
| | 814 | self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| | 815 | try self.load_commands.append(self.base.allocator, .{ |
| | 816 | .Segment = .{ |
| | 817 | .cmd = macho.LC_SEGMENT_64, |
| | 818 | .cmdsize = @sizeOf(macho.segment_command_64), |
| | 819 | .segname = makeStaticString("__PAGEZERO"), |
| | 820 | .vmaddr = 0, |
| | 821 | .vmsize = 0x1000, // size always set to 4GB |
| | 822 | .fileoff = 0, |
| | 823 | .filesize = 0, |
| | 824 | .maxprot = 0, |
| | 825 | .initprot = 0, |
| | 826 | .nsects = 0, |
| | 827 | .flags = 0, |
| | 828 | }, |
| | 829 | }); |
| | 830 | self.cmd_table_dirty = true; |
| | 831 | } |
| 781 | if (self.segment_cmd_index == null) { | 832 | if (self.segment_cmd_index == null) { |
| 782 | self.segment_cmd_index = @intCast(u16, self.load_commands.items.len); | 833 | self.segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 783 | try self.load_commands.append(self.base.allocator, .{ | 834 | try self.load_commands.append(self.base.allocator, .{ |
| ... | @@ -818,7 +869,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -818,7 +869,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 818 | segment.nsects += 1; | 869 | segment.nsects += 1; |
| 819 | | 870 | |
| 820 | const file_size = self.base.options.program_code_size_hint; | 871 | const file_size = self.base.options.program_code_size_hint; |
| 821 | const off = @intCast(u32, self.findFreeSpace(file_size, 1)); | 872 | const off = @intCast(u32, self.findFreeSpace(file_size, 64)); |
| 822 | const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS; | 873 | const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS; |
| 823 | | 874 | |
| 824 | log.debug("found __text section free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); | 875 | log.debug("found __text section free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| ... | @@ -829,7 +880,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -829,7 +880,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 829 | .addr = 0, | 880 | .addr = 0, |
| 830 | .size = file_size, | 881 | .size = file_size, |
| 831 | .offset = off, | 882 | .offset = off, |
| 832 | .@"align" = 0x1000, | 883 | .@"align" = 12, |
| 833 | .reloff = 0, | 884 | .reloff = 0, |
| 834 | .nreloc = 0, | 885 | .nreloc = 0, |
| 835 | .flags = flags, | 886 | .flags = flags, |
| ... | @@ -843,6 +894,43 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -843,6 +894,43 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 843 | segment.fileoff = off; | 894 | segment.fileoff = off; |
| 844 | | 895 | |
| 845 | log.debug("initial text section {}\n", .{self.sections.items[self.text_section_index.?]}); | 896 | log.debug("initial text section {}\n", .{self.sections.items[self.text_section_index.?]}); |
| | 897 | log.debug("update segment {}\n", .{segment}); |
| | 898 | } |
| | 899 | if (self.got_section_index == null) { |
| | 900 | self.got_section_index = @intCast(u16, self.sections.items.len); |
| | 901 | const segment = &self.load_commands.items[self.segment_cmd_index.?].Segment; |
| | 902 | const text_sect = &self.sections.items[self.text_section_index.?]; |
| | 903 | segment.cmdsize += @sizeOf(macho.section_64); |
| | 904 | segment.nsects += 1; |
| | 905 | |
| | 906 | const p_align = @sizeOf(u64); |
| | 907 | const file_size = p_align * self.base.options.symbol_count_hint; |
| | 908 | const off = @intCast(u32, self.findFreeSpace(file_size, p_align)); |
| | 909 | |
| | 910 | log.debug("found __got section free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| | 911 | |
| | 912 | const padding_size = off - text_sect.offset - text_sect.size; |
| | 913 | |
| | 914 | try self.sections.append(self.base.allocator, .{ |
| | 915 | .sectname = makeStaticString("__got"), |
| | 916 | .segname = makeStaticString("__DATA"), |
| | 917 | .addr = text_sect.addr + text_sect.size + padding_size, |
| | 918 | .size = file_size, |
| | 919 | .offset = off, |
| | 920 | .@"align" = 3, |
| | 921 | .reloff = 0, |
| | 922 | .nreloc = 0, |
| | 923 | .flags = macho.S_REGULAR, |
| | 924 | .reserved1 = 0, |
| | 925 | .reserved2 = 0, |
| | 926 | .reserved3 = 0, |
| | 927 | }); |
| | 928 | |
| | 929 | segment.vmsize += file_size + padding_size; |
| | 930 | segment.filesize += file_size + padding_size; |
| | 931 | |
| | 932 | log.debug("initial got section {}\n", .{self.sections.items[self.got_section_index.?]}); |
| | 933 | log.debug("update segment {}\n", .{segment}); |
| 846 | } | 934 | } |
| 847 | { | 935 | { |
| 848 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | 936 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| ... | @@ -875,8 +963,9 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, | ... | @@ -875,8 +963,9 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 875 | const addr = blk: { | 963 | const addr = blk: { |
| 876 | if (self.last_text_block) |last| { | 964 | if (self.last_text_block) |last| { |
| 877 | const last_symbol = self.symbol_table.items[last.symbol_table_index.?]; | 965 | const last_symbol = self.symbol_table.items[last.symbol_table_index.?]; |
| 878 | const end_addr = last_symbol.n_value + last.size; | 966 | const ideal_capacity = last.size * alloc_num / alloc_den; |
| 879 | const new_start_addr = mem.alignForwardGeneric(u64, end_addr, alignment); | 967 | const ideal_capacity_end_addr = last_symbol.n_value + ideal_capacity; |
| | 968 | const new_start_addr = mem.alignForwardGeneric(u64, ideal_capacity_end_addr, alignment); |
| 880 | block_placement = last; | 969 | block_placement = last; |
| 881 | break :blk new_start_addr; | 970 | break :blk new_start_addr; |
| 882 | } else { | 971 | } else { |
| ... | @@ -893,12 +982,6 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, | ... | @@ -893,12 +982,6 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 893 | assert(needed_size <= text_capacity); // TODO handle growth | 982 | assert(needed_size <= text_capacity); // TODO handle growth |
| 894 | | 983 | |
| 895 | self.last_text_block = text_block; | 984 | self.last_text_block = text_block; |
| 896 | text_section.size = needed_size; | | |
| 897 | segment.vmsize = needed_size; | | |
| 898 | segment.filesize = needed_size; | | |
| 899 | if (alignment < text_section.@"align") { | | |
| 900 | text_section.@"align" = @intCast(u32, alignment); | | |
| 901 | } | | |
| 902 | } | 985 | } |
| 903 | text_block.size = new_block_size; | 986 | text_block.size = new_block_size; |
| 904 | | 987 | |
| ... | @@ -961,11 +1044,8 @@ fn addPadding(self: *MachO, size: u64, file_offset: u64) !void { | ... | @@ -961,11 +1044,8 @@ fn addPadding(self: *MachO, size: u64, file_offset: u64) !void { |
| 961 | | 1044 | |
| 962 | fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { | 1045 | fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { |
| 963 | const hdr_size: u64 = @sizeOf(macho.mach_header_64); | 1046 | const hdr_size: u64 = @sizeOf(macho.mach_header_64); |
| 964 | if (start < hdr_size) | 1047 | if (start < hdr_size) return hdr_size; |
| 965 | return hdr_size; | | |
| 966 | | | |
| 967 | const end = start + satMul(size, alloc_num) / alloc_den; | 1048 | const end = start + satMul(size, alloc_num) / alloc_den; |
| 968 | | | |
| 969 | { | 1049 | { |
| 970 | const off = @sizeOf(macho.mach_header_64); | 1050 | const off = @sizeOf(macho.mach_header_64); |
| 971 | var tight_size: u64 = 0; | 1051 | var tight_size: u64 = 0; |
| ... | @@ -978,7 +1058,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { | ... | @@ -978,7 +1058,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { |
| 978 | return test_end; | 1058 | return test_end; |
| 979 | } | 1059 | } |
| 980 | } | 1060 | } |
| 981 | | | |
| 982 | for (self.sections.items) |section| { | 1061 | for (self.sections.items) |section| { |
| 983 | const increased_size = satMul(section.size, alloc_num) / alloc_den; | 1062 | const increased_size = satMul(section.size, alloc_num) / alloc_den; |
| 984 | const test_end = section.offset + increased_size; | 1063 | const test_end = section.offset + increased_size; |
| ... | @@ -986,7 +1065,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { | ... | @@ -986,7 +1065,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { |
| 986 | return test_end; | 1065 | return test_end; |
| 987 | } | 1066 | } |
| 988 | } | 1067 | } |
| 989 | | | |
| 990 | if (self.symtab_cmd_index) |symtab_index| { | 1068 | if (self.symtab_cmd_index) |symtab_index| { |
| 991 | const symtab = self.load_commands.items[symtab_index].Symtab; | 1069 | const symtab = self.load_commands.items[symtab_index].Symtab; |
| 992 | { | 1070 | { |
| ... | @@ -1005,7 +1083,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { | ... | @@ -1005,7 +1083,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { |
| 1005 | } | 1083 | } |
| 1006 | } | 1084 | } |
| 1007 | } | 1085 | } |
| 1008 | | | |
| 1009 | return null; | 1086 | return null; |
| 1010 | } | 1087 | } |
| 1011 | | 1088 | |
| ... | @@ -1048,6 +1125,16 @@ fn writeSymbol(self: *MachO, index: usize) !void { | ... | @@ -1048,6 +1125,16 @@ fn writeSymbol(self: *MachO, index: usize) !void { |
| 1048 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off); | 1125 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off); |
| 1049 | } | 1126 | } |
| 1050 | | 1127 | |
| | 1128 | fn writeOffsetTableEntry(self: *MachO, index: usize) !void { |
| | 1129 | const sect = &self.sections.items[self.got_section_index.?]; |
| | 1130 | const endian = self.base.options.target.cpu.arch.endian(); |
| | 1131 | var buf: [@sizeOf(u64)]u8 = undefined; |
| | 1132 | mem.writeInt(u64, &buf, self.offset_table.items[index], endian); |
| | 1133 | const off = sect.offset + @sizeOf(u64) * index; |
| | 1134 | log.debug("writing offset table entry 0x{x} at 0x{x}\n", .{ self.offset_table.items[index], off }); |
| | 1135 | try self.base.file.?.pwriteAll(&buf, off); |
| | 1136 | } |
| | 1137 | |
| 1051 | /// Writes Mach-O file header. | 1138 | /// Writes Mach-O file header. |
| 1052 | /// Should be invoked last as it needs up-to-date values of ncmds and sizeof_cmds bookkeeping | 1139 | /// Should be invoked last as it needs up-to-date values of ncmds and sizeof_cmds bookkeeping |
| 1053 | /// variables. | 1140 | /// variables. |