| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | | base: File, |
| 1 | base: link.File, |
| 2 | 2 | |
| 3 | 3 | /// If this is not null, an object file is created by LLVM and emitted to zcu_object_sub_path. |
| 4 | 4 | llvm_object: ?*LlvmObject = null, |
| ... | ... | @@ -6,6 +6,27 @@ llvm_object: ?*LlvmObject = null, |
| 6 | 6 | /// Debug symbols bundle (or dSym). |
| 7 | 7 | d_sym: ?DebugSymbols = null, |
| 8 | 8 | |
| 9 | /// A list of all input files. |
| 10 | /// Index of each input file also encodes the priority or precedence of one input file |
| 11 | /// over another. |
| 12 | files: std.MultiArrayList(File.Entry) = .{}, |
| 13 | internal_object: ?File.Index = null, |
| 14 | objects: std.ArrayListUnmanaged(File.Index) = .{}, |
| 15 | dylibs: std.ArrayListUnmanaged(File.Index) = .{}, |
| 16 | |
| 17 | segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{}, |
| 18 | sections: std.MultiArrayList(Section) = .{}, |
| 19 | |
| 20 | symbols: std.ArrayListUnmanaged(Symbol) = .{}, |
| 21 | symbols_extra: std.ArrayListUnmanaged(u32) = .{}, |
| 22 | globals: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .{}, |
| 23 | /// This table will be populated after `scanRelocs` has run. |
| 24 | /// Key is symbol index. |
| 25 | undefs: std.AutoHashMapUnmanaged(Symbol.Index, std.ArrayListUnmanaged(Atom.Index)) = .{}, |
| 26 | /// Global symbols we need to resolve for the link to succeed. |
| 27 | undefined_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, |
| 28 | boundary_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, |
| 29 | |
| 9 | 30 | dyld_info_cmd: macho.dyld_info_command = .{}, |
| 10 | 31 | symtab_cmd: macho.symtab_command = .{}, |
| 11 | 32 | dysymtab_cmd: macho.dysymtab_command = .{}, |
| ... | ... | @@ -14,36 +35,46 @@ data_in_code_cmd: macho.linkedit_data_command = .{ .cmd = .DATA_IN_CODE }, |
| 14 | 35 | uuid_cmd: macho.uuid_command = .{ .uuid = [_]u8{0} ** 16 }, |
| 15 | 36 | codesig_cmd: macho.linkedit_data_command = .{ .cmd = .CODE_SIGNATURE }, |
| 16 | 37 | |
| 17 | | segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{}, |
| 18 | | sections: std.MultiArrayList(Section) = .{}, |
| 19 | | |
| 20 | | pagezero_segment_cmd_index: ?u8 = null, |
| 21 | | header_segment_cmd_index: ?u8 = null, |
| 22 | | text_segment_cmd_index: ?u8 = null, |
| 23 | | data_const_segment_cmd_index: ?u8 = null, |
| 24 | | data_segment_cmd_index: ?u8 = null, |
| 25 | | linkedit_segment_cmd_index: ?u8 = null, |
| 26 | | |
| 27 | | text_section_index: ?u8 = null, |
| 28 | | data_const_section_index: ?u8 = null, |
| 29 | | data_section_index: ?u8 = null, |
| 30 | | bss_section_index: ?u8 = null, |
| 31 | | thread_vars_section_index: ?u8 = null, |
| 32 | | thread_data_section_index: ?u8 = null, |
| 33 | | thread_bss_section_index: ?u8 = null, |
| 34 | | eh_frame_section_index: ?u8 = null, |
| 35 | | unwind_info_section_index: ?u8 = null, |
| 36 | | stubs_section_index: ?u8 = null, |
| 37 | | stub_helper_section_index: ?u8 = null, |
| 38 | | got_section_index: ?u8 = null, |
| 39 | | la_symbol_ptr_section_index: ?u8 = null, |
| 40 | | tlv_ptr_section_index: ?u8 = null, |
| 41 | | |
| 42 | | strtab: StringTable = .{}, |
| 38 | pagezero_seg_index: ?u8 = null, |
| 39 | text_seg_index: ?u8 = null, |
| 40 | linkedit_seg_index: ?u8 = null, |
| 41 | data_sect_index: ?u8 = null, |
| 42 | got_sect_index: ?u8 = null, |
| 43 | stubs_sect_index: ?u8 = null, |
| 44 | stubs_helper_sect_index: ?u8 = null, |
| 45 | la_symbol_ptr_sect_index: ?u8 = null, |
| 46 | tlv_ptr_sect_index: ?u8 = null, |
| 47 | eh_frame_sect_index: ?u8 = null, |
| 48 | unwind_info_sect_index: ?u8 = null, |
| 49 | objc_stubs_sect_index: ?u8 = null, |
| 43 | 50 | |
| 44 | 51 | /// List of atoms that are either synthetic or map directly to the Zig source program. |
| 45 | 52 | atoms: std.ArrayListUnmanaged(Atom) = .{}, |
| 46 | | |
| 53 | thunks: std.ArrayListUnmanaged(Thunk) = .{}, |
| 54 | unwind_records: std.ArrayListUnmanaged(UnwindInfo.Record) = .{}, |
| 55 | |
| 56 | /// String interning table |
| 57 | strings: StringTable = .{}, |
| 58 | |
| 59 | /// Output synthetic sections |
| 60 | symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 61 | strtab: std.ArrayListUnmanaged(u8) = .{}, |
| 62 | indsymtab: Indsymtab = .{}, |
| 63 | got: GotSection = .{}, |
| 64 | stubs: StubsSection = .{}, |
| 65 | stubs_helper: StubsHelperSection = .{}, |
| 66 | objc_stubs: ObjcStubsSection = .{}, |
| 67 | la_symbol_ptr: LaSymbolPtrSection = .{}, |
| 68 | tlv_ptr: TlvPtrSection = .{}, |
| 69 | rebase: RebaseSection = .{}, |
| 70 | bind: BindSection = .{}, |
| 71 | weak_bind: WeakBindSection = .{}, |
| 72 | lazy_bind: LazyBindSection = .{}, |
| 73 | export_trie: ExportTrieSection = .{}, |
| 74 | unwind_info: UnwindInfo = .{}, |
| 75 | |
| 76 | /// Options |
| 77 | /// SDK layout |
| 47 | 78 | sdk_layout: ?SdkLayout, |
| 48 | 79 | /// Size of the __PAGEZERO segment. |
| 49 | 80 | pagezero_vmsize: ?u64, |
| ... | ... | @@ -62,6 +93,8 @@ entitlements: ?[]const u8, |
| 62 | 93 | compatibility_version: ?std.SemanticVersion, |
| 63 | 94 | /// Entry name |
| 64 | 95 | entry_name: ?[]const u8, |
| 96 | platform: Platform, |
| 97 | sdk_version: ?std.SemanticVersion, |
| 65 | 98 | |
| 66 | 99 | /// Hot-code swapping state. |
| 67 | 100 | hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{}, |
| ... | ... | @@ -144,6 +177,8 @@ pub fn createEmpty( |
| 144 | 177 | .enabled => default_entry_symbol_name, |
| 145 | 178 | .named => |name| name, |
| 146 | 179 | }, |
| 180 | .platform = Platform.fromTarget(target), |
| 181 | .sdk_version = if (options.darwin_sdk_layout) |layout| inferSdkVersion(comp, layout) else null, |
| 147 | 182 | }; |
| 148 | 183 | if (use_llvm and comp.config.have_zcu) { |
| 149 | 184 | self.llvm_object = try LlvmObject.create(arena, comp); |
| ... | ... | @@ -156,9 +191,16 @@ pub fn createEmpty( |
| 156 | 191 | .mode = link.File.determineMode(false, output_mode, link_mode), |
| 157 | 192 | }); |
| 158 | 193 | |
| 159 | | // Index 0 is always a null symbol. |
| 160 | | // try self.locals.append(gpa, null_sym); |
| 161 | | try self.strtab.buffer.append(gpa, 0); |
| 194 | // Append null file |
| 195 | try self.files.append(gpa, .null); |
| 196 | // Atom at index 0 is reserved as null atom |
| 197 | try self.atoms.append(gpa, .{}); |
| 198 | // Append empty string to string tables |
| 199 | try self.strings.buffer.append(gpa, 0); |
| 200 | try self.strtab.append(gpa, 0); |
| 201 | // Append null symbols |
| 202 | try self.symbols.append(gpa, .{}); |
| 203 | try self.symbols_extra.append(gpa, 0); |
| 162 | 204 | |
| 163 | 205 | // TODO: init |
| 164 | 206 | |
| ... | ... | @@ -208,8 +250,71 @@ pub fn open( |
| 208 | 250 | return createEmpty(arena, comp, emit, options); |
| 209 | 251 | } |
| 210 | 252 | |
| 253 | pub fn deinit(self: *MachO) void { |
| 254 | const gpa = self.base.comp.gpa; |
| 255 | |
| 256 | if (self.llvm_object) |llvm_object| llvm_object.deinit(); |
| 257 | |
| 258 | if (self.d_sym) |*d_sym| { |
| 259 | d_sym.deinit(); |
| 260 | } |
| 261 | |
| 262 | for (self.files.items(.tags), self.files.items(.data)) |tag, *data| switch (tag) { |
| 263 | .null => {}, |
| 264 | .internal => data.internal.deinit(gpa), |
| 265 | .object => data.object.deinit(gpa), |
| 266 | .dylib => data.dylib.deinit(gpa), |
| 267 | }; |
| 268 | self.files.deinit(gpa); |
| 269 | self.objects.deinit(gpa); |
| 270 | self.dylibs.deinit(gpa); |
| 271 | |
| 272 | self.segments.deinit(gpa); |
| 273 | for (self.sections.items(.atoms)) |*list| { |
| 274 | list.deinit(gpa); |
| 275 | } |
| 276 | self.sections.deinit(gpa); |
| 277 | |
| 278 | self.symbols.deinit(gpa); |
| 279 | self.symbols_extra.deinit(gpa); |
| 280 | self.globals.deinit(gpa); |
| 281 | { |
| 282 | var it = self.undefs.iterator(); |
| 283 | while (it.next()) |entry| { |
| 284 | entry.value_ptr.deinit(gpa); |
| 285 | } |
| 286 | self.undefs.deinit(gpa); |
| 287 | } |
| 288 | self.undefined_symbols.deinit(gpa); |
| 289 | self.boundary_symbols.deinit(gpa); |
| 290 | |
| 291 | self.strings.deinit(gpa); |
| 292 | self.symtab.deinit(gpa); |
| 293 | self.strtab.deinit(gpa); |
| 294 | self.got.deinit(gpa); |
| 295 | self.stubs.deinit(gpa); |
| 296 | self.objc_stubs.deinit(gpa); |
| 297 | self.tlv_ptr.deinit(gpa); |
| 298 | self.rebase.deinit(gpa); |
| 299 | self.bind.deinit(gpa); |
| 300 | self.weak_bind.deinit(gpa); |
| 301 | self.lazy_bind.deinit(gpa); |
| 302 | self.export_trie.deinit(gpa); |
| 303 | self.unwind_info.deinit(gpa); |
| 304 | |
| 305 | self.atoms.deinit(gpa); |
| 306 | for (self.thunks.items) |*thunk| { |
| 307 | thunk.deinit(gpa); |
| 308 | } |
| 309 | self.thunks.deinit(gpa); |
| 310 | self.unwind_records.deinit(gpa); |
| 311 | } |
| 312 | |
| 211 | 313 | pub fn flush(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void { |
| 212 | | // TODO: what else should we do in flush? Is it actually needed at all? |
| 314 | // TODO: I think this is just a temp and can be removed once we can emit static archives |
| 315 | if (self.base.isStaticLib() and build_options.have_llvm) { |
| 316 | return self.base.linkAsArchive(arena, prog_node); |
| 317 | } |
| 213 | 318 | try self.flushModule(arena, prog_node); |
| 214 | 319 | } |
| 215 | 320 | |
| ... | ... | @@ -219,10 +324,11 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node |
| 219 | 324 | |
| 220 | 325 | const comp = self.base.comp; |
| 221 | 326 | const gpa = comp.gpa; |
| 222 | | _ = gpa; |
| 223 | 327 | |
| 224 | 328 | if (self.llvm_object) |llvm_object| { |
| 225 | 329 | try self.base.emitLlvmObject(arena, llvm_object, prog_node); |
| 330 | // TODO: I think this is just a temp and can be removed once we can emit static archives |
| 331 | if (self.base.isStaticLib() and build_options.have_llvm) return; |
| 226 | 332 | } |
| 227 | 333 | |
| 228 | 334 | var sub_prog_node = prog_node.start("MachO Flush", 0); |
| ... | ... | @@ -240,11 +346,55 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node |
| 240 | 346 | break :blk path; |
| 241 | 347 | } |
| 242 | 348 | } else null; |
| 243 | | _ = module_obj_path; |
| 244 | 349 | |
| 245 | 350 | // --verbose-link |
| 246 | 351 | if (comp.verbose_link) try self.dumpArgv(comp); |
| 247 | 352 | |
| 353 | if (self.base.isStaticLib()) return self.flushStaticLib(comp, module_obj_path); |
| 354 | if (self.base.isObject()) return self.flushObject(comp, module_obj_path); |
| 355 | |
| 356 | var positionals = std.ArrayList(Compilation.LinkObject).init(gpa); |
| 357 | defer positionals.deinit(); |
| 358 | |
| 359 | try positionals.ensureUnusedCapacity(comp.objects.len); |
| 360 | positionals.appendSliceAssumeCapacity(comp.objects); |
| 361 | |
| 362 | // This is a set of object files emitted by clang in a single `build-exe` invocation. |
| 363 | // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up |
| 364 | // in this set. |
| 365 | try positionals.ensureUnusedCapacity(comp.c_object_table.keys().len); |
| 366 | for (comp.c_object_table.keys()) |key| { |
| 367 | positionals.appendAssumeCapacity(.{ .path = key.status.success.object_path }); |
| 368 | } |
| 369 | |
| 370 | if (module_obj_path) |path| try positionals.append(.{ .path = path }); |
| 371 | |
| 372 | // rpaths |
| 373 | var rpath_table = std.StringArrayHashMap(void).init(gpa); |
| 374 | defer rpath_table.deinit(); |
| 375 | try rpath_table.ensureUnusedCapacity(self.base.rpath_list.len); |
| 376 | |
| 377 | for (self.base.rpath_list) |rpath| { |
| 378 | _ = rpath_table.putAssumeCapacity(rpath, {}); |
| 379 | } |
| 380 | |
| 381 | for (positionals.items) |obj| { |
| 382 | self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) { |
| 383 | error.MalformedObject, |
| 384 | error.MalformedArchive, |
| 385 | error.InvalidCpuArch, |
| 386 | error.InvalidTarget, |
| 387 | => continue, // already reported |
| 388 | else => |e| try self.reportParseError( |
| 389 | obj.path, |
| 390 | "unexpected error: parsing input file failed with error {s}", |
| 391 | .{@errorName(e)}, |
| 392 | ), |
| 393 | }; |
| 394 | } |
| 395 | |
| 396 | state_log.debug("{}", .{self.dumpState()}); |
| 397 | |
| 248 | 398 | @panic("TODO"); |
| 249 | 399 | } |
| 250 | 400 | |
| ... | ... | @@ -255,7 +405,6 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { |
| 255 | 405 | defer arena_allocator.deinit(); |
| 256 | 406 | const arena = arena_allocator.allocator(); |
| 257 | 407 | |
| 258 | | const target = self.base.comp.root_mod.resolved_target.result; |
| 259 | 408 | const directory = self.base.emit.directory; |
| 260 | 409 | const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); |
| 261 | 410 | const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: { |
| ... | ... | @@ -309,18 +458,14 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { |
| 309 | 458 | } |
| 310 | 459 | } |
| 311 | 460 | |
| 312 | | { |
| 313 | | const platform = Platform.fromTarget(target); |
| 314 | | try argv.append("-platform_version"); |
| 315 | | try argv.append(@tagName(platform.os_tag)); |
| 316 | | try argv.append(try std.fmt.allocPrint(arena, "{}", .{platform.version})); |
| 317 | | |
| 318 | | const sdk_version: ?std.SemanticVersion = self.inferSdkVersion(); |
| 319 | | if (sdk_version) |ver| { |
| 320 | | try argv.append(try std.fmt.allocPrint(arena, "{d}.{d}", .{ ver.major, ver.minor })); |
| 321 | | } else { |
| 322 | | try argv.append(try std.fmt.allocPrint(arena, "{}", .{platform.version})); |
| 323 | | } |
| 461 | try argv.append("-platform_version"); |
| 462 | try argv.append(@tagName(self.platform.os_tag)); |
| 463 | try argv.append(try std.fmt.allocPrint(arena, "{}", .{self.platform.version})); |
| 464 | |
| 465 | if (self.sdk_version) |ver| { |
| 466 | try argv.append(try std.fmt.allocPrint(arena, "{d}.{d}", .{ ver.major, ver.minor })); |
| 467 | } else { |
| 468 | try argv.append(try std.fmt.allocPrint(arena, "{}", .{self.platform.version})); |
| 324 | 469 | } |
| 325 | 470 | |
| 326 | 471 | if (comp.sysroot) |syslibroot| { |
| ... | ... | @@ -419,6 +564,26 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { |
| 419 | 564 | Compilation.dump_argv(argv.items); |
| 420 | 565 | } |
| 421 | 566 | |
| 567 | fn flushStaticLib(self: *MachO, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void { |
| 568 | _ = comp; |
| 569 | _ = module_obj_path; |
| 570 | |
| 571 | var err = try self.addErrorWithNotes(0); |
| 572 | try err.addMsg(self, "TODO implement flushStaticLib", .{}); |
| 573 | |
| 574 | return error.FlushFailure; |
| 575 | } |
| 576 | |
| 577 | fn flushObject(self: *MachO, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void { |
| 578 | _ = comp; |
| 579 | _ = module_obj_path; |
| 580 | |
| 581 | var err = try self.addErrorWithNotes(0); |
| 582 | try err.addMsg(self, "TODO implement flushObject", .{}); |
| 583 | |
| 584 | return error.FlushFailure; |
| 585 | } |
| 586 | |
| 422 | 587 | /// XNU starting with Big Sur running on arm64 is caching inodes of running binaries. |
| 423 | 588 | /// Any change to the binary will effectively invalidate the kernel's cache |
| 424 | 589 | /// resulting in a SIGKILL on each subsequent run. Since when doing incremental |
| ... | ... | @@ -518,132 +683,60 @@ fn accessLibPath( |
| 518 | 683 | } |
| 519 | 684 | |
| 520 | 685 | const ParseError = error{ |
| 521 | | UnknownFileType, |
| 686 | MalformedObject, |
| 687 | MalformedArchive, |
| 688 | NotLibStub, |
| 689 | InvalidCpuArch, |
| 522 | 690 | InvalidTarget, |
| 523 | 691 | InvalidTargetFatLibrary, |
| 524 | | DylibAlreadyExists, |
| 525 | 692 | IncompatibleDylibVersion, |
| 526 | 693 | OutOfMemory, |
| 527 | 694 | Overflow, |
| 528 | 695 | InputOutput, |
| 529 | | MalformedArchive, |
| 530 | | NotLibStub, |
| 531 | 696 | EndOfStream, |
| 532 | 697 | FileSystem, |
| 533 | 698 | NotSupported, |
| 534 | 699 | } || std.os.SeekError || std.fs.File.OpenError || std.fs.File.ReadError || tapi.TapiError; |
| 535 | 700 | |
| 536 | | pub fn parsePositional( |
| 537 | | self: *MachO, |
| 538 | | file: std.fs.File, |
| 539 | | path: []const u8, |
| 540 | | must_link: bool, |
| 541 | | dependent_libs: anytype, |
| 542 | | ctx: *ParseErrorCtx, |
| 543 | | ) ParseError!void { |
| 701 | fn parsePositional(self: *MachO, path: []const u8, must_link: bool) ParseError!void { |
| 544 | 702 | const tracy = trace(@src()); |
| 545 | 703 | defer tracy.end(); |
| 704 | if (try Object.isObject(path)) { |
| 705 | try self.parseObject(path); |
| 706 | } else { |
| 707 | try self.parseLibrary(.{ .path = path }, must_link); |
| 708 | } |
| 709 | } |
| 546 | 710 | |
| 711 | fn parseLibrary(self: *MachO, lib: SystemLib, must_link: bool) ParseError!void { |
| 547 | 712 | _ = self; |
| 548 | | _ = file; |
| 549 | | _ = path; |
| 713 | _ = lib; |
| 550 | 714 | _ = must_link; |
| 551 | | _ = dependent_libs; |
| 552 | | _ = ctx; |
| 553 | 715 | } |
| 554 | 716 | |
| 555 | | pub fn deinit(self: *MachO) void { |
| 556 | | const gpa = self.base.comp.gpa; |
| 557 | | |
| 558 | | if (self.llvm_object) |llvm_object| llvm_object.deinit(); |
| 559 | | |
| 560 | | if (self.d_sym) |*d_sym| { |
| 561 | | d_sym.deinit(); |
| 562 | | } |
| 563 | | |
| 564 | | self.strtab.deinit(gpa); |
| 565 | | |
| 566 | | self.segments.deinit(gpa); |
| 567 | | |
| 568 | | for (self.sections.items(.free_list)) |*list| { |
| 569 | | list.deinit(gpa); |
| 570 | | } |
| 571 | | self.sections.deinit(gpa); |
| 572 | | } |
| 717 | fn parseObject(self: *MachO, path: []const u8) ParseError!void { |
| 718 | const tracy = trace(@src()); |
| 719 | defer tracy.end(); |
| 573 | 720 | |
| 574 | | fn freeAtom(self: *MachO, atom_index: Atom.Index) void { |
| 575 | 721 | const gpa = self.base.comp.gpa; |
| 576 | | log.debug("freeAtom {d}", .{atom_index}); |
| 577 | | |
| 578 | | // Remove any relocs and base relocs associated with this Atom |
| 579 | | Atom.freeRelocations(self, atom_index); |
| 580 | | |
| 581 | | const atom = self.getAtom(atom_index); |
| 582 | | const sect_id = atom.getSymbol(self).n_sect - 1; |
| 583 | | const free_list = &self.sections.items(.free_list)[sect_id]; |
| 584 | | var already_have_free_list_node = false; |
| 585 | | { |
| 586 | | var i: usize = 0; |
| 587 | | // TODO turn free_list into a hash map |
| 588 | | while (i < free_list.items.len) { |
| 589 | | if (free_list.items[i] == atom_index) { |
| 590 | | _ = free_list.swapRemove(i); |
| 591 | | continue; |
| 592 | | } |
| 593 | | if (free_list.items[i] == atom.prev_index) { |
| 594 | | already_have_free_list_node = true; |
| 595 | | } |
| 596 | | i += 1; |
| 597 | | } |
| 598 | | } |
| 599 | | |
| 600 | | const maybe_last_atom_index = &self.sections.items(.last_atom_index)[sect_id]; |
| 601 | | if (maybe_last_atom_index.*) |last_atom_index| { |
| 602 | | if (last_atom_index == atom_index) { |
| 603 | | if (atom.prev_index) |prev_index| { |
| 604 | | // TODO shrink the section size here |
| 605 | | maybe_last_atom_index.* = prev_index; |
| 606 | | } else { |
| 607 | | maybe_last_atom_index.* = null; |
| 608 | | } |
| 609 | | } |
| 610 | | } |
| 611 | | |
| 612 | | if (atom.prev_index) |prev_index| { |
| 613 | | const prev = self.getAtomPtr(prev_index); |
| 614 | | prev.next_index = atom.next_index; |
| 615 | | |
| 616 | | if (!already_have_free_list_node and prev.*.freeListEligible(self)) { |
| 617 | | // The free list is heuristics, it doesn't have to be perfect, so we can ignore |
| 618 | | // the OOM here. |
| 619 | | free_list.append(gpa, prev_index) catch {}; |
| 620 | | } |
| 621 | | } else { |
| 622 | | self.getAtomPtr(atom_index).prev_index = null; |
| 623 | | } |
| 624 | | |
| 625 | | if (atom.next_index) |next_index| { |
| 626 | | self.getAtomPtr(next_index).prev_index = atom.prev_index; |
| 627 | | } else { |
| 628 | | self.getAtomPtr(atom_index).next_index = null; |
| 629 | | } |
| 630 | | |
| 631 | | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. |
| 632 | | const sym_index = atom.getSymbolIndex().?; |
| 633 | | |
| 634 | | self.locals_free_list.append(gpa, sym_index) catch {}; |
| 635 | | |
| 636 | | // Try freeing GOT atom if this decl had one |
| 637 | | self.got_table.freeEntry(gpa, .{ .sym_index = sym_index }); |
| 638 | | |
| 639 | | if (self.d_sym) |*d_sym| { |
| 640 | | d_sym.swapRemoveRelocs(sym_index); |
| 641 | | } |
| 642 | | |
| 643 | | self.locals.items[sym_index].n_type = 0; |
| 644 | | _ = self.atom_by_index_table.remove(sym_index); |
| 645 | | log.debug(" adding local symbol index {d} to free list", .{sym_index}); |
| 646 | | self.getAtomPtr(atom_index).sym_index = 0; |
| 722 | const file = try std.fs.cwd().openFile(path, .{}); |
| 723 | defer file.close(); |
| 724 | const mtime: u64 = mtime: { |
| 725 | const stat = file.stat() catch break :mtime 0; |
| 726 | break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000))); |
| 727 | }; |
| 728 | const data = try file.readToEndAlloc(gpa, std.math.maxInt(u32)); |
| 729 | const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); |
| 730 | self.files.set(index, .{ .object = .{ |
| 731 | .path = try gpa.dupe(u8, path), |
| 732 | .mtime = mtime, |
| 733 | .data = data, |
| 734 | .index = index, |
| 735 | } }); |
| 736 | try self.objects.append(gpa, index); |
| 737 | |
| 738 | const object = self.getFile(index).?.object; |
| 739 | try object.parse(self); |
| 647 | 740 | } |
| 648 | 741 | |
| 649 | 742 | fn shrinkAtom(self: *MachO, atom_index: Atom.Index, new_block_size: u64) void { |
| ... | ... | @@ -716,7 +809,7 @@ pub fn updateDecl(self: *MachO, mod: *Module, decl_index: InternPool.DeclIndex) |
| 716 | 809 | |
| 717 | 810 | fn updateLazySymbolAtom( |
| 718 | 811 | self: *MachO, |
| 719 | | sym: File.LazySymbol, |
| 812 | sym: link.File.LazySymbol, |
| 720 | 813 | atom_index: Atom.Index, |
| 721 | 814 | section_index: u8, |
| 722 | 815 | ) !void { |
| ... | ... | @@ -727,7 +820,7 @@ fn updateLazySymbolAtom( |
| 727 | 820 | @panic("TODO updateLazySymbolAtom"); |
| 728 | 821 | } |
| 729 | 822 | |
| 730 | | pub fn getOrCreateAtomForLazySymbol(self: *MachO, sym: File.LazySymbol) !Atom.Index { |
| 823 | pub fn getOrCreateAtomForLazySymbol(self: *MachO, sym: link.File.LazySymbol) !Atom.Index { |
| 731 | 824 | _ = self; |
| 732 | 825 | _ = sym; |
| 733 | 826 | @panic("TODO getOrCreateAtomForLazySymbol"); |
| ... | ... | @@ -763,7 +856,7 @@ pub fn updateExports( |
| 763 | 856 | mod: *Module, |
| 764 | 857 | exported: Module.Exported, |
| 765 | 858 | exports: []const *Module.Export, |
| 766 | | ) File.UpdateExportsError!void { |
| 859 | ) link.File.UpdateExportsError!void { |
| 767 | 860 | if (build_options.skip_non_native and builtin.object_format != .macho) { |
| 768 | 861 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| 769 | 862 | } |
| ... | ... | @@ -795,7 +888,7 @@ pub fn freeDecl(self: *MachO, decl_index: InternPool.DeclIndex) void { |
| 795 | 888 | @panic("TODO freeDecl"); |
| 796 | 889 | } |
| 797 | 890 | |
| 798 | | pub fn getDeclVAddr(self: *MachO, decl_index: InternPool.DeclIndex, reloc_info: File.RelocInfo) !u64 { |
| 891 | pub fn getDeclVAddr(self: *MachO, decl_index: InternPool.DeclIndex, reloc_info: link.File.RelocInfo) !u64 { |
| 799 | 892 | assert(self.llvm_object == null); |
| 800 | 893 | _ = decl_index; |
| 801 | 894 | _ = reloc_info; |
| ... | ... | @@ -872,94 +965,224 @@ fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 { |
| 872 | 965 | return start; |
| 873 | 966 | } |
| 874 | 967 | |
| 968 | pub fn getTarget(self: MachO) std.Target { |
| 969 | return self.base.comp.root_mod.resolved_target.result; |
| 970 | } |
| 971 | |
| 875 | 972 | pub fn makeStaticString(bytes: []const u8) [16]u8 { |
| 876 | 973 | var buf = [_]u8{0} ** 16; |
| 877 | 974 | @memcpy(buf[0..bytes.len], bytes); |
| 878 | 975 | return buf; |
| 879 | 976 | } |
| 880 | 977 | |
| 881 | | pub const ParseErrorCtx = struct { |
| 882 | | arena_allocator: std.heap.ArenaAllocator, |
| 883 | | detected_dylib_id: struct { |
| 884 | | parent: u16, |
| 885 | | required_version: u32, |
| 886 | | found_version: u32, |
| 887 | | }, |
| 888 | | detected_targets: std.ArrayList([]const u8), |
| 978 | pub fn getFile(self: *MachO, index: File.Index) ?File { |
| 979 | const tag = self.files.items(.tags)[index]; |
| 980 | return switch (tag) { |
| 981 | .null => null, |
| 982 | .internal => .{ .internal = &self.files.items(.data)[index].internal }, |
| 983 | .object => .{ .object = &self.files.items(.data)[index].object }, |
| 984 | .dylib => .{ .dylib = &self.files.items(.data)[index].dylib }, |
| 985 | }; |
| 986 | } |
| 889 | 987 | |
| 890 | | pub fn init(gpa: Allocator) ParseErrorCtx { |
| 891 | | return .{ |
| 892 | | .arena_allocator = std.heap.ArenaAllocator.init(gpa), |
| 893 | | .detected_dylib_id = undefined, |
| 894 | | .detected_targets = std.ArrayList([]const u8).init(gpa), |
| 988 | pub fn getInternalObject(self: *MachO) ?*InternalObject { |
| 989 | const index = self.internal_object orelse return null; |
| 990 | return self.getFile(index).?.internal; |
| 991 | } |
| 992 | |
| 993 | pub fn addAtom(self: *MachO) error{OutOfMemory}!Atom.Index { |
| 994 | const index = @as(Atom.Index, @intCast(self.atoms.items.len)); |
| 995 | const atom = try self.atoms.addOne(self.base.comp.gpa); |
| 996 | atom.* = .{}; |
| 997 | return index; |
| 998 | } |
| 999 | |
| 1000 | pub fn getAtom(self: *MachO, index: Atom.Index) ?*Atom { |
| 1001 | if (index == 0) return null; |
| 1002 | assert(index < self.atoms.items.len); |
| 1003 | return &self.atoms.items[index]; |
| 1004 | } |
| 1005 | |
| 1006 | pub fn addSymbol(self: *MachO) !Symbol.Index { |
| 1007 | const index = @as(Symbol.Index, @intCast(self.symbols.items.len)); |
| 1008 | const symbol = try self.symbols.addOne(self.base.comp.gpa); |
| 1009 | symbol.* = .{}; |
| 1010 | return index; |
| 1011 | } |
| 1012 | |
| 1013 | pub fn getSymbol(self: *MachO, index: Symbol.Index) *Symbol { |
| 1014 | assert(index < self.symbols.items.len); |
| 1015 | return &self.symbols.items[index]; |
| 1016 | } |
| 1017 | |
| 1018 | pub fn addSymbolExtra(self: *MachO, extra: Symbol.Extra) !u32 { |
| 1019 | const fields = @typeInfo(Symbol.Extra).Struct.fields; |
| 1020 | try self.symbols_extra.ensureUnusedCapacity(self.base.comp.gpa, fields.len); |
| 1021 | return self.addSymbolExtraAssumeCapacity(extra); |
| 1022 | } |
| 1023 | |
| 1024 | pub fn addSymbolExtraAssumeCapacity(self: *MachO, extra: Symbol.Extra) u32 { |
| 1025 | const index = @as(u32, @intCast(self.symbols_extra.items.len)); |
| 1026 | const fields = @typeInfo(Symbol.Extra).Struct.fields; |
| 1027 | inline for (fields) |field| { |
| 1028 | self.symbols_extra.appendAssumeCapacity(switch (field.type) { |
| 1029 | u32 => @field(extra, field.name), |
| 1030 | else => @compileError("bad field type"), |
| 1031 | }); |
| 1032 | } |
| 1033 | return index; |
| 1034 | } |
| 1035 | |
| 1036 | pub fn getSymbolExtra(self: MachO, index: u32) ?Symbol.Extra { |
| 1037 | if (index == 0) return null; |
| 1038 | const fields = @typeInfo(Symbol.Extra).Struct.fields; |
| 1039 | var i: usize = index; |
| 1040 | var result: Symbol.Extra = undefined; |
| 1041 | inline for (fields) |field| { |
| 1042 | @field(result, field.name) = switch (field.type) { |
| 1043 | u32 => self.symbols_extra.items[i], |
| 1044 | else => @compileError("bad field type"), |
| 895 | 1045 | }; |
| 1046 | i += 1; |
| 896 | 1047 | } |
| 1048 | return result; |
| 1049 | } |
| 897 | 1050 | |
| 898 | | pub fn deinit(ctx: *ParseErrorCtx) void { |
| 899 | | ctx.arena_allocator.deinit(); |
| 900 | | ctx.detected_targets.deinit(); |
| 1051 | pub fn setSymbolExtra(self: *MachO, index: u32, extra: Symbol.Extra) void { |
| 1052 | assert(index > 0); |
| 1053 | const fields = @typeInfo(Symbol.Extra).Struct.fields; |
| 1054 | inline for (fields, 0..) |field, i| { |
| 1055 | self.symbols_extra.items[index + i] = switch (field.type) { |
| 1056 | u32 => @field(extra, field.name), |
| 1057 | else => @compileError("bad field type"), |
| 1058 | }; |
| 901 | 1059 | } |
| 1060 | } |
| 1061 | |
| 1062 | const GetOrCreateGlobalResult = struct { |
| 1063 | found_existing: bool, |
| 1064 | index: Symbol.Index, |
| 1065 | }; |
| 902 | 1066 | |
| 903 | | pub fn arena(ctx: *ParseErrorCtx) Allocator { |
| 904 | | return ctx.arena_allocator.allocator(); |
| 1067 | pub fn getOrCreateGlobal(self: *MachO, off: u32) !GetOrCreateGlobalResult { |
| 1068 | const gpa = self.base.comp.gpa; |
| 1069 | const gop = try self.globals.getOrPut(gpa, off); |
| 1070 | if (!gop.found_existing) { |
| 1071 | const index = try self.addSymbol(); |
| 1072 | const global = self.getSymbol(index); |
| 1073 | global.name = off; |
| 1074 | gop.value_ptr.* = index; |
| 1075 | } |
| 1076 | return .{ |
| 1077 | .found_existing = gop.found_existing, |
| 1078 | .index = gop.value_ptr.*, |
| 1079 | }; |
| 1080 | } |
| 1081 | |
| 1082 | pub fn getGlobalByName(self: *MachO, name: []const u8) ?Symbol.Index { |
| 1083 | const off = self.strings.getOffset(name) orelse return null; |
| 1084 | return self.globals.get(off); |
| 1085 | } |
| 1086 | |
| 1087 | pub fn addUnwindRecord(self: *MachO) !UnwindInfo.Record.Index { |
| 1088 | const index = @as(UnwindInfo.Record.Index, @intCast(self.unwind_records.items.len)); |
| 1089 | const rec = try self.unwind_records.addOne(self.base.comp.gpa); |
| 1090 | rec.* = .{}; |
| 1091 | return index; |
| 1092 | } |
| 1093 | |
| 1094 | pub fn getUnwindRecord(self: *MachO, index: UnwindInfo.Record.Index) *UnwindInfo.Record { |
| 1095 | assert(index < self.unwind_records.items.len); |
| 1096 | return &self.unwind_records.items[index]; |
| 1097 | } |
| 1098 | |
| 1099 | pub fn addThunk(self: *MachO) !Thunk.Index { |
| 1100 | const index = @as(Thunk.Index, @intCast(self.thunks.items.len)); |
| 1101 | const thunk = try self.thunks.addOne(self.base.comp.gpa); |
| 1102 | thunk.* = .{}; |
| 1103 | return index; |
| 1104 | } |
| 1105 | |
| 1106 | pub fn getThunk(self: *MachO, index: Thunk.Index) *Thunk { |
| 1107 | assert(index < self.thunks.items.len); |
| 1108 | return &self.thunks.items[index]; |
| 1109 | } |
| 1110 | |
| 1111 | pub fn eatPrefix(path: []const u8, prefix: []const u8) ?[]const u8 { |
| 1112 | if (mem.startsWith(u8, path, prefix)) return path[prefix.len..]; |
| 1113 | return null; |
| 1114 | } |
| 1115 | |
| 1116 | const ErrorWithNotes = struct { |
| 1117 | /// Allocated index in comp.link_errors array. |
| 1118 | index: usize, |
| 1119 | |
| 1120 | /// Next available note slot. |
| 1121 | note_slot: usize = 0, |
| 1122 | |
| 1123 | pub fn addMsg( |
| 1124 | err: ErrorWithNotes, |
| 1125 | macho_file: *MachO, |
| 1126 | comptime format: []const u8, |
| 1127 | args: anytype, |
| 1128 | ) error{OutOfMemory}!void { |
| 1129 | const comp = macho_file.base.comp; |
| 1130 | const gpa = comp.gpa; |
| 1131 | const err_msg = &comp.link_errors.items[err.index]; |
| 1132 | err_msg.msg = try std.fmt.allocPrint(gpa, format, args); |
| 1133 | } |
| 1134 | |
| 1135 | pub fn addNote( |
| 1136 | err: *ErrorWithNotes, |
| 1137 | macho_file: *MachO, |
| 1138 | comptime format: []const u8, |
| 1139 | args: anytype, |
| 1140 | ) error{OutOfMemory}!void { |
| 1141 | const comp = macho_file.base.comp; |
| 1142 | const gpa = comp.gpa; |
| 1143 | const err_msg = &comp.link_errors.items[err.index]; |
| 1144 | assert(err.note_slot < err_msg.notes.len); |
| 1145 | err_msg.notes[err.note_slot] = .{ .msg = try std.fmt.allocPrint(gpa, format, args) }; |
| 1146 | err.note_slot += 1; |
| 905 | 1147 | } |
| 906 | 1148 | }; |
| 907 | 1149 | |
| 908 | | pub fn handleAndReportParseError( |
| 1150 | pub fn addErrorWithNotes(self: *MachO, note_count: usize) error{OutOfMemory}!ErrorWithNotes { |
| 1151 | const comp = self.base.comp; |
| 1152 | const gpa = comp.gpa; |
| 1153 | try comp.link_errors.ensureUnusedCapacity(gpa, 1); |
| 1154 | return self.addErrorWithNotesAssumeCapacity(note_count); |
| 1155 | } |
| 1156 | |
| 1157 | fn addErrorWithNotesAssumeCapacity(self: *MachO, note_count: usize) error{OutOfMemory}!ErrorWithNotes { |
| 1158 | const comp = self.base.comp; |
| 1159 | const gpa = comp.gpa; |
| 1160 | const index = comp.link_errors.items.len; |
| 1161 | const err = comp.link_errors.addOneAssumeCapacity(); |
| 1162 | err.* = .{ .msg = undefined, .notes = try gpa.alloc(link.File.ErrorMsg, note_count) }; |
| 1163 | return .{ .index = index }; |
| 1164 | } |
| 1165 | |
| 1166 | pub fn reportParseError( |
| 909 | 1167 | self: *MachO, |
| 910 | 1168 | path: []const u8, |
| 911 | | err: ParseError, |
| 912 | | ctx: *const ParseErrorCtx, |
| 1169 | comptime format: []const u8, |
| 1170 | args: anytype, |
| 913 | 1171 | ) error{OutOfMemory}!void { |
| 914 | | const target = self.base.comp.root_mod.resolved_target.result; |
| 915 | | const gpa = self.base.comp.gpa; |
| 916 | | const cpu_arch = target.cpu.arch; |
| 917 | | switch (err) { |
| 918 | | error.DylibAlreadyExists => {}, |
| 919 | | error.IncompatibleDylibVersion => { |
| 920 | | const parent = &self.dylibs.items[ctx.detected_dylib_id.parent]; |
| 921 | | try self.reportDependencyError( |
| 922 | | if (parent.id) |id| id.name else parent.path, |
| 923 | | path, |
| 924 | | "incompatible dylib version: expected at least '{}', but found '{}'", |
| 925 | | .{ |
| 926 | | load_commands.appleVersionToSemanticVersion(ctx.detected_dylib_id.required_version), |
| 927 | | load_commands.appleVersionToSemanticVersion(ctx.detected_dylib_id.found_version), |
| 928 | | }, |
| 929 | | ); |
| 930 | | }, |
| 931 | | error.UnknownFileType => try self.reportParseError(path, "unknown file type", .{}), |
| 932 | | error.InvalidTarget, error.InvalidTargetFatLibrary => { |
| 933 | | var targets_string = std.ArrayList(u8).init(gpa); |
| 934 | | defer targets_string.deinit(); |
| 935 | | |
| 936 | | if (ctx.detected_targets.items.len > 1) { |
| 937 | | try targets_string.writer().writeAll("("); |
| 938 | | for (ctx.detected_targets.items) |t| { |
| 939 | | try targets_string.writer().print("{s}, ", .{t}); |
| 940 | | } |
| 941 | | try targets_string.resize(targets_string.items.len - 2); |
| 942 | | try targets_string.writer().writeAll(")"); |
| 943 | | } else { |
| 944 | | try targets_string.writer().writeAll(ctx.detected_targets.items[0]); |
| 945 | | } |
| 1172 | var err = try self.addErrorWithNotes(1); |
| 1173 | try err.addMsg(self, format, args); |
| 1174 | try err.addNote(self, "while parsing {s}", .{path}); |
| 1175 | } |
| 946 | 1176 | |
| 947 | | switch (err) { |
| 948 | | error.InvalidTarget => try self.reportParseError( |
| 949 | | path, |
| 950 | | "invalid target: expected '{}', but found '{s}'", |
| 951 | | .{ Platform.fromTarget(target).fmtTarget(cpu_arch), targets_string.items }, |
| 952 | | ), |
| 953 | | error.InvalidTargetFatLibrary => try self.reportParseError( |
| 954 | | path, |
| 955 | | "invalid architecture in universal library: expected '{s}', but found '{s}'", |
| 956 | | .{ @tagName(cpu_arch), targets_string.items }, |
| 957 | | ), |
| 958 | | else => unreachable, |
| 959 | | } |
| 960 | | }, |
| 961 | | else => |e| try self.reportParseError(path, "{s}: parsing object failed", .{@errorName(e)}), |
| 962 | | } |
| 1177 | pub fn reportParseError2( |
| 1178 | self: *MachO, |
| 1179 | file_index: File.Index, |
| 1180 | comptime format: []const u8, |
| 1181 | args: anytype, |
| 1182 | ) error{OutOfMemory}!void { |
| 1183 | var err = try self.addErrorWithNotes(1); |
| 1184 | try err.addMsg(self, format, args); |
| 1185 | try err.addNote(self, "while parsing {}", .{self.getFile(file_index).?.fmtPath()}); |
| 963 | 1186 | } |
| 964 | 1187 | |
| 965 | 1188 | fn reportMissingLibraryError( |
| ... | ... | @@ -968,18 +1191,11 @@ fn reportMissingLibraryError( |
| 968 | 1191 | comptime format: []const u8, |
| 969 | 1192 | args: anytype, |
| 970 | 1193 | ) error{OutOfMemory}!void { |
| 971 | | const comp = self.base.comp; |
| 972 | | const gpa = comp.gpa; |
| 973 | | try comp.link_errors.ensureUnusedCapacity(gpa, 1); |
| 974 | | const notes = try gpa.alloc(File.ErrorMsg, checked_paths.len); |
| 975 | | errdefer gpa.free(notes); |
| 976 | | for (checked_paths, notes) |path, *note| { |
| 977 | | note.* = .{ .msg = try std.fmt.allocPrint(gpa, "tried {s}", .{path}) }; |
| 1194 | var err = try self.addErrorWithNotes(checked_paths.len); |
| 1195 | try err.addMsg(self, format, args); |
| 1196 | for (checked_paths) |path| { |
| 1197 | try err.addNote(self, "tried {s}", .{path}); |
| 978 | 1198 | } |
| 979 | | comp.link_errors.appendAssumeCapacity(.{ |
| 980 | | .msg = try std.fmt.allocPrint(gpa, format, args), |
| 981 | | .notes = notes, |
| 982 | | }); |
| 983 | 1199 | } |
| 984 | 1200 | |
| 985 | 1201 | fn reportDependencyError( |
| ... | ... | @@ -992,7 +1208,7 @@ fn reportDependencyError( |
| 992 | 1208 | const comp = self.base.comp; |
| 993 | 1209 | const gpa = comp.gpa; |
| 994 | 1210 | try comp.link_errors.ensureUnusedCapacity(gpa, 1); |
| 995 | | var notes = try std.ArrayList(File.ErrorMsg).initCapacity(gpa, 2); |
| 1211 | var notes = try std.ArrayList(link.File.ErrorMsg).initCapacity(gpa, 2); |
| 996 | 1212 | defer notes.deinit(); |
| 997 | 1213 | if (path) |p| { |
| 998 | 1214 | notes.appendAssumeCapacity(.{ .msg = try std.fmt.allocPrint(gpa, "while parsing {s}", .{p}) }); |
| ... | ... | @@ -1004,42 +1220,6 @@ fn reportDependencyError( |
| 1004 | 1220 | }); |
| 1005 | 1221 | } |
| 1006 | 1222 | |
| 1007 | | pub fn reportParseError( |
| 1008 | | self: *MachO, |
| 1009 | | path: []const u8, |
| 1010 | | comptime format: []const u8, |
| 1011 | | args: anytype, |
| 1012 | | ) error{OutOfMemory}!void { |
| 1013 | | const comp = self.base.comp; |
| 1014 | | const gpa = comp.gpa; |
| 1015 | | try comp.link_errors.ensureUnusedCapacity(gpa, 1); |
| 1016 | | var notes = try gpa.alloc(File.ErrorMsg, 1); |
| 1017 | | errdefer gpa.free(notes); |
| 1018 | | notes[0] = .{ .msg = try std.fmt.allocPrint(gpa, "while parsing {s}", .{path}) }; |
| 1019 | | comp.link_errors.appendAssumeCapacity(.{ |
| 1020 | | .msg = try std.fmt.allocPrint(gpa, format, args), |
| 1021 | | .notes = notes, |
| 1022 | | }); |
| 1023 | | } |
| 1024 | | |
| 1025 | | pub fn reportUnresolvedBoundarySymbol( |
| 1026 | | self: *MachO, |
| 1027 | | sym_name: []const u8, |
| 1028 | | comptime format: []const u8, |
| 1029 | | args: anytype, |
| 1030 | | ) error{OutOfMemory}!void { |
| 1031 | | const comp = self.base.comp; |
| 1032 | | const gpa = comp.gpa; |
| 1033 | | try comp.link_errors.ensureUnusedCapacity(gpa, 1); |
| 1034 | | var notes = try gpa.alloc(File.ErrorMsg, 1); |
| 1035 | | errdefer gpa.free(notes); |
| 1036 | | notes[0] = .{ .msg = try std.fmt.allocPrint(gpa, "while resolving {s}", .{sym_name}) }; |
| 1037 | | comp.link_errors.appendAssumeCapacity(.{ |
| 1038 | | .msg = try std.fmt.allocPrint(gpa, format, args), |
| 1039 | | .notes = notes, |
| 1040 | | }); |
| 1041 | | } |
| 1042 | | |
| 1043 | 1223 | pub fn reportUndefined(self: *MachO) error{OutOfMemory}!void { |
| 1044 | 1224 | const comp = self.base.comp; |
| 1045 | 1225 | const gpa = comp.gpa; |
| ... | ... | @@ -1050,7 +1230,7 @@ pub fn reportUndefined(self: *MachO) error{OutOfMemory}!void { |
| 1050 | 1230 | const global = self.globals.items[global_index]; |
| 1051 | 1231 | const sym_name = self.getSymbolName(global); |
| 1052 | 1232 | |
| 1053 | | var notes = try std.ArrayList(File.ErrorMsg).initCapacity(gpa, 1); |
| 1233 | var notes = try std.ArrayList(link.File.ErrorMsg).initCapacity(gpa, 1); |
| 1054 | 1234 | defer notes.deinit(); |
| 1055 | 1235 | |
| 1056 | 1236 | if (global.getFile()) |file| { |
| ... | ... | @@ -1060,7 +1240,7 @@ pub fn reportUndefined(self: *MachO) error{OutOfMemory}!void { |
| 1060 | 1240 | notes.appendAssumeCapacity(.{ .msg = note }); |
| 1061 | 1241 | } |
| 1062 | 1242 | |
| 1063 | | var err_msg = File.ErrorMsg{ |
| 1243 | var err_msg = link.File.ErrorMsg{ |
| 1064 | 1244 | .msg = try std.fmt.allocPrint(gpa, "undefined reference to symbol {s}", .{sym_name}), |
| 1065 | 1245 | }; |
| 1066 | 1246 | err_msg.notes = try notes.toOwnedSlice(); |
| ... | ... | @@ -1164,6 +1344,145 @@ pub fn ptraceDetach(self: *MachO, pid: std.os.pid_t) !void { |
| 1164 | 1344 | self.hot_state.mach_task = null; |
| 1165 | 1345 | } |
| 1166 | 1346 | |
| 1347 | pub fn dumpState(self: *MachO) std.fmt.Formatter(fmtDumpState) { |
| 1348 | return .{ .data = self }; |
| 1349 | } |
| 1350 | |
| 1351 | fn fmtDumpState( |
| 1352 | self: *MachO, |
| 1353 | comptime unused_fmt_string: []const u8, |
| 1354 | options: std.fmt.FormatOptions, |
| 1355 | writer: anytype, |
| 1356 | ) !void { |
| 1357 | _ = options; |
| 1358 | _ = unused_fmt_string; |
| 1359 | for (self.objects.items) |index| { |
| 1360 | const object = self.getFile(index).?.object; |
| 1361 | try writer.print("object({d}) : {} : has_debug({})", .{ |
| 1362 | index, |
| 1363 | object.fmtPath(), |
| 1364 | object.hasDebugInfo(), |
| 1365 | }); |
| 1366 | if (!object.alive) try writer.writeAll(" : ([*])"); |
| 1367 | try writer.writeByte('\n'); |
| 1368 | try writer.print("{}{}{}{}{}\n", .{ |
| 1369 | object.fmtAtoms(self), |
| 1370 | object.fmtCies(self), |
| 1371 | object.fmtFdes(self), |
| 1372 | object.fmtUnwindRecords(self), |
| 1373 | object.fmtSymtab(self), |
| 1374 | }); |
| 1375 | } |
| 1376 | // for (self.dylibs.items) |index| { |
| 1377 | // const dylib = self.getFile(index).?.dylib; |
| 1378 | // try writer.print("dylib({d}) : {s} : needed({}) : weak({})", .{ |
| 1379 | // index, |
| 1380 | // dylib.path, |
| 1381 | // dylib.needed, |
| 1382 | // dylib.weak, |
| 1383 | // }); |
| 1384 | // if (!dylib.isAlive(self)) try writer.writeAll(" : ([*])"); |
| 1385 | // try writer.writeByte('\n'); |
| 1386 | // try writer.print("{}\n", .{dylib.fmtSymtab(self)}); |
| 1387 | // } |
| 1388 | if (self.getInternalObject()) |internal| { |
| 1389 | try writer.print("internal({d}) : internal\n", .{internal.index}); |
| 1390 | try writer.print("{}{}\n", .{ internal.fmtAtoms(self), internal.fmtSymtab(self) }); |
| 1391 | } |
| 1392 | try writer.writeAll("thunks\n"); |
| 1393 | for (self.thunks.items, 0..) |thunk, index| { |
| 1394 | try writer.print("thunk({d}) : {}\n", .{ index, thunk.fmt(self) }); |
| 1395 | } |
| 1396 | try writer.print("stubs\n{}\n", .{self.stubs.fmt(self)}); |
| 1397 | try writer.print("objc_stubs\n{}\n", .{self.objc_stubs.fmt(self)}); |
| 1398 | try writer.print("got\n{}\n", .{self.got.fmt(self)}); |
| 1399 | try writer.print("tlv_ptr\n{}\n", .{self.tlv_ptr.fmt(self)}); |
| 1400 | try writer.writeByte('\n'); |
| 1401 | try writer.print("sections\n{}\n", .{self.fmtSections()}); |
| 1402 | try writer.print("segments\n{}\n", .{self.fmtSegments()}); |
| 1403 | } |
| 1404 | |
| 1405 | fn fmtSections(self: *MachO) std.fmt.Formatter(formatSections) { |
| 1406 | return .{ .data = self }; |
| 1407 | } |
| 1408 | |
| 1409 | fn formatSections( |
| 1410 | self: *MachO, |
| 1411 | comptime unused_fmt_string: []const u8, |
| 1412 | options: std.fmt.FormatOptions, |
| 1413 | writer: anytype, |
| 1414 | ) !void { |
| 1415 | _ = options; |
| 1416 | _ = unused_fmt_string; |
| 1417 | const slice = self.sections.slice(); |
| 1418 | for (slice.items(.header), slice.items(.segment_id), 0..) |header, seg_id, i| { |
| 1419 | try writer.print("sect({d}) : seg({d}) : {s},{s} : @{x} ({x}) : align({x}) : size({x})\n", .{ |
| 1420 | i, seg_id, header.segName(), header.sectName(), header.offset, header.addr, |
| 1421 | header.@"align", header.size, |
| 1422 | }); |
| 1423 | } |
| 1424 | } |
| 1425 | |
| 1426 | fn fmtSegments(self: *MachO) std.fmt.Formatter(formatSegments) { |
| 1427 | return .{ .data = self }; |
| 1428 | } |
| 1429 | |
| 1430 | fn formatSegments( |
| 1431 | self: *MachO, |
| 1432 | comptime unused_fmt_string: []const u8, |
| 1433 | options: std.fmt.FormatOptions, |
| 1434 | writer: anytype, |
| 1435 | ) !void { |
| 1436 | _ = options; |
| 1437 | _ = unused_fmt_string; |
| 1438 | for (self.segments.items, 0..) |seg, i| { |
| 1439 | try writer.print("seg({d}) : {s} : @{x}-{x} ({x}-{x})\n", .{ |
| 1440 | i, seg.segName(), seg.vmaddr, seg.vmaddr + seg.vmsize, |
| 1441 | seg.fileoff, seg.fileoff + seg.filesize, |
| 1442 | }); |
| 1443 | } |
| 1444 | } |
| 1445 | |
| 1446 | pub fn fmtSectType(tt: u8) std.fmt.Formatter(formatSectType) { |
| 1447 | return .{ .data = tt }; |
| 1448 | } |
| 1449 | |
| 1450 | fn formatSectType( |
| 1451 | tt: u8, |
| 1452 | comptime unused_fmt_string: []const u8, |
| 1453 | options: std.fmt.FormatOptions, |
| 1454 | writer: anytype, |
| 1455 | ) !void { |
| 1456 | _ = options; |
| 1457 | _ = unused_fmt_string; |
| 1458 | const name = switch (tt) { |
| 1459 | macho.S_REGULAR => "REGULAR", |
| 1460 | macho.S_ZEROFILL => "ZEROFILL", |
| 1461 | macho.S_CSTRING_LITERALS => "CSTRING_LITERALS", |
| 1462 | macho.S_4BYTE_LITERALS => "4BYTE_LITERALS", |
| 1463 | macho.S_8BYTE_LITERALS => "8BYTE_LITERALS", |
| 1464 | macho.S_16BYTE_LITERALS => "16BYTE_LITERALS", |
| 1465 | macho.S_LITERAL_POINTERS => "LITERAL_POINTERS", |
| 1466 | macho.S_NON_LAZY_SYMBOL_POINTERS => "NON_LAZY_SYMBOL_POINTERS", |
| 1467 | macho.S_LAZY_SYMBOL_POINTERS => "LAZY_SYMBOL_POINTERS", |
| 1468 | macho.S_SYMBOL_STUBS => "SYMBOL_STUBS", |
| 1469 | macho.S_MOD_INIT_FUNC_POINTERS => "MOD_INIT_FUNC_POINTERS", |
| 1470 | macho.S_MOD_TERM_FUNC_POINTERS => "MOD_TERM_FUNC_POINTERS", |
| 1471 | macho.S_COALESCED => "COALESCED", |
| 1472 | macho.S_GB_ZEROFILL => "GB_ZEROFILL", |
| 1473 | macho.S_INTERPOSING => "INTERPOSING", |
| 1474 | macho.S_DTRACE_DOF => "DTRACE_DOF", |
| 1475 | macho.S_THREAD_LOCAL_REGULAR => "THREAD_LOCAL_REGULAR", |
| 1476 | macho.S_THREAD_LOCAL_ZEROFILL => "THREAD_LOCAL_ZEROFILL", |
| 1477 | macho.S_THREAD_LOCAL_VARIABLES => "THREAD_LOCAL_VARIABLES", |
| 1478 | macho.S_THREAD_LOCAL_VARIABLE_POINTERS => "THREAD_LOCAL_VARIABLE_POINTERS", |
| 1479 | macho.S_THREAD_LOCAL_INIT_FUNCTION_POINTERS => "THREAD_LOCAL_INIT_FUNCTION_POINTERS", |
| 1480 | macho.S_INIT_FUNC_OFFSETS => "INIT_FUNC_OFFSETS", |
| 1481 | else => |x| return writer.print("UNKNOWN({x})", .{x}), |
| 1482 | }; |
| 1483 | try writer.print("{s}", .{name}); |
| 1484 | } |
| 1485 | |
| 1167 | 1486 | const is_hot_update_compatible = switch (builtin.target.os.tag) { |
| 1168 | 1487 | .macos => true, |
| 1169 | 1488 | else => false, |
| ... | ... | @@ -1171,32 +1490,14 @@ const is_hot_update_compatible = switch (builtin.target.os.tag) { |
| 1171 | 1490 | |
| 1172 | 1491 | const default_entry_symbol_name = "_main"; |
| 1173 | 1492 | |
| 1174 | | pub const base_tag: File.Tag = File.Tag.macho; |
| 1493 | pub const base_tag: link.File.Tag = link.File.Tag.macho; |
| 1175 | 1494 | pub const N_DEAD: u16 = @as(u16, @bitCast(@as(i16, -1))); |
| 1176 | 1495 | pub const N_BOUNDARY: u16 = @as(u16, @bitCast(@as(i16, -2))); |
| 1177 | 1496 | |
| 1178 | | pub const Section = struct { |
| 1497 | const Section = struct { |
| 1179 | 1498 | header: macho.section_64, |
| 1180 | | segment_index: u8, |
| 1181 | | first_atom_index: ?Atom.Index = null, |
| 1182 | | last_atom_index: ?Atom.Index = null, |
| 1183 | | |
| 1184 | | /// A list of atoms that have surplus capacity. This list can have false |
| 1185 | | /// positives, as functions grow and shrink over time, only sometimes being added |
| 1186 | | /// or removed from the freelist. |
| 1187 | | /// |
| 1188 | | /// An atom has surplus capacity when its overcapacity value is greater than |
| 1189 | | /// padToIdeal(minimum_atom_size). That is, when it has so |
| 1190 | | /// much extra capacity, that we could fit a small new symbol in it, itself with |
| 1191 | | /// ideal_capacity or more. |
| 1192 | | /// |
| 1193 | | /// Ideal capacity is defined by size + (size / ideal_factor). |
| 1194 | | /// |
| 1195 | | /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that |
| 1196 | | /// overcapacity can be negative. A simple way to have negative overcapacity is to |
| 1197 | | /// allocate a fresh atom, which will have ideal capacity, and then grow it |
| 1198 | | /// by 1 byte. It will then have -1 overcapacity. |
| 1199 | | free_list: std.ArrayListUnmanaged(Atom.Index) = .{}, |
| 1499 | segment_id: u8, |
| 1500 | atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, |
| 1200 | 1501 | }; |
| 1201 | 1502 | |
| 1202 | 1503 | const HotUpdateState = struct { |
| ... | ... | @@ -1385,15 +1686,13 @@ pub inline fn appleVersionToSemanticVersion(version: u32) std.SemanticVersion { |
| 1385 | 1686 | }; |
| 1386 | 1687 | } |
| 1387 | 1688 | |
| 1388 | | fn inferSdkVersion(self: *MachO) ?std.SemanticVersion { |
| 1389 | | const comp = self.base.comp; |
| 1689 | fn inferSdkVersion(comp: *Compilation, sdk_layout: SdkLayout) ?std.SemanticVersion { |
| 1390 | 1690 | const gpa = comp.gpa; |
| 1391 | 1691 | |
| 1392 | 1692 | var arena_allocator = std.heap.ArenaAllocator.init(gpa); |
| 1393 | 1693 | defer arena_allocator.deinit(); |
| 1394 | 1694 | const arena = arena_allocator.allocator(); |
| 1395 | 1695 | |
| 1396 | | const sdk_layout = self.sdk_layout orelse return null; |
| 1397 | 1696 | const sdk_dir = switch (sdk_layout) { |
| 1398 | 1697 | .sdk => comp.sysroot.?, |
| 1399 | 1698 | .vendored => std.fs.path.join(arena, &.{ comp.zig_lib_directory.path.?, "libc", "darwin" }) catch return null, |
| ... | ... | @@ -1402,6 +1701,7 @@ fn inferSdkVersion(self: *MachO) ?std.SemanticVersion { |
| 1402 | 1701 | return parseSdkVersion(ver); |
| 1403 | 1702 | } else |_| { |
| 1404 | 1703 | // Read from settings should always succeed when vendored. |
| 1704 | // TODO: convert to fatal linker error |
| 1405 | 1705 | if (sdk_layout == .vendored) @panic("zig installation bug: unable to parse SDK version"); |
| 1406 | 1706 | } |
| 1407 | 1707 | |
| ... | ... | @@ -1470,6 +1770,15 @@ pub const default_pagezero_vmsize: u64 = 0x100000000; |
| 1470 | 1770 | /// potential future extensions. |
| 1471 | 1771 | pub const default_headerpad_size: u32 = 0x1000; |
| 1472 | 1772 | |
| 1773 | const SystemLib = struct { |
| 1774 | path: []const u8, |
| 1775 | needed: bool = false, |
| 1776 | weak: bool = false, |
| 1777 | hidden: bool = false, |
| 1778 | reexport: bool = false, |
| 1779 | must_link: bool = false, |
| 1780 | }; |
| 1781 | |
| 1473 | 1782 | const MachO = @This(); |
| 1474 | 1783 | |
| 1475 | 1784 | const std = @import("std"); |
| ... | ... | @@ -1479,6 +1788,7 @@ const assert = std.debug.assert; |
| 1479 | 1788 | const dwarf = std.dwarf; |
| 1480 | 1789 | const fs = std.fs; |
| 1481 | 1790 | const log = std.log.scoped(.link); |
| 1791 | const state_log = std.log.scoped(.link_state); |
| 1482 | 1792 | const macho = std.macho; |
| 1483 | 1793 | const math = std.math; |
| 1484 | 1794 | const mem = std.mem; |
| ... | ... | @@ -1488,6 +1798,7 @@ const aarch64 = @import("../arch/aarch64/bits.zig"); |
| 1488 | 1798 | const calcUuid = @import("MachO/uuid.zig").calcUuid; |
| 1489 | 1799 | const codegen = @import("../codegen.zig"); |
| 1490 | 1800 | const dead_strip = @import("MachO/dead_strip.zig"); |
| 1801 | const eh_frame = @import("MachO/eh_frame.zig"); |
| 1491 | 1802 | const fat = @import("MachO/fat.zig"); |
| 1492 | 1803 | const link = @import("../link.zig"); |
| 1493 | 1804 | const llvm_backend = @import("../codegen/llvm.zig"); |
| ... | ... | @@ -1496,12 +1807,14 @@ const tapi = @import("tapi.zig"); |
| 1496 | 1807 | const target_util = @import("../target.zig"); |
| 1497 | 1808 | const thunks = @import("MachO/thunks.zig"); |
| 1498 | 1809 | const trace = @import("../tracy.zig").trace; |
| 1810 | const synthetic = @import("MachO/synthetic.zig"); |
| 1499 | 1811 | |
| 1500 | 1812 | const Air = @import("../Air.zig"); |
| 1501 | 1813 | const Alignment = Atom.Alignment; |
| 1502 | 1814 | const Allocator = mem.Allocator; |
| 1503 | 1815 | const Archive = @import("MachO/Archive.zig"); |
| 1504 | 1816 | pub const Atom = @import("MachO/Atom.zig"); |
| 1817 | const BindSection = synthetic.BindSection; |
| 1505 | 1818 | const Cache = std.Build.Cache; |
| 1506 | 1819 | const CodeSignature = @import("MachO/CodeSignature.zig"); |
| 1507 | 1820 | const Compilation = @import("../Compilation.zig"); |
| ... | ... | @@ -1509,17 +1822,29 @@ pub const DebugSymbols = @import("MachO/DebugSymbols.zig"); |
| 1509 | 1822 | const Dwarf = File.Dwarf; |
| 1510 | 1823 | const DwarfInfo = @import("MachO/DwarfInfo.zig"); |
| 1511 | 1824 | const Dylib = @import("MachO/Dylib.zig"); |
| 1512 | | const File = link.File; |
| 1825 | const ExportTrieSection = synthetic.ExportTrieSection; |
| 1826 | const File = @import("MachO/file.zig").File; |
| 1827 | const GotSection = synthetic.GotSection; |
| 1828 | const Indsymtab = synthetic.Indsymtab; |
| 1829 | const InternalObject = @import("MachO/InternalObject.zig"); |
| 1830 | const ObjcStubsSection = synthetic.ObjcStubsSection; |
| 1513 | 1831 | const Object = @import("MachO/Object.zig"); |
| 1832 | const LazyBindSection = synthetic.LazyBindSection; |
| 1833 | const LaSymbolPtrSection = synthetic.LaSymbolPtrSection; |
| 1514 | 1834 | const LibStub = tapi.LibStub; |
| 1515 | 1835 | const Liveness = @import("../Liveness.zig"); |
| 1516 | 1836 | const LlvmObject = @import("../codegen/llvm.zig").Object; |
| 1517 | 1837 | const Md5 = std.crypto.hash.Md5; |
| 1518 | 1838 | const Module = @import("../Module.zig"); |
| 1519 | 1839 | const InternPool = @import("../InternPool.zig"); |
| 1840 | const RebaseSection = synthetic.RebaseSection; |
| 1520 | 1841 | const Relocation = @import("MachO/Relocation.zig"); |
| 1521 | 1842 | const StringTable = @import("StringTable.zig"); |
| 1522 | | const TableSection = @import("table_section.zig").TableSection; |
| 1523 | | const Type = @import("../type.zig").Type; |
| 1843 | const StubsSection = synthetic.StubsSection; |
| 1844 | const StubsHelperSection = synthetic.StubsHelperSection; |
| 1845 | const Symbol = @import("MachO/Symbol.zig"); |
| 1846 | const Thunk = thunks.Thunk; |
| 1847 | const TlvPtrSection = synthetic.TlvPtrSection; |
| 1524 | 1848 | const TypedValue = @import("../TypedValue.zig"); |
| 1525 | | const Value = @import("../value.zig").Value; |
| 1849 | const UnwindInfo = @import("MachO/UnwindInfo.zig"); |
| 1850 | const WeakBindSection = synthetic.WeakBindSection; |