| ... | @@ -16,8 +16,6 @@ const Module = @import("../Module.zig"); | ... | @@ -16,8 +16,6 @@ const Module = @import("../Module.zig"); |
| 16 | const link = @import("../link.zig"); | 16 | const link = @import("../link.zig"); |
| 17 | const File = link.File; | 17 | const File = link.File; |
| 18 | | 18 | |
| 19 | const is_darwin = std.Target.current.os.tag.isDarwin(); | | |
| 20 | | | |
| 21 | pub const base_tag: File.Tag = File.Tag.macho; | 19 | pub const base_tag: File.Tag = File.Tag.macho; |
| 22 | | 20 | |
| 23 | base: File, | 21 | base: File, |
| ... | @@ -30,24 +28,26 @@ command_file_offset: ?u64 = null, | ... | @@ -30,24 +28,26 @@ command_file_offset: ?u64 = null, |
| 30 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. | 28 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. |
| 31 | /// Same order as in the file. | 29 | /// Same order as in the file. |
| 32 | segments: std.ArrayListUnmanaged(macho.segment_command_64) = std.ArrayListUnmanaged(macho.segment_command_64){}, | 30 | segments: std.ArrayListUnmanaged(macho.segment_command_64) = std.ArrayListUnmanaged(macho.segment_command_64){}, |
| | 31 | /// Section (headers) *always* follow segment (load commands) directly! |
| 33 | sections: std.ArrayListUnmanaged(macho.section_64) = std.ArrayListUnmanaged(macho.section_64){}, | 32 | sections: std.ArrayListUnmanaged(macho.section_64) = std.ArrayListUnmanaged(macho.section_64){}, |
| 34 | segment_table_offset: ?u64 = null, | 33 | |
| | 34 | /// Offset (index) into __TEXT segment load command. |
| | 35 | text_segment_offset: ?u64 = null, |
| | 36 | /// Offset (index) into __LINKEDIT segment load command. |
| | 37 | linkedit_segment_offset: ?u664 = null, |
| 35 | | 38 | |
| 36 | /// Entry point load command | 39 | /// Entry point load command |
| 37 | entry_point_cmd: ?macho.entry_point_command = null, | 40 | entry_point_cmd: ?macho.entry_point_command = null, |
| 38 | entry_addr: ?u64 = null, | 41 | entry_addr: ?u64 = null, |
| 39 | | 42 | |
| 40 | /// Default VM start address set at 4GB | 43 | /// The first 4GB of process' memory is reserved for the null (__PAGEZERO) segment. |
| | 44 | /// This is also the start address for our binary. |
| 41 | vm_start_address: u64 = 0x100000000, | 45 | vm_start_address: u64 = 0x100000000, |
| 42 | | 46 | |
| 43 | seg_table_dirty: bool = false, | 47 | seg_table_dirty: bool = false, |
| 44 | | 48 | |
| 45 | error_flags: File.ErrorFlags = File.ErrorFlags{}, | 49 | error_flags: File.ErrorFlags = File.ErrorFlags{}, |
| 46 | | 50 | |
| 47 | /// TODO ultimately this will be propagated down from main() and set (in this form or another) | | |
| 48 | /// when user links against system lib. | | |
| 49 | link_against_system: bool = false, | | |
| 50 | | | |
| 51 | /// `alloc_num / alloc_den` is the factor of padding when allocating. | 51 | /// `alloc_num / alloc_den` is the factor of padding when allocating. |
| 52 | const alloc_num = 4; | 52 | const alloc_num = 4; |
| 53 | const alloc_den = 3; | 53 | const alloc_den = 3; |
| ... | @@ -138,8 +138,8 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Mach | ... | @@ -138,8 +138,8 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Mach |
| 138 | .vmsize = self.vm_start_address, | 138 | .vmsize = self.vm_start_address, |
| 139 | .fileoff = 0, | 139 | .fileoff = 0, |
| 140 | .filesize = 0, | 140 | .filesize = 0, |
| 141 | .maxprot = 0, | 141 | .maxprot = macho.VM_PROT_NONE, |
| 142 | .initprot = 0, | 142 | .initprot = macho.VM_PROT_NONE, |
| 143 | .nsects = 0, | 143 | .nsects = 0, |
| 144 | .flags = 0, | 144 | .flags = 0, |
| 145 | }; | 145 | }; |
| ... | @@ -225,67 +225,61 @@ pub fn flush(self: *MachO, module: *Module) !void { | ... | @@ -225,67 +225,61 @@ pub fn flush(self: *MachO, module: *Module) !void { |
| 225 | | 225 | |
| 226 | switch (self.base.options.output_mode) { | 226 | switch (self.base.options.output_mode) { |
| 227 | .Exe => { | 227 | .Exe => { |
| 228 | if (self.link_against_system) { | 228 | { |
| 229 | if (is_darwin) { | 229 | // Specify path to dynamic linker dyld |
| 230 | { | 230 | const cmdsize = commandSize(@sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH)); |
| 231 | // Specify path to dynamic linker dyld | 231 | const load_dylinker = [1]macho.dylinker_command{ |
| 232 | const cmdsize = commandSize(@intCast(u32, @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH))); | 232 | .{ |
| 233 | const load_dylinker = [1]macho.dylinker_command{ | 233 | .cmd = macho.LC_LOAD_DYLINKER, |
| 234 | .{ | 234 | .cmdsize = cmdsize, |
| 235 | .cmd = macho.LC_LOAD_DYLINKER, | 235 | .name = @sizeOf(macho.dylinker_command), |
| 236 | .cmdsize = cmdsize, | 236 | }, |
| 237 | .name = @sizeOf(macho.dylinker_command), | 237 | }; |
| 238 | }, | 238 | try self.commands.append(self.base.allocator, .{ |
| 239 | }; | 239 | .cmd = macho.LC_LOAD_DYLINKER, |
| 240 | try self.commands.append(self.base.allocator, .{ | 240 | .cmdsize = cmdsize, |
| 241 | .cmd = macho.LC_LOAD_DYLINKER, | 241 | }); |
| 242 | .cmdsize = cmdsize, | 242 | |
| 243 | }); | 243 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylinker[0..1]), self.command_file_offset.?); |
| 244 | | 244 | |
| 245 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylinker[0..1]), self.command_file_offset.?); | 245 | const file_offset = self.command_file_offset.? + @sizeOf(macho.dylinker_command); |
| 246 | | 246 | try self.addPadding(cmdsize - @sizeOf(macho.dylinker_command), file_offset); |
| 247 | const file_offset = self.command_file_offset.? + @sizeOf(macho.dylinker_command); | 247 | |
| 248 | try self.addPadding(cmdsize - @sizeOf(macho.dylinker_command), file_offset); | 248 | try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), file_offset); |
| 249 | | 249 | self.command_file_offset.? += cmdsize; |
| 250 | try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), file_offset); | 250 | } |
| 251 | self.command_file_offset.? += cmdsize; | 251 | |
| 252 | } | 252 | { |
| 253 | | 253 | // Link against libSystem |
| 254 | { | 254 | const cmdsize = commandSize(@sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH)); |
| 255 | // Link against libSystem | 255 | // TODO Find a way to work out runtime version from the OS version triple stored in std.Target. |
| 256 | const cmdsize = commandSize(@intCast(u32, @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH))); | 256 | // In the meantime, we're gonna hardcode to the minimum compatibility version of 1.0.0. |
| 257 | // According to Apple's manual, we should obtain current libSystem version using libc call | 257 | const min_version = 0x10000; |
| 258 | // NSVersionOfRunTimeLibrary. | 258 | const dylib = .{ |
| 259 | const version = std.c.NSVersionOfRunTimeLibrary(LIB_SYSTEM_NAME); | 259 | .name = @sizeOf(macho.dylib_command), |
| 260 | const dylib = .{ | 260 | .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files |
| 261 | .name = @sizeOf(macho.dylib_command), | 261 | .current_version = min_version, |
| 262 | .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files | 262 | .compatibility_version = min_version, |
| 263 | .current_version = version, | 263 | }; |
| 264 | .compatibility_version = 0x10000, // not sure why this either; value from reverse engineering | 264 | const load_dylib = [1]macho.dylib_command{ |
| 265 | }; | 265 | .{ |
| 266 | const load_dylib = [1]macho.dylib_command{ | 266 | .cmd = macho.LC_LOAD_DYLIB, |
| 267 | .{ | 267 | .cmdsize = cmdsize, |
| 268 | .cmd = macho.LC_LOAD_DYLIB, | 268 | .dylib = dylib, |
| 269 | .cmdsize = cmdsize, | 269 | }, |
| 270 | .dylib = dylib, | 270 | }; |
| 271 | }, | 271 | try self.commands.append(self.base.allocator, .{ |
| 272 | }; | 272 | .cmd = macho.LC_LOAD_DYLIB, |
| 273 | try self.commands.append(self.base.allocator, .{ | 273 | .cmdsize = cmdsize, |
| 274 | .cmd = macho.LC_LOAD_DYLIB, | 274 | }); |
| 275 | .cmdsize = cmdsize, | 275 | |
| 276 | }); | 276 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylib[0..1]), self.command_file_offset.?); |
| 277 | | 277 | |
| 278 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylib[0..1]), self.command_file_offset.?); | 278 | const file_offset = self.command_file_offset.? + @sizeOf(macho.dylib_command); |
| 279 | | 279 | try self.addPadding(cmdsize - @sizeOf(macho.dylib_command), file_offset); |
| 280 | const file_offset = self.command_file_offset.? + @sizeOf(macho.dylib_command); | 280 | |
| 281 | try self.addPadding(cmdsize - @sizeOf(macho.dylib_command), file_offset); | 281 | try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), file_offset); |
| 282 | | 282 | self.command_file_offset.? += cmdsize; |
| 283 | try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), file_offset); | | |
| 284 | self.command_file_offset.? += cmdsize; | | |
| 285 | } | | |
| 286 | } else { | | |
| 287 | @panic("linking against libSystem on non-native target is unsupported"); | | |
| 288 | } | | |
| 289 | } | 283 | } |
| 290 | }, | 284 | }, |
| 291 | .Obj => return error.TODOImplementWritingObjFiles, | 285 | .Obj => return error.TODOImplementWritingObjFiles, |
| ... | @@ -327,20 +321,53 @@ pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 { | ... | @@ -327,20 +321,53 @@ pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 { |
| 327 | @panic("TODO implement getDeclVAddr for MachO"); | 321 | @panic("TODO implement getDeclVAddr for MachO"); |
| 328 | } | 322 | } |
| 329 | | 323 | |
| 330 | pub fn populateMissingMetadata(self: *MachO) !void {} | 324 | pub fn populateMissingMetadata(self: *MachO) !void { |
| | 325 | if (self.text_segment_offset == null) { |
| | 326 | self.text_segment_offset = @intCast(u64, self.segments.items.len); |
| | 327 | const file_size = alignSize(u64, self.base.options.program_code_size_hint, 0x1000); |
| | 328 | log.debug("vmsize/filesize = {}", .{file_size}); |
| | 329 | const file_offset = 0; |
| | 330 | const vm_address = self.vm_start_address; // the end of __PAGEZERO segment in VM |
| | 331 | const protection = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE; |
| | 332 | const cmdsize = commandSize(@sizeOf(macho.segment_command_64)); |
| | 333 | const text_segment = .{ |
| | 334 | .cmd = macho.LC_SEGMENT_64, |
| | 335 | .cmdsize = cmdsize, |
| | 336 | .segname = makeString("__TEXT"), |
| | 337 | .vmaddr = vm_address, |
| | 338 | .vmsize = file_size, |
| | 339 | .fileoff = 0, // __TEXT segment *always* starts at 0 file offset |
| | 340 | .filesize = 0, //file_size, |
| | 341 | .maxprot = protection, |
| | 342 | .initprot = protection, |
| | 343 | .nsects = 0, |
| | 344 | .flags = 0, |
| | 345 | }; |
| | 346 | try self.commands.append(self.base.allocator, .{ |
| | 347 | .cmd = macho.LC_SEGMENT_64, |
| | 348 | .cmdsize = cmdsize, |
| | 349 | }); |
| | 350 | try self.segments.append(self.base.allocator, text_segment); |
| | 351 | } |
| | 352 | } |
| 331 | | 353 | |
| 332 | fn makeString(comptime bytes: []const u8) [16]u8 { | 354 | fn makeString(comptime bytes: []const u8) [16]u8 { |
| 333 | var buf: [16]u8 = undefined; | 355 | var buf = [_]u8{0} ** 16; |
| 334 | if (bytes.len > buf.len) @compileError("MachO segment/section name too long"); | 356 | if (bytes.len > buf.len) @compileError("MachO segment/section name too long"); |
| 335 | mem.copy(u8, buf[0..], bytes); | 357 | mem.copy(u8, buf[0..], bytes); |
| 336 | return buf; | 358 | return buf; |
| 337 | } | 359 | } |
| 338 | | 360 | |
| 339 | fn commandSize(min_size: u32) u32 { | 361 | fn alignSize(comptime Int: type, min_size: anytype, alignment: Int) Int { |
| 340 | if (min_size % @sizeOf(u64) == 0) return min_size; | 362 | const size = @intCast(Int, min_size); |
| | 363 | if (size % alignment == 0) return size; |
| | 364 | |
| | 365 | const div = size / alignment; |
| | 366 | return (div + 1) * alignment; |
| | 367 | } |
| 341 | | 368 | |
| 342 | const div = min_size / @sizeOf(u64); | 369 | fn commandSize(min_size: anytype) u32 { |
| 343 | return (div + 1) * @sizeOf(u64); | 370 | return alignSize(u32, min_size, @sizeOf(u64)); |
| 344 | } | 371 | } |
| 345 | | 372 | |
| 346 | fn addPadding(self: *MachO, size: u32, file_offset: u64) !void { | 373 | fn addPadding(self: *MachO, size: u32, file_offset: u64) !void { |