| ... | @@ -16,6 +16,8 @@ const Module = @import("../Module.zig"); | ... | @@ -16,6 +16,8 @@ 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 | |
| 19 | pub const base_tag: File.Tag = File.Tag.macho; | 21 | pub const base_tag: File.Tag = File.Tag.macho; |
| 20 | | 22 | |
| 21 | base: File, | 23 | base: File, |
| ... | @@ -42,16 +44,27 @@ seg_table_dirty: bool = false, | ... | @@ -42,16 +44,27 @@ seg_table_dirty: bool = false, |
| 42 | | 44 | |
| 43 | error_flags: File.ErrorFlags = File.ErrorFlags{}, | 45 | error_flags: File.ErrorFlags = File.ErrorFlags{}, |
| 44 | | 46 | |
| | 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 | |
| 45 | /// `alloc_num / alloc_den` is the factor of padding when allocating. | 51 | /// `alloc_num / alloc_den` is the factor of padding when allocating. |
| 46 | const alloc_num = 4; | 52 | const alloc_num = 4; |
| 47 | const alloc_den = 3; | 53 | const alloc_den = 3; |
| 48 | | 54 | |
| 49 | /// Default path to dyld | 55 | /// Default path to dyld |
| | 56 | /// TODO instead of hardcoding it, we should probably look through some env vars and search paths |
| | 57 | /// instead but this will do for now. |
| 50 | const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld"; | 58 | const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld"; |
| 51 | | 59 | |
| 52 | /// We always have to link against libSystem since macOS Catalina (TODO link) | 60 | /// Default lib search path |
| | 61 | /// TODO instead of hardcoding it, we should probably look through some env vars and search paths |
| | 62 | /// instead but this will do for now. |
| | 63 | const DEFAULT_LIB_SEARCH_PATH: []const u8 = "/usr/lib"; |
| | 64 | |
| 53 | const LIB_SYSTEM_NAME: [*:0]const u8 = "System"; | 65 | const LIB_SYSTEM_NAME: [*:0]const u8 = "System"; |
| 54 | const LIB_SYSTEM_PATH: [*:0]const u8 = "/usr/lib/libSystem.B.dylib"; | 66 | /// TODO we should search for libSystem and fail if it doesn't exist, instead of hardcoding it |
| | 67 | const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.dylib"; |
| 55 | | 68 | |
| 56 | pub const TextBlock = struct { | 69 | pub const TextBlock = struct { |
| 57 | pub const empty = TextBlock{}; | 70 | pub const empty = TextBlock{}; |
| ... | @@ -212,74 +225,81 @@ pub fn flush(self: *MachO, module: *Module) !void { | ... | @@ -212,74 +225,81 @@ pub fn flush(self: *MachO, module: *Module) !void { |
| 212 | | 225 | |
| 213 | switch (self.base.options.output_mode) { | 226 | switch (self.base.options.output_mode) { |
| 214 | .Exe => { | 227 | .Exe => { |
| 215 | { | 228 | if (self.link_against_system) { |
| 216 | // We need to add LC_LOAD_DYLINKER and LC_LOAD_DYLIB since we always | 229 | if (is_darwin) { |
| 217 | // have to link against libSystem.dylib | 230 | { |
| 218 | const cmdsize = commandSize(@intCast(u32, @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH))); | 231 | // Specify path to dynamic linker dyld |
| 219 | const load_dylinker = [1]macho.dylinker_command{ | 232 | const cmdsize = commandSize(@intCast(u32, @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH))); |
| 220 | .{ | 233 | const load_dylinker = [1]macho.dylinker_command{ |
| 221 | .cmd = macho.LC_LOAD_DYLINKER, | 234 | .{ |
| 222 | .cmdsize = cmdsize, | 235 | .cmd = macho.LC_LOAD_DYLINKER, |
| 223 | .name = @sizeOf(macho.dylinker_command), | 236 | .cmdsize = cmdsize, |
| 224 | }, | 237 | .name = @sizeOf(macho.dylinker_command), |
| 225 | }; | 238 | }, |
| 226 | try self.commands.append(self.base.allocator, .{ | 239 | }; |
| 227 | .cmd = macho.LC_LOAD_DYLINKER, | 240 | try self.commands.append(self.base.allocator, .{ |
| 228 | .cmdsize = cmdsize, | 241 | .cmd = macho.LC_LOAD_DYLINKER, |
| 229 | }); | 242 | .cmdsize = cmdsize, |
| 230 | | 243 | }); |
| 231 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylinker[0..1]), self.command_file_offset.?); | 244 | |
| 232 | | 245 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylinker[0..1]), self.command_file_offset.?); |
| 233 | const file_offset = self.command_file_offset.? + @sizeOf(macho.dylinker_command); | 246 | |
| 234 | try self.addPadding(cmdsize - @sizeOf(macho.dylinker_command), file_offset); | 247 | const file_offset = self.command_file_offset.? + @sizeOf(macho.dylinker_command); |
| 235 | | 248 | try self.addPadding(cmdsize - @sizeOf(macho.dylinker_command), file_offset); |
| 236 | try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), file_offset); | 249 | |
| 237 | self.command_file_offset.? += cmdsize; | 250 | try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), file_offset); |
| 238 | } | 251 | self.command_file_offset.? += cmdsize; |
| 239 | | 252 | } |
| 240 | { | 253 | |
| 241 | // Link against libSystem | 254 | { |
| 242 | const cmdsize = commandSize(@intCast(u32, @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH))); | 255 | // Link against libSystem |
| 243 | const version = std.c.NSVersionOfRunTimeLibrary(LIB_SYSTEM_NAME); | 256 | const cmdsize = commandSize(@intCast(u32, @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH))); |
| 244 | const dylib = .{ | 257 | // According to Apple's manual, we should obtain current libSystem version using libc call |
| 245 | .name = @sizeOf(macho.dylib_command), | 258 | // NSVersionOfRunTimeLibrary. |
| 246 | .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files | 259 | const version = std.c.NSVersionOfRunTimeLibrary(LIB_SYSTEM_NAME); |
| 247 | .current_version = version, | 260 | const dylib = .{ |
| 248 | .compatibility_version = 0x10000, // not sure why this either; value from reverse engineering | 261 | .name = @sizeOf(macho.dylib_command), |
| 249 | }; | 262 | .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files |
| 250 | const load_dylib = [1]macho.dylib_command{ | 263 | .current_version = version, |
| 251 | .{ | 264 | .compatibility_version = 0x10000, // not sure why this either; value from reverse engineering |
| 252 | .cmd = macho.LC_LOAD_DYLIB, | 265 | }; |
| 253 | .cmdsize = cmdsize, | 266 | const load_dylib = [1]macho.dylib_command{ |
| 254 | .dylib = dylib, | 267 | .{ |
| 255 | }, | 268 | .cmd = macho.LC_LOAD_DYLIB, |
| 256 | }; | 269 | .cmdsize = cmdsize, |
| 257 | try self.commands.append(self.base.allocator, .{ | 270 | .dylib = dylib, |
| 258 | .cmd = macho.LC_LOAD_DYLIB, | 271 | }, |
| 259 | .cmdsize = cmdsize, | 272 | }; |
| 260 | }); | 273 | try self.commands.append(self.base.allocator, .{ |
| 261 | | 274 | .cmd = macho.LC_LOAD_DYLIB, |
| 262 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylib[0..1]), self.command_file_offset.?); | 275 | .cmdsize = cmdsize, |
| 263 | | 276 | }); |
| 264 | const file_offset = self.command_file_offset.? + @sizeOf(macho.dylib_command); | 277 | |
| 265 | try self.addPadding(cmdsize - @sizeOf(macho.dylib_command), file_offset); | 278 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylib[0..1]), self.command_file_offset.?); |
| 266 | | 279 | |
| 267 | try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), file_offset); | 280 | const file_offset = self.command_file_offset.? + @sizeOf(macho.dylib_command); |
| 268 | self.command_file_offset.? += cmdsize; | 281 | try self.addPadding(cmdsize - @sizeOf(macho.dylib_command), file_offset); |
| | 282 | |
| | 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 | } |
| 269 | } | 289 | } |
| 270 | }, | 290 | }, |
| 271 | .Obj => return error.TODOImplementWritingObjFiles, | 291 | .Obj => return error.TODOImplementWritingObjFiles, |
| 272 | .Lib => return error.TODOImplementWritingLibFiles, | 292 | .Lib => return error.TODOImplementWritingLibFiles, |
| 273 | } | 293 | } |
| 274 | | 294 | |
| 275 | // if (self.entry_addr == null and self.base.options.output_mode == .Exe) { | 295 | if (self.entry_addr == null and self.base.options.output_mode == .Exe) { |
| 276 | // log.debug("flushing. no_entry_point_found = true\n", .{}); | 296 | log.debug("flushing. no_entry_point_found = true\n", .{}); |
| 277 | // self.error_flags.no_entry_point_found = true; | 297 | self.error_flags.no_entry_point_found = true; |
| 278 | // } else { | 298 | } else { |
| 279 | log.debug("flushing. no_entry_point_found = false\n", .{}); | 299 | log.debug("flushing. no_entry_point_found = false\n", .{}); |
| 280 | self.error_flags.no_entry_point_found = false; | 300 | self.error_flags.no_entry_point_found = false; |
| 281 | try self.writeMachOHeader(); | 301 | try self.writeMachOHeader(); |
| 282 | // } | 302 | } |
| 283 | } | 303 | } |
| 284 | | 304 | |
| 285 | pub fn deinit(self: *MachO) void { | 305 | pub fn deinit(self: *MachO) void { |
| ... | @@ -290,51 +310,7 @@ pub fn deinit(self: *MachO) void { | ... | @@ -290,51 +310,7 @@ pub fn deinit(self: *MachO) void { |
| 290 | | 310 | |
| 291 | pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {} | 311 | pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {} |
| 292 | | 312 | |
| 293 | pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { | 313 | pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {} |
| 294 | // const tracy = trace(@src()); | | |
| 295 | // defer tracy.end(); | | |
| 296 | | | |
| 297 | // var code_buffer = std.ArrayList(u8).init(self.base.allocator); | | |
| 298 | // defer code_buffer.deinit(); | | |
| 299 | | | |
| 300 | // var dbg_line_buffer = std.ArrayList(u8).init(self.base.allocator); | | |
| 301 | // defer dbg_line_buffer.deinit(); | | |
| 302 | | | |
| 303 | // var dbg_info_buffer = std.ArrayList(u8).init(self.base.allocator); | | |
| 304 | // defer dbg_info_buffer.deinit(); | | |
| 305 | | | |
| 306 | // var dbg_info_type_relocs: File.DbgInfoTypeRelocsTable = .{}; | | |
| 307 | // defer { | | |
| 308 | // for (dbg_info_type_relocs.items()) |*entry| { | | |
| 309 | // entry.value.relocs.deinit(self.base.allocator); | | |
| 310 | // } | | |
| 311 | // dbg_info_type_relocs.deinit(self.base.allocator); | | |
| 312 | // } | | |
| 313 | | | |
| 314 | // const typed_value = decl.typed_value.most_recent.typed_value; | | |
| 315 | // log.debug("typed_value = {}", .{typed_value}); | | |
| 316 | | | |
| 317 | // const res = try codegen.generateSymbol( | | |
| 318 | // &self.base, | | |
| 319 | // decl.src(), | | |
| 320 | // typed_value, | | |
| 321 | // &code_buffer, | | |
| 322 | // &dbg_line_buffer, | | |
| 323 | // &dbg_info_buffer, | | |
| 324 | // &dbg_info_type_relocs, | | |
| 325 | // ); | | |
| 326 | // log.debug("res = {}", .{res}); | | |
| 327 | | | |
| 328 | // const code = switch (res) { | | |
| 329 | // .externally_managed => |x| x, | | |
| 330 | // .appended => code_buffer.items, | | |
| 331 | // .fail => |em| { | | |
| 332 | // decl.analysis = .codegen_failure; | | |
| 333 | // try module.failed_decls.put(module.gpa, decl, em); | | |
| 334 | // return; | | |
| 335 | // }, | | |
| 336 | // }; | | |
| 337 | } | | |
| 338 | | 314 | |
| 339 | pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {} | 315 | pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {} |
| 340 | | 316 | |
| ... | @@ -351,105 +327,7 @@ pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 { | ... | @@ -351,105 +327,7 @@ pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 { |
| 351 | @panic("TODO implement getDeclVAddr for MachO"); | 327 | @panic("TODO implement getDeclVAddr for MachO"); |
| 352 | } | 328 | } |
| 353 | | 329 | |
| 354 | pub fn populateMissingMetadata(self: *MachO) !void { | 330 | pub fn populateMissingMetadata(self: *MachO) !void {} |
| 355 | // if (self.seg_load_re_index == null) { | | |
| 356 | // self.seg_load_re_index = @intCast(u16, self.segment_cmds.items.len); | | |
| 357 | // const file_size = self.base.options.program_code_size_hint; | | |
| 358 | // const p_align = 0x1000; | | |
| 359 | // const off = self.findFreeSpace(file_size, p_align); | | |
| 360 | // log.debug("found LC_SEGMENT_64 free space 0x{x} to 0x{x}", .{ off, off + file_size }); | | |
| 361 | // try self.segment_cmds.append(self.base.allocator, .{}); | | |
| 362 | // self.entry_addr = null; | | |
| 363 | // self.seg_table_dirty = true; | | |
| 364 | // } | | |
| 365 | // if (self.seg_got_index == null) { | | |
| 366 | // self.seg_got_index = @intCast(u16, self.segment_cmds.items.len); | | |
| 367 | // const file_size = 8 * self.base.options.symbol_count_hint; | | |
| 368 | // // Apple recommends to page align for better performance. | | |
| 369 | // // TODO This is not necessarily true for MH_OBJECT which means we | | |
| 370 | // // could potentially shave off a couple of bytes when generating | | |
| 371 | // // only object files. | | |
| 372 | // const p_align = 0x1000; | | |
| 373 | // const off = self.findFreeSpace(file_size, p_align); | | |
| 374 | // log.debug("found LC_SEGMENT_64 free space 0x{x} to 0x{x}", .{ off, off + file_size }); | | |
| 375 | // const default_vmaddr = 0x4000000; | | |
| 376 | // try self.segment_cmds.append(self.base.allocator, .{ | | |
| 377 | // .cmd = macho.LC_SEGMENT_64, | | |
| 378 | // .cmdsize = @sizeOf(macho.segment_command_64), | | |
| 379 | // .segname = self.makeString("__TEXT"), | | |
| 380 | // .vmaddr = default_vmaddr, | | |
| 381 | // .vmsize = file_size, | | |
| 382 | // .fileoff = off, | | |
| 383 | // .filesize = file_size, | | |
| 384 | // .maxprot = 0x5, | | |
| 385 | // .initprot = 0x5, | | |
| 386 | // .nsects = 0, | | |
| 387 | // .flags = 0, | | |
| 388 | // }); | | |
| 389 | // self.seg_table_dirty = true; | | |
| 390 | // } | | |
| 391 | } | | |
| 392 | | | |
| 393 | /// Returns end pos of collision, if any. | | |
| 394 | fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { | | |
| 395 | const header_size: u64 = @sizeOf(macho.mach_header_64); | | |
| 396 | if (start < header_size) | | |
| 397 | return header_size; | | |
| 398 | | | |
| 399 | const end = start + satMul(size, alloc_num) / alloc_den; | | |
| 400 | | | |
| 401 | // if (self.sec_table_offset) |off| { | | |
| 402 | // const section_size: u64 = @sizeOf(macho.section_64); | | |
| 403 | // const tight_size = self.sections.items.len * section_size; | | |
| 404 | // const increased_size = satMul(tight_size, alloc_num) / alloc_den; | | |
| 405 | // const test_end = off + increased_size; | | |
| 406 | // if (end > off and start < test_end) { | | |
| 407 | // return test_end; | | |
| 408 | // } | | |
| 409 | // } | | |
| 410 | | | |
| 411 | // if (self.seg_table_offset) |off| { | | |
| 412 | // const segment_size: u64 = @sizeOf(macho.segment_command_64); | | |
| 413 | // const tight_size = self.segment_cmds.items.len * segment_size; | | |
| 414 | // const increased_size = satMul(tight_size, alloc_num) / alloc_den; | | |
| 415 | // const test_end = off + increased_size; | | |
| 416 | // if (end > off and start < test_end) { | | |
| 417 | // return test_end; | | |
| 418 | // } | | |
| 419 | // } | | |
| 420 | | | |
| 421 | // for (self.sections.items) |section| { | | |
| 422 | // const increased_size = satMul(section.size, alloc_num) / alloc_den; | | |
| 423 | // const test_end = section.offset + increased_size; | | |
| 424 | // if (end > section.offset and start < test_end) { | | |
| 425 | // return test_end; | | |
| 426 | // } | | |
| 427 | // } | | |
| 428 | | | |
| 429 | for (self.segments.items) |segment| { | | |
| 430 | const increased_size = satMul(segment.filesize, alloc_num) / alloc_den; | | |
| 431 | const test_end = segment_cmd.fileoff + increased_size; | | |
| 432 | if (end > segment_cmd.fileoff and start < test_end) { | | |
| 433 | return test_end; | | |
| 434 | } | | |
| 435 | } | | |
| 436 | | | |
| 437 | return null; | | |
| 438 | } | | |
| 439 | | | |
| 440 | fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u16) u64 { | | |
| 441 | var start: u64 = 0; | | |
| 442 | while (self.detectAllocCollision(start, object_size)) |item_end| { | | |
| 443 | start = mem.alignForwardGeneric(u64, item_end, min_alignment); | | |
| 444 | } | | |
| 445 | return start; | | |
| 446 | } | | |
| 447 | | | |
| 448 | /// Saturating multiplication | | |
| 449 | fn satMul(a: anytype, b: anytype) @TypeOf(a, b) { | | |
| 450 | const T = @TypeOf(a, b); | | |
| 451 | return std.math.mul(T, a, b) catch std.math.maxInt(T); | | |
| 452 | } | | |
| 453 | | 331 | |
| 454 | fn makeString(comptime bytes: []const u8) [16]u8 { | 332 | fn makeString(comptime bytes: []const u8) [16]u8 { |
| 455 | var buf: [16]u8 = undefined; | 333 | var buf: [16]u8 = undefined; |