blob a4ee93cf (131871B) - Raw
1 const Zld = @This(); 2 3 const std = @import("std"); 4 const assert = std.debug.assert; 5 const leb = std.leb; 6 const mem = std.mem; 7 const meta = std.meta; 8 const fs = std.fs; 9 const macho = std.macho; 10 const math = std.math; 11 const log = std.log.scoped(.zld); 12 const aarch64 = @import("../../codegen/aarch64.zig"); 13 const reloc = @import("reloc.zig"); 14 15 const Allocator = mem.Allocator; 16 const Archive = @import("Archive.zig"); 17 const CodeSignature = @import("CodeSignature.zig"); 18 const Dylib = @import("Dylib.zig"); 19 const Object = @import("Object.zig"); 20 const Symbol = @import("Symbol.zig"); 21 const Trie = @import("Trie.zig"); 22 23 usingnamespace @import("commands.zig"); 24 usingnamespace @import("bind.zig"); 25 26 allocator: *Allocator, 27 28 arch: ?std.Target.Cpu.Arch = null, 29 page_size: ?u16 = null, 30 file: ?fs.File = null, 31 out_path: ?[]const u8 = null, 32 33 // TODO these args will become obselete once Zld is coalesced with incremental 34 // linker. 35 syslibroot: ?[]const u8 = null, 36 stack_size: u64 = 0, 37 38 objects: std.ArrayListUnmanaged(*Object) = .{}, 39 archives: std.ArrayListUnmanaged(*Archive) = .{}, 40 dylibs: std.ArrayListUnmanaged(*Dylib) = .{}, 41 42 libsystem_dylib_index: ?u16 = null, 43 next_dylib_ordinal: u16 = 1, 44 45 load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, 46 47 pagezero_segment_cmd_index: ?u16 = null, 48 text_segment_cmd_index: ?u16 = null, 49 data_const_segment_cmd_index: ?u16 = null, 50 data_segment_cmd_index: ?u16 = null, 51 linkedit_segment_cmd_index: ?u16 = null, 52 dyld_info_cmd_index: ?u16 = null, 53 symtab_cmd_index: ?u16 = null, 54 dysymtab_cmd_index: ?u16 = null, 55 dylinker_cmd_index: ?u16 = null, 56 data_in_code_cmd_index: ?u16 = null, 57 function_starts_cmd_index: ?u16 = null, 58 main_cmd_index: ?u16 = null, 59 version_min_cmd_index: ?u16 = null, 60 source_version_cmd_index: ?u16 = null, 61 uuid_cmd_index: ?u16 = null, 62 code_signature_cmd_index: ?u16 = null, 63 64 // __TEXT segment sections 65 text_section_index: ?u16 = null, 66 stubs_section_index: ?u16 = null, 67 stub_helper_section_index: ?u16 = null, 68 text_const_section_index: ?u16 = null, 69 cstring_section_index: ?u16 = null, 70 ustring_section_index: ?u16 = null, 71 gcc_except_tab_section_index: ?u16 = null, 72 unwind_info_section_index: ?u16 = null, 73 eh_frame_section_index: ?u16 = null, 74 75 objc_methlist_section_index: ?u16 = null, 76 objc_methname_section_index: ?u16 = null, 77 objc_methtype_section_index: ?u16 = null, 78 objc_classname_section_index: ?u16 = null, 79 80 // __DATA_CONST segment sections 81 got_section_index: ?u16 = null, 82 mod_init_func_section_index: ?u16 = null, 83 mod_term_func_section_index: ?u16 = null, 84 data_const_section_index: ?u16 = null, 85 86 objc_cfstring_section_index: ?u16 = null, 87 objc_classlist_section_index: ?u16 = null, 88 objc_imageinfo_section_index: ?u16 = null, 89 90 // __DATA segment sections 91 tlv_section_index: ?u16 = null, 92 tlv_data_section_index: ?u16 = null, 93 tlv_bss_section_index: ?u16 = null, 94 la_symbol_ptr_section_index: ?u16 = null, 95 data_section_index: ?u16 = null, 96 bss_section_index: ?u16 = null, 97 common_section_index: ?u16 = null, 98 99 objc_const_section_index: ?u16 = null, 100 objc_selrefs_section_index: ?u16 = null, 101 objc_classrefs_section_index: ?u16 = null, 102 objc_data_section_index: ?u16 = null, 103 104 globals: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, 105 imports: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, 106 unresolved: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, 107 tentatives: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, 108 109 /// Offset into __DATA,__common section. 110 /// Set if the linker found tentative definitions in any of the objects. 111 tentative_defs_offset: u64 = 0, 112 113 strtab: std.ArrayListUnmanaged(u8) = .{}, 114 strtab_dir: std.StringHashMapUnmanaged(u32) = .{}, 115 116 threadlocal_offsets: std.ArrayListUnmanaged(TlvOffset) = .{}, // TODO merge with Symbol abstraction 117 local_rebases: std.ArrayListUnmanaged(Pointer) = .{}, 118 stubs: std.ArrayListUnmanaged(*Symbol) = .{}, 119 got_entries: std.ArrayListUnmanaged(*Symbol) = .{}, 120 121 stub_helper_stubs_start_off: ?u64 = null, 122 123 const TlvOffset = struct { 124 source_addr: u64, 125 offset: u64, 126 127 fn cmp(context: void, a: TlvOffset, b: TlvOffset) bool { 128 _ = context; 129 return a.source_addr < b.source_addr; 130 } 131 }; 132 133 /// Default path to dyld 134 const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld"; 135 136 pub fn init(allocator: *Allocator) Zld { 137 return .{ .allocator = allocator }; 138 } 139 140 pub fn deinit(self: *Zld) void { 141 self.threadlocal_offsets.deinit(self.allocator); 142 self.local_rebases.deinit(self.allocator); 143 self.stubs.deinit(self.allocator); 144 self.got_entries.deinit(self.allocator); 145 146 for (self.load_commands.items) |*lc| { 147 lc.deinit(self.allocator); 148 } 149 self.load_commands.deinit(self.allocator); 150 151 for (self.objects.items) |object| { 152 object.deinit(); 153 self.allocator.destroy(object); 154 } 155 self.objects.deinit(self.allocator); 156 157 for (self.archives.items) |archive| { 158 archive.deinit(); 159 self.allocator.destroy(archive); 160 } 161 self.archives.deinit(self.allocator); 162 163 for (self.dylibs.items) |dylib| { 164 dylib.deinit(); 165 self.allocator.destroy(dylib); 166 } 167 self.dylibs.deinit(self.allocator); 168 169 for (self.imports.values()) |proxy| { 170 proxy.deinit(self.allocator); 171 self.allocator.destroy(proxy); 172 } 173 self.imports.deinit(self.allocator); 174 175 self.tentatives.deinit(self.allocator); 176 self.globals.deinit(self.allocator); 177 self.unresolved.deinit(self.allocator); 178 self.strtab.deinit(self.allocator); 179 180 { 181 var it = self.strtab_dir.keyIterator(); 182 while (it.next()) |key| { 183 self.allocator.free(key.*); 184 } 185 } 186 self.strtab_dir.deinit(self.allocator); 187 } 188 189 pub fn closeFiles(self: Zld) void { 190 for (self.objects.items) |object| { 191 object.closeFile(); 192 } 193 for (self.archives.items) |archive| { 194 archive.closeFile(); 195 } 196 if (self.file) |f| f.close(); 197 } 198 199 const LinkArgs = struct { 200 libs: []const []const u8, 201 rpaths: []const []const u8, 202 libc_stub_path: []const u8, 203 }; 204 205 pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: LinkArgs) !void { 206 if (files.len == 0) return error.NoInputFiles; 207 if (out_path.len == 0) return error.EmptyOutputPath; 208 209 if (self.arch == null) { 210 // Try inferring the arch from the object files. 211 self.arch = blk: { 212 const file = try fs.cwd().openFile(files[0], .{}); 213 defer file.close(); 214 var reader = file.reader(); 215 const header = try reader.readStruct(macho.mach_header_64); 216 const arch: std.Target.Cpu.Arch = switch (header.cputype) { 217 macho.CPU_TYPE_X86_64 => .x86_64, 218 macho.CPU_TYPE_ARM64 => .aarch64, 219 else => |value| { 220 log.err("unsupported cpu architecture 0x{x}", .{value}); 221 return error.UnsupportedCpuArchitecture; 222 }, 223 }; 224 break :blk arch; 225 }; 226 } 227 228 self.page_size = switch (self.arch.?) { 229 .aarch64 => 0x4000, 230 .x86_64 => 0x1000, 231 else => unreachable, 232 }; 233 self.out_path = out_path; 234 self.file = try fs.cwd().createFile(out_path, .{ 235 .truncate = true, 236 .read = true, 237 .mode = if (std.Target.current.os.tag == .windows) 0 else 0o777, 238 }); 239 240 try self.populateMetadata(); 241 try self.addRpaths(args.rpaths); 242 try self.parseInputFiles(files); 243 try self.parseLibs(args.libs); 244 try self.parseLibSystem(args.libc_stub_path); 245 try self.resolveSymbols(); 246 try self.resolveStubsAndGotEntries(); 247 try self.updateMetadata(); 248 try self.sortSections(); 249 try self.allocateTextSegment(); 250 try self.allocateDataConstSegment(); 251 try self.allocateDataSegment(); 252 self.allocateLinkeditSegment(); 253 try self.allocateSymbols(); 254 try self.allocateTentativeSymbols(); 255 try self.allocateProxyBindAddresses(); 256 try self.flush(); 257 } 258 259 fn parseInputFiles(self: *Zld, files: []const []const u8) !void { 260 for (files) |file_name| { 261 const full_path = full_path: { 262 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; 263 const path = try std.fs.realpath(file_name, &buffer); 264 break :full_path try self.allocator.dupe(u8, path); 265 }; 266 267 if (try Object.createAndParseFromPath(self.allocator, self.arch.?, full_path)) |object| { 268 try self.objects.append(self.allocator, object); 269 continue; 270 } 271 272 if (try Archive.createAndParseFromPath(self.allocator, self.arch.?, full_path)) |archive| { 273 try self.archives.append(self.allocator, archive); 274 continue; 275 } 276 277 if (try Dylib.createAndParseFromPath( 278 self.allocator, 279 self.arch.?, 280 full_path, 281 self.syslibroot, 282 true, 283 )) |dylib| { 284 try self.dylibs.append(self.allocator, dylib); 285 continue; 286 } 287 288 log.warn("unknown filetype for positional input file: '{s}'", .{file_name}); 289 } 290 } 291 292 fn parseLibs(self: *Zld, libs: []const []const u8) !void { 293 const DylibDeps = struct { 294 fn bubbleUp(out: *std.ArrayList(*Dylib), next: *Dylib) error{OutOfMemory}!void { 295 try out.ensureUnusedCapacity(next.dylibs.items.len); 296 for (next.dylibs.items) |dylib| { 297 out.appendAssumeCapacity(dylib); 298 } 299 for (next.dylibs.items) |dylib| { 300 try bubbleUp(out, dylib); 301 } 302 } 303 }; 304 305 for (libs) |lib| { 306 if (try Dylib.createAndParseFromPath( 307 self.allocator, 308 self.arch.?, 309 lib, 310 self.syslibroot, 311 true, 312 )) |dylib| { 313 try self.dylibs.append(self.allocator, dylib); 314 continue; 315 } 316 317 if (try Archive.createAndParseFromPath(self.allocator, self.arch.?, lib)) |archive| { 318 try self.archives.append(self.allocator, archive); 319 continue; 320 } 321 322 log.warn("unknown filetype for a library: '{s}'", .{lib}); 323 } 324 325 // Flatten out any parsed dependencies. 326 var deps = std.ArrayList(*Dylib).init(self.allocator); 327 defer deps.deinit(); 328 329 for (self.dylibs.items) |dylib| { 330 try DylibDeps.bubbleUp(&deps, dylib); 331 } 332 333 try self.dylibs.appendSlice(self.allocator, deps.toOwnedSlice()); 334 } 335 336 fn parseLibSystem(self: *Zld, libc_stub_path: []const u8) !void { 337 const dylib = (try Dylib.createAndParseFromPath( 338 self.allocator, 339 self.arch.?, 340 libc_stub_path, 341 self.syslibroot, 342 false, 343 )) orelse return error.FailedToParseLibSystem; 344 self.libsystem_dylib_index = @intCast(u16, self.dylibs.items.len); 345 try self.dylibs.append(self.allocator, dylib); 346 347 // Add LC_LOAD_DYLIB load command. 348 dylib.ordinal = self.next_dylib_ordinal; 349 const dylib_id = dylib.id orelse unreachable; 350 var dylib_cmd = try createLoadDylibCommand( 351 self.allocator, 352 dylib_id.name, 353 dylib_id.timestamp, 354 dylib_id.current_version, 355 dylib_id.compatibility_version, 356 ); 357 errdefer dylib_cmd.deinit(self.allocator); 358 try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd }); 359 self.next_dylib_ordinal += 1; 360 } 361 362 fn mapAndUpdateSections( 363 self: *Zld, 364 object: *Object, 365 source_sect_id: u16, 366 target_seg_id: u16, 367 target_sect_id: u16, 368 ) !void { 369 const source_sect = &object.sections.items[source_sect_id]; 370 const target_seg = &self.load_commands.items[target_seg_id].Segment; 371 const target_sect = &target_seg.sections.items[target_sect_id]; 372 373 const alignment = try math.powi(u32, 2, target_sect.@"align"); 374 const offset = mem.alignForwardGeneric(u64, target_sect.size, alignment); 375 const size = mem.alignForwardGeneric(u64, source_sect.inner.size, alignment); 376 377 log.debug("{s}: '{s},{s}' mapped to '{s},{s}' from 0x{x} to 0x{x}", .{ 378 object.name.?, 379 parseName(&source_sect.inner.segname), 380 parseName(&source_sect.inner.sectname), 381 parseName(&target_sect.segname), 382 parseName(&target_sect.sectname), 383 offset, 384 offset + size, 385 }); 386 387 source_sect.target_map = .{ 388 .segment_id = target_seg_id, 389 .section_id = target_sect_id, 390 .offset = @intCast(u32, offset), 391 }; 392 target_sect.size = offset + size; 393 } 394 395 fn updateMetadata(self: *Zld) !void { 396 for (self.objects.items) |object| { 397 // Find ideal section alignment and update section mappings 398 for (object.sections.items) |sect, sect_id| { 399 const match = (try self.getMatchingSection(sect)) orelse { 400 log.debug("{s}: unhandled section type 0x{x} for '{s},{s}'", .{ 401 object.name.?, 402 sect.flags(), 403 sect.segname(), 404 sect.sectname(), 405 }); 406 continue; 407 }; 408 const target_seg = &self.load_commands.items[match.seg].Segment; 409 const target_sect = &target_seg.sections.items[match.sect]; 410 target_sect.@"align" = math.max(target_sect.@"align", sect.inner.@"align"); 411 412 try self.mapAndUpdateSections(object, @intCast(u16, sect_id), match.seg, match.sect); 413 } 414 } 415 416 // Ensure we have __DATA,__common section if we have tentative definitions. 417 // Update size and alignment of __DATA,__common section. 418 if (self.tentatives.values().len > 0) { 419 const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; 420 const common_section_index = self.common_section_index orelse ind: { 421 self.common_section_index = @intCast(u16, data_seg.sections.items.len); 422 try data_seg.addSection(self.allocator, "__common", .{ 423 .flags = macho.S_ZEROFILL, 424 }); 425 break :ind self.common_section_index.?; 426 }; 427 const common_sect = &data_seg.sections.items[common_section_index]; 428 429 var max_align: u16 = 0; 430 var added_size: u64 = 0; 431 for (self.tentatives.values()) |sym| { 432 const tent = sym.cast(Symbol.Tentative) orelse unreachable; 433 max_align = math.max(max_align, tent.alignment); 434 added_size += tent.size; 435 } 436 437 common_sect.@"align" = math.max(common_sect.@"align", max_align); 438 439 const alignment = try math.powi(u32, 2, common_sect.@"align"); 440 const offset = mem.alignForwardGeneric(u64, common_sect.size, alignment); 441 const size = mem.alignForwardGeneric(u64, added_size, alignment); 442 443 common_sect.size = offset + size; 444 self.tentative_defs_offset = offset; 445 } 446 447 tlv_align: { 448 const has_tlv = 449 self.tlv_section_index != null or 450 self.tlv_data_section_index != null or 451 self.tlv_bss_section_index != null; 452 453 if (!has_tlv) break :tlv_align; 454 455 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; 456 457 if (self.tlv_section_index) |index| { 458 const sect = &seg.sections.items[index]; 459 sect.@"align" = 3; // __thread_vars is always 8byte aligned 460 } 461 462 // Apparently __tlv_data and __tlv_bss need to have matching alignment, so fix it up. 463 // <rdar://problem/24221680> All __thread_data and __thread_bss sections must have same alignment 464 // https://github.com/apple-opensource/ld64/blob/e28c028b20af187a16a7161d89e91868a450cadc/src/ld/ld.cpp#L1172 465 const data_align: u32 = data: { 466 if (self.tlv_data_section_index) |index| { 467 const sect = &seg.sections.items[index]; 468 break :data sect.@"align"; 469 } 470 break :tlv_align; 471 }; 472 const bss_align: u32 = bss: { 473 if (self.tlv_bss_section_index) |index| { 474 const sect = &seg.sections.items[index]; 475 break :bss sect.@"align"; 476 } 477 break :tlv_align; 478 }; 479 const max_align = math.max(data_align, bss_align); 480 481 if (self.tlv_data_section_index) |index| { 482 const sect = &seg.sections.items[index]; 483 sect.@"align" = max_align; 484 } 485 if (self.tlv_bss_section_index) |index| { 486 const sect = &seg.sections.items[index]; 487 sect.@"align" = max_align; 488 } 489 } 490 } 491 492 const MatchingSection = struct { 493 seg: u16, 494 sect: u16, 495 }; 496 497 fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection { 498 const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; 499 const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; 500 const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; 501 const segname = sect.segname(); 502 const sectname = sect.sectname(); 503 504 const res: ?MatchingSection = blk: { 505 switch (sect.sectionType()) { 506 macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => { 507 if (self.text_const_section_index == null) { 508 self.text_const_section_index = @intCast(u16, text_seg.sections.items.len); 509 try text_seg.addSection(self.allocator, "__const", .{}); 510 } 511 512 break :blk .{ 513 .seg = self.text_segment_cmd_index.?, 514 .sect = self.text_const_section_index.?, 515 }; 516 }, 517 macho.S_CSTRING_LITERALS => { 518 if (mem.eql(u8, sectname, "__objc_methname")) { 519 // TODO it seems the common values within the sections in objects are deduplicated/merged 520 // on merging the sections' contents. 521 if (self.objc_methname_section_index == null) { 522 self.objc_methname_section_index = @intCast(u16, text_seg.sections.items.len); 523 try text_seg.addSection(self.allocator, "__objc_methname", .{ 524 .flags = macho.S_CSTRING_LITERALS, 525 }); 526 } 527 528 break :blk .{ 529 .seg = self.text_segment_cmd_index.?, 530 .sect = self.objc_methname_section_index.?, 531 }; 532 } else if (mem.eql(u8, sectname, "__objc_methtype")) { 533 if (self.objc_methtype_section_index == null) { 534 self.objc_methtype_section_index = @intCast(u16, text_seg.sections.items.len); 535 try text_seg.addSection(self.allocator, "__objc_methtype", .{ 536 .flags = macho.S_CSTRING_LITERALS, 537 }); 538 } 539 540 break :blk .{ 541 .seg = self.text_segment_cmd_index.?, 542 .sect = self.objc_methtype_section_index.?, 543 }; 544 } else if (mem.eql(u8, sectname, "__objc_classname")) { 545 if (self.objc_classname_section_index == null) { 546 self.objc_classname_section_index = @intCast(u16, text_seg.sections.items.len); 547 try text_seg.addSection(self.allocator, "__objc_classname", .{}); 548 } 549 550 break :blk .{ 551 .seg = self.text_segment_cmd_index.?, 552 .sect = self.objc_classname_section_index.?, 553 }; 554 } 555 556 if (self.cstring_section_index == null) { 557 self.cstring_section_index = @intCast(u16, text_seg.sections.items.len); 558 try text_seg.addSection(self.allocator, "__cstring", .{ 559 .flags = macho.S_CSTRING_LITERALS, 560 }); 561 } 562 563 break :blk .{ 564 .seg = self.text_segment_cmd_index.?, 565 .sect = self.cstring_section_index.?, 566 }; 567 }, 568 macho.S_LITERAL_POINTERS => { 569 if (mem.eql(u8, segname, "__DATA") and mem.eql(u8, sectname, "__objc_selrefs")) { 570 if (self.objc_selrefs_section_index == null) { 571 self.objc_selrefs_section_index = @intCast(u16, data_seg.sections.items.len); 572 try data_seg.addSection(self.allocator, "__objc_selrefs", .{ 573 .flags = macho.S_LITERAL_POINTERS, 574 }); 575 } 576 577 break :blk .{ 578 .seg = self.data_segment_cmd_index.?, 579 .sect = self.objc_selrefs_section_index.?, 580 }; 581 } 582 583 // TODO investigate 584 break :blk null; 585 }, 586 macho.S_MOD_INIT_FUNC_POINTERS => { 587 if (self.mod_init_func_section_index == null) { 588 self.mod_init_func_section_index = @intCast(u16, data_const_seg.sections.items.len); 589 try data_const_seg.addSection(self.allocator, "__mod_init_func", .{ 590 .flags = macho.S_MOD_INIT_FUNC_POINTERS, 591 }); 592 } 593 594 break :blk .{ 595 .seg = self.data_const_segment_cmd_index.?, 596 .sect = self.mod_init_func_section_index.?, 597 }; 598 }, 599 macho.S_MOD_TERM_FUNC_POINTERS => { 600 if (self.mod_term_func_section_index == null) { 601 self.mod_term_func_section_index = @intCast(u16, data_const_seg.sections.items.len); 602 try data_const_seg.addSection(self.allocator, "__mod_term_func", .{ 603 .flags = macho.S_MOD_TERM_FUNC_POINTERS, 604 }); 605 } 606 607 break :blk .{ 608 .seg = self.data_const_segment_cmd_index.?, 609 .sect = self.mod_term_func_section_index.?, 610 }; 611 }, 612 macho.S_ZEROFILL => { 613 if (mem.eql(u8, sectname, "__common")) { 614 if (self.common_section_index == null) { 615 self.common_section_index = @intCast(u16, data_seg.sections.items.len); 616 try data_seg.addSection(self.allocator, "__common", .{ 617 .flags = macho.S_ZEROFILL, 618 }); 619 } 620 621 break :blk .{ 622 .seg = self.data_segment_cmd_index.?, 623 .sect = self.common_section_index.?, 624 }; 625 } else { 626 if (self.bss_section_index == null) { 627 self.bss_section_index = @intCast(u16, data_seg.sections.items.len); 628 try data_seg.addSection(self.allocator, "__bss", .{ 629 .flags = macho.S_ZEROFILL, 630 }); 631 } 632 633 break :blk .{ 634 .seg = self.data_segment_cmd_index.?, 635 .sect = self.bss_section_index.?, 636 }; 637 } 638 }, 639 macho.S_THREAD_LOCAL_VARIABLES => { 640 if (self.tlv_section_index == null) { 641 self.tlv_section_index = @intCast(u16, data_seg.sections.items.len); 642 try data_seg.addSection(self.allocator, "__thread_vars", .{ 643 .flags = macho.S_THREAD_LOCAL_VARIABLES, 644 }); 645 } 646 647 break :blk .{ 648 .seg = self.data_segment_cmd_index.?, 649 .sect = self.tlv_section_index.?, 650 }; 651 }, 652 macho.S_THREAD_LOCAL_REGULAR => { 653 if (self.tlv_data_section_index == null) { 654 self.tlv_data_section_index = @intCast(u16, data_seg.sections.items.len); 655 try data_seg.addSection(self.allocator, "__thread_data", .{ 656 .flags = macho.S_THREAD_LOCAL_REGULAR, 657 }); 658 } 659 660 break :blk .{ 661 .seg = self.data_segment_cmd_index.?, 662 .sect = self.tlv_data_section_index.?, 663 }; 664 }, 665 macho.S_THREAD_LOCAL_ZEROFILL => { 666 if (self.tlv_bss_section_index == null) { 667 self.tlv_bss_section_index = @intCast(u16, data_seg.sections.items.len); 668 try data_seg.addSection(self.allocator, "__thread_bss", .{ 669 .flags = macho.S_THREAD_LOCAL_ZEROFILL, 670 }); 671 } 672 673 break :blk .{ 674 .seg = self.data_segment_cmd_index.?, 675 .sect = self.tlv_bss_section_index.?, 676 }; 677 }, 678 macho.S_COALESCED => { 679 if (mem.eql(u8, "__TEXT", segname) and mem.eql(u8, "__eh_frame", sectname)) { 680 // TODO I believe __eh_frame is currently part of __unwind_info section 681 // in the latest ld64 output. 682 if (self.eh_frame_section_index == null) { 683 self.eh_frame_section_index = @intCast(u16, text_seg.sections.items.len); 684 try text_seg.addSection(self.allocator, "__eh_frame", .{}); 685 } 686 687 break :blk .{ 688 .seg = self.text_segment_cmd_index.?, 689 .sect = self.eh_frame_section_index.?, 690 }; 691 } 692 693 // TODO audit this: is this the right mapping? 694 if (self.data_const_section_index == null) { 695 self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); 696 try data_const_seg.addSection(self.allocator, "__const", .{}); 697 } 698 699 break :blk .{ 700 .seg = self.data_const_segment_cmd_index.?, 701 .sect = self.data_const_section_index.?, 702 }; 703 }, 704 macho.S_REGULAR => { 705 if (sect.isCode()) { 706 if (self.text_section_index == null) { 707 self.text_section_index = @intCast(u16, text_seg.sections.items.len); 708 try text_seg.addSection(self.allocator, "__text", .{ 709 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, 710 }); 711 } 712 713 break :blk .{ 714 .seg = self.text_segment_cmd_index.?, 715 .sect = self.text_section_index.?, 716 }; 717 } 718 if (sect.isDebug()) { 719 // TODO debug attributes 720 if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) { 721 log.debug("TODO compact unwind section: type 0x{x}, name '{s},{s}'", .{ 722 sect.flags(), segname, sectname, 723 }); 724 } 725 break :blk null; 726 } 727 728 if (mem.eql(u8, segname, "__TEXT")) { 729 if (mem.eql(u8, sectname, "__ustring")) { 730 if (self.ustring_section_index == null) { 731 self.ustring_section_index = @intCast(u16, text_seg.sections.items.len); 732 try text_seg.addSection(self.allocator, "__ustring", .{}); 733 } 734 735 break :blk .{ 736 .seg = self.text_segment_cmd_index.?, 737 .sect = self.ustring_section_index.?, 738 }; 739 } else if (mem.eql(u8, sectname, "__gcc_except_tab")) { 740 if (self.gcc_except_tab_section_index == null) { 741 self.gcc_except_tab_section_index = @intCast(u16, text_seg.sections.items.len); 742 try text_seg.addSection(self.allocator, "__gcc_except_tab", .{}); 743 } 744 745 break :blk .{ 746 .seg = self.text_segment_cmd_index.?, 747 .sect = self.gcc_except_tab_section_index.?, 748 }; 749 } else if (mem.eql(u8, sectname, "__objc_methlist")) { 750 if (self.objc_methlist_section_index == null) { 751 self.objc_methlist_section_index = @intCast(u16, text_seg.sections.items.len); 752 try text_seg.addSection(self.allocator, "__objc_methlist", .{}); 753 } 754 755 break :blk .{ 756 .seg = self.text_segment_cmd_index.?, 757 .sect = self.objc_methlist_section_index.?, 758 }; 759 } else { 760 if (self.text_const_section_index == null) { 761 self.text_const_section_index = @intCast(u16, text_seg.sections.items.len); 762 try text_seg.addSection(self.allocator, "__const", .{}); 763 } 764 765 break :blk .{ 766 .seg = self.text_segment_cmd_index.?, 767 .sect = self.text_const_section_index.?, 768 }; 769 } 770 } 771 772 if (mem.eql(u8, segname, "__DATA_CONST")) { 773 if (self.data_const_section_index == null) { 774 self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); 775 try data_const_seg.addSection(self.allocator, "__const", .{}); 776 } 777 778 break :blk .{ 779 .seg = self.data_const_segment_cmd_index.?, 780 .sect = self.data_const_section_index.?, 781 }; 782 } 783 784 if (mem.eql(u8, segname, "__DATA")) { 785 if (mem.eql(u8, sectname, "__const")) { 786 if (self.data_const_section_index == null) { 787 self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); 788 try data_const_seg.addSection(self.allocator, "__const", .{}); 789 } 790 791 break :blk .{ 792 .seg = self.data_const_segment_cmd_index.?, 793 .sect = self.data_const_section_index.?, 794 }; 795 } else if (mem.eql(u8, sectname, "__cfstring")) { 796 if (self.objc_cfstring_section_index == null) { 797 self.objc_cfstring_section_index = @intCast(u16, data_const_seg.sections.items.len); 798 try data_const_seg.addSection(self.allocator, "__cfstring", .{}); 799 } 800 801 break :blk .{ 802 .seg = self.data_const_segment_cmd_index.?, 803 .sect = self.objc_cfstring_section_index.?, 804 }; 805 } else if (mem.eql(u8, sectname, "__objc_classlist")) { 806 if (self.objc_classlist_section_index == null) { 807 self.objc_classlist_section_index = @intCast(u16, data_const_seg.sections.items.len); 808 try data_const_seg.addSection(self.allocator, "__objc_classlist", .{}); 809 } 810 811 break :blk .{ 812 .seg = self.data_const_segment_cmd_index.?, 813 .sect = self.objc_classlist_section_index.?, 814 }; 815 } else if (mem.eql(u8, sectname, "__objc_imageinfo")) { 816 if (self.objc_imageinfo_section_index == null) { 817 self.objc_imageinfo_section_index = @intCast(u16, data_const_seg.sections.items.len); 818 try data_const_seg.addSection(self.allocator, "__objc_imageinfo", .{}); 819 } 820 821 break :blk .{ 822 .seg = self.data_const_segment_cmd_index.?, 823 .sect = self.objc_imageinfo_section_index.?, 824 }; 825 } else if (mem.eql(u8, sectname, "__objc_const")) { 826 if (self.objc_const_section_index == null) { 827 self.objc_const_section_index = @intCast(u16, data_seg.sections.items.len); 828 try data_seg.addSection(self.allocator, "__objc_const", .{}); 829 } 830 831 break :blk .{ 832 .seg = self.data_segment_cmd_index.?, 833 .sect = self.objc_const_section_index.?, 834 }; 835 } else if (mem.eql(u8, sectname, "__objc_classrefs")) { 836 if (self.objc_classrefs_section_index == null) { 837 self.objc_classrefs_section_index = @intCast(u16, data_seg.sections.items.len); 838 try data_seg.addSection(self.allocator, "__objc_classrefs", .{}); 839 } 840 841 break :blk .{ 842 .seg = self.data_segment_cmd_index.?, 843 .sect = self.objc_classrefs_section_index.?, 844 }; 845 } else if (mem.eql(u8, sectname, "__objc_data")) { 846 if (self.objc_data_section_index == null) { 847 self.objc_data_section_index = @intCast(u16, data_seg.sections.items.len); 848 try data_seg.addSection(self.allocator, "__objc_data", .{}); 849 } 850 851 break :blk .{ 852 .seg = self.data_segment_cmd_index.?, 853 .sect = self.objc_data_section_index.?, 854 }; 855 } else { 856 if (self.data_section_index == null) { 857 self.data_section_index = @intCast(u16, data_seg.sections.items.len); 858 try data_seg.addSection(self.allocator, "__data", .{}); 859 } 860 861 break :blk .{ 862 .seg = self.data_segment_cmd_index.?, 863 .sect = self.data_section_index.?, 864 }; 865 } 866 } 867 868 if (mem.eql(u8, "__LLVM", segname) and mem.eql(u8, "__asm", sectname)) { 869 log.debug("TODO LLVM asm section: type 0x{x}, name '{s},{s}'", .{ 870 sect.flags(), segname, sectname, 871 }); 872 } 873 874 break :blk null; 875 }, 876 else => break :blk null, 877 } 878 }; 879 880 return res; 881 } 882 883 fn sortSections(self: *Zld) !void { 884 var text_index_mapping = std.AutoHashMap(u16, u16).init(self.allocator); 885 defer text_index_mapping.deinit(); 886 var data_const_index_mapping = std.AutoHashMap(u16, u16).init(self.allocator); 887 defer data_const_index_mapping.deinit(); 888 var data_index_mapping = std.AutoHashMap(u16, u16).init(self.allocator); 889 defer data_index_mapping.deinit(); 890 891 { 892 // __TEXT segment 893 const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; 894 var sections = seg.sections.toOwnedSlice(self.allocator); 895 defer self.allocator.free(sections); 896 try seg.sections.ensureCapacity(self.allocator, sections.len); 897 898 const indices = &[_]*?u16{ 899 &self.text_section_index, 900 &self.stubs_section_index, 901 &self.stub_helper_section_index, 902 &self.gcc_except_tab_section_index, 903 &self.cstring_section_index, 904 &self.ustring_section_index, 905 &self.text_const_section_index, 906 &self.objc_methname_section_index, 907 &self.objc_methtype_section_index, 908 &self.objc_classname_section_index, 909 &self.eh_frame_section_index, 910 }; 911 for (indices) |maybe_index| { 912 const new_index: u16 = if (maybe_index.*) |index| blk: { 913 const idx = @intCast(u16, seg.sections.items.len); 914 seg.sections.appendAssumeCapacity(sections[index]); 915 try text_index_mapping.putNoClobber(index, idx); 916 break :blk idx; 917 } else continue; 918 maybe_index.* = new_index; 919 } 920 } 921 922 { 923 // __DATA_CONST segment 924 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; 925 var sections = seg.sections.toOwnedSlice(self.allocator); 926 defer self.allocator.free(sections); 927 try seg.sections.ensureCapacity(self.allocator, sections.len); 928 929 const indices = &[_]*?u16{ 930 &self.got_section_index, 931 &self.mod_init_func_section_index, 932 &self.mod_term_func_section_index, 933 &self.data_const_section_index, 934 &self.objc_cfstring_section_index, 935 &self.objc_classlist_section_index, 936 &self.objc_imageinfo_section_index, 937 }; 938 for (indices) |maybe_index| { 939 const new_index: u16 = if (maybe_index.*) |index| blk: { 940 const idx = @intCast(u16, seg.sections.items.len); 941 seg.sections.appendAssumeCapacity(sections[index]); 942 try data_const_index_mapping.putNoClobber(index, idx); 943 break :blk idx; 944 } else continue; 945 maybe_index.* = new_index; 946 } 947 } 948 949 { 950 // __DATA segment 951 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; 952 var sections = seg.sections.toOwnedSlice(self.allocator); 953 defer self.allocator.free(sections); 954 try seg.sections.ensureCapacity(self.allocator, sections.len); 955 956 // __DATA segment 957 const indices = &[_]*?u16{ 958 &self.la_symbol_ptr_section_index, 959 &self.objc_const_section_index, 960 &self.objc_selrefs_section_index, 961 &self.objc_classrefs_section_index, 962 &self.objc_data_section_index, 963 &self.data_section_index, 964 &self.tlv_section_index, 965 &self.tlv_data_section_index, 966 &self.tlv_bss_section_index, 967 &self.bss_section_index, 968 &self.common_section_index, 969 }; 970 for (indices) |maybe_index| { 971 const new_index: u16 = if (maybe_index.*) |index| blk: { 972 const idx = @intCast(u16, seg.sections.items.len); 973 seg.sections.appendAssumeCapacity(sections[index]); 974 try data_index_mapping.putNoClobber(index, idx); 975 break :blk idx; 976 } else continue; 977 maybe_index.* = new_index; 978 } 979 } 980 981 for (self.objects.items) |object| { 982 for (object.sections.items) |*sect| { 983 const target_map = sect.target_map orelse continue; 984 985 const new_index = blk: { 986 if (self.text_segment_cmd_index.? == target_map.segment_id) { 987 break :blk text_index_mapping.get(target_map.section_id) orelse unreachable; 988 } else if (self.data_const_segment_cmd_index.? == target_map.segment_id) { 989 break :blk data_const_index_mapping.get(target_map.section_id) orelse unreachable; 990 } else if (self.data_segment_cmd_index.? == target_map.segment_id) { 991 break :blk data_index_mapping.get(target_map.section_id) orelse unreachable; 992 } else unreachable; 993 }; 994 995 log.debug("remapping in {s}: '{s},{s}': {} => {}", .{ 996 object.name.?, 997 parseName(§.inner.segname), 998 parseName(§.inner.sectname), 999 target_map.section_id, 1000 new_index, 1001 }); 1002 1003 sect.target_map = .{ 1004 .segment_id = target_map.segment_id, 1005 .section_id = new_index, 1006 .offset = target_map.offset, 1007 }; 1008 } 1009 } 1010 } 1011 1012 fn allocateTextSegment(self: *Zld) !void { 1013 const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; 1014 const nstubs = @intCast(u32, self.stubs.items.len); 1015 1016 const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].Segment.inner.vmsize; 1017 seg.inner.fileoff = 0; 1018 seg.inner.vmaddr = base_vmaddr; 1019 1020 // Set stubs and stub_helper sizes 1021 const stubs = &seg.sections.items[self.stubs_section_index.?]; 1022 const stub_helper = &seg.sections.items[self.stub_helper_section_index.?]; 1023 stubs.size += nstubs * stubs.reserved2; 1024 1025 const stub_size: u4 = switch (self.arch.?) { 1026 .x86_64 => 10, 1027 .aarch64 => 3 * @sizeOf(u32), 1028 else => unreachable, 1029 }; 1030 stub_helper.size += nstubs * stub_size; 1031 1032 var sizeofcmds: u64 = 0; 1033 for (self.load_commands.items) |lc| { 1034 sizeofcmds += lc.cmdsize(); 1035 } 1036 1037 try self.allocateSegment(self.text_segment_cmd_index.?, @sizeOf(macho.mach_header_64) + sizeofcmds); 1038 1039 // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments. 1040 var min_alignment: u32 = 0; 1041 for (seg.sections.items) |sect| { 1042 const alignment = try math.powi(u32, 2, sect.@"align"); 1043 min_alignment = math.max(min_alignment, alignment); 1044 } 1045 1046 assert(min_alignment > 0); 1047 const last_sect_idx = seg.sections.items.len - 1; 1048 const last_sect = seg.sections.items[last_sect_idx]; 1049 const shift: u32 = blk: { 1050 const diff = seg.inner.filesize - last_sect.offset - last_sect.size; 1051 const factor = @divTrunc(diff, min_alignment); 1052 break :blk @intCast(u32, factor * min_alignment); 1053 }; 1054 1055 if (shift > 0) { 1056 for (seg.sections.items) |*sect| { 1057 sect.offset += shift; 1058 sect.addr += shift; 1059 } 1060 } 1061 } 1062 1063 fn allocateDataConstSegment(self: *Zld) !void { 1064 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; 1065 const nentries = @intCast(u32, self.got_entries.items.len); 1066 1067 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; 1068 seg.inner.fileoff = text_seg.inner.fileoff + text_seg.inner.filesize; 1069 seg.inner.vmaddr = text_seg.inner.vmaddr + text_seg.inner.vmsize; 1070 1071 // Set got size 1072 const got = &seg.sections.items[self.got_section_index.?]; 1073 got.size += nentries * @sizeOf(u64); 1074 1075 try self.allocateSegment(self.data_const_segment_cmd_index.?, 0); 1076 } 1077 1078 fn allocateDataSegment(self: *Zld) !void { 1079 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; 1080 const nstubs = @intCast(u32, self.stubs.items.len); 1081 1082 const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; 1083 seg.inner.fileoff = data_const_seg.inner.fileoff + data_const_seg.inner.filesize; 1084 seg.inner.vmaddr = data_const_seg.inner.vmaddr + data_const_seg.inner.vmsize; 1085 1086 // Set la_symbol_ptr and data size 1087 const la_symbol_ptr = &seg.sections.items[self.la_symbol_ptr_section_index.?]; 1088 const data = &seg.sections.items[self.data_section_index.?]; 1089 la_symbol_ptr.size += nstubs * @sizeOf(u64); 1090 data.size += @sizeOf(u64); // We need at least 8bytes for address of dyld_stub_binder 1091 1092 try self.allocateSegment(self.data_segment_cmd_index.?, 0); 1093 } 1094 1095 fn allocateLinkeditSegment(self: *Zld) void { 1096 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; 1097 const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; 1098 seg.inner.fileoff = data_seg.inner.fileoff + data_seg.inner.filesize; 1099 seg.inner.vmaddr = data_seg.inner.vmaddr + data_seg.inner.vmsize; 1100 } 1101 1102 fn allocateSegment(self: *Zld, index: u16, offset: u64) !void { 1103 const seg = &self.load_commands.items[index].Segment; 1104 1105 // Allocate the sections according to their alignment at the beginning of the segment. 1106 var start: u64 = offset; 1107 for (seg.sections.items) |*sect| { 1108 const alignment = try math.powi(u32, 2, sect.@"align"); 1109 const start_aligned = mem.alignForwardGeneric(u64, start, alignment); 1110 const end_aligned = mem.alignForwardGeneric(u64, start_aligned + sect.size, alignment); 1111 sect.offset = @intCast(u32, seg.inner.fileoff + start_aligned); 1112 sect.addr = seg.inner.vmaddr + start_aligned; 1113 start = end_aligned; 1114 } 1115 1116 const seg_size_aligned = mem.alignForwardGeneric(u64, start, self.page_size.?); 1117 seg.inner.filesize = seg_size_aligned; 1118 seg.inner.vmsize = seg_size_aligned; 1119 } 1120 1121 fn allocateSymbols(self: *Zld) !void { 1122 for (self.objects.items) |object| { 1123 for (object.symbols.items) |sym| { 1124 const reg = sym.cast(Symbol.Regular) orelse continue; 1125 1126 const source_sect = &object.sections.items[reg.section]; 1127 const target_map = source_sect.target_map orelse { 1128 log.debug("section '{s},{s}' not mapped for symbol '{s}'", .{ 1129 parseName(&source_sect.inner.segname), 1130 parseName(&source_sect.inner.sectname), 1131 sym.name, 1132 }); 1133 continue; 1134 }; 1135 1136 const target_seg = self.load_commands.items[target_map.segment_id].Segment; 1137 const target_sect = target_seg.sections.items[target_map.section_id]; 1138 const target_addr = target_sect.addr + target_map.offset; 1139 const address = reg.address - source_sect.inner.addr + target_addr; 1140 1141 log.debug("resolving symbol '{s}' at 0x{x}", .{ sym.name, address }); 1142 1143 // TODO there might be a more generic way of doing this. 1144 var section: u8 = 0; 1145 for (self.load_commands.items) |cmd, cmd_id| { 1146 if (cmd != .Segment) break; 1147 if (cmd_id == target_map.segment_id) { 1148 section += @intCast(u8, target_map.section_id) + 1; 1149 break; 1150 } 1151 section += @intCast(u8, cmd.Segment.sections.items.len); 1152 } 1153 1154 reg.address = address; 1155 reg.section = section; 1156 } 1157 } 1158 } 1159 1160 fn allocateTentativeSymbols(self: *Zld) !void { 1161 if (self.tentatives.values().len == 0) return; 1162 1163 const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; 1164 const common_sect = &data_seg.sections.items[self.common_section_index.?]; 1165 1166 const alignment = try math.powi(u32, 2, common_sect.@"align"); 1167 var base_address: u64 = common_sect.addr + self.tentative_defs_offset; 1168 1169 log.debug("base address for tentative definitions 0x{x}", .{base_address}); 1170 1171 // TODO there might be a more generic way of doing this. 1172 var section: u8 = 0; 1173 for (self.load_commands.items) |cmd, cmd_id| { 1174 if (cmd != .Segment) break; 1175 if (cmd_id == self.data_segment_cmd_index.?) { 1176 section += @intCast(u8, self.common_section_index.?) + 1; 1177 break; 1178 } 1179 section += @intCast(u8, cmd.Segment.sections.items.len); 1180 } 1181 1182 // Convert tentative definitions into regular symbols. 1183 for (self.tentatives.values()) |sym| { 1184 const tent = sym.cast(Symbol.Tentative) orelse unreachable; 1185 const reg = try self.allocator.create(Symbol.Regular); 1186 errdefer self.allocator.destroy(reg); 1187 1188 reg.* = .{ 1189 .base = .{ 1190 .@"type" = .regular, 1191 .name = try self.allocator.dupe(u8, tent.base.name), 1192 .got_index = tent.base.got_index, 1193 .stubs_index = tent.base.stubs_index, 1194 }, 1195 .linkage = .global, 1196 .address = base_address, 1197 .section = section, 1198 .weak_ref = false, 1199 .file = tent.file, 1200 .stab = .{ 1201 .kind = .global, 1202 .size = 0, 1203 }, 1204 }; 1205 1206 try self.globals.putNoClobber(self.allocator, reg.base.name, ®.base); 1207 tent.base.alias = ®.base; 1208 1209 if (tent.base.got_index) |idx| { 1210 self.got_entries.items[idx] = ®.base; 1211 } 1212 if (tent.base.stubs_index) |idx| { 1213 self.stubs.items[idx] = ®.base; 1214 } 1215 1216 const address = mem.alignForwardGeneric(u64, base_address + tent.size, alignment); 1217 1218 log.debug("tentative definition '{s}' allocated from 0x{x} to 0x{x}", .{ 1219 tent.base.name, 1220 base_address, 1221 address, 1222 }); 1223 1224 base_address = address; 1225 } 1226 } 1227 1228 fn allocateProxyBindAddresses(self: *Zld) !void { 1229 for (self.objects.items) |object| { 1230 for (object.sections.items) |sect| { 1231 const relocs = sect.relocs orelse continue; 1232 1233 for (relocs) |rel| { 1234 if (rel.@"type" != .unsigned) continue; // GOT is currently special-cased 1235 if (rel.target != .symbol) continue; 1236 1237 const sym = rel.target.symbol.getTopmostAlias(); 1238 if (sym.cast(Symbol.Proxy)) |proxy| { 1239 const target_map = sect.target_map orelse continue; 1240 const target_seg = self.load_commands.items[target_map.segment_id].Segment; 1241 const target_sect = target_seg.sections.items[target_map.section_id]; 1242 1243 try proxy.bind_info.append(self.allocator, .{ 1244 .segment_id = target_map.segment_id, 1245 .address = target_sect.addr + target_map.offset + rel.offset, 1246 }); 1247 } 1248 } 1249 } 1250 } 1251 } 1252 1253 fn writeStubHelperCommon(self: *Zld) !void { 1254 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; 1255 const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?]; 1256 const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; 1257 const got = &data_const_segment.sections.items[self.got_section_index.?]; 1258 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; 1259 const data = &data_segment.sections.items[self.data_section_index.?]; 1260 1261 self.stub_helper_stubs_start_off = blk: { 1262 switch (self.arch.?) { 1263 .x86_64 => { 1264 const code_size = 15; 1265 var code: [code_size]u8 = undefined; 1266 // lea %r11, [rip + disp] 1267 code[0] = 0x4c; 1268 code[1] = 0x8d; 1269 code[2] = 0x1d; 1270 { 1271 const target_addr = data.addr + data.size - @sizeOf(u64); 1272 const displacement = try math.cast(u32, target_addr - stub_helper.addr - 7); 1273 mem.writeIntLittle(u32, code[3..7], displacement); 1274 } 1275 // push %r11 1276 code[7] = 0x41; 1277 code[8] = 0x53; 1278 // jmp [rip + disp] 1279 code[9] = 0xff; 1280 code[10] = 0x25; 1281 { 1282 const dyld_stub_binder = self.imports.get("dyld_stub_binder").?; 1283 const addr = (got.addr + dyld_stub_binder.got_index.? * @sizeOf(u64)); 1284 const displacement = try math.cast(u32, addr - stub_helper.addr - code_size); 1285 mem.writeIntLittle(u32, code[11..], displacement); 1286 } 1287 try self.file.?.pwriteAll(&code, stub_helper.offset); 1288 break :blk stub_helper.offset + code_size; 1289 }, 1290 .aarch64 => { 1291 var code: [6 * @sizeOf(u32)]u8 = undefined; 1292 data_blk_outer: { 1293 const this_addr = stub_helper.addr; 1294 const target_addr = data.addr + data.size - @sizeOf(u64); 1295 data_blk: { 1296 const displacement = math.cast(i21, target_addr - this_addr) catch break :data_blk; 1297 // adr x17, disp 1298 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, displacement).toU32()); 1299 // nop 1300 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32()); 1301 break :data_blk_outer; 1302 } 1303 data_blk: { 1304 const new_this_addr = this_addr + @sizeOf(u32); 1305 const displacement = math.cast(i21, target_addr - new_this_addr) catch break :data_blk; 1306 // nop 1307 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32()); 1308 // adr x17, disp 1309 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.adr(.x17, displacement).toU32()); 1310 break :data_blk_outer; 1311 } 1312 // Jump is too big, replace adr with adrp and add. 1313 const this_page = @intCast(i32, this_addr >> 12); 1314 const target_page = @intCast(i32, target_addr >> 12); 1315 const pages = @intCast(i21, target_page - this_page); 1316 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x17, pages).toU32()); 1317 const narrowed = @truncate(u12, target_addr); 1318 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.add(.x17, .x17, narrowed, false).toU32()); 1319 } 1320 // stp x16, x17, [sp, #-16]! 1321 code[8] = 0xf0; 1322 code[9] = 0x47; 1323 code[10] = 0xbf; 1324 code[11] = 0xa9; 1325 binder_blk_outer: { 1326 const dyld_stub_binder = self.imports.get("dyld_stub_binder").?; 1327 const this_addr = stub_helper.addr + 3 * @sizeOf(u32); 1328 const target_addr = (got.addr + dyld_stub_binder.got_index.? * @sizeOf(u64)); 1329 binder_blk: { 1330 const displacement = math.divExact(u64, target_addr - this_addr, 4) catch break :binder_blk; 1331 const literal = math.cast(u18, displacement) catch break :binder_blk; 1332 // ldr x16, label 1333 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.ldr(.x16, .{ 1334 .literal = literal, 1335 }).toU32()); 1336 // nop 1337 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.nop().toU32()); 1338 break :binder_blk_outer; 1339 } 1340 binder_blk: { 1341 const new_this_addr = this_addr + @sizeOf(u32); 1342 const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch break :binder_blk; 1343 const literal = math.cast(u18, displacement) catch break :binder_blk; 1344 // Pad with nop to please division. 1345 // nop 1346 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.nop().toU32()); 1347 // ldr x16, label 1348 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{ 1349 .literal = literal, 1350 }).toU32()); 1351 break :binder_blk_outer; 1352 } 1353 // Use adrp followed by ldr(immediate). 1354 const this_page = @intCast(i32, this_addr >> 12); 1355 const target_page = @intCast(i32, target_addr >> 12); 1356 const pages = @intCast(i21, target_page - this_page); 1357 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.adrp(.x16, pages).toU32()); 1358 const narrowed = @truncate(u12, target_addr); 1359 const offset = try math.divExact(u12, narrowed, 8); 1360 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{ 1361 .register = .{ 1362 .rn = .x16, 1363 .offset = aarch64.Instruction.LoadStoreOffset.imm(offset), 1364 }, 1365 }).toU32()); 1366 } 1367 // br x16 1368 code[20] = 0x00; 1369 code[21] = 0x02; 1370 code[22] = 0x1f; 1371 code[23] = 0xd6; 1372 try self.file.?.pwriteAll(&code, stub_helper.offset); 1373 break :blk stub_helper.offset + 6 * @sizeOf(u32); 1374 }, 1375 else => unreachable, 1376 } 1377 }; 1378 1379 for (self.stubs.items) |sym| { 1380 // TODO weak bound pointers 1381 const index = sym.stubs_index orelse unreachable; 1382 try self.writeLazySymbolPointer(index); 1383 try self.writeStub(index); 1384 try self.writeStubInStubHelper(index); 1385 } 1386 } 1387 1388 fn writeLazySymbolPointer(self: *Zld, index: u32) !void { 1389 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; 1390 const stub_helper = text_segment.sections.items[self.stub_helper_section_index.?]; 1391 const data_segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment; 1392 const la_symbol_ptr = data_segment.sections.items[self.la_symbol_ptr_section_index.?]; 1393 1394 const stub_size: u4 = switch (self.arch.?) { 1395 .x86_64 => 10, 1396 .aarch64 => 3 * @sizeOf(u32), 1397 else => unreachable, 1398 }; 1399 const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size; 1400 const end = stub_helper.addr + stub_off - stub_helper.offset; 1401 var buf: [@sizeOf(u64)]u8 = undefined; 1402 mem.writeIntLittle(u64, &buf, end); 1403 const off = la_symbol_ptr.offset + index * @sizeOf(u64); 1404 log.debug("writing lazy symbol pointer entry 0x{x} at 0x{x}", .{ end, off }); 1405 try self.file.?.pwriteAll(&buf, off); 1406 } 1407 1408 fn writeStub(self: *Zld, index: u32) !void { 1409 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; 1410 const stubs = text_segment.sections.items[self.stubs_section_index.?]; 1411 const data_segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment; 1412 const la_symbol_ptr = data_segment.sections.items[self.la_symbol_ptr_section_index.?]; 1413 1414 const stub_off = stubs.offset + index * stubs.reserved2; 1415 const stub_addr = stubs.addr + index * stubs.reserved2; 1416 const la_ptr_addr = la_symbol_ptr.addr + index * @sizeOf(u64); 1417 log.debug("writing stub at 0x{x}", .{stub_off}); 1418 var code = try self.allocator.alloc(u8, stubs.reserved2); 1419 defer self.allocator.free(code); 1420 switch (self.arch.?) { 1421 .x86_64 => { 1422 assert(la_ptr_addr >= stub_addr + stubs.reserved2); 1423 const displacement = try math.cast(u32, la_ptr_addr - stub_addr - stubs.reserved2); 1424 // jmp 1425 code[0] = 0xff; 1426 code[1] = 0x25; 1427 mem.writeIntLittle(u32, code[2..][0..4], displacement); 1428 }, 1429 .aarch64 => { 1430 assert(la_ptr_addr >= stub_addr); 1431 outer: { 1432 const this_addr = stub_addr; 1433 const target_addr = la_ptr_addr; 1434 inner: { 1435 const displacement = math.divExact(u64, target_addr - this_addr, 4) catch break :inner; 1436 const literal = math.cast(u18, displacement) catch break :inner; 1437 // ldr x16, literal 1438 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{ 1439 .literal = literal, 1440 }).toU32()); 1441 // nop 1442 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32()); 1443 break :outer; 1444 } 1445 inner: { 1446 const new_this_addr = this_addr + @sizeOf(u32); 1447 const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch break :inner; 1448 const literal = math.cast(u18, displacement) catch break :inner; 1449 // nop 1450 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32()); 1451 // ldr x16, literal 1452 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(.x16, .{ 1453 .literal = literal, 1454 }).toU32()); 1455 break :outer; 1456 } 1457 // Use adrp followed by ldr(immediate). 1458 const this_page = @intCast(i32, this_addr >> 12); 1459 const target_page = @intCast(i32, target_addr >> 12); 1460 const pages = @intCast(i21, target_page - this_page); 1461 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x16, pages).toU32()); 1462 const narrowed = @truncate(u12, target_addr); 1463 const offset = try math.divExact(u12, narrowed, 8); 1464 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(.x16, .{ 1465 .register = .{ 1466 .rn = .x16, 1467 .offset = aarch64.Instruction.LoadStoreOffset.imm(offset), 1468 }, 1469 }).toU32()); 1470 } 1471 // br x16 1472 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.br(.x16).toU32()); 1473 }, 1474 else => unreachable, 1475 } 1476 try self.file.?.pwriteAll(code, stub_off); 1477 } 1478 1479 fn writeStubInStubHelper(self: *Zld, index: u32) !void { 1480 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; 1481 const stub_helper = text_segment.sections.items[self.stub_helper_section_index.?]; 1482 1483 const stub_size: u4 = switch (self.arch.?) { 1484 .x86_64 => 10, 1485 .aarch64 => 3 * @sizeOf(u32), 1486 else => unreachable, 1487 }; 1488 const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size; 1489 var code = try self.allocator.alloc(u8, stub_size); 1490 defer self.allocator.free(code); 1491 switch (self.arch.?) { 1492 .x86_64 => { 1493 const displacement = try math.cast( 1494 i32, 1495 @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - stub_size, 1496 ); 1497 // pushq 1498 code[0] = 0x68; 1499 mem.writeIntLittle(u32, code[1..][0..4], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`. 1500 // jmpq 1501 code[5] = 0xe9; 1502 mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement)); 1503 }, 1504 .aarch64 => { 1505 const displacement = try math.cast(i28, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4); 1506 const literal = @divExact(stub_size - @sizeOf(u32), 4); 1507 // ldr w16, literal 1508 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{ 1509 .literal = literal, 1510 }).toU32()); 1511 // b disp 1512 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(displacement).toU32()); 1513 mem.writeIntLittle(u32, code[8..12], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`. 1514 }, 1515 else => unreachable, 1516 } 1517 try self.file.?.pwriteAll(code, stub_off); 1518 } 1519 1520 fn resolveSymbolsInObject(self: *Zld, object: *Object) !void { 1521 log.debug("resolving symbols in '{s}'", .{object.name}); 1522 1523 for (object.symbols.items) |sym| { 1524 if (sym.cast(Symbol.Regular)) |reg| { 1525 if (reg.linkage == .translation_unit) continue; // Symbol local to TU. 1526 1527 if (self.tentatives.fetchSwapRemove(sym.name)) |kv| { 1528 // Create link to the global. 1529 kv.value.alias = sym; 1530 } 1531 if (self.unresolved.fetchSwapRemove(sym.name)) |kv| { 1532 // Create link to the global. 1533 kv.value.alias = sym; 1534 } 1535 const sym_ptr = self.globals.getPtr(sym.name) orelse { 1536 // Put new global symbol into the symbol table. 1537 try self.globals.putNoClobber(self.allocator, sym.name, sym); 1538 continue; 1539 }; 1540 const g_sym = sym_ptr.*; 1541 const g_reg = g_sym.cast(Symbol.Regular) orelse unreachable; 1542 1543 switch (g_reg.linkage) { 1544 .translation_unit => unreachable, 1545 .linkage_unit => { 1546 if (reg.linkage == .linkage_unit) { 1547 // Create link to the first encountered linkage_unit symbol. 1548 sym.alias = g_sym; 1549 continue; 1550 } 1551 }, 1552 .global => { 1553 if (reg.linkage == .global) { 1554 log.debug("symbol '{s}' defined multiple times", .{reg.base.name}); 1555 return error.MultipleSymbolDefinitions; 1556 } 1557 sym.alias = g_sym; 1558 continue; 1559 }, 1560 } 1561 1562 g_sym.alias = sym; 1563 sym_ptr.* = sym; 1564 } else if (sym.cast(Symbol.Tentative)) |tent| { 1565 if (self.globals.get(sym.name)) |g_sym| { 1566 sym.alias = g_sym; 1567 continue; 1568 } 1569 1570 if (self.unresolved.fetchSwapRemove(sym.name)) |kv| { 1571 kv.value.alias = sym; 1572 } 1573 1574 const sym_ptr = self.tentatives.getPtr(sym.name) orelse { 1575 // Put new tentative definition symbol into symbol table. 1576 try self.tentatives.putNoClobber(self.allocator, sym.name, sym); 1577 continue; 1578 }; 1579 1580 // Compare by size and pick the largest tentative definition. 1581 // We model this like a heap where the tentative definition with the 1582 // largest size always washes up on top. 1583 const t_sym = sym_ptr.*; 1584 const t_tent = t_sym.cast(Symbol.Tentative) orelse unreachable; 1585 1586 if (tent.size < t_tent.size) { 1587 sym.alias = t_sym; 1588 continue; 1589 } 1590 1591 t_sym.alias = sym; 1592 sym_ptr.* = sym; 1593 } else if (sym.cast(Symbol.Unresolved)) |_| { 1594 if (self.globals.get(sym.name)) |g_sym| { 1595 sym.alias = g_sym; 1596 continue; 1597 } 1598 if (self.tentatives.get(sym.name)) |t_sym| { 1599 sym.alias = t_sym; 1600 continue; 1601 } 1602 if (self.unresolved.get(sym.name)) |u_sym| { 1603 sym.alias = u_sym; 1604 continue; 1605 } 1606 1607 try self.unresolved.putNoClobber(self.allocator, sym.name, sym); 1608 } else unreachable; 1609 } 1610 } 1611 1612 fn resolveSymbols(self: *Zld) !void { 1613 // First pass, resolve symbols in provided objects. 1614 for (self.objects.items) |object| { 1615 try self.resolveSymbolsInObject(object); 1616 } 1617 1618 // Second pass, resolve symbols in static libraries. 1619 var next_sym: usize = 0; 1620 while (true) { 1621 if (next_sym == self.unresolved.count()) break; 1622 1623 const sym = self.unresolved.values()[next_sym]; 1624 1625 var reset: bool = false; 1626 for (self.archives.items) |archive| { 1627 // Check if the entry exists in a static archive. 1628 const offsets = archive.toc.get(sym.name) orelse { 1629 // No hit. 1630 continue; 1631 }; 1632 assert(offsets.items.len > 0); 1633 1634 const object = try archive.parseObject(offsets.items[0]); 1635 try self.objects.append(self.allocator, object); 1636 try self.resolveSymbolsInObject(object); 1637 1638 reset = true; 1639 break; 1640 } 1641 1642 if (reset) { 1643 next_sym = 0; 1644 } else { 1645 next_sym += 1; 1646 } 1647 } 1648 1649 // Third pass, resolve symbols in dynamic libraries. 1650 var unresolved = std.ArrayList(*Symbol).init(self.allocator); 1651 defer unresolved.deinit(); 1652 1653 try unresolved.ensureCapacity(self.unresolved.count()); 1654 for (self.unresolved.values()) |value| { 1655 unresolved.appendAssumeCapacity(value); 1656 } 1657 self.unresolved.clearRetainingCapacity(); 1658 1659 var referenced = std.AutoHashMap(*Dylib, void).init(self.allocator); 1660 defer referenced.deinit(); 1661 1662 loop: while (unresolved.popOrNull()) |undef| { 1663 const proxy = self.imports.get(undef.name) orelse outer: { 1664 const proxy = inner: { 1665 for (self.dylibs.items) |dylib, i| { 1666 const proxy = (try dylib.createProxy(undef.name)) orelse continue; 1667 if (self.libsystem_dylib_index.? != @intCast(u16, i)) { // LibSystem gets load command seperately. 1668 try referenced.put(dylib, {}); 1669 } 1670 break :inner proxy; 1671 } 1672 if (mem.eql(u8, undef.name, "___dso_handle")) { 1673 // TODO this is just a temp patch until I work out what to actually 1674 // do with ___dso_handle and __mh_execute_header symbols which are 1675 // synthetically created by the linker on macOS. 1676 const name = try self.allocator.dupe(u8, undef.name); 1677 const proxy = try self.allocator.create(Symbol.Proxy); 1678 errdefer self.allocator.destroy(proxy); 1679 proxy.* = .{ 1680 .base = .{ 1681 .@"type" = .proxy, 1682 .name = name, 1683 }, 1684 .file = null, 1685 }; 1686 break :inner &proxy.base; 1687 } 1688 1689 self.unresolved.putAssumeCapacityNoClobber(undef.name, undef); 1690 continue :loop; 1691 }; 1692 1693 try self.imports.putNoClobber(self.allocator, proxy.name, proxy); 1694 break :outer proxy; 1695 }; 1696 undef.alias = proxy; 1697 } 1698 1699 // Add LC_LOAD_DYLIB load command for each referenced dylib/stub. 1700 var it = referenced.iterator(); 1701 while (it.next()) |entry| { 1702 const dylib = entry.key_ptr.*; 1703 dylib.ordinal = self.next_dylib_ordinal; 1704 const dylib_id = dylib.id orelse unreachable; 1705 var dylib_cmd = try createLoadDylibCommand( 1706 self.allocator, 1707 dylib_id.name, 1708 dylib_id.timestamp, 1709 dylib_id.current_version, 1710 dylib_id.compatibility_version, 1711 ); 1712 errdefer dylib_cmd.deinit(self.allocator); 1713 try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd }); 1714 self.next_dylib_ordinal += 1; 1715 } 1716 1717 if (self.unresolved.count() > 0) { 1718 for (self.unresolved.values()) |undef| { 1719 log.err("undefined reference to symbol '{s}'", .{undef.name}); 1720 log.err(" | referenced in {s}", .{ 1721 undef.cast(Symbol.Unresolved).?.file.name.?, 1722 }); 1723 } 1724 1725 return error.UndefinedSymbolReference; 1726 } 1727 1728 // Finally put dyld_stub_binder as an Import 1729 const libsystem_dylib = self.dylibs.items[self.libsystem_dylib_index.?]; 1730 const proxy = (try libsystem_dylib.createProxy("dyld_stub_binder")) orelse { 1731 log.err("undefined reference to symbol 'dyld_stub_binder'", .{}); 1732 return error.UndefinedSymbolReference; 1733 }; 1734 try self.imports.putNoClobber(self.allocator, proxy.name, proxy); 1735 } 1736 1737 fn resolveStubsAndGotEntries(self: *Zld) !void { 1738 for (self.objects.items) |object| { 1739 log.debug("resolving stubs and got entries from {s}", .{object.name}); 1740 1741 for (object.sections.items) |sect| { 1742 const relocs = sect.relocs orelse continue; 1743 for (relocs) |rel| { 1744 switch (rel.@"type") { 1745 .unsigned => continue, 1746 .got_page, .got_page_off, .got_load, .got, .pointer_to_got => { 1747 const sym = rel.target.symbol.getTopmostAlias(); 1748 if (sym.got_index != null) continue; 1749 1750 const index = @intCast(u32, self.got_entries.items.len); 1751 sym.got_index = index; 1752 try self.got_entries.append(self.allocator, sym); 1753 1754 log.debug(" | found GOT entry {s}: {*}", .{ sym.name, sym }); 1755 }, 1756 else => { 1757 if (rel.target != .symbol) continue; 1758 1759 const sym = rel.target.symbol.getTopmostAlias(); 1760 assert(sym.@"type" != .unresolved); 1761 1762 if (sym.stubs_index != null) continue; 1763 if (sym.@"type" != .proxy) continue; 1764 1765 const index = @intCast(u32, self.stubs.items.len); 1766 sym.stubs_index = index; 1767 try self.stubs.append(self.allocator, sym); 1768 1769 log.debug(" | found stub {s}: {*}", .{ sym.name, sym }); 1770 }, 1771 } 1772 } 1773 } 1774 } 1775 1776 // Finally, put dyld_stub_binder as the final GOT entry 1777 const sym = self.imports.get("dyld_stub_binder") orelse unreachable; 1778 const index = @intCast(u32, self.got_entries.items.len); 1779 sym.got_index = index; 1780 try self.got_entries.append(self.allocator, sym); 1781 1782 log.debug(" | found GOT entry {s}: {*}", .{ sym.name, sym }); 1783 } 1784 1785 fn resolveRelocsAndWriteSections(self: *Zld) !void { 1786 for (self.objects.items) |object| { 1787 log.debug("relocating object {s}", .{object.name}); 1788 1789 for (object.sections.items) |sect| { 1790 if (sect.inner.flags == macho.S_MOD_INIT_FUNC_POINTERS or 1791 sect.inner.flags == macho.S_MOD_TERM_FUNC_POINTERS) continue; 1792 1793 const segname = parseName(§.inner.segname); 1794 const sectname = parseName(§.inner.sectname); 1795 1796 log.debug("relocating section '{s},{s}'", .{ segname, sectname }); 1797 1798 // Get target mapping 1799 const target_map = sect.target_map orelse { 1800 log.debug("no mapping for '{s},{s}'; skipping", .{ segname, sectname }); 1801 continue; 1802 }; 1803 const target_seg = self.load_commands.items[target_map.segment_id].Segment; 1804 const target_sect = target_seg.sections.items[target_map.section_id]; 1805 const target_sect_addr = target_sect.addr + target_map.offset; 1806 const target_sect_off = target_sect.offset + target_map.offset; 1807 1808 if (sect.relocs) |relocs| { 1809 for (relocs) |rel| { 1810 const source_addr = target_sect_addr + rel.offset; 1811 1812 var args: reloc.Relocation.ResolveArgs = .{ 1813 .source_addr = source_addr, 1814 .target_addr = undefined, 1815 }; 1816 1817 switch (rel.@"type") { 1818 .unsigned => { 1819 args.target_addr = try self.relocTargetAddr(object, rel.target); 1820 1821 const unsigned = rel.cast(reloc.Unsigned) orelse unreachable; 1822 if (unsigned.subtractor) |subtractor| { 1823 args.subtractor = try self.relocTargetAddr(object, subtractor); 1824 } 1825 if (rel.target == .section) { 1826 const source_sect = object.sections.items[rel.target.section]; 1827 args.source_source_sect_addr = sect.inner.addr; 1828 args.source_target_sect_addr = source_sect.inner.addr; 1829 } 1830 1831 const flags = @truncate(u8, target_sect.flags & 0xff); 1832 const should_rebase = rebase: { 1833 if (!unsigned.is_64bit) break :rebase false; 1834 1835 // TODO actually, a check similar to what dyld is doing, that is, verifying 1836 // that the segment is writable should be enough here. 1837 const is_right_segment = blk: { 1838 if (self.data_segment_cmd_index) |idx| { 1839 if (target_map.segment_id == idx) { 1840 break :blk true; 1841 } 1842 } 1843 if (self.data_const_segment_cmd_index) |idx| { 1844 if (target_map.segment_id == idx) { 1845 break :blk true; 1846 } 1847 } 1848 break :blk false; 1849 }; 1850 1851 if (!is_right_segment) break :rebase false; 1852 if (flags != macho.S_LITERAL_POINTERS and 1853 flags != macho.S_REGULAR) 1854 { 1855 break :rebase false; 1856 } 1857 if (rel.target == .symbol) { 1858 const final = rel.target.symbol.getTopmostAlias(); 1859 if (final.cast(Symbol.Proxy)) |_| { 1860 break :rebase false; 1861 } 1862 } 1863 1864 break :rebase true; 1865 }; 1866 1867 if (should_rebase) { 1868 try self.local_rebases.append(self.allocator, .{ 1869 .offset = source_addr - target_seg.inner.vmaddr, 1870 .segment_id = target_map.segment_id, 1871 }); 1872 } 1873 1874 // TLV is handled via a separate offset mechanism. 1875 // Calculate the offset to the initializer. 1876 if (flags == macho.S_THREAD_LOCAL_VARIABLES) tlv: { 1877 // TODO we don't want to save offset to tlv_bootstrap 1878 if (mem.eql(u8, rel.target.symbol.name, "__tlv_bootstrap")) break :tlv; 1879 1880 const base_addr = blk: { 1881 if (self.tlv_data_section_index) |index| { 1882 const tlv_data = target_seg.sections.items[index]; 1883 break :blk tlv_data.addr; 1884 } else { 1885 const tlv_bss = target_seg.sections.items[self.tlv_bss_section_index.?]; 1886 break :blk tlv_bss.addr; 1887 } 1888 }; 1889 // Since we require TLV data to always preceed TLV bss section, we calculate 1890 // offsets wrt to the former if it is defined; otherwise, wrt to the latter. 1891 try self.threadlocal_offsets.append(self.allocator, .{ 1892 .source_addr = args.source_addr, 1893 .offset = args.target_addr - base_addr, 1894 }); 1895 } 1896 }, 1897 .got_page, .got_page_off, .got_load, .got, .pointer_to_got => { 1898 const dc_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; 1899 const got = dc_seg.sections.items[self.got_section_index.?]; 1900 const final = rel.target.symbol.getTopmostAlias(); 1901 const got_index = final.got_index orelse { 1902 log.err("expected GOT index relocating symbol '{s}'", .{final.name}); 1903 log.err("this is an internal linker error", .{}); 1904 return error.FailedToResolveRelocationTarget; 1905 }; 1906 args.target_addr = got.addr + got_index * @sizeOf(u64); 1907 }, 1908 else => |tt| { 1909 if (tt == .signed and rel.target == .section) { 1910 const source_sect = object.sections.items[rel.target.section]; 1911 args.source_source_sect_addr = sect.inner.addr; 1912 args.source_target_sect_addr = source_sect.inner.addr; 1913 } 1914 args.target_addr = try self.relocTargetAddr(object, rel.target); 1915 }, 1916 } 1917 1918 try rel.resolve(args); 1919 } 1920 } 1921 1922 log.debug("writing contents of '{s},{s}' section from '{s}' from 0x{x} to 0x{x}", .{ 1923 segname, 1924 sectname, 1925 object.name, 1926 target_sect_off, 1927 target_sect_off + sect.code.len, 1928 }); 1929 1930 if (target_sect.flags == macho.S_ZEROFILL or 1931 target_sect.flags == macho.S_THREAD_LOCAL_ZEROFILL or 1932 target_sect.flags == macho.S_THREAD_LOCAL_VARIABLES) 1933 { 1934 log.debug("zeroing out '{s},{s}' from 0x{x} to 0x{x}", .{ 1935 parseName(&target_sect.segname), 1936 parseName(&target_sect.sectname), 1937 target_sect_off, 1938 target_sect_off + sect.code.len, 1939 }); 1940 1941 // Zero-out the space 1942 var zeroes = try self.allocator.alloc(u8, sect.code.len); 1943 defer self.allocator.free(zeroes); 1944 mem.set(u8, zeroes, 0); 1945 try self.file.?.pwriteAll(zeroes, target_sect_off); 1946 } else { 1947 try self.file.?.pwriteAll(sect.code, target_sect_off); 1948 } 1949 } 1950 } 1951 } 1952 1953 fn relocTargetAddr(self: *Zld, object: *const Object, target: reloc.Relocation.Target) !u64 { 1954 const target_addr = blk: { 1955 switch (target) { 1956 .symbol => |sym| { 1957 const final = sym.getTopmostAlias(); 1958 if (final.cast(Symbol.Regular)) |reg| { 1959 log.debug(" | regular '{s}'", .{sym.name}); 1960 break :blk reg.address; 1961 } else if (final.cast(Symbol.Proxy)) |proxy| { 1962 if (mem.eql(u8, sym.name, "__tlv_bootstrap")) { 1963 log.debug(" | symbol '__tlv_bootstrap'", .{}); 1964 const segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment; 1965 const tlv = segment.sections.items[self.tlv_section_index.?]; 1966 break :blk tlv.addr; 1967 } 1968 1969 log.debug(" | symbol stub '{s}'", .{sym.name}); 1970 const segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; 1971 const stubs = segment.sections.items[self.stubs_section_index.?]; 1972 const stubs_index = proxy.base.stubs_index orelse { 1973 if (proxy.bind_info.items.len > 0) { 1974 break :blk 0; // Dynamically bound by dyld. 1975 } 1976 log.err( 1977 "expected stubs index or dynamic bind address when relocating symbol '{s}'", 1978 .{final.name}, 1979 ); 1980 log.err("this is an internal linker error", .{}); 1981 return error.FailedToResolveRelocationTarget; 1982 }; 1983 break :blk stubs.addr + stubs_index * stubs.reserved2; 1984 } else { 1985 log.err("failed to resolve symbol '{s}' as a relocation target", .{sym.name}); 1986 log.err("this is an internal linker error", .{}); 1987 return error.FailedToResolveRelocationTarget; 1988 } 1989 }, 1990 .section => |sect_id| { 1991 log.debug(" | section offset", .{}); 1992 const source_sect = object.sections.items[sect_id]; 1993 log.debug(" | section '{s},{s}'", .{ 1994 parseName(&source_sect.inner.segname), 1995 parseName(&source_sect.inner.sectname), 1996 }); 1997 const target_map = source_sect.target_map orelse unreachable; 1998 const target_seg = self.load_commands.items[target_map.segment_id].Segment; 1999 const target_sect = target_seg.sections.items[target_map.section_id]; 2000 break :blk target_sect.addr + target_map.offset; 2001 }, 2002 } 2003 }; 2004 return target_addr; 2005 } 2006 2007 fn populateMetadata(self: *Zld) !void { 2008 if (self.pagezero_segment_cmd_index == null) { 2009 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len); 2010 try self.load_commands.append(self.allocator, .{ 2011 .Segment = SegmentCommand.empty("__PAGEZERO", .{ 2012 .vmsize = 0x100000000, // size always set to 4GB 2013 }), 2014 }); 2015 } 2016 2017 if (self.text_segment_cmd_index == null) { 2018 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len); 2019 try self.load_commands.append(self.allocator, .{ 2020 .Segment = SegmentCommand.empty("__TEXT", .{ 2021 .vmaddr = 0x100000000, // always starts at 4GB 2022 .maxprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE, 2023 .initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE, 2024 }), 2025 }); 2026 } 2027 2028 if (self.text_section_index == null) { 2029 const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; 2030 self.text_section_index = @intCast(u16, text_seg.sections.items.len); 2031 const alignment: u2 = switch (self.arch.?) { 2032 .x86_64 => 0, 2033 .aarch64 => 2, 2034 else => unreachable, // unhandled architecture type 2035 }; 2036 try text_seg.addSection(self.allocator, "__text", .{ 2037 .@"align" = alignment, 2038 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, 2039 }); 2040 } 2041 2042 if (self.stubs_section_index == null) { 2043 const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; 2044 self.stubs_section_index = @intCast(u16, text_seg.sections.items.len); 2045 const alignment: u2 = switch (self.arch.?) { 2046 .x86_64 => 0, 2047 .aarch64 => 2, 2048 else => unreachable, // unhandled architecture type 2049 }; 2050 const stub_size: u4 = switch (self.arch.?) { 2051 .x86_64 => 6, 2052 .aarch64 => 3 * @sizeOf(u32), 2053 else => unreachable, // unhandled architecture type 2054 }; 2055 try text_seg.addSection(self.allocator, "__stubs", .{ 2056 .@"align" = alignment, 2057 .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, 2058 .reserved2 = stub_size, 2059 }); 2060 } 2061 2062 if (self.stub_helper_section_index == null) { 2063 const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; 2064 self.stub_helper_section_index = @intCast(u16, text_seg.sections.items.len); 2065 const alignment: u2 = switch (self.arch.?) { 2066 .x86_64 => 0, 2067 .aarch64 => 2, 2068 else => unreachable, // unhandled architecture type 2069 }; 2070 const stub_helper_size: u6 = switch (self.arch.?) { 2071 .x86_64 => 15, 2072 .aarch64 => 6 * @sizeOf(u32), 2073 else => unreachable, 2074 }; 2075 try text_seg.addSection(self.allocator, "__stub_helper", .{ 2076 .size = stub_helper_size, 2077 .@"align" = alignment, 2078 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, 2079 }); 2080 } 2081 2082 if (self.data_const_segment_cmd_index == null) { 2083 self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len); 2084 try self.load_commands.append(self.allocator, .{ 2085 .Segment = SegmentCommand.empty("__DATA_CONST", .{ 2086 .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, 2087 .initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, 2088 }), 2089 }); 2090 } 2091 2092 if (self.got_section_index == null) { 2093 const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; 2094 self.got_section_index = @intCast(u16, data_const_seg.sections.items.len); 2095 try data_const_seg.addSection(self.allocator, "__got", .{ 2096 .@"align" = 3, // 2^3 = @sizeOf(u64) 2097 .flags = macho.S_NON_LAZY_SYMBOL_POINTERS, 2098 }); 2099 } 2100 2101 if (self.data_segment_cmd_index == null) { 2102 self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len); 2103 try self.load_commands.append(self.allocator, .{ 2104 .Segment = SegmentCommand.empty("__DATA", .{ 2105 .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, 2106 .initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, 2107 }), 2108 }); 2109 } 2110 2111 if (self.la_symbol_ptr_section_index == null) { 2112 const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; 2113 self.la_symbol_ptr_section_index = @intCast(u16, data_seg.sections.items.len); 2114 try data_seg.addSection(self.allocator, "__la_symbol_ptr", .{ 2115 .@"align" = 3, // 2^3 = @sizeOf(u64) 2116 .flags = macho.S_LAZY_SYMBOL_POINTERS, 2117 }); 2118 } 2119 2120 if (self.data_section_index == null) { 2121 const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; 2122 self.data_section_index = @intCast(u16, data_seg.sections.items.len); 2123 try data_seg.addSection(self.allocator, "__data", .{ 2124 .@"align" = 3, // 2^3 = @sizeOf(u64) 2125 }); 2126 } 2127 2128 if (self.linkedit_segment_cmd_index == null) { 2129 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len); 2130 try self.load_commands.append(self.allocator, .{ 2131 .Segment = SegmentCommand.empty("__LINKEDIT", .{ 2132 .maxprot = macho.VM_PROT_READ, 2133 .initprot = macho.VM_PROT_READ, 2134 }), 2135 }); 2136 } 2137 2138 if (self.dyld_info_cmd_index == null) { 2139 self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len); 2140 try self.load_commands.append(self.allocator, .{ 2141 .DyldInfoOnly = .{ 2142 .cmd = macho.LC_DYLD_INFO_ONLY, 2143 .cmdsize = @sizeOf(macho.dyld_info_command), 2144 .rebase_off = 0, 2145 .rebase_size = 0, 2146 .bind_off = 0, 2147 .bind_size = 0, 2148 .weak_bind_off = 0, 2149 .weak_bind_size = 0, 2150 .lazy_bind_off = 0, 2151 .lazy_bind_size = 0, 2152 .export_off = 0, 2153 .export_size = 0, 2154 }, 2155 }); 2156 } 2157 2158 if (self.symtab_cmd_index == null) { 2159 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len); 2160 try self.load_commands.append(self.allocator, .{ 2161 .Symtab = .{ 2162 .cmd = macho.LC_SYMTAB, 2163 .cmdsize = @sizeOf(macho.symtab_command), 2164 .symoff = 0, 2165 .nsyms = 0, 2166 .stroff = 0, 2167 .strsize = 0, 2168 }, 2169 }); 2170 try self.strtab.append(self.allocator, 0); 2171 } 2172 2173 if (self.dysymtab_cmd_index == null) { 2174 self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len); 2175 try self.load_commands.append(self.allocator, .{ 2176 .Dysymtab = .{ 2177 .cmd = macho.LC_DYSYMTAB, 2178 .cmdsize = @sizeOf(macho.dysymtab_command), 2179 .ilocalsym = 0, 2180 .nlocalsym = 0, 2181 .iextdefsym = 0, 2182 .nextdefsym = 0, 2183 .iundefsym = 0, 2184 .nundefsym = 0, 2185 .tocoff = 0, 2186 .ntoc = 0, 2187 .modtaboff = 0, 2188 .nmodtab = 0, 2189 .extrefsymoff = 0, 2190 .nextrefsyms = 0, 2191 .indirectsymoff = 0, 2192 .nindirectsyms = 0, 2193 .extreloff = 0, 2194 .nextrel = 0, 2195 .locreloff = 0, 2196 .nlocrel = 0, 2197 }, 2198 }); 2199 } 2200 2201 if (self.dylinker_cmd_index == null) { 2202 self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len); 2203 const cmdsize = @intCast(u32, mem.alignForwardGeneric( 2204 u64, 2205 @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH), 2206 @sizeOf(u64), 2207 )); 2208 var dylinker_cmd = emptyGenericCommandWithData(macho.dylinker_command{ 2209 .cmd = macho.LC_LOAD_DYLINKER, 2210 .cmdsize = cmdsize, 2211 .name = @sizeOf(macho.dylinker_command), 2212 }); 2213 dylinker_cmd.data = try self.allocator.alloc(u8, cmdsize - dylinker_cmd.inner.name); 2214 mem.set(u8, dylinker_cmd.data, 0); 2215 mem.copy(u8, dylinker_cmd.data, mem.spanZ(DEFAULT_DYLD_PATH)); 2216 try self.load_commands.append(self.allocator, .{ .Dylinker = dylinker_cmd }); 2217 } 2218 2219 if (self.main_cmd_index == null) { 2220 self.main_cmd_index = @intCast(u16, self.load_commands.items.len); 2221 try self.load_commands.append(self.allocator, .{ 2222 .Main = .{ 2223 .cmd = macho.LC_MAIN, 2224 .cmdsize = @sizeOf(macho.entry_point_command), 2225 .entryoff = 0x0, 2226 .stacksize = 0, 2227 }, 2228 }); 2229 } 2230 2231 if (self.source_version_cmd_index == null) { 2232 self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len); 2233 try self.load_commands.append(self.allocator, .{ 2234 .SourceVersion = .{ 2235 .cmd = macho.LC_SOURCE_VERSION, 2236 .cmdsize = @sizeOf(macho.source_version_command), 2237 .version = 0x0, 2238 }, 2239 }); 2240 } 2241 2242 if (self.uuid_cmd_index == null) { 2243 self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len); 2244 var uuid_cmd: macho.uuid_command = .{ 2245 .cmd = macho.LC_UUID, 2246 .cmdsize = @sizeOf(macho.uuid_command), 2247 .uuid = undefined, 2248 }; 2249 std.crypto.random.bytes(&uuid_cmd.uuid); 2250 try self.load_commands.append(self.allocator, .{ .Uuid = uuid_cmd }); 2251 } 2252 2253 if (self.code_signature_cmd_index == null and self.arch.? == .aarch64) { 2254 self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len); 2255 try self.load_commands.append(self.allocator, .{ 2256 .LinkeditData = .{ 2257 .cmd = macho.LC_CODE_SIGNATURE, 2258 .cmdsize = @sizeOf(macho.linkedit_data_command), 2259 .dataoff = 0, 2260 .datasize = 0, 2261 }, 2262 }); 2263 } 2264 2265 if (self.data_in_code_cmd_index == null and self.arch.? == .x86_64) { 2266 self.data_in_code_cmd_index = @intCast(u16, self.load_commands.items.len); 2267 try self.load_commands.append(self.allocator, .{ 2268 .LinkeditData = .{ 2269 .cmd = macho.LC_DATA_IN_CODE, 2270 .cmdsize = @sizeOf(macho.linkedit_data_command), 2271 .dataoff = 0, 2272 .datasize = 0, 2273 }, 2274 }); 2275 } 2276 } 2277 2278 fn addRpaths(self: *Zld, rpaths: []const []const u8) !void { 2279 for (rpaths) |rpath| { 2280 const cmdsize = @intCast(u32, mem.alignForwardGeneric( 2281 u64, 2282 @sizeOf(macho.rpath_command) + rpath.len + 1, 2283 @sizeOf(u64), 2284 )); 2285 var rpath_cmd = emptyGenericCommandWithData(macho.rpath_command{ 2286 .cmd = macho.LC_RPATH, 2287 .cmdsize = cmdsize, 2288 .path = @sizeOf(macho.rpath_command), 2289 }); 2290 rpath_cmd.data = try self.allocator.alloc(u8, cmdsize - rpath_cmd.inner.path); 2291 mem.set(u8, rpath_cmd.data, 0); 2292 mem.copy(u8, rpath_cmd.data, rpath); 2293 try self.load_commands.append(self.allocator, .{ .Rpath = rpath_cmd }); 2294 } 2295 } 2296 2297 fn flush(self: *Zld) !void { 2298 try self.writeStubHelperCommon(); 2299 try self.resolveRelocsAndWriteSections(); 2300 2301 if (self.common_section_index) |index| { 2302 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; 2303 const sect = &seg.sections.items[index]; 2304 sect.offset = 0; 2305 } 2306 2307 if (self.bss_section_index) |index| { 2308 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; 2309 const sect = &seg.sections.items[index]; 2310 sect.offset = 0; 2311 } 2312 2313 if (self.tlv_bss_section_index) |index| { 2314 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; 2315 const sect = &seg.sections.items[index]; 2316 sect.offset = 0; 2317 } 2318 2319 if (self.tlv_section_index) |index| { 2320 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; 2321 const sect = &seg.sections.items[index]; 2322 2323 var buffer = try self.allocator.alloc(u8, @intCast(usize, sect.size)); 2324 defer self.allocator.free(buffer); 2325 _ = try self.file.?.preadAll(buffer, sect.offset); 2326 2327 var stream = std.io.fixedBufferStream(buffer); 2328 var writer = stream.writer(); 2329 2330 std.sort.sort(TlvOffset, self.threadlocal_offsets.items, {}, TlvOffset.cmp); 2331 2332 const seek_amt = 2 * @sizeOf(u64); 2333 for (self.threadlocal_offsets.items) |tlv| { 2334 try writer.context.seekBy(seek_amt); 2335 try writer.writeIntLittle(u64, tlv.offset); 2336 } 2337 2338 try self.file.?.pwriteAll(buffer, sect.offset); 2339 } 2340 2341 if (self.mod_init_func_section_index) |index| { 2342 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; 2343 const sect = &seg.sections.items[index]; 2344 2345 var initializers = std.ArrayList(u64).init(self.allocator); 2346 defer initializers.deinit(); 2347 2348 for (self.objects.items) |object| { 2349 for (object.initializers.items) |initializer| { 2350 const address = initializer.cast(Symbol.Regular).?.address; 2351 try initializers.append(address); 2352 } 2353 } 2354 2355 _ = try self.file.?.pwriteAll(mem.sliceAsBytes(initializers.items), sect.offset); 2356 sect.size = @intCast(u32, initializers.items.len * @sizeOf(u64)); 2357 } 2358 2359 try self.writeGotEntries(); 2360 try self.setEntryPoint(); 2361 try self.writeRebaseInfoTable(); 2362 try self.writeBindInfoTable(); 2363 try self.writeLazyBindInfoTable(); 2364 try self.writeExportInfo(); 2365 if (self.arch.? == .x86_64) { 2366 try self.writeDataInCode(); 2367 } 2368 2369 { 2370 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; 2371 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; 2372 symtab.symoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); 2373 } 2374 2375 try self.writeDebugInfo(); 2376 try self.writeSymbolTable(); 2377 try self.writeStringTable(); 2378 2379 { 2380 // Seal __LINKEDIT size 2381 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; 2382 seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size.?); 2383 } 2384 2385 if (self.arch.? == .aarch64) { 2386 try self.writeCodeSignaturePadding(); 2387 } 2388 2389 try self.writeLoadCommands(); 2390 try self.writeHeader(); 2391 2392 if (self.arch.? == .aarch64) { 2393 try self.writeCodeSignature(); 2394 } 2395 2396 if (comptime std.Target.current.isDarwin() and std.Target.current.cpu.arch == .aarch64) { 2397 try fs.cwd().copyFile(self.out_path.?, fs.cwd(), self.out_path.?, .{}); 2398 } 2399 } 2400 2401 fn writeGotEntries(self: *Zld) !void { 2402 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; 2403 const sect = seg.sections.items[self.got_section_index.?]; 2404 2405 var buffer = try self.allocator.alloc(u8, self.got_entries.items.len * @sizeOf(u64)); 2406 defer self.allocator.free(buffer); 2407 2408 var stream = std.io.fixedBufferStream(buffer); 2409 var writer = stream.writer(); 2410 2411 for (self.got_entries.items) |sym| { 2412 const address: u64 = if (sym.cast(Symbol.Regular)) |reg| reg.address else 0; 2413 try writer.writeIntLittle(u64, address); 2414 } 2415 2416 log.debug("writing GOT pointers at 0x{x} to 0x{x}", .{ sect.offset, sect.offset + buffer.len }); 2417 2418 try self.file.?.pwriteAll(buffer, sect.offset); 2419 } 2420 2421 fn setEntryPoint(self: *Zld) !void { 2422 // TODO we should respect the -entry flag passed in by the user to set a custom 2423 // entrypoint. For now, assume default of `_main`. 2424 const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; 2425 const sym = self.globals.get("_main") orelse return error.MissingMainEntrypoint; 2426 const entry_sym = sym.cast(Symbol.Regular) orelse unreachable; 2427 const ec = &self.load_commands.items[self.main_cmd_index.?].Main; 2428 ec.entryoff = @intCast(u32, entry_sym.address - seg.inner.vmaddr); 2429 ec.stacksize = self.stack_size; 2430 } 2431 2432 fn writeRebaseInfoTable(self: *Zld) !void { 2433 var pointers = std.ArrayList(Pointer).init(self.allocator); 2434 defer pointers.deinit(); 2435 2436 try pointers.ensureCapacity(self.local_rebases.items.len); 2437 pointers.appendSliceAssumeCapacity(self.local_rebases.items); 2438 2439 if (self.got_section_index) |idx| { 2440 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; 2441 const sect = seg.sections.items[idx]; 2442 const base_offset = sect.addr - seg.inner.vmaddr; 2443 const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); 2444 2445 for (self.got_entries.items) |sym| { 2446 if (sym.@"type" == .proxy) continue; 2447 try pointers.append(.{ 2448 .offset = base_offset + sym.got_index.? * @sizeOf(u64), 2449 .segment_id = segment_id, 2450 }); 2451 } 2452 } 2453 2454 if (self.mod_init_func_section_index) |idx| { 2455 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; 2456 const sect = seg.sections.items[idx]; 2457 const base_offset = sect.addr - seg.inner.vmaddr; 2458 const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); 2459 2460 var index: u64 = 0; 2461 for (self.objects.items) |object| { 2462 for (object.initializers.items) |_| { 2463 try pointers.append(.{ 2464 .offset = base_offset + index * @sizeOf(u64), 2465 .segment_id = segment_id, 2466 }); 2467 index += 1; 2468 } 2469 } 2470 } 2471 2472 if (self.la_symbol_ptr_section_index) |idx| { 2473 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; 2474 const sect = seg.sections.items[idx]; 2475 const base_offset = sect.addr - seg.inner.vmaddr; 2476 const segment_id = @intCast(u16, self.data_segment_cmd_index.?); 2477 2478 try pointers.ensureCapacity(pointers.items.len + self.stubs.items.len); 2479 for (self.stubs.items) |sym| { 2480 pointers.appendAssumeCapacity(.{ 2481 .offset = base_offset + sym.stubs_index.? * @sizeOf(u64), 2482 .segment_id = segment_id, 2483 }); 2484 } 2485 } 2486 2487 std.sort.sort(Pointer, pointers.items, {}, pointerCmp); 2488 2489 const size = try rebaseInfoSize(pointers.items); 2490 var buffer = try self.allocator.alloc(u8, @intCast(usize, size)); 2491 defer self.allocator.free(buffer); 2492 2493 var stream = std.io.fixedBufferStream(buffer); 2494 try writeRebaseInfo(pointers.items, stream.writer()); 2495 2496 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; 2497 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; 2498 dyld_info.rebase_off = @intCast(u32, seg.inner.fileoff); 2499 dyld_info.rebase_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @sizeOf(u64))); 2500 seg.inner.filesize += dyld_info.rebase_size; 2501 2502 log.debug("writing rebase info from 0x{x} to 0x{x}", .{ dyld_info.rebase_off, dyld_info.rebase_off + dyld_info.rebase_size }); 2503 2504 try self.file.?.pwriteAll(buffer, dyld_info.rebase_off); 2505 } 2506 2507 fn writeBindInfoTable(self: *Zld) !void { 2508 var pointers = std.ArrayList(Pointer).init(self.allocator); 2509 defer pointers.deinit(); 2510 2511 if (self.got_section_index) |idx| { 2512 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; 2513 const sect = seg.sections.items[idx]; 2514 const base_offset = sect.addr - seg.inner.vmaddr; 2515 const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); 2516 2517 for (self.got_entries.items) |sym| { 2518 if (sym.cast(Symbol.Proxy)) |proxy| { 2519 try pointers.append(.{ 2520 .offset = base_offset + proxy.base.got_index.? * @sizeOf(u64), 2521 .segment_id = segment_id, 2522 .dylib_ordinal = proxy.dylibOrdinal(), 2523 .name = proxy.base.name, 2524 }); 2525 } 2526 } 2527 } 2528 2529 for (self.imports.values()) |sym| { 2530 if (sym.cast(Symbol.Proxy)) |proxy| { 2531 for (proxy.bind_info.items) |info| { 2532 const seg = self.load_commands.items[info.segment_id].Segment; 2533 try pointers.append(.{ 2534 .offset = info.address - seg.inner.vmaddr, 2535 .segment_id = info.segment_id, 2536 .dylib_ordinal = proxy.dylibOrdinal(), 2537 .name = proxy.base.name, 2538 }); 2539 } 2540 } 2541 } 2542 2543 if (self.tlv_section_index) |idx| { 2544 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; 2545 const sect = seg.sections.items[idx]; 2546 const base_offset = sect.addr - seg.inner.vmaddr; 2547 const segment_id = @intCast(u16, self.data_segment_cmd_index.?); 2548 2549 const sym = self.imports.get("__tlv_bootstrap") orelse unreachable; 2550 const proxy = sym.cast(Symbol.Proxy) orelse unreachable; 2551 2552 try pointers.append(.{ 2553 .offset = base_offset, 2554 .segment_id = segment_id, 2555 .dylib_ordinal = proxy.dylibOrdinal(), 2556 .name = proxy.base.name, 2557 }); 2558 } 2559 2560 const size = try bindInfoSize(pointers.items); 2561 var buffer = try self.allocator.alloc(u8, @intCast(usize, size)); 2562 defer self.allocator.free(buffer); 2563 2564 var stream = std.io.fixedBufferStream(buffer); 2565 try writeBindInfo(pointers.items, stream.writer()); 2566 2567 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; 2568 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; 2569 dyld_info.bind_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); 2570 dyld_info.bind_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64))); 2571 seg.inner.filesize += dyld_info.bind_size; 2572 2573 log.debug("writing binding info from 0x{x} to 0x{x}", .{ dyld_info.bind_off, dyld_info.bind_off + dyld_info.bind_size }); 2574 2575 try self.file.?.pwriteAll(buffer, dyld_info.bind_off); 2576 } 2577 2578 fn writeLazyBindInfoTable(self: *Zld) !void { 2579 var pointers = std.ArrayList(Pointer).init(self.allocator); 2580 defer pointers.deinit(); 2581 2582 if (self.la_symbol_ptr_section_index) |idx| { 2583 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; 2584 const sect = seg.sections.items[idx]; 2585 const base_offset = sect.addr - seg.inner.vmaddr; 2586 const segment_id = @intCast(u16, self.data_segment_cmd_index.?); 2587 2588 try pointers.ensureCapacity(self.stubs.items.len); 2589 2590 for (self.stubs.items) |sym| { 2591 const proxy = sym.cast(Symbol.Proxy) orelse unreachable; 2592 pointers.appendAssumeCapacity(.{ 2593 .offset = base_offset + sym.stubs_index.? * @sizeOf(u64), 2594 .segment_id = segment_id, 2595 .dylib_ordinal = proxy.dylibOrdinal(), 2596 .name = sym.name, 2597 }); 2598 } 2599 } 2600 2601 const size = try lazyBindInfoSize(pointers.items); 2602 var buffer = try self.allocator.alloc(u8, @intCast(usize, size)); 2603 defer self.allocator.free(buffer); 2604 2605 var stream = std.io.fixedBufferStream(buffer); 2606 try writeLazyBindInfo(pointers.items, stream.writer()); 2607 2608 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; 2609 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; 2610 dyld_info.lazy_bind_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); 2611 dyld_info.lazy_bind_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64))); 2612 seg.inner.filesize += dyld_info.lazy_bind_size; 2613 2614 log.debug("writing lazy binding info from 0x{x} to 0x{x}", .{ dyld_info.lazy_bind_off, dyld_info.lazy_bind_off + dyld_info.lazy_bind_size }); 2615 2616 try self.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off); 2617 try self.populateLazyBindOffsetsInStubHelper(buffer); 2618 } 2619 2620 fn populateLazyBindOffsetsInStubHelper(self: *Zld, buffer: []const u8) !void { 2621 var stream = std.io.fixedBufferStream(buffer); 2622 var reader = stream.reader(); 2623 var offsets = std.ArrayList(u32).init(self.allocator); 2624 try offsets.append(0); 2625 defer offsets.deinit(); 2626 var valid_block = false; 2627 2628 while (true) { 2629 const inst = reader.readByte() catch |err| switch (err) { 2630 error.EndOfStream => break, 2631 else => return err, 2632 }; 2633 const opcode: u8 = inst & macho.BIND_OPCODE_MASK; 2634 2635 switch (opcode) { 2636 macho.BIND_OPCODE_DO_BIND => { 2637 valid_block = true; 2638 }, 2639 macho.BIND_OPCODE_DONE => { 2640 if (valid_block) { 2641 const offset = try stream.getPos(); 2642 try offsets.append(@intCast(u32, offset)); 2643 } 2644 valid_block = false; 2645 }, 2646 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => { 2647 var next = try reader.readByte(); 2648 while (next != @as(u8, 0)) { 2649 next = try reader.readByte(); 2650 } 2651 }, 2652 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => { 2653 _ = try leb.readULEB128(u64, reader); 2654 }, 2655 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => { 2656 _ = try leb.readULEB128(u64, reader); 2657 }, 2658 macho.BIND_OPCODE_SET_ADDEND_SLEB => { 2659 _ = try leb.readILEB128(i64, reader); 2660 }, 2661 else => {}, 2662 } 2663 } 2664 assert(self.stubs.items.len <= offsets.items.len); 2665 2666 const stub_size: u4 = switch (self.arch.?) { 2667 .x86_64 => 10, 2668 .aarch64 => 3 * @sizeOf(u32), 2669 else => unreachable, 2670 }; 2671 const off: u4 = switch (self.arch.?) { 2672 .x86_64 => 1, 2673 .aarch64 => 2 * @sizeOf(u32), 2674 else => unreachable, 2675 }; 2676 var buf: [@sizeOf(u32)]u8 = undefined; 2677 for (self.stubs.items) |sym| { 2678 const index = sym.stubs_index orelse unreachable; 2679 const placeholder_off = self.stub_helper_stubs_start_off.? + index * stub_size + off; 2680 mem.writeIntLittle(u32, &buf, offsets.items[index]); 2681 try self.file.?.pwriteAll(&buf, placeholder_off); 2682 } 2683 } 2684 2685 fn writeExportInfo(self: *Zld) !void { 2686 var trie = Trie.init(self.allocator); 2687 defer trie.deinit(); 2688 2689 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; 2690 2691 // TODO export items for dylibs 2692 const sym = self.globals.get("_main") orelse return error.MissingMainEntrypoint; 2693 const reg = sym.cast(Symbol.Regular) orelse unreachable; 2694 assert(reg.address >= text_segment.inner.vmaddr); 2695 2696 try trie.put(.{ 2697 .name = sym.name, 2698 .vmaddr_offset = reg.address - text_segment.inner.vmaddr, 2699 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR, 2700 }); 2701 2702 try trie.finalize(); 2703 2704 var buffer = try self.allocator.alloc(u8, @intCast(usize, trie.size)); 2705 defer self.allocator.free(buffer); 2706 2707 var stream = std.io.fixedBufferStream(buffer); 2708 const nwritten = try trie.write(stream.writer()); 2709 assert(nwritten == trie.size); 2710 2711 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; 2712 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; 2713 dyld_info.export_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); 2714 dyld_info.export_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64))); 2715 seg.inner.filesize += dyld_info.export_size; 2716 2717 log.debug("writing export info from 0x{x} to 0x{x}", .{ dyld_info.export_off, dyld_info.export_off + dyld_info.export_size }); 2718 2719 try self.file.?.pwriteAll(buffer, dyld_info.export_off); 2720 } 2721 2722 fn writeDebugInfo(self: *Zld) !void { 2723 var stabs = std.ArrayList(macho.nlist_64).init(self.allocator); 2724 defer stabs.deinit(); 2725 2726 for (self.objects.items) |object| { 2727 const tu_path = object.tu_path orelse continue; 2728 const tu_mtime = object.tu_mtime orelse continue; 2729 _ = tu_mtime; 2730 const dirname = std.fs.path.dirname(tu_path) orelse "./"; 2731 // Current dir 2732 try stabs.append(.{ 2733 .n_strx = try self.makeString(tu_path[0 .. dirname.len + 1]), 2734 .n_type = macho.N_SO, 2735 .n_sect = 0, 2736 .n_desc = 0, 2737 .n_value = 0, 2738 }); 2739 // Artifact name 2740 try stabs.append(.{ 2741 .n_strx = try self.makeString(tu_path[dirname.len + 1 ..]), 2742 .n_type = macho.N_SO, 2743 .n_sect = 0, 2744 .n_desc = 0, 2745 .n_value = 0, 2746 }); 2747 // Path to object file with debug info 2748 try stabs.append(.{ 2749 .n_strx = try self.makeString(object.name.?), 2750 .n_type = macho.N_OSO, 2751 .n_sect = 0, 2752 .n_desc = 1, 2753 .n_value = 0, //tu_mtime, TODO figure out why precalculated mtime value doesn't work 2754 }); 2755 2756 for (object.symbols.items) |sym| { 2757 const reg = reg: { 2758 switch (sym.@"type") { 2759 .regular => break :reg sym.cast(Symbol.Regular) orelse unreachable, 2760 .tentative => { 2761 const final = sym.getTopmostAlias().cast(Symbol.Regular) orelse unreachable; 2762 if (object != final.file) continue; 2763 break :reg final; 2764 }, 2765 else => continue, 2766 } 2767 }; 2768 2769 if (reg.isTemp() or reg.stab == null) continue; 2770 const stab = reg.stab orelse unreachable; 2771 2772 switch (stab.kind) { 2773 .function => { 2774 try stabs.append(.{ 2775 .n_strx = 0, 2776 .n_type = macho.N_BNSYM, 2777 .n_sect = reg.section, 2778 .n_desc = 0, 2779 .n_value = reg.address, 2780 }); 2781 try stabs.append(.{ 2782 .n_strx = try self.makeString(sym.name), 2783 .n_type = macho.N_FUN, 2784 .n_sect = reg.section, 2785 .n_desc = 0, 2786 .n_value = reg.address, 2787 }); 2788 try stabs.append(.{ 2789 .n_strx = 0, 2790 .n_type = macho.N_FUN, 2791 .n_sect = 0, 2792 .n_desc = 0, 2793 .n_value = stab.size, 2794 }); 2795 try stabs.append(.{ 2796 .n_strx = 0, 2797 .n_type = macho.N_ENSYM, 2798 .n_sect = reg.section, 2799 .n_desc = 0, 2800 .n_value = stab.size, 2801 }); 2802 }, 2803 .global => { 2804 try stabs.append(.{ 2805 .n_strx = try self.makeString(sym.name), 2806 .n_type = macho.N_GSYM, 2807 .n_sect = 0, 2808 .n_desc = 0, 2809 .n_value = 0, 2810 }); 2811 }, 2812 .static => { 2813 try stabs.append(.{ 2814 .n_strx = try self.makeString(sym.name), 2815 .n_type = macho.N_STSYM, 2816 .n_sect = reg.section, 2817 .n_desc = 0, 2818 .n_value = reg.address, 2819 }); 2820 }, 2821 } 2822 } 2823 2824 // Close the source file! 2825 try stabs.append(.{ 2826 .n_strx = 0, 2827 .n_type = macho.N_SO, 2828 .n_sect = 0, 2829 .n_desc = 0, 2830 .n_value = 0, 2831 }); 2832 } 2833 2834 if (stabs.items.len == 0) return; 2835 2836 // Write stabs into the symbol table 2837 const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; 2838 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; 2839 2840 symtab.nsyms = @intCast(u32, stabs.items.len); 2841 2842 const stabs_off = symtab.symoff; 2843 const stabs_size = symtab.nsyms * @sizeOf(macho.nlist_64); 2844 log.debug("writing symbol stabs from 0x{x} to 0x{x}", .{ stabs_off, stabs_size + stabs_off }); 2845 try self.file.?.pwriteAll(mem.sliceAsBytes(stabs.items), stabs_off); 2846 2847 linkedit.inner.filesize += stabs_size; 2848 2849 // Update dynamic symbol table. 2850 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; 2851 dysymtab.nlocalsym = symtab.nsyms; 2852 } 2853 2854 fn writeSymbolTable(self: *Zld) !void { 2855 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; 2856 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; 2857 2858 var locals = std.ArrayList(macho.nlist_64).init(self.allocator); 2859 defer locals.deinit(); 2860 2861 var exports = std.ArrayList(macho.nlist_64).init(self.allocator); 2862 defer exports.deinit(); 2863 2864 for (self.objects.items) |object| { 2865 for (object.symbols.items) |sym| { 2866 const final = sym.getTopmostAlias(); 2867 if (final.@"type" != .regular) continue; 2868 2869 const reg = final.cast(Symbol.Regular) orelse unreachable; 2870 if (reg.isTemp()) continue; 2871 if (reg.visited) continue; 2872 2873 switch (reg.linkage) { 2874 .translation_unit => { 2875 try locals.append(.{ 2876 .n_strx = try self.makeString(sym.name), 2877 .n_type = macho.N_SECT, 2878 .n_sect = reg.section, 2879 .n_desc = 0, 2880 .n_value = reg.address, 2881 }); 2882 }, 2883 else => { 2884 try exports.append(.{ 2885 .n_strx = try self.makeString(sym.name), 2886 .n_type = macho.N_SECT | macho.N_EXT, 2887 .n_sect = reg.section, 2888 .n_desc = 0, 2889 .n_value = reg.address, 2890 }); 2891 }, 2892 } 2893 2894 reg.visited = true; 2895 } 2896 } 2897 2898 var undefs = std.ArrayList(macho.nlist_64).init(self.allocator); 2899 defer undefs.deinit(); 2900 2901 for (self.imports.values()) |sym| { 2902 const proxy = sym.cast(Symbol.Proxy) orelse unreachable; 2903 try undefs.append(.{ 2904 .n_strx = try self.makeString(sym.name), 2905 .n_type = macho.N_UNDF | macho.N_EXT, 2906 .n_sect = 0, 2907 .n_desc = (proxy.dylibOrdinal() * macho.N_SYMBOL_RESOLVER) | macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY, 2908 .n_value = 0, 2909 }); 2910 } 2911 2912 const nlocals = locals.items.len; 2913 const nexports = exports.items.len; 2914 const nundefs = undefs.items.len; 2915 2916 const locals_off = symtab.symoff + symtab.nsyms * @sizeOf(macho.nlist_64); 2917 const locals_size = nlocals * @sizeOf(macho.nlist_64); 2918 log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off }); 2919 try self.file.?.pwriteAll(mem.sliceAsBytes(locals.items), locals_off); 2920 2921 const exports_off = locals_off + locals_size; 2922 const exports_size = nexports * @sizeOf(macho.nlist_64); 2923 log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off }); 2924 try self.file.?.pwriteAll(mem.sliceAsBytes(exports.items), exports_off); 2925 2926 const undefs_off = exports_off + exports_size; 2927 const undefs_size = nundefs * @sizeOf(macho.nlist_64); 2928 log.debug("writing undefined symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off }); 2929 try self.file.?.pwriteAll(mem.sliceAsBytes(undefs.items), undefs_off); 2930 2931 symtab.nsyms += @intCast(u32, nlocals + nexports + nundefs); 2932 seg.inner.filesize += locals_size + exports_size + undefs_size; 2933 2934 // Update dynamic symbol table. 2935 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; 2936 dysymtab.nlocalsym += @intCast(u32, nlocals); 2937 dysymtab.iextdefsym = dysymtab.nlocalsym; 2938 dysymtab.nextdefsym = @intCast(u32, nexports); 2939 dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym; 2940 dysymtab.nundefsym = @intCast(u32, nundefs); 2941 2942 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; 2943 const stubs = &text_segment.sections.items[self.stubs_section_index.?]; 2944 const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; 2945 const got = &data_const_segment.sections.items[self.got_section_index.?]; 2946 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; 2947 const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?]; 2948 2949 const nstubs = @intCast(u32, self.stubs.items.len); 2950 const ngot_entries = @intCast(u32, self.got_entries.items.len); 2951 2952 dysymtab.indirectsymoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); 2953 dysymtab.nindirectsyms = nstubs * 2 + ngot_entries; 2954 2955 const needed_size = dysymtab.nindirectsyms * @sizeOf(u32); 2956 seg.inner.filesize += needed_size; 2957 2958 log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ 2959 dysymtab.indirectsymoff, 2960 dysymtab.indirectsymoff + needed_size, 2961 }); 2962 2963 var buf = try self.allocator.alloc(u8, needed_size); 2964 defer self.allocator.free(buf); 2965 2966 var stream = std.io.fixedBufferStream(buf); 2967 var writer = stream.writer(); 2968 2969 stubs.reserved1 = 0; 2970 for (self.stubs.items) |sym| { 2971 const id = self.imports.getIndex(sym.name) orelse unreachable; 2972 try writer.writeIntLittle(u32, dysymtab.iundefsym + @intCast(u32, id)); 2973 } 2974 2975 got.reserved1 = nstubs; 2976 for (self.got_entries.items) |sym| { 2977 if (sym.@"type" == .proxy) { 2978 const id = self.imports.getIndex(sym.name) orelse unreachable; 2979 try writer.writeIntLittle(u32, dysymtab.iundefsym + @intCast(u32, id)); 2980 } else { 2981 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL); 2982 } 2983 } 2984 2985 la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries; 2986 for (self.stubs.items) |sym| { 2987 const id = self.imports.getIndex(sym.name) orelse unreachable; 2988 try writer.writeIntLittle(u32, dysymtab.iundefsym + @intCast(u32, id)); 2989 } 2990 2991 try self.file.?.pwriteAll(buf, dysymtab.indirectsymoff); 2992 } 2993 2994 fn writeStringTable(self: *Zld) !void { 2995 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; 2996 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; 2997 symtab.stroff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); 2998 symtab.strsize = @intCast(u32, mem.alignForwardGeneric(u64, self.strtab.items.len, @alignOf(u64))); 2999 seg.inner.filesize += symtab.strsize; 3000 3001 log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize }); 3002 3003 try self.file.?.pwriteAll(self.strtab.items, symtab.stroff); 3004 3005 if (symtab.strsize > self.strtab.items.len and self.arch.? == .x86_64) { 3006 // This is the last section, so we need to pad it out. 3007 try self.file.?.pwriteAll(&[_]u8{0}, seg.inner.fileoff + seg.inner.filesize - 1); 3008 } 3009 } 3010 3011 fn writeDataInCode(self: *Zld) !void { 3012 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; 3013 const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].LinkeditData; 3014 const fileoff = seg.inner.fileoff + seg.inner.filesize; 3015 3016 var buf = std.ArrayList(u8).init(self.allocator); 3017 defer buf.deinit(); 3018 3019 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; 3020 const text_sect = text_seg.sections.items[self.text_section_index.?]; 3021 for (self.objects.items) |object| { 3022 const source_sect = object.sections.items[object.text_section_index.?]; 3023 const target_map = source_sect.target_map orelse continue; 3024 3025 try buf.ensureCapacity( 3026 buf.items.len + object.data_in_code_entries.items.len * @sizeOf(macho.data_in_code_entry), 3027 ); 3028 for (object.data_in_code_entries.items) |dice| { 3029 const new_dice: macho.data_in_code_entry = .{ 3030 .offset = text_sect.offset + target_map.offset + dice.offset, 3031 .length = dice.length, 3032 .kind = dice.kind, 3033 }; 3034 buf.appendSliceAssumeCapacity(mem.asBytes(&new_dice)); 3035 } 3036 } 3037 const datasize = @intCast(u32, buf.items.len); 3038 3039 dice_cmd.dataoff = @intCast(u32, fileoff); 3040 dice_cmd.datasize = datasize; 3041 seg.inner.filesize += datasize; 3042 3043 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ fileoff, fileoff + datasize }); 3044 3045 try self.file.?.pwriteAll(buf.items, fileoff); 3046 } 3047 3048 fn writeCodeSignaturePadding(self: *Zld) !void { 3049 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; 3050 const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData; 3051 const fileoff = seg.inner.fileoff + seg.inner.filesize; 3052 const needed_size = CodeSignature.calcCodeSignaturePaddingSize( 3053 self.out_path.?, 3054 fileoff, 3055 self.page_size.?, 3056 ); 3057 code_sig_cmd.dataoff = @intCast(u32, fileoff); 3058 code_sig_cmd.datasize = needed_size; 3059 3060 // Advance size of __LINKEDIT segment 3061 seg.inner.filesize += needed_size; 3062 seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size.?); 3063 3064 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ fileoff, fileoff + needed_size }); 3065 3066 // Pad out the space. We need to do this to calculate valid hashes for everything in the file 3067 // except for code signature data. 3068 try self.file.?.pwriteAll(&[_]u8{0}, fileoff + needed_size - 1); 3069 } 3070 3071 fn writeCodeSignature(self: *Zld) !void { 3072 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; 3073 const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData; 3074 3075 var code_sig = CodeSignature.init(self.allocator, self.page_size.?); 3076 defer code_sig.deinit(); 3077 try code_sig.calcAdhocSignature( 3078 self.file.?, 3079 self.out_path.?, 3080 text_seg.inner, 3081 code_sig_cmd, 3082 .Exe, 3083 ); 3084 3085 var buffer = try self.allocator.alloc(u8, code_sig.size()); 3086 defer self.allocator.free(buffer); 3087 var stream = std.io.fixedBufferStream(buffer); 3088 try code_sig.write(stream.writer()); 3089 3090 log.debug("writing code signature from 0x{x} to 0x{x}", .{ code_sig_cmd.dataoff, code_sig_cmd.dataoff + buffer.len }); 3091 try self.file.?.pwriteAll(buffer, code_sig_cmd.dataoff); 3092 } 3093 3094 fn writeLoadCommands(self: *Zld) !void { 3095 var sizeofcmds: u32 = 0; 3096 for (self.load_commands.items) |lc| { 3097 sizeofcmds += lc.cmdsize(); 3098 } 3099 3100 var buffer = try self.allocator.alloc(u8, sizeofcmds); 3101 defer self.allocator.free(buffer); 3102 var writer = std.io.fixedBufferStream(buffer).writer(); 3103 for (self.load_commands.items) |lc| { 3104 try lc.write(writer); 3105 } 3106 3107 const off = @sizeOf(macho.mach_header_64); 3108 log.debug("writing {} load commands from 0x{x} to 0x{x}", .{ self.load_commands.items.len, off, off + sizeofcmds }); 3109 try self.file.?.pwriteAll(buffer, off); 3110 } 3111 3112 fn writeHeader(self: *Zld) !void { 3113 var header: macho.mach_header_64 = undefined; 3114 header.magic = macho.MH_MAGIC_64; 3115 3116 const CpuInfo = struct { 3117 cpu_type: macho.cpu_type_t, 3118 cpu_subtype: macho.cpu_subtype_t, 3119 }; 3120 3121 const cpu_info: CpuInfo = switch (self.arch.?) { 3122 .aarch64 => .{ 3123 .cpu_type = macho.CPU_TYPE_ARM64, 3124 .cpu_subtype = macho.CPU_SUBTYPE_ARM_ALL, 3125 }, 3126 .x86_64 => .{ 3127 .cpu_type = macho.CPU_TYPE_X86_64, 3128 .cpu_subtype = macho.CPU_SUBTYPE_X86_64_ALL, 3129 }, 3130 else => return error.UnsupportedCpuArchitecture, 3131 }; 3132 header.cputype = cpu_info.cpu_type; 3133 header.cpusubtype = cpu_info.cpu_subtype; 3134 header.filetype = macho.MH_EXECUTE; 3135 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL; 3136 header.reserved = 0; 3137 3138 if (self.tlv_section_index) |_| 3139 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS; 3140 3141 header.ncmds = @intCast(u32, self.load_commands.items.len); 3142 header.sizeofcmds = 0; 3143 for (self.load_commands.items) |cmd| { 3144 header.sizeofcmds += cmd.cmdsize(); 3145 } 3146 log.debug("writing Mach-O header {}", .{header}); 3147 try self.file.?.pwriteAll(mem.asBytes(&header), 0); 3148 } 3149 3150 fn makeString(self: *Zld, bytes: []const u8) !u32 { 3151 if (self.strtab_dir.get(bytes)) |offset| { 3152 log.debug("reusing '{s}' from string table at offset 0x{x}", .{ bytes, offset }); 3153 return offset; 3154 } 3155 3156 try self.strtab.ensureCapacity(self.allocator, self.strtab.items.len + bytes.len + 1); 3157 const offset = @intCast(u32, self.strtab.items.len); 3158 log.debug("writing new string '{s}' into string table at offset 0x{x}", .{ bytes, offset }); 3159 self.strtab.appendSliceAssumeCapacity(bytes); 3160 self.strtab.appendAssumeCapacity(0); 3161 try self.strtab_dir.putNoClobber(self.allocator, try self.allocator.dupe(u8, bytes), offset); 3162 return offset; 3163 } 3164 3165 fn getString(self: *const Zld, str_off: u32) []const u8 { 3166 assert(str_off < self.strtab.items.len); 3167 return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + str_off)); 3168 } 3169 3170 pub fn parseName(name: *const [16]u8) []const u8 { 3171 const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len; 3172 return name[0..len]; 3173 } 3174 3175 fn printSymbols(self: *Zld) void { 3176 log.debug("globals", .{}); 3177 for (self.globals.values()) |value| { 3178 const sym = value.cast(Symbol.Regular) orelse unreachable; 3179 log.debug(" | {s} @ {*}", .{ sym.base.name, value }); 3180 log.debug(" => alias of {*}", .{sym.base.alias}); 3181 log.debug(" => linkage {s}", .{sym.linkage}); 3182 log.debug(" => defined in {s}", .{sym.file.name.?}); 3183 } 3184 for (self.objects.items) |object| { 3185 log.debug("locals in {s}", .{object.name.?}); 3186 for (object.symbols.items) |sym| { 3187 log.debug(" | {s} @ {*}", .{ sym.name, sym }); 3188 log.debug(" => alias of {*}", .{sym.alias}); 3189 if (sym.cast(Symbol.Regular)) |reg| { 3190 log.debug(" => linkage {s}", .{reg.linkage}); 3191 } else { 3192 log.debug(" => unresolved", .{}); 3193 } 3194 } 3195 } 3196 log.debug("proxies", .{}); 3197 for (self.imports.values()) |value| { 3198 const sym = value.cast(Symbol.Proxy) orelse unreachable; 3199 log.debug(" | {s} @ {*}", .{ sym.base.name, value }); 3200 log.debug(" => alias of {*}", .{sym.base.alias}); 3201 log.debug(" => defined in libSystem.B.dylib", .{}); 3202 } 3203 }