| ... | @@ -60,6 +60,17 @@ pub const SearchStrategy = enum { | ... | @@ -60,6 +60,17 @@ pub const SearchStrategy = enum { |
| 60 | dylibs_first, | 60 | dylibs_first, |
| 61 | }; | 61 | }; |
| 62 | | 62 | |
| | 63 | /// Mode of operation of the linker. |
| | 64 | pub const Mode = enum { |
| | 65 | /// Incremental mode will preallocate segments/sections and is compatible with |
| | 66 | /// watch and HCS modes of operation. |
| | 67 | incremental, |
| | 68 | /// Zld mode will link relocatables in a traditional, one-shot |
| | 69 | /// fashion (default for LLVM backend). It acts as a drop-in replacement for |
| | 70 | /// LLD. |
| | 71 | zld, |
| | 72 | }; |
| | 73 | |
| 63 | const Section = struct { | 74 | const Section = struct { |
| 64 | header: macho.section_64, | 75 | header: macho.section_64, |
| 65 | segment_index: u8, | 76 | segment_index: u8, |
| ... | @@ -98,10 +109,7 @@ d_sym: ?DebugSymbols = null, | ... | @@ -98,10 +109,7 @@ d_sym: ?DebugSymbols = null, |
| 98 | /// For x86_64 that's 4KB, whereas for aarch64, that's 16KB. | 109 | /// For x86_64 that's 4KB, whereas for aarch64, that's 16KB. |
| 99 | page_size: u16, | 110 | page_size: u16, |
| 100 | | 111 | |
| 101 | /// Mode of operation: incremental - will preallocate segments/sections and is compatible with | 112 | mode: Mode, |
| 102 | /// watch and HCS modes of operation; one_shot - will link relocatables in a traditional, one-shot | | |
| 103 | /// fashion (default for LLVM backend). | | |
| 104 | mode: enum { incremental, one_shot }, | | |
| 105 | | 113 | |
| 106 | dyld_info_cmd: macho.dyld_info_command = .{}, | 114 | dyld_info_cmd: macho.dyld_info_command = .{}, |
| 107 | symtab_cmd: macho.symtab_command = .{}, | 115 | symtab_cmd: macho.symtab_command = .{}, |
| ... | @@ -314,33 +322,42 @@ pub const default_headerpad_size: u32 = 0x1000; | ... | @@ -314,33 +322,42 @@ pub const default_headerpad_size: u32 = 0x1000; |
| 314 | pub fn openPath(allocator: Allocator, options: link.Options) !*MachO { | 322 | pub fn openPath(allocator: Allocator, options: link.Options) !*MachO { |
| 315 | assert(options.target.ofmt == .macho); | 323 | assert(options.target.ofmt == .macho); |
| 316 | | 324 | |
| 317 | if (options.emit == null or options.module == null) { | 325 | if (options.emit == null) { |
| 318 | return createEmpty(allocator, options); | 326 | return createEmpty(allocator, options); |
| 319 | } | 327 | } |
| 320 | | 328 | |
| 321 | const emit = options.emit.?; | 329 | const emit = options.emit.?; |
| | 330 | const mode: Mode = mode: { |
| | 331 | if (options.use_llvm or options.module == null or options.cache_mode == .whole) |
| | 332 | break :mode .zld; |
| | 333 | break :mode .incremental; |
| | 334 | }; |
| | 335 | const sub_path = if (mode == .zld) blk: { |
| | 336 | if (options.module == null) { |
| | 337 | // No point in opening a file, we would not write anything to it. |
| | 338 | // Initialize with empty. |
| | 339 | return createEmpty(allocator, options); |
| | 340 | } |
| | 341 | // Open a temporary object file, not the final output file because we |
| | 342 | // want to link with LLD. |
| | 343 | break :blk try std.fmt.allocPrint(allocator, "{s}{s}", .{ |
| | 344 | emit.sub_path, options.target.ofmt.fileExt(options.target.cpu.arch), |
| | 345 | }); |
| | 346 | } else emit.sub_path; |
| | 347 | errdefer if (mode == .zld) allocator.free(sub_path); |
| | 348 | |
| 322 | const self = try createEmpty(allocator, options); | 349 | const self = try createEmpty(allocator, options); |
| 323 | errdefer { | 350 | errdefer self.base.destroy(); |
| 324 | self.base.file = null; | | |
| 325 | self.base.destroy(); | | |
| 326 | } | | |
| 327 | | 351 | |
| 328 | if (build_options.have_llvm and options.use_llvm and options.module != null) { | 352 | if (mode == .zld) { |
| 329 | // TODO this intermediary_basename isn't enough; in the case of `zig build-exe`, | 353 | // TODO this intermediary_basename isn't enough; in the case of `zig build-exe`, |
| 330 | // we also want to put the intermediary object file in the cache while the | 354 | // we also want to put the intermediary object file in the cache while the |
| 331 | // main emit directory is the cwd. | 355 | // main emit directory is the cwd. |
| 332 | self.base.intermediary_basename = try std.fmt.allocPrint(allocator, "{s}{s}", .{ | 356 | self.base.intermediary_basename = sub_path; |
| 333 | emit.sub_path, options.target.ofmt.fileExt(options.target.cpu.arch), | 357 | return self; |
| 334 | }); | | |
| 335 | } | 358 | } |
| 336 | | 359 | |
| 337 | if (self.base.intermediary_basename != null) switch (options.output_mode) { | 360 | const file = try emit.directory.handle.createFile(sub_path, .{ |
| 338 | .Obj => return self, | | |
| 339 | .Lib => if (options.link_mode == .Static) return self, | | |
| 340 | else => {}, | | |
| 341 | }; | | |
| 342 | | | |
| 343 | const file = try emit.directory.handle.createFile(emit.sub_path, .{ | | |
| 344 | .truncate = false, | 361 | .truncate = false, |
| 345 | .read = true, | 362 | .read = true, |
| 346 | .mode = link.determineMode(options), | 363 | .mode = link.determineMode(options), |
| ... | @@ -348,23 +365,21 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO { | ... | @@ -348,23 +365,21 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO { |
| 348 | errdefer file.close(); | 365 | errdefer file.close(); |
| 349 | self.base.file = file; | 366 | self.base.file = file; |
| 350 | | 367 | |
| 351 | if (self.mode == .one_shot) return self; | | |
| 352 | | | |
| 353 | if (!options.strip and options.module != null) { | 368 | if (!options.strip and options.module != null) { |
| 354 | // Create dSYM bundle. | 369 | // Create dSYM bundle. |
| 355 | log.debug("creating {s}.dSYM bundle", .{emit.sub_path}); | 370 | log.debug("creating {s}.dSYM bundle", .{sub_path}); |
| 356 | | 371 | |
| 357 | const d_sym_path = try fmt.allocPrint( | 372 | const d_sym_path = try fmt.allocPrint( |
| 358 | allocator, | 373 | allocator, |
| 359 | "{s}.dSYM" ++ fs.path.sep_str ++ "Contents" ++ fs.path.sep_str ++ "Resources" ++ fs.path.sep_str ++ "DWARF", | 374 | "{s}.dSYM" ++ fs.path.sep_str ++ "Contents" ++ fs.path.sep_str ++ "Resources" ++ fs.path.sep_str ++ "DWARF", |
| 360 | .{emit.sub_path}, | 375 | .{sub_path}, |
| 361 | ); | 376 | ); |
| 362 | defer allocator.free(d_sym_path); | 377 | defer allocator.free(d_sym_path); |
| 363 | | 378 | |
| 364 | var d_sym_bundle = try emit.directory.handle.makeOpenPath(d_sym_path, .{}); | 379 | var d_sym_bundle = try emit.directory.handle.makeOpenPath(d_sym_path, .{}); |
| 365 | defer d_sym_bundle.close(); | 380 | defer d_sym_bundle.close(); |
| 366 | | 381 | |
| 367 | const d_sym_file = try d_sym_bundle.createFile(emit.sub_path, .{ | 382 | const d_sym_file = try d_sym_bundle.createFile(sub_path, .{ |
| 368 | .truncate = false, | 383 | .truncate = false, |
| 369 | .read = true, | 384 | .read = true, |
| 370 | }); | 385 | }); |
| ... | @@ -413,7 +428,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO { | ... | @@ -413,7 +428,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO { |
| 413 | }, | 428 | }, |
| 414 | .page_size = page_size, | 429 | .page_size = page_size, |
| 415 | .mode = if (use_llvm or options.module == null or options.cache_mode == .whole) | 430 | .mode = if (use_llvm or options.module == null or options.cache_mode == .whole) |
| 416 | .one_shot | 431 | .zld |
| 417 | else | 432 | else |
| 418 | .incremental, | 433 | .incremental, |
| 419 | }; | 434 | }; |
| ... | @@ -447,7 +462,7 @@ pub fn flush(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) li | ... | @@ -447,7 +462,7 @@ pub fn flush(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) li |
| 447 | } | 462 | } |
| 448 | | 463 | |
| 449 | switch (self.mode) { | 464 | switch (self.mode) { |
| 450 | .one_shot => return zld.linkWithZld(self, comp, prog_node), | 465 | .zld => return zld.linkWithZld(self, comp, prog_node), |
| 451 | .incremental => return self.flushModule(comp, prog_node), | 466 | .incremental => return self.flushModule(comp, prog_node), |
| 452 | } | 467 | } |
| 453 | } | 468 | } |