| ... | ... | @@ -27,6 +27,10 @@ const LoadCommand = union(enum) { |
| 27 | 27 | LinkeditData: macho.linkedit_data_command, |
| 28 | 28 | Symtab: macho.symtab_command, |
| 29 | 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 | 35 | pub fn cmdsize(self: LoadCommand) u32 { |
| 32 | 36 | return switch (self) { |
| ... | ... | @@ -34,6 +38,10 @@ const LoadCommand = union(enum) { |
| 34 | 38 | .LinkeditData => |x| x.cmdsize, |
| 35 | 39 | .Symtab => |x| x.cmdsize, |
| 36 | 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 | 51 | .LinkeditData => |cmd| writeGeneric(cmd, file, offset), |
| 44 | 52 | .Symtab => |cmd| writeGeneric(cmd, file, offset), |
| 45 | 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,30 +68,52 @@ base: File, |
| 56 | 68 | |
| 57 | 69 | /// Table of all load commands |
| 58 | 70 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, |
| 59 | | segment_cmd_index: ?u16 = null, |
| 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, |
| 79 | /// Dyld info |
| 80 | dyld_info_cmd_index: ?u16 = null, |
| 81 | /// Symbol table |
| 60 | 82 | symtab_cmd_index: ?u16 = null, |
| 83 | /// Dynamic symbol table |
| 61 | 84 | dysymtab_cmd_index: ?u16 = null, |
| 85 | /// Path to dyld linker |
| 86 | dylinker_cmd_index: ?u16 = null, |
| 87 | /// Path to libSystem |
| 88 | libsystem_cmd_index: ?u16 = null, |
| 89 | /// Data-in-code section of __LINKEDIT segment |
| 62 | 90 | data_in_code_cmd_index: ?u16 = null, |
| 91 | /// Address to entry point function |
| 92 | function_starts_cmd_index: ?u16 = null, |
| 93 | /// Main/entry point |
| 94 | /// Specifies offset wrt __TEXT segment start address to the main entry point |
| 95 | /// of the binary. |
| 96 | main_cmd_index: ?u16 = null, |
| 63 | 97 | |
| 64 | 98 | /// Table of all sections |
| 65 | 99 | sections: std.ArrayListUnmanaged(macho.section_64) = .{}, |
| 66 | 100 | |
| 67 | | /// __TEXT segment sections |
| 101 | /// __TEXT,__text section |
| 68 | 102 | 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 | 103 | |
| 74 | | /// __DATA segment sections |
| 104 | /// __DATA,__got section |
| 75 | 105 | got_section_index: ?u16 = null, |
| 76 | | const_data_section_index: ?u16 = null, |
| 77 | 106 | |
| 78 | 107 | entry_addr: ?u64 = null, |
| 79 | 108 | |
| 80 | | /// Table of all symbols used. |
| 109 | /// Table of all local symbols |
| 81 | 110 | /// Internally references string table for names (which are optional). |
| 82 | | symbol_table: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 111 | local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 112 | /// Table of all defined global symbols |
| 113 | global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 114 | /// Table of all undefined symbols |
| 115 | undef_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 116 | dyld_stub_binder_index: ?u16 = null, |
| 83 | 117 | |
| 84 | 118 | /// Table of symbol names aka the string table. |
| 85 | 119 | string_table: std.ArrayListUnmanaged(u8) = .{}, |
| ... | ... | @@ -115,19 +149,27 @@ const LIB_SYSTEM_NAME: [*:0]const u8 = "System"; |
| 115 | 149 | const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.dylib"; |
| 116 | 150 | |
| 117 | 151 | pub const TextBlock = struct { |
| 118 | | /// Index into the symbol table |
| 119 | | symbol_table_index: ?u32, |
| 152 | /// Each decl always gets a local symbol with the fully qualified name. |
| 153 | /// The vaddr and size are found here directly. |
| 154 | /// The file offset is found by computing the vaddr offset from the section vaddr |
| 155 | /// the symbol references, and adding that to the file offset of the section. |
| 156 | /// If this field is 0, it means the codegen size = 0 and there is no symbol or |
| 157 | /// offset table entry. |
| 158 | local_sym_index: u32, |
| 120 | 159 | /// Index into offset table |
| 121 | | offset_table_index: ?u32, |
| 160 | /// This field is undefined for symbols with size = 0. |
| 161 | offset_table_index: u32, |
| 122 | 162 | /// Size of this text block |
| 163 | /// Unlike in Elf, we need to store the size of this symbol as part of |
| 164 | /// the TextBlock since macho.nlist_64 lacks this information. |
| 123 | 165 | size: u64, |
| 124 | 166 | /// Points to the previous and next neighbours |
| 125 | 167 | prev: ?*TextBlock, |
| 126 | 168 | next: ?*TextBlock, |
| 127 | 169 | |
| 128 | 170 | pub const empty = TextBlock{ |
| 129 | | .symbol_table_index = null, |
| 130 | | .offset_table_index = null, |
| 171 | .local_sym_index = 0, |
| 172 | .offset_table_index = undefined, |
| 131 | 173 | .size = 0, |
| 132 | 174 | .prev = null, |
| 133 | 175 | .next = null, |
| ... | ... | @@ -156,6 +198,15 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 156 | 198 | |
| 157 | 199 | self.base.file = file; |
| 158 | 200 | |
| 201 | // Index 0 is always a null symbol. |
| 202 | try self.local_symbols.append(allocator, .{ |
| 203 | .n_strx = 0, |
| 204 | .n_type = 0, |
| 205 | .n_sect = 0, |
| 206 | .n_desc = 0, |
| 207 | .n_value = 0, |
| 208 | }); |
| 209 | |
| 159 | 210 | switch (options.output_mode) { |
| 160 | 211 | .Exe => {}, |
| 161 | 212 | .Obj => {}, |
| ... | ... | @@ -196,88 +247,83 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 196 | 247 | const tracy = trace(@src()); |
| 197 | 248 | defer tracy.end(); |
| 198 | 249 | |
| 250 | // Unfortunately these have to be buffered and done at the end because MachO does not allow |
| 251 | // mixing local, global and undefined symbols within a symbol table. |
| 252 | try self.writeAllGlobalSymbols(); |
| 253 | try self.writeAllUndefSymbols(); |
| 254 | |
| 255 | try self.writeStringTable(); |
| 256 | |
| 199 | 257 | switch (self.base.options.output_mode) { |
| 200 | 258 | .Exe => { |
| 201 | | var last_cmd_offset: usize = @sizeOf(macho.mach_header_64); |
| 202 | | { |
| 203 | | // Specify path to dynamic linker dyld |
| 204 | | const cmdsize = commandSize(@sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH)); |
| 205 | | const load_dylinker = [1]macho.dylinker_command{ |
| 206 | | .{ |
| 207 | | .cmd = macho.LC_LOAD_DYLINKER, |
| 208 | | .cmdsize = cmdsize, |
| 209 | | .name = @sizeOf(macho.dylinker_command), |
| 210 | | }, |
| 211 | | }; |
| 212 | | |
| 213 | | try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylinker[0..1]), last_cmd_offset); |
| 214 | | |
| 215 | | const file_offset = last_cmd_offset + @sizeOf(macho.dylinker_command); |
| 216 | | try self.addPadding(cmdsize - @sizeOf(macho.dylinker_command), file_offset); |
| 217 | | |
| 218 | | try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), file_offset); |
| 219 | | last_cmd_offset += cmdsize; |
| 259 | if (self.entry_addr) |addr| { |
| 260 | // Write export trie. |
| 261 | try self.writeExportTrie(); |
| 262 | |
| 263 | // Update LC_MAIN with entry offset |
| 264 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 265 | const main_cmd = &self.load_commands.items[self.main_cmd_index.?].EntryPoint; |
| 266 | main_cmd.entryoff = addr - text_segment.vmaddr; |
| 220 | 267 | } |
| 221 | 268 | |
| 222 | 269 | { |
| 223 | | // Link against libSystem |
| 224 | | const cmdsize = commandSize(@sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH)); |
| 225 | | // TODO Find a way to work out runtime version from the OS version triple stored in std.Target. |
| 226 | | // In the meantime, we're gonna hardcode to the minimum compatibility version of 1.0.0. |
| 227 | | const min_version = 0x10000; |
| 228 | | const dylib = .{ |
| 229 | | .name = @sizeOf(macho.dylib_command), |
| 230 | | .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files |
| 231 | | .current_version = min_version, |
| 232 | | .compatibility_version = min_version, |
| 233 | | }; |
| 234 | | const load_dylib = [1]macho.dylib_command{ |
| 235 | | .{ |
| 236 | | .cmd = macho.LC_LOAD_DYLIB, |
| 237 | | .cmdsize = cmdsize, |
| 238 | | .dylib = dylib, |
| 239 | | }, |
| 240 | | }; |
| 241 | | |
| 242 | | try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylib[0..1]), last_cmd_offset); |
| 243 | | |
| 244 | | const file_offset = last_cmd_offset + @sizeOf(macho.dylib_command); |
| 245 | | try self.addPadding(cmdsize - @sizeOf(macho.dylib_command), file_offset); |
| 246 | | |
| 247 | | try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), file_offset); |
| 248 | | last_cmd_offset += cmdsize; |
| 270 | // Update dynamic symbol table. |
| 271 | const nlocals = @intCast(u32, self.local_symbols.items.len); |
| 272 | const nglobals = @intCast(u32, self.global_symbols.items.len); |
| 273 | const nundefs = @intCast(u32, self.undef_symbols.items.len); |
| 274 | const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; |
| 275 | dysymtab.nlocalsym = nlocals; |
| 276 | dysymtab.iextdefsym = nlocals; |
| 277 | dysymtab.nextdefsym = nglobals; |
| 278 | dysymtab.iundefsym = nlocals + nglobals; |
| 279 | dysymtab.nundefsym = nundefs; |
| 249 | 280 | } |
| 250 | | }, |
| 251 | | .Obj => { |
| 252 | 281 | { |
| 253 | | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 254 | | symtab.nsyms = @intCast(u32, self.symbol_table.items.len); |
| 255 | | const allocated_size = self.allocatedSize(symtab.stroff); |
| 256 | | const needed_size = self.string_table.items.len; |
| 257 | | log.debug("allocated_size = 0x{x}, needed_size = 0x{x}\n", .{ allocated_size, needed_size }); |
| 258 | | |
| 259 | | if (needed_size > allocated_size) { |
| 260 | | symtab.strsize = 0; |
| 261 | | symtab.stroff = @intCast(u32, self.findFreeSpace(needed_size, 1)); |
| 282 | // Write path to dyld loader. |
| 283 | var off: usize = @sizeOf(macho.mach_header_64); |
| 284 | for (self.load_commands.items) |cmd| { |
| 285 | if (cmd == .Dylinker) break; |
| 286 | off += cmd.cmdsize(); |
| 262 | 287 | } |
| 263 | | symtab.strsize = @intCast(u32, needed_size); |
| 264 | | |
| 265 | | log.debug("writing string table from 0x{x} to 0x{x}\n", .{ symtab.stroff, symtab.stroff + symtab.strsize }); |
| 266 | | |
| 267 | | try self.base.file.?.pwriteAll(self.string_table.items, symtab.stroff); |
| 288 | const cmd = &self.load_commands.items[self.dylinker_cmd_index.?].Dylinker; |
| 289 | off += cmd.name; |
| 290 | const padding = cmd.cmdsize - @sizeOf(macho.dylinker_command); |
| 291 | log.debug("writing LC_LOAD_DYLINKER padding of size {} at 0x{x}\n", .{ padding, off }); |
| 292 | try self.addPadding(padding, off); |
| 293 | log.debug("writing LC_LOAD_DYLINKER path to dyld at 0x{x}\n", .{off}); |
| 294 | try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), off); |
| 268 | 295 | } |
| 269 | | |
| 270 | | var last_cmd_offset: usize = @sizeOf(macho.mach_header_64); |
| 271 | | for (self.load_commands.items) |cmd| { |
| 272 | | try cmd.write(&self.base.file.?, last_cmd_offset); |
| 273 | | last_cmd_offset += cmd.cmdsize(); |
| 296 | { |
| 297 | // Write path to libSystem. |
| 298 | var off: usize = @sizeOf(macho.mach_header_64); |
| 299 | for (self.load_commands.items) |cmd| { |
| 300 | if (cmd == .Dylib) break; |
| 301 | off += cmd.cmdsize(); |
| 302 | } |
| 303 | const cmd = &self.load_commands.items[self.libsystem_cmd_index.?].Dylib; |
| 304 | off += cmd.dylib.name; |
| 305 | const padding = cmd.cmdsize - @sizeOf(macho.dylib_command); |
| 306 | log.debug("writing LC_LOAD_DYLIB padding of size {} at 0x{x}\n", .{ padding, off }); |
| 307 | try self.addPadding(padding, off); |
| 308 | log.debug("writing LC_LOAD_DYLIB path to libSystem at 0x{x}\n", .{off}); |
| 309 | try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), off); |
| 274 | 310 | } |
| 275 | | const off = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64); |
| 276 | | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items), off); |
| 277 | 311 | }, |
| 312 | .Obj => {}, |
| 278 | 313 | .Lib => return error.TODOImplementWritingLibFiles, |
| 279 | 314 | } |
| 280 | 315 | |
| 316 | if (self.cmd_table_dirty) try self.writeCmdHeaders(); |
| 317 | |
| 318 | { |
| 319 | // Update symbol table. |
| 320 | const nlocals = @intCast(u32, self.local_symbols.items.len); |
| 321 | const nglobals = @intCast(u32, self.global_symbols.items.len); |
| 322 | const nundefs = @intCast(u32, self.undef_symbols.items.len); |
| 323 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 324 | symtab.nsyms = nlocals + nglobals + nundefs; |
| 325 | } |
| 326 | |
| 281 | 327 | if (self.entry_addr == null and self.base.options.output_mode == .Exe) { |
| 282 | 328 | log.debug("flushing. no_entry_point_found = true\n", .{}); |
| 283 | 329 | self.error_flags.no_entry_point_found = true; |
| ... | ... | @@ -669,32 +715,34 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 { |
| 669 | 715 | pub fn deinit(self: *MachO) void { |
| 670 | 716 | self.offset_table.deinit(self.base.allocator); |
| 671 | 717 | self.string_table.deinit(self.base.allocator); |
| 672 | | self.symbol_table.deinit(self.base.allocator); |
| 718 | self.undef_symbols.deinit(self.base.allocator); |
| 719 | self.global_symbols.deinit(self.base.allocator); |
| 720 | self.local_symbols.deinit(self.base.allocator); |
| 673 | 721 | self.sections.deinit(self.base.allocator); |
| 674 | 722 | self.load_commands.deinit(self.base.allocator); |
| 675 | 723 | } |
| 676 | 724 | |
| 677 | 725 | pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void { |
| 678 | | if (decl.link.macho.symbol_table_index) |_| return; |
| 726 | if (decl.link.macho.local_sym_index != 0) return; |
| 679 | 727 | |
| 680 | | try self.symbol_table.ensureCapacity(self.base.allocator, self.symbol_table.items.len + 1); |
| 728 | try self.local_symbols.ensureCapacity(self.base.allocator, self.local_symbols.items.len + 1); |
| 681 | 729 | try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); |
| 682 | 730 | |
| 683 | | log.debug("allocating symbol index {} for {}\n", .{ self.symbol_table.items.len, decl.name }); |
| 684 | | decl.link.macho.symbol_table_index = @intCast(u32, self.symbol_table.items.len); |
| 685 | | _ = self.symbol_table.addOneAssumeCapacity(); |
| 731 | log.debug("allocating symbol index {} for {}\n", .{ self.local_symbols.items.len, decl.name }); |
| 732 | decl.link.macho.local_sym_index = @intCast(u32, self.local_symbols.items.len); |
| 733 | _ = self.local_symbols.addOneAssumeCapacity(); |
| 686 | 734 | |
| 687 | 735 | decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len); |
| 688 | 736 | _ = self.offset_table.addOneAssumeCapacity(); |
| 689 | 737 | |
| 690 | | self.symbol_table.items[decl.link.macho.symbol_table_index.?] = .{ |
| 738 | self.local_symbols.items[decl.link.macho.local_sym_index] = .{ |
| 691 | 739 | .n_strx = 0, |
| 692 | 740 | .n_type = 0, |
| 693 | 741 | .n_sect = 0, |
| 694 | 742 | .n_desc = 0, |
| 695 | 743 | .n_value = 0, |
| 696 | 744 | }; |
| 697 | | self.offset_table.items[decl.link.macho.offset_table_index.?] = 0; |
| 745 | self.offset_table.items[decl.link.macho.offset_table_index] = 0; |
| 698 | 746 | } |
| 699 | 747 | |
| 700 | 748 | pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| ... | ... | @@ -716,16 +764,14 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 716 | 764 | return; |
| 717 | 765 | }, |
| 718 | 766 | }; |
| 719 | | log.debug("generated code {}\n", .{code}); |
| 720 | 767 | |
| 721 | 768 | const required_alignment = typed_value.ty.abiAlignment(self.base.options.target); |
| 722 | | const symbol = &self.symbol_table.items[decl.link.macho.symbol_table_index.?]; |
| 769 | const symbol = &self.local_symbols.items[decl.link.macho.local_sym_index]; |
| 723 | 770 | |
| 724 | 771 | const decl_name = mem.spanZ(decl.name); |
| 725 | 772 | const name_str_index = try self.makeString(decl_name); |
| 726 | 773 | const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment); |
| 727 | 774 | log.debug("allocated text block for {} at 0x{x}\n", .{ decl_name, addr }); |
| 728 | | log.debug("updated text section {}\n", .{self.sections.items[self.text_section_index.?]}); |
| 729 | 775 | |
| 730 | 776 | symbol.* = .{ |
| 731 | 777 | .n_strx = name_str_index, |
| ... | ... | @@ -734,18 +780,20 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 734 | 780 | .n_desc = 0, |
| 735 | 781 | .n_value = addr, |
| 736 | 782 | }; |
| 783 | self.offset_table.items[decl.link.macho.offset_table_index] = addr; |
| 737 | 784 | |
| 738 | | // 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{}; |
| 740 | | try self.updateDeclExports(module, decl, decl_exports); |
| 741 | | try self.writeSymbol(decl.link.macho.symbol_table_index.?); |
| 785 | try self.writeSymbol(decl.link.macho.local_sym_index); |
| 786 | try self.writeOffsetTableEntry(decl.link.macho.offset_table_index); |
| 742 | 787 | |
| 743 | 788 | const text_section = self.sections.items[self.text_section_index.?]; |
| 744 | 789 | const section_offset = symbol.n_value - text_section.addr; |
| 745 | 790 | const file_offset = text_section.offset + section_offset; |
| 746 | | log.debug("file_offset 0x{x}\n", .{file_offset}); |
| 747 | 791 | |
| 748 | 792 | try self.base.file.?.pwriteAll(code, file_offset); |
| 793 | |
| 794 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. |
| 795 | const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; |
| 796 | try self.updateDeclExports(module, decl, decl_exports); |
| 749 | 797 | } |
| 750 | 798 | |
| 751 | 799 | pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {} |
| ... | ... | @@ -759,34 +807,89 @@ pub fn updateDeclExports( |
| 759 | 807 | const tracy = trace(@src()); |
| 760 | 808 | defer tracy.end(); |
| 761 | 809 | |
| 762 | | if (decl.link.macho.symbol_table_index == null) return; |
| 763 | | |
| 764 | | const decl_sym = &self.symbol_table.items[decl.link.macho.symbol_table_index.?]; |
| 765 | | // TODO implement |
| 766 | | if (exports.len == 0) return; |
| 767 | | |
| 768 | | const exp = exports[0]; |
| 769 | | self.entry_addr = decl_sym.n_value; |
| 770 | | decl_sym.n_type |= macho.N_EXT; |
| 771 | | exp.link.sym_index = 0; |
| 810 | try self.global_symbols.ensureCapacity(self.base.allocator, self.global_symbols.items.len + exports.len); |
| 811 | if (decl.link.macho.local_sym_index == 0) return; |
| 812 | const decl_sym = &self.local_symbols.items[decl.link.macho.local_sym_index]; |
| 813 | |
| 814 | for (exports) |exp| { |
| 815 | if (exp.options.section) |section_name| { |
| 816 | if (!mem.eql(u8, section_name, "__text")) { |
| 817 | try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1); |
| 818 | module.failed_exports.putAssumeCapacityNoClobber( |
| 819 | exp, |
| 820 | try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}), |
| 821 | ); |
| 822 | continue; |
| 823 | } |
| 824 | } |
| 825 | const n_desc = switch (exp.options.linkage) { |
| 826 | .Internal => macho.REFERENCE_FLAG_PRIVATE_DEFINED, |
| 827 | .Strong => blk: { |
| 828 | if (mem.eql(u8, exp.options.name, "_start")) { |
| 829 | self.entry_addr = decl_sym.n_value; |
| 830 | } |
| 831 | break :blk macho.REFERENCE_FLAG_DEFINED; |
| 832 | }, |
| 833 | .Weak => macho.N_WEAK_REF, |
| 834 | .LinkOnce => { |
| 835 | try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1); |
| 836 | module.failed_exports.putAssumeCapacityNoClobber( |
| 837 | exp, |
| 838 | try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}), |
| 839 | ); |
| 840 | continue; |
| 841 | }, |
| 842 | }; |
| 843 | const n_type = decl_sym.n_type | macho.N_EXT; |
| 844 | if (exp.link.sym_index) |i| { |
| 845 | const sym = &self.global_symbols.items[i]; |
| 846 | sym.* = .{ |
| 847 | .n_strx = try self.updateString(sym.n_strx, exp.options.name), |
| 848 | .n_type = n_type, |
| 849 | .n_sect = @intCast(u8, self.text_section_index.?) + 1, |
| 850 | .n_desc = n_desc, |
| 851 | .n_value = decl_sym.n_value, |
| 852 | }; |
| 853 | } else { |
| 854 | const name_str_index = try self.makeString(exp.options.name); |
| 855 | _ = self.global_symbols.addOneAssumeCapacity(); |
| 856 | const i = self.global_symbols.items.len - 1; |
| 857 | self.global_symbols.items[i] = .{ |
| 858 | .n_strx = name_str_index, |
| 859 | .n_type = n_type, |
| 860 | .n_sect = @intCast(u8, self.text_section_index.?) + 1, |
| 861 | .n_desc = n_desc, |
| 862 | .n_value = decl_sym.n_value, |
| 863 | }; |
| 864 | |
| 865 | exp.link.sym_index = @intCast(u32, i); |
| 866 | } |
| 867 | } |
| 772 | 868 | } |
| 773 | 869 | |
| 774 | 870 | pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {} |
| 775 | 871 | |
| 776 | 872 | pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 { |
| 777 | | return self.symbol_table.items[decl.link.macho.symbol_table_index.?].n_value; |
| 873 | assert(decl.link.macho.local_sym_index != 0); |
| 874 | return self.local_symbols.items[decl.link.macho.local_sym_index].n_value; |
| 778 | 875 | } |
| 779 | 876 | |
| 780 | 877 | pub fn populateMissingMetadata(self: *MachO) !void { |
| 781 | | if (self.segment_cmd_index == null) { |
| 782 | | self.segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 878 | switch (self.base.options.output_mode) { |
| 879 | .Exe => {}, |
| 880 | .Obj => return error.TODOImplementWritingObjFiles, |
| 881 | .Lib => return error.TODOImplementWritingLibFiles, |
| 882 | } |
| 883 | |
| 884 | if (self.pagezero_segment_cmd_index == null) { |
| 885 | self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 783 | 886 | try self.load_commands.append(self.base.allocator, .{ |
| 784 | 887 | .Segment = .{ |
| 785 | 888 | .cmd = macho.LC_SEGMENT_64, |
| 786 | 889 | .cmdsize = @sizeOf(macho.segment_command_64), |
| 787 | | .segname = makeStaticString(""), |
| 890 | .segname = makeStaticString("__PAGEZERO"), |
| 788 | 891 | .vmaddr = 0, |
| 789 | | .vmsize = 0, |
| 892 | .vmsize = 0x100000000, // size always set to 4GB |
| 790 | 893 | .fileoff = 0, |
| 791 | 894 | .filesize = 0, |
| 792 | 895 | .maxprot = 0, |
| ... | ... | @@ -797,28 +900,34 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 797 | 900 | }); |
| 798 | 901 | self.cmd_table_dirty = true; |
| 799 | 902 | } |
| 800 | | if (self.symtab_cmd_index == null) { |
| 801 | | self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 903 | if (self.text_segment_cmd_index == null) { |
| 904 | self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 905 | const prot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE; |
| 802 | 906 | try self.load_commands.append(self.base.allocator, .{ |
| 803 | | .Symtab = .{ |
| 804 | | .cmd = macho.LC_SYMTAB, |
| 805 | | .cmdsize = @sizeOf(macho.symtab_command), |
| 806 | | .symoff = 0, |
| 807 | | .nsyms = 0, |
| 808 | | .stroff = 0, |
| 809 | | .strsize = 0, |
| 907 | .Segment = .{ |
| 908 | .cmd = macho.LC_SEGMENT_64, |
| 909 | .cmdsize = @sizeOf(macho.segment_command_64), |
| 910 | .segname = makeStaticString("__TEXT"), |
| 911 | .vmaddr = 0x100000000, // always starts at 4GB |
| 912 | .vmsize = 0, |
| 913 | .fileoff = 0, |
| 914 | .filesize = 0, |
| 915 | .maxprot = prot, |
| 916 | .initprot = prot, |
| 917 | .nsects = 0, |
| 918 | .flags = 0, |
| 810 | 919 | }, |
| 811 | 920 | }); |
| 812 | 921 | self.cmd_table_dirty = true; |
| 813 | 922 | } |
| 814 | 923 | if (self.text_section_index == null) { |
| 815 | 924 | self.text_section_index = @intCast(u16, self.sections.items.len); |
| 816 | | const segment = &self.load_commands.items[self.segment_cmd_index.?].Segment; |
| 817 | | segment.cmdsize += @sizeOf(macho.section_64); |
| 818 | | segment.nsects += 1; |
| 925 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 926 | text_segment.cmdsize += @sizeOf(macho.section_64); |
| 927 | text_segment.nsects += 1; |
| 819 | 928 | |
| 820 | | const file_size = self.base.options.program_code_size_hint; |
| 821 | | const off = @intCast(u32, self.findFreeSpace(file_size, 1)); |
| 929 | const file_size = mem.alignForwardGeneric(u64, self.base.options.program_code_size_hint, 0x1000); |
| 930 | const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000)); // TODO maybe findFreeSpace should return u32 directly? |
| 822 | 931 | const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS; |
| 823 | 932 | |
| 824 | 933 | log.debug("found __text section free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| ... | ... | @@ -826,10 +935,10 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 826 | 935 | try self.sections.append(self.base.allocator, .{ |
| 827 | 936 | .sectname = makeStaticString("__text"), |
| 828 | 937 | .segname = makeStaticString("__TEXT"), |
| 829 | | .addr = 0, |
| 938 | .addr = text_segment.vmaddr + off, |
| 830 | 939 | .size = file_size, |
| 831 | 940 | .offset = off, |
| 832 | | .@"align" = 0x1000, |
| 941 | .@"align" = 12, // 2^12 = 4096 |
| 833 | 942 | .reloff = 0, |
| 834 | 943 | .nreloc = 0, |
| 835 | 944 | .flags = flags, |
| ... | ... | @@ -838,43 +947,256 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 838 | 947 | .reserved3 = 0, |
| 839 | 948 | }); |
| 840 | 949 | |
| 841 | | segment.vmsize += file_size; |
| 842 | | segment.filesize += file_size; |
| 843 | | segment.fileoff = off; |
| 950 | text_segment.vmsize = file_size + off; // We add off here since __TEXT segment includes everything prior to __text section. |
| 951 | text_segment.filesize = file_size + off; |
| 952 | } |
| 953 | if (self.data_segment_cmd_index == null) { |
| 954 | self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 955 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 956 | const prot = macho.VM_PROT_READ | macho.VM_PROT_WRITE; |
| 957 | try self.load_commands.append(self.base.allocator, .{ |
| 958 | .Segment = .{ |
| 959 | .cmd = macho.LC_SEGMENT_64, |
| 960 | .cmdsize = @sizeOf(macho.segment_command_64), |
| 961 | .segname = makeStaticString("__DATA"), |
| 962 | .vmaddr = text_segment.vmaddr + text_segment.vmsize, |
| 963 | .vmsize = 0, |
| 964 | .fileoff = 0, |
| 965 | .filesize = 0, |
| 966 | .maxprot = prot, |
| 967 | .initprot = prot, |
| 968 | .nsects = 0, |
| 969 | .flags = 0, |
| 970 | }, |
| 971 | }); |
| 972 | self.cmd_table_dirty = true; |
| 973 | } |
| 974 | if (self.got_section_index == null) { |
| 975 | self.got_section_index = @intCast(u16, self.sections.items.len); |
| 976 | const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; |
| 977 | data_segment.cmdsize += @sizeOf(macho.section_64); |
| 978 | data_segment.nsects += 1; |
| 979 | |
| 980 | const file_size = @sizeOf(u64) * self.base.options.symbol_count_hint; |
| 981 | const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000)); |
| 982 | |
| 983 | log.debug("found __got section free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| 984 | |
| 985 | try self.sections.append(self.base.allocator, .{ |
| 986 | .sectname = makeStaticString("__got"), |
| 987 | .segname = makeStaticString("__DATA"), |
| 988 | .addr = data_segment.vmaddr, |
| 989 | .size = file_size, |
| 990 | .offset = off, |
| 991 | .@"align" = 3, // 2^3 = 8 |
| 992 | .reloff = 0, |
| 993 | .nreloc = 0, |
| 994 | .flags = macho.S_REGULAR, |
| 995 | .reserved1 = 0, |
| 996 | .reserved2 = 0, |
| 997 | .reserved3 = 0, |
| 998 | }); |
| 844 | 999 | |
| 845 | | log.debug("initial text section {}\n", .{self.sections.items[self.text_section_index.?]}); |
| 1000 | const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000); |
| 1001 | data_segment.vmsize = segment_size; |
| 1002 | data_segment.filesize = segment_size; |
| 1003 | data_segment.fileoff = off; |
| 1004 | } |
| 1005 | if (self.linkedit_segment_cmd_index == null) { |
| 1006 | self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1007 | const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; |
| 1008 | const prot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE; |
| 1009 | try self.load_commands.append(self.base.allocator, .{ |
| 1010 | .Segment = .{ |
| 1011 | .cmd = macho.LC_SEGMENT_64, |
| 1012 | .cmdsize = @sizeOf(macho.segment_command_64), |
| 1013 | .segname = makeStaticString("__LINKEDIT"), |
| 1014 | .vmaddr = data_segment.vmaddr + data_segment.vmsize, |
| 1015 | .vmsize = 0, |
| 1016 | .fileoff = 0, |
| 1017 | .filesize = 0, |
| 1018 | .maxprot = prot, |
| 1019 | .initprot = prot, |
| 1020 | .nsects = 0, |
| 1021 | .flags = 0, |
| 1022 | }, |
| 1023 | }); |
| 1024 | self.cmd_table_dirty = true; |
| 1025 | } |
| 1026 | if (self.dyld_info_cmd_index == null) { |
| 1027 | self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1028 | try self.load_commands.append(self.base.allocator, .{ |
| 1029 | .DyldInfo = .{ |
| 1030 | .cmd = macho.LC_DYLD_INFO_ONLY, |
| 1031 | .cmdsize = @sizeOf(macho.dyld_info_command), |
| 1032 | .rebase_off = 0, |
| 1033 | .rebase_size = 0, |
| 1034 | .bind_off = 0, |
| 1035 | .bind_size = 0, |
| 1036 | .weak_bind_off = 0, |
| 1037 | .weak_bind_size = 0, |
| 1038 | .lazy_bind_off = 0, |
| 1039 | .lazy_bind_size = 0, |
| 1040 | .export_off = 0, |
| 1041 | .export_size = 0, |
| 1042 | }, |
| 1043 | }); |
| 1044 | self.cmd_table_dirty = true; |
| 1045 | } |
| 1046 | if (self.symtab_cmd_index == null) { |
| 1047 | self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1048 | try self.load_commands.append(self.base.allocator, .{ |
| 1049 | .Symtab = .{ |
| 1050 | .cmd = macho.LC_SYMTAB, |
| 1051 | .cmdsize = @sizeOf(macho.symtab_command), |
| 1052 | .symoff = 0, |
| 1053 | .nsyms = 0, |
| 1054 | .stroff = 0, |
| 1055 | .strsize = 0, |
| 1056 | }, |
| 1057 | }); |
| 1058 | self.cmd_table_dirty = true; |
| 1059 | } |
| 1060 | if (self.dysymtab_cmd_index == null) { |
| 1061 | self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1062 | try self.load_commands.append(self.base.allocator, .{ |
| 1063 | .Dysymtab = .{ |
| 1064 | .cmd = macho.LC_DYSYMTAB, |
| 1065 | .cmdsize = @sizeOf(macho.dysymtab_command), |
| 1066 | .ilocalsym = 0, |
| 1067 | .nlocalsym = 0, |
| 1068 | .iextdefsym = 0, |
| 1069 | .nextdefsym = 0, |
| 1070 | .iundefsym = 0, |
| 1071 | .nundefsym = 0, |
| 1072 | .tocoff = 0, |
| 1073 | .ntoc = 0, |
| 1074 | .modtaboff = 0, |
| 1075 | .nmodtab = 0, |
| 1076 | .extrefsymoff = 0, |
| 1077 | .nextrefsyms = 0, |
| 1078 | .indirectsymoff = 0, |
| 1079 | .nindirectsyms = 0, |
| 1080 | .extreloff = 0, |
| 1081 | .nextrel = 0, |
| 1082 | .locreloff = 0, |
| 1083 | .nlocrel = 0, |
| 1084 | }, |
| 1085 | }); |
| 1086 | self.cmd_table_dirty = true; |
| 1087 | } |
| 1088 | if (self.dylinker_cmd_index == null) { |
| 1089 | self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1090 | const cmdsize = mem.alignForwardGeneric(u64, @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH), @sizeOf(u64)); |
| 1091 | try self.load_commands.append(self.base.allocator, .{ |
| 1092 | .Dylinker = .{ |
| 1093 | .cmd = macho.LC_LOAD_DYLINKER, |
| 1094 | .cmdsize = @intCast(u32, cmdsize), |
| 1095 | .name = @sizeOf(macho.dylinker_command), |
| 1096 | }, |
| 1097 | }); |
| 1098 | self.cmd_table_dirty = true; |
| 1099 | } |
| 1100 | if (self.libsystem_cmd_index == null) { |
| 1101 | self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1102 | const cmdsize = mem.alignForwardGeneric(u64, @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH), @sizeOf(u64)); |
| 1103 | // TODO Find a way to work out runtime version from the OS version triple stored in std.Target. |
| 1104 | // In the meantime, we're gonna hardcode to the minimum compatibility version of 1.0.0. |
| 1105 | const min_version = 0x10000; |
| 1106 | const dylib = .{ |
| 1107 | .name = @sizeOf(macho.dylib_command), |
| 1108 | .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files |
| 1109 | .current_version = min_version, |
| 1110 | .compatibility_version = min_version, |
| 1111 | }; |
| 1112 | try self.load_commands.append(self.base.allocator, .{ |
| 1113 | .Dylib = .{ |
| 1114 | .cmd = macho.LC_LOAD_DYLIB, |
| 1115 | .cmdsize = @intCast(u32, cmdsize), |
| 1116 | .dylib = dylib, |
| 1117 | }, |
| 1118 | }); |
| 1119 | self.cmd_table_dirty = true; |
| 1120 | } |
| 1121 | if (self.main_cmd_index == null) { |
| 1122 | self.main_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1123 | try self.load_commands.append(self.base.allocator, .{ |
| 1124 | .EntryPoint = .{ |
| 1125 | .cmd = macho.LC_MAIN, |
| 1126 | .cmdsize = @sizeOf(macho.entry_point_command), |
| 1127 | .entryoff = 0x0, |
| 1128 | .stacksize = 0, |
| 1129 | }, |
| 1130 | }); |
| 1131 | self.cmd_table_dirty = true; |
| 1132 | } |
| 1133 | { |
| 1134 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 1135 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfo; |
| 1136 | if (dyld_info.export_off == 0) { |
| 1137 | const nsyms = self.base.options.symbol_count_hint; |
| 1138 | const file_size = @sizeOf(u64) * nsyms; |
| 1139 | const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000)); |
| 1140 | log.debug("found export trie free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| 1141 | dyld_info.export_off = off; |
| 1142 | dyld_info.export_size = @intCast(u32, file_size); |
| 1143 | |
| 1144 | const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000); |
| 1145 | linkedit.vmsize += segment_size; |
| 1146 | linkedit.fileoff = off; |
| 1147 | } |
| 846 | 1148 | } |
| 847 | 1149 | { |
| 1150 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 848 | 1151 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 849 | 1152 | if (symtab.symoff == 0) { |
| 850 | | const p_align = @sizeOf(macho.nlist_64); |
| 851 | 1153 | const nsyms = self.base.options.symbol_count_hint; |
| 852 | | const file_size = p_align * nsyms; |
| 853 | | const off = @intCast(u32, self.findFreeSpace(file_size, p_align)); |
| 1154 | const file_size = @sizeOf(macho.nlist_64) * nsyms; |
| 1155 | const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000)); |
| 854 | 1156 | log.debug("found symbol table free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| 855 | 1157 | symtab.symoff = off; |
| 856 | 1158 | symtab.nsyms = @intCast(u32, nsyms); |
| 1159 | |
| 1160 | const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000); |
| 1161 | linkedit.vmsize += segment_size; |
| 857 | 1162 | } |
| 858 | 1163 | if (symtab.stroff == 0) { |
| 859 | 1164 | try self.string_table.append(self.base.allocator, 0); |
| 860 | 1165 | const file_size = @intCast(u32, self.string_table.items.len); |
| 861 | | const off = @intCast(u32, self.findFreeSpace(file_size, 1)); |
| 1166 | const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000)); |
| 862 | 1167 | log.debug("found string table free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| 863 | 1168 | symtab.stroff = off; |
| 864 | 1169 | symtab.strsize = file_size; |
| 1170 | |
| 1171 | const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000); |
| 1172 | linkedit.vmsize += segment_size; |
| 865 | 1173 | } |
| 866 | 1174 | } |
| 1175 | if (self.dyld_stub_binder_index == null) { |
| 1176 | self.dyld_stub_binder_index = @intCast(u16, self.undef_symbols.items.len); |
| 1177 | const name = try self.makeString("dyld_stub_binder"); |
| 1178 | try self.undef_symbols.append(self.base.allocator, .{ |
| 1179 | .n_strx = name, |
| 1180 | .n_type = macho.N_UNDF | macho.N_EXT, |
| 1181 | .n_sect = 0, |
| 1182 | .n_desc = macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | macho.N_SYMBOL_RESOLVER, |
| 1183 | .n_value = 0, |
| 1184 | }); |
| 1185 | } |
| 867 | 1186 | } |
| 868 | 1187 | |
| 869 | 1188 | fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { |
| 870 | | const segment = &self.load_commands.items[self.segment_cmd_index.?].Segment; |
| 871 | 1189 | const text_section = &self.sections.items[self.text_section_index.?]; |
| 872 | 1190 | const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den; |
| 873 | 1191 | |
| 874 | 1192 | var block_placement: ?*TextBlock = null; |
| 875 | 1193 | const addr = blk: { |
| 876 | 1194 | if (self.last_text_block) |last| { |
| 877 | | const last_symbol = self.symbol_table.items[last.symbol_table_index.?]; |
| 1195 | const last_symbol = self.local_symbols.items[last.local_sym_index]; |
| 1196 | // TODO pad out with NOPs and reenable |
| 1197 | // const ideal_capacity = last.size * alloc_num / alloc_den; |
| 1198 | // const ideal_capacity_end_addr = last_symbol.n_value + ideal_capacity; |
| 1199 | // const new_start_addr = mem.alignForwardGeneric(u64, ideal_capacity_end_addr, alignment); |
| 878 | 1200 | const end_addr = last_symbol.n_value + last.size; |
| 879 | 1201 | const new_start_addr = mem.alignForwardGeneric(u64, end_addr, alignment); |
| 880 | 1202 | block_placement = last; |
| ... | ... | @@ -883,22 +1205,15 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 883 | 1205 | break :blk text_section.addr; |
| 884 | 1206 | } |
| 885 | 1207 | }; |
| 886 | | log.debug("computed symbol address 0x{x}\n", .{addr}); |
| 887 | 1208 | |
| 888 | 1209 | const expand_text_section = block_placement == null or block_placement.?.next == null; |
| 889 | 1210 | if (expand_text_section) { |
| 890 | 1211 | const text_capacity = self.allocatedSize(text_section.offset); |
| 891 | 1212 | const needed_size = (addr + new_block_size) - text_section.addr; |
| 892 | | log.debug("text capacity 0x{x}, needed size 0x{x}\n", .{ text_capacity, needed_size }); |
| 893 | 1213 | assert(needed_size <= text_capacity); // TODO handle growth |
| 894 | 1214 | |
| 895 | 1215 | 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 | | } |
| 1216 | text_section.size = needed_size; // TODO temp until we pad out with NOPs |
| 902 | 1217 | } |
| 903 | 1218 | text_block.size = new_block_size; |
| 904 | 1219 | |
| ... | ... | @@ -936,16 +1251,17 @@ fn makeString(self: *MachO, bytes: []const u8) !u32 { |
| 936 | 1251 | return @intCast(u32, result); |
| 937 | 1252 | } |
| 938 | 1253 | |
| 939 | | fn alignSize(comptime Int: type, min_size: anytype, alignment: Int) Int { |
| 940 | | const size = @intCast(Int, min_size); |
| 941 | | if (size % alignment == 0) return size; |
| 942 | | |
| 943 | | const div = size / alignment; |
| 944 | | return (div + 1) * alignment; |
| 1254 | fn getString(self: *MachO, str_off: u32) []const u8 { |
| 1255 | assert(str_off < self.string_table.items.len); |
| 1256 | return mem.spanZ(@ptrCast([*:0]const u8, self.string_table.items.ptr + str_off)); |
| 945 | 1257 | } |
| 946 | 1258 | |
| 947 | | fn commandSize(min_size: anytype) u32 { |
| 948 | | return alignSize(u32, min_size, @sizeOf(u64)); |
| 1259 | fn updateString(self: *MachO, old_str_off: u32, new_name: []const u8) !u32 { |
| 1260 | const existing_name = self.getString(old_str_off); |
| 1261 | if (mem.eql(u8, existing_name, new_name)) { |
| 1262 | return old_str_off; |
| 1263 | } |
| 1264 | return self.makeString(new_name); |
| 949 | 1265 | } |
| 950 | 1266 | |
| 951 | 1267 | fn addPadding(self: *MachO, size: u64, file_offset: u64) !void { |
| ... | ... | @@ -961,11 +1277,8 @@ fn addPadding(self: *MachO, size: u64, file_offset: u64) !void { |
| 961 | 1277 | |
| 962 | 1278 | fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { |
| 963 | 1279 | const hdr_size: u64 = @sizeOf(macho.mach_header_64); |
| 964 | | if (start < hdr_size) |
| 965 | | return hdr_size; |
| 966 | | |
| 1280 | if (start < hdr_size) return hdr_size; |
| 967 | 1281 | const end = start + satMul(size, alloc_num) / alloc_den; |
| 968 | | |
| 969 | 1282 | { |
| 970 | 1283 | const off = @sizeOf(macho.mach_header_64); |
| 971 | 1284 | var tight_size: u64 = 0; |
| ... | ... | @@ -978,7 +1291,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { |
| 978 | 1291 | return test_end; |
| 979 | 1292 | } |
| 980 | 1293 | } |
| 981 | | |
| 982 | 1294 | for (self.sections.items) |section| { |
| 983 | 1295 | const increased_size = satMul(section.size, alloc_num) / alloc_den; |
| 984 | 1296 | const test_end = section.offset + increased_size; |
| ... | ... | @@ -986,7 +1298,15 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { |
| 986 | 1298 | return test_end; |
| 987 | 1299 | } |
| 988 | 1300 | } |
| 989 | | |
| 1301 | if (self.dyld_info_cmd_index) |dyld_info_index| { |
| 1302 | const dyld_info = self.load_commands.items[dyld_info_index].DyldInfo; |
| 1303 | const tight_size = dyld_info.export_size; |
| 1304 | const increased_size = satMul(tight_size, alloc_num) / alloc_den; |
| 1305 | const test_end = dyld_info.export_off + increased_size; |
| 1306 | if (end > dyld_info.export_off and start < test_end) { |
| 1307 | return test_end; |
| 1308 | } |
| 1309 | } |
| 990 | 1310 | if (self.symtab_cmd_index) |symtab_index| { |
| 991 | 1311 | const symtab = self.load_commands.items[symtab_index].Symtab; |
| 992 | 1312 | { |
| ... | ... | @@ -1005,7 +1325,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { |
| 1005 | 1325 | } |
| 1006 | 1326 | } |
| 1007 | 1327 | } |
| 1008 | | |
| 1009 | 1328 | return null; |
| 1010 | 1329 | } |
| 1011 | 1330 | |
| ... | ... | @@ -1021,6 +1340,10 @@ fn allocatedSize(self: *MachO, start: u64) u64 { |
| 1021 | 1340 | if (section.offset <= start) continue; |
| 1022 | 1341 | if (section.offset < min_pos) min_pos = section.offset; |
| 1023 | 1342 | } |
| 1343 | if (self.dyld_info_cmd_index) |dyld_info_index| { |
| 1344 | const dyld_info = self.load_commands.items[dyld_info_index].DyldInfo; |
| 1345 | if (dyld_info.export_off > start and dyld_info.export_off < min_pos) min_pos = dyld_info.export_off; |
| 1346 | } |
| 1024 | 1347 | if (self.symtab_cmd_index) |symtab_index| { |
| 1025 | 1348 | const symtab = self.load_commands.items[symtab_index].Symtab; |
| 1026 | 1349 | if (symtab.symoff > start and symtab.symoff < min_pos) min_pos = symtab.symoff; |
| ... | ... | @@ -1042,12 +1365,133 @@ fn writeSymbol(self: *MachO, index: usize) !void { |
| 1042 | 1365 | defer tracy.end(); |
| 1043 | 1366 | |
| 1044 | 1367 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 1045 | | const sym = [1]macho.nlist_64{self.symbol_table.items[index]}; |
| 1368 | const sym = [1]macho.nlist_64{self.local_symbols.items[index]}; |
| 1046 | 1369 | const off = symtab.symoff + @sizeOf(macho.nlist_64) * index; |
| 1047 | 1370 | log.debug("writing symbol {} at 0x{x}\n", .{ sym[0], off }); |
| 1048 | 1371 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off); |
| 1049 | 1372 | } |
| 1050 | 1373 | |
| 1374 | fn writeOffsetTableEntry(self: *MachO, index: usize) !void { |
| 1375 | const sect = &self.sections.items[self.got_section_index.?]; |
| 1376 | const endian = self.base.options.target.cpu.arch.endian(); |
| 1377 | var buf: [@sizeOf(u64)]u8 = undefined; |
| 1378 | mem.writeInt(u64, &buf, self.offset_table.items[index], endian); |
| 1379 | const off = sect.offset + @sizeOf(u64) * index; |
| 1380 | log.debug("writing offset table entry 0x{x} at 0x{x}\n", .{ self.offset_table.items[index], off }); |
| 1381 | try self.base.file.?.pwriteAll(&buf, off); |
| 1382 | } |
| 1383 | |
| 1384 | fn writeAllGlobalSymbols(self: *MachO) !void { |
| 1385 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 1386 | const off = symtab.symoff + self.local_symbols.items.len * @sizeOf(macho.nlist_64); |
| 1387 | const file_size = self.global_symbols.items.len * @sizeOf(macho.nlist_64); |
| 1388 | log.debug("writing global symbols from 0x{x} to 0x{x}\n", .{ off, file_size + off }); |
| 1389 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.global_symbols.items), off); |
| 1390 | } |
| 1391 | |
| 1392 | fn writeAllUndefSymbols(self: *MachO) !void { |
| 1393 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 1394 | const nlocals = self.local_symbols.items.len; |
| 1395 | const nglobals = self.global_symbols.items.len; |
| 1396 | const off = symtab.symoff + (nlocals + nglobals) * @sizeOf(macho.nlist_64); |
| 1397 | const file_size = self.undef_symbols.items.len * @sizeOf(macho.nlist_64); |
| 1398 | log.debug("writing undef symbols from 0x{x} to 0x{x}\n", .{ off, file_size + off }); |
| 1399 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.undef_symbols.items), off); |
| 1400 | } |
| 1401 | |
| 1402 | fn writeExportTrie(self: *MachO) !void { |
| 1403 | assert(self.entry_addr != null); |
| 1404 | |
| 1405 | // TODO implement mechanism for generating a prefix tree of the exported symbols |
| 1406 | // single branch export trie |
| 1407 | var buf = [_]u8{0} ** 24; |
| 1408 | buf[0] = 0; // root node |
| 1409 | buf[1] = 1; // 1 branch from root |
| 1410 | mem.copy(u8, buf[2..], "_start"); |
| 1411 | buf[8] = 0; |
| 1412 | buf[9] = 9 + 1; |
| 1413 | |
| 1414 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1415 | const addr = self.entry_addr.? - text_segment.vmaddr; |
| 1416 | const written = try std.debug.leb.writeULEB128Mem(buf[12..], addr); |
| 1417 | buf[10] = @intCast(u8, written) + 1; |
| 1418 | buf[11] = 0; |
| 1419 | |
| 1420 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfo; |
| 1421 | try self.base.file.?.pwriteAll(buf[0..], dyld_info.export_off); |
| 1422 | } |
| 1423 | |
| 1424 | fn writeStringTable(self: *MachO) !void { |
| 1425 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 1426 | const allocated_size = self.allocatedSize(symtab.stroff); |
| 1427 | const needed_size = self.string_table.items.len; |
| 1428 | |
| 1429 | if (needed_size > allocated_size) { |
| 1430 | symtab.strsize = 0; |
| 1431 | symtab.stroff = @intCast(u32, self.findFreeSpace(needed_size, 1)); |
| 1432 | } |
| 1433 | symtab.strsize = @intCast(u32, needed_size); |
| 1434 | |
| 1435 | log.debug("writing string table from 0x{x} to 0x{x}\n", .{ symtab.stroff, symtab.stroff + symtab.strsize }); |
| 1436 | |
| 1437 | try self.base.file.?.pwriteAll(self.string_table.items, symtab.stroff); |
| 1438 | |
| 1439 | // TODO rework how we preallocate space for the entire __LINKEDIT segment instead of |
| 1440 | // doing dynamic updates like this. |
| 1441 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 1442 | linkedit.filesize = symtab.stroff + symtab.strsize - linkedit.fileoff; |
| 1443 | } |
| 1444 | |
| 1445 | fn writeCmdHeaders(self: *MachO) !void { |
| 1446 | assert(self.cmd_table_dirty); |
| 1447 | |
| 1448 | // Write all load command headers first. |
| 1449 | // Since command sizes are up-to-date and accurate, we will correctly |
| 1450 | // leave space for any section headers that any of the segment load |
| 1451 | // commands might consist of. |
| 1452 | var last_cmd_offset: usize = @sizeOf(macho.mach_header_64); |
| 1453 | for (self.load_commands.items) |cmd| { |
| 1454 | try cmd.write(&self.base.file.?, last_cmd_offset); |
| 1455 | last_cmd_offset += cmd.cmdsize(); |
| 1456 | } |
| 1457 | { |
| 1458 | // write __text section header |
| 1459 | const off = if (self.text_segment_cmd_index) |text_segment_index| blk: { |
| 1460 | var i: usize = 0; |
| 1461 | var cmdsize: usize = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64); |
| 1462 | while (i < text_segment_index) : (i += 1) { |
| 1463 | cmdsize += self.load_commands.items[i].cmdsize(); |
| 1464 | } |
| 1465 | break :blk cmdsize; |
| 1466 | } else { |
| 1467 | // If we've landed in here, we are building a MachO object file, so we have |
| 1468 | // only one, noname segment to append this section header to. |
| 1469 | return error.TODOImplementWritingObjFiles; |
| 1470 | }; |
| 1471 | const idx = self.text_section_index.?; |
| 1472 | log.debug("writing text section {} at 0x{x}\n", .{ self.sections.items[idx .. idx + 1], off }); |
| 1473 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[idx .. idx + 1]), off); |
| 1474 | } |
| 1475 | { |
| 1476 | // write __got section header |
| 1477 | const off = if (self.data_segment_cmd_index) |data_segment_index| blk: { |
| 1478 | var i: usize = 0; |
| 1479 | var cmdsize: usize = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64); |
| 1480 | while (i < data_segment_index) : (i += 1) { |
| 1481 | cmdsize += self.load_commands.items[i].cmdsize(); |
| 1482 | } |
| 1483 | break :blk cmdsize; |
| 1484 | } else { |
| 1485 | // If we've landed in here, we are building a MachO object file, so we have |
| 1486 | // only one, noname segment to append this section header to. |
| 1487 | return error.TODOImplementWritingObjFiles; |
| 1488 | }; |
| 1489 | const idx = self.got_section_index.?; |
| 1490 | log.debug("writing got section {} at 0x{x}\n", .{ self.sections.items[idx .. idx + 1], off }); |
| 1491 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[idx .. idx + 1]), off); |
| 1492 | } |
| 1493 | } |
| 1494 | |
| 1051 | 1495 | /// Writes Mach-O file header. |
| 1052 | 1496 | /// Should be invoked last as it needs up-to-date values of ncmds and sizeof_cmds bookkeeping |
| 1053 | 1497 | /// variables. |