diff --git a/ci/x86_64-macos-release.sh b/ci/x86_64-macos-release.sh index 5bc031425a..ecd6d66886 100755 --- a/ci/x86_64-macos-release.sh +++ b/ci/x86_64-macos-release.sh @@ -25,6 +25,15 @@ cd $ZIGDIR git fetch --unshallow || true git fetch --tags +# Test building from source without LLVM. +git clean -fd +rm -rf zig-out +cc -o bootstrap bootstrap.c +./bootstrap +./zig2 build -Dno-lib +# In order to run these behavior tests we need to move the `@cImport` ones to somewhere else. +# ./zig-out/bin/zig test test/behavior.zig + rm -rf build mkdir build cd build diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 70e51799cb..fc85d32d52 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -764,7 +764,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace, ret_addr builtin.zig_backend == .stage2_arm or builtin.zig_backend == .stage2_aarch64 or builtin.zig_backend == .stage2_x86 or - (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) or + (builtin.zig_backend == .stage2_x86_64 and (builtin.target.ofmt != .elf and builtin.target.ofmt != .macho)) or builtin.zig_backend == .stage2_riscv64 or builtin.zig_backend == .stage2_sparc64 or builtin.zig_backend == .stage2_spirv64) diff --git a/src/Compilation.zig b/src/Compilation.zig index 8d7d3ea8b1..91879094dd 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -6278,7 +6278,7 @@ fn canBuildLibCompilerRt(target: std.Target, use_llvm: bool) bool { } return switch (target_util.zigBackend(target, use_llvm)) { .stage2_llvm => true, - .stage2_x86_64 => if (target.ofmt == .elf) true else build_options.have_llvm, + .stage2_x86_64 => if (target.ofmt == .elf or target.ofmt == .macho) true else build_options.have_llvm, else => build_options.have_llvm, }; } @@ -6296,7 +6296,7 @@ fn canBuildZigLibC(target: std.Target, use_llvm: bool) bool { } return switch (target_util.zigBackend(target, use_llvm)) { .stage2_llvm => true, - .stage2_x86_64 => if (target.ofmt == .elf) true else build_options.have_llvm, + .stage2_x86_64 => if (target.ofmt == .elf or target.ofmt == .macho) true else build_options.have_llvm, else => build_options.have_llvm, }; } diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 6607cdb119..a5dd57a406 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -1282,10 +1282,17 @@ pub fn commitDeclState( try pwriteDbgLineNops(elf_file.base.file.?, file_pos, 0, &[0]u8{}, src_fn.len); }, .macho => { - const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?; - const debug_line_sect = d_sym.getSectionPtr(d_sym.debug_line_section_index.?); - const file_pos = debug_line_sect.offset + src_fn.off; - try pwriteDbgLineNops(d_sym.file, file_pos, 0, &[0]u8{}, src_fn.len); + const macho_file = self.bin_file.cast(File.MachO).?; + if (macho_file.base.isRelocatable()) { + const debug_line_sect = &macho_file.sections.items(.header)[macho_file.debug_line_sect_index.?]; + const file_pos = debug_line_sect.offset + src_fn.off; + try pwriteDbgLineNops(macho_file.base.file.?, file_pos, 0, &[0]u8{}, src_fn.len); + } else { + const d_sym = macho_file.getDebugSymbols().?; + const debug_line_sect = d_sym.getSectionPtr(d_sym.debug_line_section_index.?); + const file_pos = debug_line_sect.offset + src_fn.off; + try pwriteDbgLineNops(d_sym.file, file_pos, 0, &[0]u8{}, src_fn.len); + } }, .wasm => { const wasm_file = self.bin_file.cast(File.Wasm).?; @@ -1351,18 +1358,33 @@ pub fn commitDeclState( }, .macho => { - const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?; - const sect_index = d_sym.debug_line_section_index.?; - try d_sym.growSection(sect_index, needed_size, true); - const sect = d_sym.getSection(sect_index); - const file_pos = sect.offset + src_fn.off; - try pwriteDbgLineNops( - d_sym.file, - file_pos, - prev_padding_size, - dbg_line_buffer.items, - next_padding_size, - ); + const macho_file = self.bin_file.cast(File.MachO).?; + if (macho_file.base.isRelocatable()) { + const sect_index = macho_file.debug_line_sect_index.?; + try macho_file.growSection(sect_index, needed_size); + const sect = macho_file.sections.items(.header)[sect_index]; + const file_pos = sect.offset + src_fn.off; + try pwriteDbgLineNops( + macho_file.base.file.?, + file_pos, + prev_padding_size, + dbg_line_buffer.items, + next_padding_size, + ); + } else { + const d_sym = macho_file.getDebugSymbols().?; + const sect_index = d_sym.debug_line_section_index.?; + try d_sym.growSection(sect_index, needed_size, true, macho_file); + const sect = d_sym.getSection(sect_index); + const file_pos = sect.offset + src_fn.off; + try pwriteDbgLineNops( + d_sym.file, + file_pos, + prev_padding_size, + dbg_line_buffer.items, + next_padding_size, + ); + } }, .wasm => { @@ -1459,16 +1481,21 @@ pub fn commitDeclState( while (decl_state.exprloc_relocs.popOrNull()) |reloc| { switch (self.bin_file.tag) { .macho => { - const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?; - try d_sym.relocs.append(d_sym.allocator, .{ - .type = switch (reloc.type) { - .direct_load => .direct_load, - .got_load => .got_load, - }, - .target = reloc.target, - .offset = reloc.offset + self.getAtom(.di_atom, di_atom_index).off, - .addend = 0, - }); + const macho_file = self.bin_file.cast(File.MachO).?; + if (macho_file.base.isRelocatable()) { + // TODO + } else { + const d_sym = macho_file.getDebugSymbols().?; + try d_sym.relocs.append(d_sym.allocator, .{ + .type = switch (reloc.type) { + .direct_load => .direct_load, + .got_load => .got_load, + }, + .target = reloc.target, + .offset = reloc.offset + self.getAtom(.di_atom, di_atom_index).off, + .addend = 0, + }); + } }, .elf => {}, // TODO else => unreachable, @@ -1511,10 +1538,17 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, atom_index: Atom.Index, len: u32) try pwriteDbgInfoNops(elf_file.base.file.?, file_pos, 0, &[0]u8{}, atom.len, false); }, .macho => { - const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?; - const debug_info_sect = d_sym.getSectionPtr(d_sym.debug_info_section_index.?); - const file_pos = debug_info_sect.offset + atom.off; - try pwriteDbgInfoNops(d_sym.file, file_pos, 0, &[0]u8{}, atom.len, false); + const macho_file = self.bin_file.cast(File.MachO).?; + if (macho_file.base.isRelocatable()) { + const debug_info_sect = macho_file.sections.items(.header)[macho_file.debug_info_sect_index.?]; + const file_pos = debug_info_sect.offset + atom.off; + try pwriteDbgInfoNops(macho_file.base.file.?, file_pos, 0, &[0]u8{}, atom.len, false); + } else { + const d_sym = macho_file.getDebugSymbols().?; + const debug_info_sect = d_sym.getSectionPtr(d_sym.debug_info_section_index.?); + const file_pos = debug_info_sect.offset + atom.off; + try pwriteDbgInfoNops(d_sym.file, file_pos, 0, &[0]u8{}, atom.len, false); + } }, .wasm => { const wasm_file = self.bin_file.cast(File.Wasm).?; @@ -1597,19 +1631,35 @@ fn writeDeclDebugInfo(self: *Dwarf, atom_index: Atom.Index, dbg_info_buf: []cons }, .macho => { - const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?; - const sect_index = d_sym.debug_info_section_index.?; - try d_sym.growSection(sect_index, needed_size, true); - const sect = d_sym.getSection(sect_index); - const file_pos = sect.offset + atom.off; - try pwriteDbgInfoNops( - d_sym.file, - file_pos, - prev_padding_size, - dbg_info_buf, - next_padding_size, - trailing_zero, - ); + const macho_file = self.bin_file.cast(File.MachO).?; + if (macho_file.base.isRelocatable()) { + const sect_index = macho_file.debug_info_sect_index.?; + try macho_file.growSection(sect_index, needed_size); + const sect = macho_file.sections.items(.header)[sect_index]; + const file_pos = sect.offset + atom.off; + try pwriteDbgInfoNops( + macho_file.base.file.?, + file_pos, + prev_padding_size, + dbg_info_buf, + next_padding_size, + trailing_zero, + ); + } else { + const d_sym = macho_file.getDebugSymbols().?; + const sect_index = d_sym.debug_info_section_index.?; + try d_sym.growSection(sect_index, needed_size, true, macho_file); + const sect = d_sym.getSection(sect_index); + const file_pos = sect.offset + atom.off; + try pwriteDbgInfoNops( + d_sym.file, + file_pos, + prev_padding_size, + dbg_info_buf, + next_padding_size, + trailing_zero, + ); + } }, .wasm => { @@ -1670,10 +1720,17 @@ pub fn updateDeclLineNumber(self: *Dwarf, mod: *Module, decl_index: InternPool.D try elf_file.base.file.?.pwriteAll(&data, file_pos); }, .macho => { - const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?; - const sect = d_sym.getSection(d_sym.debug_line_section_index.?); - const file_pos = sect.offset + atom.off + self.getRelocDbgLineOff(); - try d_sym.file.pwriteAll(&data, file_pos); + const macho_file = self.bin_file.cast(File.MachO).?; + if (macho_file.base.isRelocatable()) { + const sect = macho_file.sections.items(.header)[macho_file.debug_line_sect_index.?]; + const file_pos = sect.offset + atom.off + self.getRelocDbgLineOff(); + try macho_file.base.file.?.pwriteAll(&data, file_pos); + } else { + const d_sym = macho_file.getDebugSymbols().?; + const sect = d_sym.getSection(d_sym.debug_line_section_index.?); + const file_pos = sect.offset + atom.off + self.getRelocDbgLineOff(); + try d_sym.file.pwriteAll(&data, file_pos); + } }, .wasm => { const wasm_file = self.bin_file.cast(File.Wasm).?; @@ -1877,12 +1934,21 @@ pub fn writeDbgAbbrev(self: *Dwarf) !void { try elf_file.base.file.?.pwriteAll(&abbrev_buf, file_pos); }, .macho => { - const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?; - const sect_index = d_sym.debug_abbrev_section_index.?; - try d_sym.growSection(sect_index, needed_size, false); - const sect = d_sym.getSection(sect_index); - const file_pos = sect.offset + abbrev_offset; - try d_sym.file.pwriteAll(&abbrev_buf, file_pos); + const macho_file = self.bin_file.cast(File.MachO).?; + if (macho_file.base.isRelocatable()) { + const sect_index = macho_file.debug_abbrev_sect_index.?; + try macho_file.growSection(sect_index, needed_size); + const sect = macho_file.sections.items(.header)[sect_index]; + const file_pos = sect.offset + abbrev_offset; + try macho_file.base.file.?.pwriteAll(&abbrev_buf, file_pos); + } else { + const d_sym = macho_file.getDebugSymbols().?; + const sect_index = d_sym.debug_abbrev_section_index.?; + try d_sym.growSection(sect_index, needed_size, false, macho_file); + const sect = d_sym.getSection(sect_index); + const file_pos = sect.offset + abbrev_offset; + try d_sym.file.pwriteAll(&abbrev_buf, file_pos); + } }, .wasm => { const wasm_file = self.bin_file.cast(File.Wasm).?; @@ -1967,10 +2033,17 @@ pub fn writeDbgInfoHeader(self: *Dwarf, zcu: *Module, low_pc: u64, high_pc: u64) try pwriteDbgInfoNops(elf_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt, false); }, .macho => { - const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?; - const debug_info_sect = d_sym.getSection(d_sym.debug_info_section_index.?); - const file_pos = debug_info_sect.offset; - try pwriteDbgInfoNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt, false); + const macho_file = self.bin_file.cast(File.MachO).?; + if (macho_file.base.isRelocatable()) { + const debug_info_sect = macho_file.sections.items(.header)[macho_file.debug_info_sect_index.?]; + const file_pos = debug_info_sect.offset; + try pwriteDbgInfoNops(macho_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt, false); + } else { + const d_sym = macho_file.getDebugSymbols().?; + const debug_info_sect = d_sym.getSection(d_sym.debug_info_section_index.?); + const file_pos = debug_info_sect.offset; + try pwriteDbgInfoNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt, false); + } }, .wasm => { const wasm_file = self.bin_file.cast(File.Wasm).?; @@ -2292,12 +2365,21 @@ pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void { try elf_file.base.file.?.pwriteAll(di_buf.items, file_pos); }, .macho => { - const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?; - const sect_index = d_sym.debug_aranges_section_index.?; - try d_sym.growSection(sect_index, needed_size, false); - const sect = d_sym.getSection(sect_index); - const file_pos = sect.offset; - try d_sym.file.pwriteAll(di_buf.items, file_pos); + const macho_file = self.bin_file.cast(File.MachO).?; + if (macho_file.base.isRelocatable()) { + const sect_index = macho_file.debug_aranges_sect_index.?; + try macho_file.growSection(sect_index, needed_size); + const sect = macho_file.sections.items(.header)[sect_index]; + const file_pos = sect.offset; + try macho_file.base.file.?.pwriteAll(di_buf.items, file_pos); + } else { + const d_sym = macho_file.getDebugSymbols().?; + const sect_index = d_sym.debug_aranges_section_index.?; + try d_sym.growSection(sect_index, needed_size, false, macho_file); + const sect = d_sym.getSection(sect_index); + const file_pos = sect.offset; + try d_sym.file.pwriteAll(di_buf.items, file_pos); + } }, .wasm => { const wasm_file = self.bin_file.cast(File.Wasm).?; @@ -2432,16 +2514,29 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void { try elf_file.base.file.?.pwriteAll(buffer, file_pos + delta); }, .macho => { - const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?; - const sect_index = d_sym.debug_line_section_index.?; - const needed_size: u32 = @intCast(d_sym.getSection(sect_index).size + delta); - try d_sym.growSection(sect_index, needed_size, true); - const file_pos = d_sym.getSection(sect_index).offset + first_fn.off; + const macho_file = self.bin_file.cast(File.MachO).?; + if (macho_file.base.isRelocatable()) { + const sect_index = macho_file.debug_line_sect_index.?; + const needed_size: u32 = @intCast(macho_file.sections.items(.header)[sect_index].size + delta); + try macho_file.growSection(sect_index, needed_size); + const file_pos = macho_file.sections.items(.header)[sect_index].offset + first_fn.off; - const amt = try d_sym.file.preadAll(buffer, file_pos); - if (amt != buffer.len) return error.InputOutput; + const amt = try macho_file.base.file.?.preadAll(buffer, file_pos); + if (amt != buffer.len) return error.InputOutput; - try d_sym.file.pwriteAll(buffer, file_pos + delta); + try macho_file.base.file.?.pwriteAll(buffer, file_pos + delta); + } else { + const d_sym = macho_file.getDebugSymbols().?; + const sect_index = d_sym.debug_line_section_index.?; + const needed_size: u32 = @intCast(d_sym.getSection(sect_index).size + delta); + try d_sym.growSection(sect_index, needed_size, true, macho_file); + const file_pos = d_sym.getSection(sect_index).offset + first_fn.off; + + const amt = try d_sym.file.preadAll(buffer, file_pos); + if (amt != buffer.len) return error.InputOutput; + + try d_sym.file.pwriteAll(buffer, file_pos + delta); + } }, .wasm => { const wasm_file = self.bin_file.cast(File.Wasm).?; @@ -2487,10 +2582,17 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void { try pwriteDbgLineNops(elf_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt); }, .macho => { - const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?; - const debug_line_sect = d_sym.getSection(d_sym.debug_line_section_index.?); - const file_pos = debug_line_sect.offset; - try pwriteDbgLineNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt); + const macho_file = self.bin_file.cast(File.MachO).?; + if (macho_file.base.isRelocatable()) { + const debug_line_sect = macho_file.sections.items(.header)[macho_file.debug_line_sect_index.?]; + const file_pos = debug_line_sect.offset; + try pwriteDbgLineNops(macho_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt); + } else { + const d_sym = macho_file.getDebugSymbols().?; + const debug_line_sect = d_sym.getSection(d_sym.debug_line_section_index.?); + const file_pos = debug_line_sect.offset; + try pwriteDbgLineNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt); + } }, .wasm => { const wasm_file = self.bin_file.cast(File.Wasm).?; @@ -2608,9 +2710,15 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void { break :pos debug_info_sect.sh_offset; }, .macho => pos: { - const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?; - const debug_info_sect = d_sym.getSectionPtr(d_sym.debug_info_section_index.?); - break :pos debug_info_sect.offset; + const macho_file = self.bin_file.cast(File.MachO).?; + if (macho_file.base.isRelocatable()) { + const debug_info_sect = &macho_file.sections.items(.header)[macho_file.debug_info_sect_index.?]; + break :pos debug_info_sect.offset; + } else { + const d_sym = macho_file.getDebugSymbols().?; + const debug_info_sect = d_sym.getSectionPtr(d_sym.debug_info_section_index.?); + break :pos debug_info_sect.offset; + } }, // for wasm, the offset is always 0 as we write to memory first .wasm => 0, @@ -2628,8 +2736,13 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void { try elf_file.base.file.?.pwriteAll(&buf, file_pos + atom.off + reloc.offset); }, .macho => { - const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?; - try d_sym.file.pwriteAll(&buf, file_pos + atom.off + reloc.offset); + const macho_file = self.bin_file.cast(File.MachO).?; + if (macho_file.base.isRelocatable()) { + try macho_file.base.file.?.pwriteAll(&buf, file_pos + atom.off + reloc.offset); + } else { + const d_sym = macho_file.getDebugSymbols().?; + try d_sym.file.pwriteAll(&buf, file_pos + atom.off + reloc.offset); + } }, .wasm => { const wasm_file = self.bin_file.cast(File.Wasm).?; @@ -2653,8 +2766,13 @@ fn addDIFile(self: *Dwarf, mod: *Module, decl_index: InternPool.DeclIndex) !u28 elf_file.markDirty(elf_file.debug_line_section_index.?); }, .macho => { - const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?; - d_sym.markDirty(d_sym.debug_line_section_index.?); + const macho_file = self.bin_file.cast(File.MachO).?; + if (macho_file.base.isRelocatable()) { + macho_file.markDirty(macho_file.debug_line_sect_index.?); + } else { + const d_sym = macho_file.getDebugSymbols().?; + d_sym.markDirty(d_sym.debug_line_section_index.?, macho_file); + } }, .wasm => {}, else => unreachable, diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 84de97f4e9..7dfafef8eb 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -103,6 +103,14 @@ zig_const_sect_index: ?u8 = null, zig_data_sect_index: ?u8 = null, zig_bss_sect_index: ?u8 = null, +/// Tracked DWARF section headers that apply only when we emit relocatable. +/// For executable and loadable images, DWARF is tracked directly by dSYM bundle object. +debug_info_sect_index: ?u8 = null, +debug_abbrev_sect_index: ?u8 = null, +debug_str_sect_index: ?u8 = null, +debug_aranges_sect_index: ?u8 = null, +debug_line_sect_index: ?u8 = null, + has_tlv: bool = false, binds_to_weak: bool = false, weak_defines: bool = false, @@ -255,44 +263,15 @@ pub fn createEmpty( )}), } }); self.zig_object = index; - try self.getZigObject().?.init(self); + const zo = self.getZigObject().?; + try zo.init(self); + try self.initMetadata(.{ + .emit = emit, + .zo = zo, .symbol_count_hint = options.symbol_count_hint, .program_code_size_hint = options.program_code_size_hint, }); - - switch (comp.config.debug_format) { - .strip => {}, - .dwarf => if (!self.base.isRelocatable()) { - // Create dSYM bundle. - log.debug("creating {s}.dSYM bundle", .{emit.sub_path}); - - const sep = fs.path.sep_str; - const d_sym_path = try std.fmt.allocPrint( - arena, - "{s}.dSYM" ++ sep ++ "Contents" ++ sep ++ "Resources" ++ sep ++ "DWARF", - .{emit.sub_path}, - ); - - var d_sym_bundle = try emit.directory.handle.makeOpenPath(d_sym_path, .{}); - defer d_sym_bundle.close(); - - const d_sym_file = try d_sym_bundle.createFile(emit.sub_path, .{ - .truncate = false, - .read = true, - }); - - self.d_sym = .{ - .allocator = gpa, - .dwarf = link.File.Dwarf.init(&self.base, .dwarf32), - .file = d_sym_file, - }; - try self.d_sym.?.initMetadata(self); - } else { - @panic("TODO: implement generating and emitting __DWARF in .o file"); - }, - .code_view => unreachable, - } } } @@ -978,7 +957,6 @@ fn parseObject(self: *MachO, path: []const u8) ParseError!void { const gpa = self.base.comp.gpa; const file = try std.fs.cwd().openFile(path, .{}); - errdefer file.close(); const handle = try self.addFileHandle(file); const mtime: u64 = mtime: { const stat = file.stat() catch break :mtime 0; @@ -1015,7 +993,6 @@ fn parseArchive(self: *MachO, lib: SystemLib, must_link: bool, fat_arch: ?fat.Ar const gpa = self.base.comp.gpa; const file = try std.fs.cwd().openFile(lib.path, .{}); - errdefer file.close(); const handle = try self.addFileHandle(file); var archive = Archive{}; @@ -2015,6 +1992,11 @@ pub fn sortSections(self: *MachO) !void { &self.eh_frame_sect_index, &self.unwind_info_sect_index, &self.objc_stubs_sect_index, + &self.debug_info_sect_index, + &self.debug_str_sect_index, + &self.debug_line_sect_index, + &self.debug_abbrev_sect_index, + &self.debug_info_sect_index, }) |maybe_index| { if (maybe_index.*) |*index| { index.* = backlinks[index.*]; @@ -2314,11 +2296,11 @@ fn allocateSections(self: *MachO) !void { // Must move the entire section. const new_offset = self.findFreeSpace(existing_size, page_size); - log.debug("new '{s},{s}' file offset 0x{x} to 0x{x}", .{ + log.debug("moving '{s},{s}' from 0x{x} to 0x{x}", .{ header.segName(), header.sectName(), + header.offset, new_offset, - new_offset + existing_size, }); try self.copyRangeAllZeroOut(header.offset, new_offset, existing_size); @@ -3152,7 +3134,7 @@ pub fn updateDecl(self: *MachO, mod: *Module, decl_index: InternPool.DeclIndex) pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl_index: InternPool.DeclIndex) !void { if (self.llvm_object) |_| return; - return self.getZigObject().?.updateDeclLineNumber(self, module, decl_index); + return self.getZigObject().?.updateDeclLineNumber(module, decl_index); } pub fn updateExports( @@ -3221,7 +3203,7 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { for (self.sections.items(.header)) |header| { if (header.isZerofill()) continue; const increased_size = padToIdeal(header.size); - const test_end = header.offset + increased_size; + const test_end = header.offset +| increased_size; if (end > header.offset and start < test_end) { return test_end; } @@ -3249,7 +3231,7 @@ fn detectAllocCollisionVirtual(self: *MachO, start: u64, size: u64) ?u64 { for (self.sections.items(.header)) |header| { const increased_size = padToIdeal(header.size); - const test_end = header.addr + increased_size; + const test_end = header.addr +| increased_size; if (end > header.addr and start < test_end) { return test_end; } @@ -3266,27 +3248,39 @@ fn detectAllocCollisionVirtual(self: *MachO, start: u64, size: u64) ?u64 { return null; } -fn allocatedSize(self: *MachO, start: u64) u64 { +pub fn allocatedSize(self: *MachO, start: u64) u64 { if (start == 0) return 0; + var min_pos: u64 = std.math.maxInt(u64); + for (self.sections.items(.header)) |header| { if (header.offset <= start) continue; if (header.offset < min_pos) min_pos = header.offset; } + for (self.segments.items) |seg| { if (seg.fileoff <= start) continue; if (seg.fileoff < min_pos) min_pos = seg.fileoff; } + return min_pos - start; } -fn allocatedSizeVirtual(self: *MachO, start: u64) u64 { +pub fn allocatedSizeVirtual(self: *MachO, start: u64) u64 { if (start == 0) return 0; + var min_pos: u64 = std.math.maxInt(u64); + + for (self.sections.items(.header)) |header| { + if (header.addr <= start) continue; + if (header.addr < min_pos) min_pos = header.addr; + } + for (self.segments.items) |seg| { if (seg.vmaddr <= start) continue; if (seg.vmaddr < min_pos) min_pos = seg.vmaddr; } + return min_pos - start; } @@ -3325,6 +3319,8 @@ fn copyRangeAllZeroOut(self: *MachO, old_offset: u64, new_offset: u64, size: u64 } const InitMetadataOptions = struct { + emit: Compilation.Emit, + zo: *ZigObject, symbol_count_hint: u64, program_code_size_hint: u64, }; @@ -3393,6 +3389,31 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void { .prot = macho.PROT.READ | macho.PROT.WRITE, }); } + + if (options.zo.dwarf) |_| { + // Create dSYM bundle. + log.debug("creating {s}.dSYM bundle", .{options.emit.sub_path}); + + const gpa = self.base.comp.gpa; + const sep = fs.path.sep_str; + const d_sym_path = try std.fmt.allocPrint( + gpa, + "{s}.dSYM" ++ sep ++ "Contents" ++ sep ++ "Resources" ++ sep ++ "DWARF", + .{options.emit.sub_path}, + ); + defer gpa.free(d_sym_path); + + var d_sym_bundle = try options.emit.directory.handle.makeOpenPath(d_sym_path, .{}); + defer d_sym_bundle.close(); + + const d_sym_file = try d_sym_bundle.createFile(options.emit.sub_path, .{ + .truncate = false, + .read = true, + }); + + self.d_sym = .{ .allocator = gpa, .file = d_sym_file }; + try self.d_sym.?.initMetadata(self); + } } const appendSect = struct { @@ -3470,6 +3491,44 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void { appendSect(self, self.zig_bss_sect_index.?, self.zig_bss_seg_index.?); } } + + if (self.base.isRelocatable() and options.zo.dwarf != null) { + { + self.debug_str_sect_index = try self.addSection("__DWARF", "__debug_str", .{ + .flags = macho.S_ATTR_DEBUG, + }); + try allocSect(self, self.debug_str_sect_index.?, 200); + } + + { + self.debug_info_sect_index = try self.addSection("__DWARF", "__debug_info", .{ + .flags = macho.S_ATTR_DEBUG, + }); + try allocSect(self, self.debug_info_sect_index.?, 200); + } + + { + self.debug_abbrev_sect_index = try self.addSection("__DWARF", "__debug_abbrev", .{ + .flags = macho.S_ATTR_DEBUG, + }); + try allocSect(self, self.debug_abbrev_sect_index.?, 128); + } + + { + self.debug_aranges_sect_index = try self.addSection("__DWARF", "__debug_aranges", .{ + .alignment = 4, + .flags = macho.S_ATTR_DEBUG, + }); + try allocSect(self, self.debug_aranges_sect_index.?, 160); + } + + { + self.debug_line_sect_index = try self.addSection("__DWARF", "__debug_line", .{ + .flags = macho.S_ATTR_DEBUG, + }); + try allocSect(self, self.debug_line_sect_index.?, 250); + } + } } pub fn growSection(self: *MachO, sect_index: u8, needed_size: u64) !void { @@ -3491,11 +3550,11 @@ fn growSectionNonRelocatable(self: *MachO, sect_index: u8, needed_size: u64) !vo const alignment = self.getPageSize(); const new_offset = self.findFreeSpace(needed_size, alignment); - log.debug("new '{s},{s}' file offset 0x{x} to 0x{x}", .{ + log.debug("moving '{s},{s}' from 0x{x} to 0x{x}", .{ sect.segName(), sect.sectName(), + sect.offset, new_offset, - new_offset + existing_size, }); try self.copyRangeAllZeroOut(sect.offset, new_offset, existing_size); @@ -3557,6 +3616,22 @@ fn growSectionRelocatable(self: *MachO, sect_index: u8, needed_size: u64) !void sect.size = needed_size; } +pub fn markDirty(self: *MachO, sect_index: u8) void { + if (self.getZigObject()) |zo| { + if (self.debug_info_sect_index.? == sect_index) { + zo.debug_info_header_dirty = true; + } else if (self.debug_line_sect_index.? == sect_index) { + zo.debug_line_header_dirty = true; + } else if (self.debug_abbrev_sect_index.? == sect_index) { + zo.debug_abbrev_dirty = true; + } else if (self.debug_str_sect_index.? == sect_index) { + zo.debug_strtab_dirty = true; + } else if (self.debug_aranges_sect_index.? == sect_index) { + zo.debug_aranges_dirty = true; + } + } +} + pub fn getTarget(self: MachO) std.Target { return self.base.comp.root_mod.resolved_target.result; } @@ -3632,6 +3707,21 @@ pub fn isZigSection(self: MachO, sect_id: u8) bool { return false; } +pub fn isDebugSection(self: MachO, sect_id: u8) bool { + inline for (&[_]?u8{ + self.debug_info_sect_index, + self.debug_abbrev_sect_index, + self.debug_str_sect_index, + self.debug_aranges_sect_index, + self.debug_line_sect_index, + }) |maybe_index| { + if (maybe_index) |index| { + if (index == sect_id) return true; + } + } + return false; +} + pub fn addSegment(self: *MachO, name: []const u8, opts: struct { vmaddr: u64 = 0, vmsize: u64 = 0, diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index 10f6717118..959a88b4da 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -1,5 +1,4 @@ allocator: Allocator, -dwarf: Dwarf, file: fs.File, symtab_cmd: macho.symtab_command = .{}, @@ -17,12 +16,6 @@ debug_str_section_index: ?u8 = null, debug_aranges_section_index: ?u8 = null, debug_line_section_index: ?u8 = null, -debug_string_table_dirty: bool = false, -debug_abbrev_section_dirty: bool = false, -debug_aranges_section_dirty: bool = false, -debug_info_header_dirty: bool = false, -debug_line_header_dirty: bool = false, - relocs: std.ArrayListUnmanaged(Reloc) = .{}, /// Output synthetic sections @@ -44,7 +37,7 @@ pub const Reloc = struct { pub fn initMetadata(self: *DebugSymbols, macho_file: *MachO) !void { try self.strtab.append(self.allocator, 0); - if (self.dwarf_segment_cmd_index == null) { + { self.dwarf_segment_cmd_index = @as(u8, @intCast(self.segments.items.len)); const page_size = macho_file.getPageSize(); @@ -63,46 +56,19 @@ pub fn initMetadata(self: *DebugSymbols, macho_file: *MachO) !void { }); } - if (self.debug_str_section_index == null) { - assert(self.dwarf.strtab.buffer.items.len == 0); - try self.dwarf.strtab.buffer.append(self.allocator, 0); - self.debug_str_section_index = try self.allocateSection( - "__debug_str", - @as(u32, @intCast(self.dwarf.strtab.buffer.items.len)), - 0, - ); - self.debug_string_table_dirty = true; - } + self.debug_str_section_index = try self.allocateSection("__debug_str", 200, 0); + self.debug_info_section_index = try self.allocateSection("__debug_info", 200, 0); + self.debug_abbrev_section_index = try self.allocateSection("__debug_abbrev", 128, 0); + self.debug_aranges_section_index = try self.allocateSection("__debug_aranges", 160, 4); + self.debug_line_section_index = try self.allocateSection("__debug_line", 250, 0); - if (self.debug_info_section_index == null) { - self.debug_info_section_index = try self.allocateSection("__debug_info", 200, 0); - self.debug_info_header_dirty = true; - } - - if (self.debug_abbrev_section_index == null) { - self.debug_abbrev_section_index = try self.allocateSection("__debug_abbrev", 128, 0); - self.debug_abbrev_section_dirty = true; - } - - if (self.debug_aranges_section_index == null) { - self.debug_aranges_section_index = try self.allocateSection("__debug_aranges", 160, 4); - self.debug_aranges_section_dirty = true; - } - - if (self.debug_line_section_index == null) { - self.debug_line_section_index = try self.allocateSection("__debug_line", 250, 0); - self.debug_line_header_dirty = true; - } - - if (self.linkedit_segment_cmd_index == null) { - self.linkedit_segment_cmd_index = @as(u8, @intCast(self.segments.items.len)); - try self.segments.append(self.allocator, .{ - .segname = makeStaticString("__LINKEDIT"), - .maxprot = macho.PROT.READ, - .initprot = macho.PROT.READ, - .cmdsize = @sizeOf(macho.segment_command_64), - }); - } + self.linkedit_segment_cmd_index = @as(u8, @intCast(self.segments.items.len)); + try self.segments.append(self.allocator, .{ + .segname = makeStaticString("__LINKEDIT"), + .maxprot = macho.PROT.READ, + .initprot = macho.PROT.READ, + .cmdsize = @sizeOf(macho.segment_command_64), + }); } fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignment: u16) !u8 { @@ -133,7 +99,13 @@ fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignme return index; } -pub fn growSection(self: *DebugSymbols, sect_index: u8, needed_size: u32, requires_file_copy: bool) !void { +pub fn growSection( + self: *DebugSymbols, + sect_index: u8, + needed_size: u32, + requires_file_copy: bool, + macho_file: *MachO, +) !void { const sect = self.getSectionPtr(sect_index); if (needed_size > self.allocatedSize(sect.offset)) { @@ -162,20 +134,22 @@ pub fn growSection(self: *DebugSymbols, sect_index: u8, needed_size: u32, requir } sect.size = needed_size; - self.markDirty(sect_index); + self.markDirty(sect_index, macho_file); } -pub fn markDirty(self: *DebugSymbols, sect_index: u8) void { - if (self.debug_info_section_index.? == sect_index) { - self.debug_info_header_dirty = true; - } else if (self.debug_line_section_index.? == sect_index) { - self.debug_line_header_dirty = true; - } else if (self.debug_abbrev_section_index.? == sect_index) { - self.debug_abbrev_section_dirty = true; - } else if (self.debug_str_section_index.? == sect_index) { - self.debug_string_table_dirty = true; - } else if (self.debug_aranges_section_index.? == sect_index) { - self.debug_aranges_section_dirty = true; +pub fn markDirty(self: *DebugSymbols, sect_index: u8, macho_file: *MachO) void { + if (macho_file.getZigObject()) |zo| { + if (self.debug_info_section_index.? == sect_index) { + zo.debug_info_header_dirty = true; + } else if (self.debug_line_section_index.? == sect_index) { + zo.debug_line_header_dirty = true; + } else if (self.debug_abbrev_section_index.? == sect_index) { + zo.debug_abbrev_dirty = true; + } else if (self.debug_str_section_index.? == sect_index) { + zo.debug_strtab_dirty = true; + } else if (self.debug_aranges_section_index.? == sect_index) { + zo.debug_aranges_dirty = true; + } } } @@ -201,13 +175,6 @@ fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64) u64 } pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void { - const comp = macho_file.base.comp; - // TODO This linker code currently assumes there is only 1 compilation unit - // and it corresponds to the Zig source code. - const zcu = comp.module orelse return error.LinkingWithoutZigSourceUnimplemented; - - try self.dwarf.flushModule(zcu); - for (self.relocs.items) |*reloc| { const sym = macho_file.getSymbol(reloc.target); const sym_name = sym.getName(macho_file); @@ -226,54 +193,12 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void { try self.file.pwriteAll(mem.asBytes(&addr), file_offset); } - if (self.debug_abbrev_section_dirty) { - try self.dwarf.writeDbgAbbrev(); - self.debug_abbrev_section_dirty = false; - } - - if (self.debug_info_header_dirty) { - // Currently only one compilation unit is supported, so the address range is simply - // identical to the main program header virtual address and memory size. - const text_section = macho_file.sections.items(.header)[macho_file.zig_text_sect_index.?]; - const low_pc = text_section.addr; - const high_pc = text_section.addr + text_section.size; - try self.dwarf.writeDbgInfoHeader(zcu, low_pc, high_pc); - self.debug_info_header_dirty = false; - } - - if (self.debug_aranges_section_dirty) { - // Currently only one compilation unit is supported, so the address range is simply - // identical to the main program header virtual address and memory size. - const text_section = macho_file.sections.items(.header)[macho_file.zig_text_sect_index.?]; - try self.dwarf.writeDbgAranges(text_section.addr, text_section.size); - self.debug_aranges_section_dirty = false; - } - - if (self.debug_line_header_dirty) { - try self.dwarf.writeDbgLineHeader(); - self.debug_line_header_dirty = false; - } - - { - const sect_index = self.debug_str_section_index.?; - if (self.debug_string_table_dirty or self.dwarf.strtab.buffer.items.len != self.getSection(sect_index).size) { - const needed_size = @as(u32, @intCast(self.dwarf.strtab.buffer.items.len)); - try self.growSection(sect_index, needed_size, false); - try self.file.pwriteAll(self.dwarf.strtab.buffer.items, self.getSection(sect_index).offset); - self.debug_string_table_dirty = false; - } - } - self.finalizeDwarfSegment(macho_file); try self.writeLinkeditSegmentData(macho_file); // Write load commands const ncmds, const sizeofcmds = try self.writeLoadCommands(macho_file); try self.writeHeader(macho_file, ncmds, sizeofcmds); - - assert(!self.debug_abbrev_section_dirty); - assert(!self.debug_aranges_section_dirty); - assert(!self.debug_string_table_dirty); } pub fn deinit(self: *DebugSymbols) void { @@ -281,7 +206,6 @@ pub fn deinit(self: *DebugSymbols) void { self.file.close(); self.segments.deinit(gpa); self.sections.deinit(gpa); - self.dwarf.deinit(); self.relocs.deinit(gpa); self.symtab.deinit(gpa); self.strtab.deinit(gpa); @@ -534,7 +458,6 @@ const padToIdeal = MachO.padToIdeal; const trace = @import("../../tracy.zig").trace; const Allocator = mem.Allocator; -const Dwarf = @import("../Dwarf.zig"); const MachO = @import("../MachO.zig"); const StringTable = @import("../StringTable.zig"); const Type = @import("../../type.zig").Type; diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index ecd2470733..3a28e824d5 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -46,16 +46,38 @@ tlv_initializers: TlvInitializerTable = .{}, /// A table of relocations. relocs: RelocationTable = .{}, +dwarf: ?Dwarf = null, + dynamic_relocs: MachO.DynamicRelocs = .{}, output_symtab_ctx: MachO.SymtabCtx = .{}, output_ar_state: Archive.ArState = .{}, +debug_strtab_dirty: bool = false, +debug_abbrev_dirty: bool = false, +debug_aranges_dirty: bool = false, +debug_info_header_dirty: bool = false, +debug_line_header_dirty: bool = false, + pub fn init(self: *ZigObject, macho_file: *MachO) !void { const comp = macho_file.base.comp; const gpa = comp.gpa; try self.atoms.append(gpa, 0); // null input section try self.strtab.buffer.append(gpa, 0); + + switch (comp.config.debug_format) { + .strip => {}, + .dwarf => |v| { + assert(v == .@"32"); + self.dwarf = Dwarf.init(&macho_file.base, .dwarf32); + self.debug_strtab_dirty = true; + self.debug_abbrev_dirty = true; + self.debug_aranges_dirty = true; + self.debug_info_header_dirty = true; + self.debug_line_header_dirty = true; + }, + .code_view => unreachable, + } } pub fn deinit(self: *ZigObject, allocator: Allocator) void { @@ -101,6 +123,10 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void { tlv_init.deinit(allocator); } self.tlv_initializers.deinit(allocator); + + if (self.dwarf) |*dw| { + dw.deinit(); + } } fn addNlist(self: *ZigObject, allocator: Allocator) !Symbol.Index { @@ -407,6 +433,66 @@ pub fn flushModule(self: *ZigObject, macho_file: *MachO) !void { if (metadata.text_state != .unused) metadata.text_state = .flushed; if (metadata.const_state != .unused) metadata.const_state = .flushed; } + + if (self.dwarf) |*dw| { + const zcu = macho_file.base.comp.module.?; + try dw.flushModule(zcu); + + if (self.debug_abbrev_dirty) { + try dw.writeDbgAbbrev(); + self.debug_abbrev_dirty = false; + } + + if (self.debug_info_header_dirty) { + // Currently only one compilation unit is supported, so the address range is simply + // identical to the main program header virtual address and memory size. + const text_section = macho_file.sections.items(.header)[macho_file.zig_text_sect_index.?]; + const low_pc = text_section.addr; + const high_pc = text_section.addr + text_section.size; + try dw.writeDbgInfoHeader(zcu, low_pc, high_pc); + self.debug_info_header_dirty = false; + } + + if (self.debug_aranges_dirty) { + // Currently only one compilation unit is supported, so the address range is simply + // identical to the main program header virtual address and memory size. + const text_section = macho_file.sections.items(.header)[macho_file.zig_text_sect_index.?]; + try dw.writeDbgAranges(text_section.addr, text_section.size); + self.debug_aranges_dirty = false; + } + + if (self.debug_line_header_dirty) { + try dw.writeDbgLineHeader(); + self.debug_line_header_dirty = false; + } + + if (!macho_file.base.isRelocatable()) { + const d_sym = macho_file.getDebugSymbols().?; + const sect_index = d_sym.debug_str_section_index.?; + if (self.debug_strtab_dirty or dw.strtab.buffer.items.len != d_sym.getSection(sect_index).size) { + const needed_size = @as(u32, @intCast(dw.strtab.buffer.items.len)); + try d_sym.growSection(sect_index, needed_size, false, macho_file); + try d_sym.file.pwriteAll(dw.strtab.buffer.items, d_sym.getSection(sect_index).offset); + self.debug_strtab_dirty = false; + } + } else { + const sect_index = macho_file.debug_str_sect_index.?; + if (self.debug_strtab_dirty or dw.strtab.buffer.items.len != macho_file.sections.items(.header)[sect_index].size) { + const needed_size = @as(u32, @intCast(dw.strtab.buffer.items.len)); + try macho_file.growSection(sect_index, needed_size); + try macho_file.base.file.?.pwriteAll(dw.strtab.buffer.items, macho_file.sections.items(.header)[sect_index].offset); + self.debug_strtab_dirty = false; + } + } + } + + // The point of flushModule() is to commit changes, so in theory, nothing should + // be dirty after this. However, it is possible for some things to remain + // dirty because they fail to be written in the event of compile errors, + // such as debug_line_header_dirty and debug_info_header_dirty. + assert(!self.debug_abbrev_dirty); + assert(!self.debug_aranges_dirty); + assert(!self.debug_strtab_dirty); } pub fn getDeclVAddr( @@ -572,7 +658,7 @@ pub fn updateFunc( var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); - var decl_state: ?Dwarf.DeclState = if (macho_file.getDebugSymbols()) |d_sym| try d_sym.dwarf.initDeclState(mod, decl_index) else null; + var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(mod, decl_index) else null; defer if (decl_state) |*ds| ds.deinit(); const dio: codegen.DebugInfoOutput = if (decl_state) |*ds| .{ .dwarf = ds } else .none; @@ -600,7 +686,7 @@ pub fn updateFunc( if (decl_state) |*ds| { const sym = macho_file.getSymbol(sym_index); - try macho_file.getDebugSymbols().?.dwarf.commitDeclState( + try self.dwarf.?.commitDeclState( mod, decl_index, sym.getAddress(.{}, macho_file), @@ -647,7 +733,7 @@ pub fn updateDecl( var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); - var decl_state: ?Dwarf.DeclState = if (macho_file.getDebugSymbols()) |d_sym| try d_sym.dwarf.initDeclState(mod, decl_index) else null; + var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(mod, decl_index) else null; defer if (decl_state) |*ds| ds.deinit(); const decl_val = if (decl.val.getVariable(mod)) |variable| Value.fromInterned(variable.init) else decl.val; @@ -681,7 +767,7 @@ pub fn updateDecl( if (decl_state) |*ds| { const sym = macho_file.getSymbol(sym_index); - try macho_file.getDebugSymbols().?.dwarf.commitDeclState( + try self.dwarf.?.commitDeclState( mod, decl_index, sym.getAddress(.{}, macho_file), @@ -1257,15 +1343,9 @@ fn updateLazySymbol( } /// Must be called only after a successful call to `updateDecl`. -pub fn updateDeclLineNumber( - self: *ZigObject, - macho_file: *MachO, - mod: *Module, - decl_index: InternPool.DeclIndex, -) !void { - _ = self; - if (macho_file.getDebugSymbols()) |d_sym| { - try d_sym.dwarf.updateDeclLineNumber(mod, decl_index); +pub fn updateDeclLineNumber(self: *ZigObject, mod: *Module, decl_index: InternPool.DeclIndex) !void { + if (self.dwarf) |*dw| { + try dw.updateDeclLineNumber(mod, decl_index); } } diff --git a/src/link/MachO/relocatable.zig b/src/link/MachO/relocatable.zig index 8f5bf97696..1600328ced 100644 --- a/src/link/MachO/relocatable.zig +++ b/src/link/MachO/relocatable.zig @@ -193,8 +193,8 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? // Update file offsets of contributing objects const total_size: usize = blk: { var pos: usize = Archive.SARMAG; - pos += @sizeOf(Archive.ar_hdr) + Archive.SYMDEF.len + 1; - pos = mem.alignForward(usize, pos, ptr_width); + pos += @sizeOf(Archive.ar_hdr); + pos += mem.alignForward(usize, Archive.SYMDEF.len + 1, ptr_width); pos += ar_symtab.size(format); for (files.items) |index| { @@ -209,10 +209,10 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? .object => |x| x.path, else => unreachable, }; - pos = mem.alignForward(usize, pos, ptr_width); + pos = mem.alignForward(usize, pos, 2); state.file_off = pos; - pos += @sizeOf(Archive.ar_hdr) + path.len + 1; - pos = mem.alignForward(usize, pos, ptr_width); + pos += @sizeOf(Archive.ar_hdr); + pos += mem.alignForward(usize, path.len + 1, ptr_width); pos += math.cast(usize, state.size) orelse return error.Overflow; } @@ -236,7 +236,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? // Write object files for (files.items) |index| { - const aligned = mem.alignForward(usize, buffer.items.len, ptr_width); + const aligned = mem.alignForward(usize, buffer.items.len, 2); const padding = aligned - buffer.items.len; if (padding > 0) { try writer.writeByteNTimes(0, padding); @@ -402,7 +402,7 @@ fn calcSectionSizes(macho_file: *MachO) !void { const atom = macho_file.getAtom(atom_index) orelse continue; if (!atom.flags.alive) continue; const header = &macho_file.sections.items(.header)[atom.out_n_sect]; - if (!macho_file.isZigSection(atom.out_n_sect)) continue; + if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue; header.nreloc += atom.calcNumRelocs(macho_file); } } @@ -436,18 +436,20 @@ fn calcCompactUnwindSize(macho_file: *MachO, sect_index: u8) void { fn allocateSections(macho_file: *MachO) !void { const slice = macho_file.sections.slice(); - - const last_index = for (0..slice.items(.header).len) |i| { - if (macho_file.isZigSection(@intCast(i))) break i; - } else slice.items(.header).len; - - for (slice.items(.header)[0..last_index]) |*header| { + for (slice.items(.header)) |*header| { + const needed_size = header.size; + header.size = 0; const alignment = try math.powi(u32, 2, header.@"align"); if (!header.isZerofill()) { - header.offset = math.cast(u32, macho_file.findFreeSpace(header.size, alignment)) orelse - return error.Overflow; + if (needed_size > macho_file.allocatedSize(header.offset)) { + header.offset = math.cast(u32, macho_file.findFreeSpace(needed_size, alignment)) orelse + return error.Overflow; + } } - header.addr = macho_file.findFreeSpaceVirtual(header.size, alignment); + if (needed_size > macho_file.allocatedSizeVirtual(header.addr)) { + header.addr = macho_file.findFreeSpaceVirtual(needed_size, alignment); + } + header.size = needed_size; } } @@ -539,7 +541,7 @@ fn writeAtoms(macho_file: *MachO) !void { for (slice.items(.header), slice.items(.atoms), 0..) |header, atoms, i| { if (atoms.items.len == 0) continue; if (header.isZerofill()) continue; - if (macho_file.isZigSection(@intCast(i))) continue; + if (macho_file.isZigSection(@intCast(i)) or macho_file.isDebugSection(@intCast(i))) continue; const size = math.cast(usize, header.size) orelse return error.Overflow; const code = try gpa.alloc(u8, size); @@ -580,7 +582,7 @@ fn writeAtoms(macho_file: *MachO) !void { for (macho_file.sections.items(.header), 0..) |header, n_sect| { if (header.isZerofill()) continue; - if (!macho_file.isZigSection(@intCast(n_sect))) continue; + if (!macho_file.isZigSection(@intCast(n_sect)) and !macho_file.isDebugSection(@intCast(n_sect))) continue; const gop = try relocs.getOrPut(@intCast(n_sect)); if (gop.found_existing) continue; gop.value_ptr.* = try std.ArrayList(macho.relocation_info).initCapacity(gpa, header.nreloc); @@ -591,7 +593,7 @@ fn writeAtoms(macho_file: *MachO) !void { if (!atom.flags.alive) continue; const header = macho_file.sections.items(.header)[atom.out_n_sect]; if (header.isZerofill()) continue; - if (!macho_file.isZigSection(atom.out_n_sect)) continue; + if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue; if (atom.getRelocs(macho_file).len == 0) continue; const atom_size = math.cast(usize, atom.size) orelse return error.Overflow; const code = try gpa.alloc(u8, atom_size); diff --git a/test/link/macho.zig b/test/link/macho.zig index 6d8a6062de..9208995ae0 100644 --- a/test/link/macho.zig +++ b/test/link/macho.zig @@ -20,8 +20,11 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step { }; // Exercise linker with self-hosted backend (no LLVM) + macho_step.dependOn(testEmptyZig(b, .{ .use_llvm = false, .target = x86_64_target })); macho_step.dependOn(testHelloZig(b, .{ .use_llvm = false, .target = x86_64_target })); - macho_step.dependOn(testRelocatableZig(b, .{ .use_llvm = false, .strip = true, .target = x86_64_target })); + macho_step.dependOn(testLinkingStaticLib(b, .{ .use_llvm = false, .target = x86_64_target })); + macho_step.dependOn(testReexportsZig(b, .{ .use_llvm = false, .target = x86_64_target })); + macho_step.dependOn(testRelocatableZig(b, .{ .use_llvm = false, .target = x86_64_target })); // Exercise linker with LLVM backend macho_step.dependOn(testDeadStrip(b, .{ .target = default_target })); @@ -33,6 +36,7 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step { macho_step.dependOn(testHelloZig(b, .{ .target = default_target })); macho_step.dependOn(testLargeBss(b, .{ .target = default_target })); macho_step.dependOn(testLayout(b, .{ .target = default_target })); + macho_step.dependOn(testLinkingStaticLib(b, .{ .target = default_target })); macho_step.dependOn(testLinksection(b, .{ .target = default_target })); macho_step.dependOn(testMhExecuteHeader(b, .{ .target = default_target })); macho_step.dependOn(testNoDeadStrip(b, .{ .target = default_target })); @@ -843,6 +847,45 @@ fn testLinkDirectlyCppTbd(b: *Build, opts: Options) *Step { return test_step; } +fn testLinkingStaticLib(b: *Build, opts: Options) *Step { + const test_step = addTestStep(b, "linking-static-lib", opts); + + const obj = addObject(b, opts, .{ + .name = "bobj", + .zig_source_bytes = "export var bar: i32 = -42;", + .strip = true, // TODO for self-hosted, we don't really emit any valid DWARF yet since we only export a global + }); + + const lib = addStaticLibrary(b, opts, .{ + .name = "alib", + .zig_source_bytes = + \\export fn foo() i32 { + \\ return 42; + \\} + , + }); + lib.addObject(obj); + + const exe = addExecutable(b, opts, .{ + .name = "testlib", + .zig_source_bytes = + \\const std = @import("std"); + \\extern fn foo() i32; + \\extern var bar: i32; + \\pub fn main() void { + \\ std.debug.print("{d}\n", .{foo() + bar}); + \\} + , + }); + exe.linkLibrary(lib); + + const run = addRunArtifact(exe); + run.expectStdErrEqual("0\n"); + test_step.dependOn(&run.step); + + return test_step; +} + fn testLinksection(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "macho-linksection", opts); @@ -1243,14 +1286,7 @@ fn testRelocatableZig(b: *Build, opts: Options) *Step { const run = addRunArtifact(exe); run.addCheck(.{ .expect_stderr_match = b.dupe("incrFoo=1") }); run.addCheck(.{ .expect_stderr_match = b.dupe("decrFoo=0") }); - if (opts.use_llvm) { - // TODO: enable this once self-hosted can print panics and stack traces - run.addCheck(.{ .expect_stderr_match = b.dupe("panic: Oh no!") }); - } - if (builtin.os.tag == .macos) { - const signal: u32 = if (opts.use_llvm) std.os.darwin.SIG.ABRT else std.os.darwin.SIG.TRAP; - run.addCheck(.{ .expect_term = .{ .Signal = signal } }); - } + run.addCheck(.{ .expect_stderr_match = b.dupe("panic: Oh no!") }); test_step.dependOn(&run.step); return test_step;