From 481ee1b598b02cf5790ed12ed162a9f749a1ac92 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 2 Nov 2023 14:12:08 +0100 Subject: [PATCH] elf: enable static-lib flush path --- src/arch/x86_64/Emit.zig | 12 +++++--- src/arch/x86_64/Lower.zig | 10 +++++-- src/link/Elf.zig | 58 +++++++++++++++++++++++--------------- src/link/Elf/Atom.zig | 3 +- src/link/Elf/ZigObject.zig | 8 +++--- 5 files changed, 57 insertions(+), 34 deletions(-) diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 50fa0c1ffd..a1a2e2cb1c 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -85,15 +85,19 @@ pub fn emitMir(emit: *Emit) Error!void { @tagName(emit.lower.bin_file.tag), }), .linker_reloc => |data| if (emit.lower.bin_file.cast(link.File.Elf)) |elf_file| { - const is_obj = emit.lower.bin_file.options.effectiveOutputMode() == .Obj; + const is_obj_or_static_lib = switch (emit.lower.bin_file.options.output_mode) { + .Exe => false, + .Obj => true, + .Lib => emit.lower.bin_file.options.link_mode == .Static, + }; const atom = elf_file.symbol(data.atom_index).atom(elf_file).?; const sym_index = elf_file.zigObjectPtr().?.symbol(data.sym_index); const sym = elf_file.symbol(sym_index); - if (sym.flags.needs_zig_got and !is_obj) { + if (sym.flags.needs_zig_got and !is_obj_or_static_lib) { _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file); } if (emit.lower.bin_file.options.pic) { - const r_type: u32 = if (sym.flags.needs_zig_got and !is_obj) + const r_type: u32 = if (sym.flags.needs_zig_got and !is_obj_or_static_lib) link.File.Elf.R_X86_64_ZIG_GOTPCREL else if (sym.flags.needs_got) std.elf.R_X86_64_GOTPCREL @@ -105,7 +109,7 @@ pub fn emitMir(emit: *Emit) Error!void { .r_addend = -4, }); } else { - const r_type: u32 = if (sym.flags.needs_zig_got and !is_obj) + const r_type: u32 = if (sym.flags.needs_zig_got and !is_obj_or_static_lib) link.File.Elf.R_X86_64_ZIG_GOT32 else if (sym.flags.needs_got) std.elf.R_X86_64_GOT32 diff --git a/src/arch/x86_64/Lower.zig b/src/arch/x86_64/Lower.zig index fb1d867384..913e55f00a 100644 --- a/src/arch/x86_64/Lower.zig +++ b/src/arch/x86_64/Lower.zig @@ -327,7 +327,11 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) } }.needsZigGot; - const is_obj = lower.bin_file.options.effectiveOutputMode() == .Obj; + const is_obj_or_static_lib = switch (lower.bin_file.options.output_mode) { + .Exe => false, + .Obj => true, + .Lib => lower.bin_file.options.link_mode == .Static, + }; var emit_prefix = prefix; var emit_mnemonic = mnemonic; var emit_ops_storage: [4]Operand = undefined; @@ -347,7 +351,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) }; }, .mov => { - if (is_obj and needsZigGot(sym, lower.bin_file)) emit_mnemonic = .lea; + if (is_obj_or_static_lib and needsZigGot(sym, lower.bin_file)) emit_mnemonic = .lea; break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) }; }, else => unreachable, @@ -360,7 +364,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) break :op .{ .imm = Immediate.s(0) }; }, .mov => { - if (is_obj and needsZigGot(sym, lower.bin_file)) emit_mnemonic = .lea; + if (is_obj_or_static_lib and needsZigGot(sym, lower.bin_file)) emit_mnemonic = .lea; break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{ .base = .{ .reg = .ds }, }) }; diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 42d3c3ef20..a10d17d75f 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -498,7 +498,7 @@ pub fn initMetadata(self: *Elf) !void { const fillSection = struct { fn fillSection(elf_file: *Elf, shdr: *elf.Elf64_Shdr, size: u64, phndx: ?u16) void { - if (elf_file.isObject()) { + if (elf_file.isRelocatable()) { const off = elf_file.findFreeSpace(size, shdr.sh_addralign); shdr.sh_offset = off; shdr.sh_size = size; @@ -513,7 +513,7 @@ pub fn initMetadata(self: *Elf) !void { comptime assert(number_of_zig_segments == 5); - if (!self.isObject()) { + if (!self.isRelocatable()) { if (self.phdr_zig_load_re_index == null) { const filesz = self.base.options.program_code_size_hint; const off = self.findFreeSpace(filesz, self.page_size); @@ -597,7 +597,7 @@ pub fn initMetadata(self: *Elf) !void { }); const shdr = &self.shdrs.items[self.zig_text_section_index.?]; fillSection(self, shdr, self.base.options.program_code_size_hint, self.phdr_zig_load_re_index); - if (self.isObject()) { + if (self.isRelocatable()) { try zig_object.addSectionSymbol(self.zig_text_section_index.?, self); self.zig_text_rela_section_index = try self.addRelaShdr( ".rela.text.zig", @@ -613,7 +613,7 @@ pub fn initMetadata(self: *Elf) !void { try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_text_section_index.?, .{}); } - if (self.zig_got_section_index == null and !self.isObject()) { + if (self.zig_got_section_index == null and !self.isRelocatable()) { self.zig_got_section_index = try self.addSection(.{ .name = ".got.zig", .type = elf.SHT_PROGBITS, @@ -644,7 +644,7 @@ pub fn initMetadata(self: *Elf) !void { }); const shdr = &self.shdrs.items[self.zig_data_rel_ro_section_index.?]; fillSection(self, shdr, 1024, self.phdr_zig_load_ro_index); - if (self.isObject()) { + if (self.isRelocatable()) { try zig_object.addSectionSymbol(self.zig_data_rel_ro_section_index.?, self); self.zig_data_rel_ro_rela_section_index = try self.addRelaShdr( ".rela.data.rel.ro.zig", @@ -670,7 +670,7 @@ pub fn initMetadata(self: *Elf) !void { }); const shdr = &self.shdrs.items[self.zig_data_section_index.?]; fillSection(self, shdr, 1024, self.phdr_zig_load_rw_index); - if (self.isObject()) { + if (self.isRelocatable()) { try zig_object.addSectionSymbol(self.zig_data_section_index.?, self); self.zig_data_rela_section_index = try self.addRelaShdr( ".rela.data.zig", @@ -904,10 +904,6 @@ pub fn flush(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) link if (use_lld) { return self.linkWithLLD(comp, prog_node); } - if (self.base.options.output_mode == .Lib and self.isStatic()) { - // TODO writing static library files - return error.TODOImplementWritingLibFiles; - } try self.flushModule(comp, prog_node); } @@ -943,7 +939,12 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node } else null; const gc_sections = self.base.options.gc_sections orelse false; - if (self.isObject() and self.zig_object_index == null) { + if (self.isRelocatable() and self.zig_object_index == null) { + if (self.isStaticLib()) { + var err = try self.addErrorWithNotes(0); + try err.addMsg(self, "fatal linker error: emitting static libs unimplemented", .{}); + return; + } // TODO this will become -r route I guess. For now, just copy the object file. assert(self.base.file == null); // TODO uncomment once we implement -r const the_object_path = blk: { @@ -1389,6 +1390,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node } if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self); + if (self.isStaticLib()) return self.flushStaticLib(comp); // Dedup shared objects { @@ -1424,9 +1426,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node self.resolveSymbols(); self.markEhFrameAtomsDead(); - if (self.isObject()) { - return self.flushObject(comp); - } + if (self.isObject()) return self.flushObject(comp); try self.convertCommonSymbols(); self.markImportsExports(); @@ -1511,7 +1511,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node try self.writeAtoms(); try self.writeSyntheticSections(); - if (self.entry_index == null and self.base.options.effectiveOutputMode() == .Exe) { + if (self.entry_index == null and self.isExe()) { log.debug("flushing. no_entry_point_found = true", .{}); self.error_flags.no_entry_point_found = true; } else { @@ -1521,6 +1521,12 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node } } +pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void { + _ = comp; + var err = try self.addErrorWithNotes(0); + try err.addMsg(self, "fatal linker error: emitting static libs unimplemented", .{}); +} + pub fn flushObject(self: *Elf, comp: *Compilation) link.File.FlushError!void { _ = comp; self.claimUnresolvedObject(); @@ -2822,7 +2828,7 @@ fn writeHeader(self: *Elf) !void { assert(index == 16); - const elf_type: elf.ET = switch (self.base.options.effectiveOutputMode()) { + const elf_type: elf.ET = switch (self.base.options.output_mode) { .Exe => if (self.base.options.pie) .DYN else .EXEC, .Obj => .REL, .Lib => switch (self.base.options.link_mode) { @@ -3147,11 +3153,11 @@ fn initSections(self: *Elf) !void { }; const ptr_size = self.ptrWidthBytes(); - for (self.objects.items) |index| { + if (!self.isStaticLib()) for (self.objects.items) |index| { try self.file(index).?.object.initOutputSections(self); - } + }; - const needs_eh_frame = for (self.objects.items) |index| { + const needs_eh_frame = if (self.isStaticLib()) false else for (self.objects.items) |index| { if (self.file(index).?.object.cies.items.len > 0) break true; } else false; if (needs_eh_frame) { @@ -4954,15 +4960,23 @@ pub fn isStatic(self: Elf) bool { } pub fn isObject(self: Elf) bool { - return self.base.options.effectiveOutputMode() == .Obj; + return self.base.options.output_mode == .Obj; } pub fn isExe(self: Elf) bool { - return self.base.options.effectiveOutputMode() == .Exe; + return self.base.options.output_mode == .Exe; +} + +pub fn isStaticLib(self: Elf) bool { + return self.base.options.output_mode == .Lib and self.isStatic(); +} + +pub fn isRelocatable(self: Elf) bool { + return self.isObject() or self.isStaticLib(); } pub fn isDynLib(self: Elf) bool { - return self.base.options.effectiveOutputMode() == .Lib and self.base.options.link_mode == .Dynamic; + return self.base.options.output_mode == .Lib and !self.isStatic(); } pub fn isZigSection(self: Elf, shndx: u16) bool { diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 6ba2325638..1f6f77cc4b 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -605,7 +605,8 @@ fn dynAbsRelocAction(symbol: *const Symbol, elf_file: *Elf) RelocAction { } fn outputType(elf_file: *Elf) u2 { - return switch (elf_file.base.options.effectiveOutputMode()) { + assert(!elf_file.isRelocatable()); + return switch (elf_file.base.options.output_mode) { .Obj => unreachable, .Lib => 0, .Exe => if (elf_file.base.options.pie) 1 else 2, diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index d65a39d6d2..5f6cc5488f 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -287,7 +287,7 @@ pub fn addAtom(self: *ZigObject, elf_file: *Elf) !Symbol.Index { } pub fn addSectionSymbol(self: *ZigObject, shndx: u16, elf_file: *Elf) !void { - assert(elf_file.isObject()); + assert(elf_file.isRelocatable()); const gpa = elf_file.base.allocator; const symbol_index = try elf_file.addSymbol(); try self.local_symbols.append(gpa, symbol_index); @@ -886,7 +886,7 @@ fn updateDeclCode( sym.value = atom_ptr.value; esym.st_value = atom_ptr.value; - if (!elf_file.isObject()) { + if (!elf_file.isRelocatable()) { log.debug(" (writing new offset table entry)", .{}); assert(sym.flags.has_zig_got); const extra = sym.extra(elf_file).?; @@ -904,7 +904,7 @@ fn updateDeclCode( sym.flags.needs_zig_got = true; esym.st_value = atom_ptr.value; - if (!elf_file.isObject()) { + if (!elf_file.isRelocatable()) { const gop = try sym.getOrCreateZigGotEntry(sym_index, elf_file); try elf_file.zig_got.writeOne(elf_file, gop.index); } @@ -1160,7 +1160,7 @@ fn updateLazySymbol( local_sym.flags.needs_zig_got = true; local_esym.st_value = atom_ptr.value; - if (!elf_file.isObject()) { + if (!elf_file.isRelocatable()) { const gop = try local_sym.getOrCreateZigGotEntry(symbol_index, elf_file); try elf_file.zig_got.writeOne(elf_file, gop.index); }