From 3df53d1722da9e4fcc8606315c68ffb884a0dd5a Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 7 Nov 2023 11:19:55 +0100 Subject: [PATCH 01/29] elf: create skeleton of required changes for supporting -r mode --- src/link/Elf.zig | 219 +++++++++++++++++++++++++++---------- src/link/Elf/Object.zig | 15 +++ src/link/Elf/ZigObject.zig | 2 +- 3 files changed, 179 insertions(+), 57 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index f4b9b5ff33..300f4b9501 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -216,10 +216,6 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option sub_path, options.target.ofmt.fileExt(options.target.cpu.arch), }); } - if (is_obj) { - // TODO until we implement -r option, we don't want to open a file at this stage. - return self; - } } errdefer if (self.base.intermediary_basename) |path| allocator.free(path); @@ -642,7 +638,7 @@ pub fn initMetadata(self: *Elf) !void { .name = ".data.rel.ro.zig", .type = elf.SHT_PROGBITS, .addralign = 1, - .flags = elf.SHF_ALLOC | elf.SHF_WRITE, // TODO rename this section to .data.rel.ro + .flags = elf.SHF_ALLOC | elf.SHF_WRITE, .offset = std.math.maxInt(u64), }); const shdr = &self.shdrs.items[self.zig_data_rel_ro_section_index.?]; @@ -942,31 +938,6 @@ 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) { - // TODO this will become -r route I guess. For now, just copy the object file. - const the_object_path = blk: { - if (self.base.options.objects.len != 0) { - break :blk self.base.options.objects[0].path; - } - - if (comp.c_object_table.count() != 0) - break :blk comp.c_object_table.keys()[0].status.success.object_path; - - if (module_obj_path) |p| - break :blk p; - - // TODO I think this is unreachable. Audit this situation when solving the above TODO - // regarding eliding redundant object -> object transformations. - return error.NoObjectsToLink; - }; - // This can happen when using --enable-cache and using the stage1 backend. In this case - // we can skip the file copy. - if (!mem.eql(u8, the_object_path, full_out_path)) { - try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{}); - } - return; - } - var csu = try CsuObjects.init(arena, self.base.options, comp); const compiler_rt_path: ?[]const u8 = blk: { if (comp.compiler_rt_lib) |x| break :blk x.full_object_path; @@ -1388,7 +1359,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node try self.handleAndReportParseError(obj.path, err, &parse_ctx); } - if (self.isStaticLib()) return self.flushStaticLib(comp); + if (self.isStaticLib()) return self.flushStaticLib(); // Init all objects for (self.objects.items) |index| { @@ -1432,7 +1403,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(); try self.convertCommonSymbols(); self.markImportsExports(); @@ -1527,8 +1498,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node } } -pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void { - _ = comp; +pub fn flushStaticLib(self: *Elf) link.File.FlushError!void { const gpa = self.base.allocator; // First, we flush relocatable object file generated with our backends. @@ -1539,7 +1509,7 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void try self.initSymtab(); try self.initShStrtab(); try self.sortShdrs(); - zig_object.updateRelaSectionSizes(self); + zig_object.updateRelaSectionsSizes(self); self.updateSymtabSizeObject(zig_object); self.updateShStrtabSize(); @@ -1639,30 +1609,27 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void try self.base.file.?.pwriteAll(buffer.items, 0); } -pub fn flushObject(self: *Elf, comp: *Compilation) link.File.FlushError!void { - _ = comp; - - if (self.objects.items.len > 0) { - var err = try self.addErrorWithNotes(1); - try err.addMsg(self, "fatal linker error: too many input positionals", .{}); - try err.addNote(self, "TODO implement '-r' option", .{}); - return; - } - +pub fn flushObject(self: *Elf) link.File.FlushError!void { self.claimUnresolvedObject(); - try self.initSections(); + try self.initSectionsObject(); try self.sortShdrs(); - try self.updateSectionSizes(); + for (self.objects.items) |index| { + try self.file(index).?.object.addAtomsToOutputSections(self); + } + try self.updateSectionSizesObject(); + try self.allocateAllocSectionsObject(); try self.allocateNonAllocSections(); + self.allocateAtoms(); if (build_options.enable_logging) { state_log.debug("{}", .{self.dumpState()}); } + try self.writeAtomsObject(); + try self.writeSyntheticSectionsObject(); try self.writeShdrTable(); - try self.writeSyntheticSections(); try self.writeElfHeader(); } @@ -3460,6 +3427,32 @@ fn initSections(self: *Elf) !void { try self.initShStrtab(); } +fn initSectionsObject(self: *Elf) !void { + const ptr_size = self.ptrWidthBytes(); + + for (self.objects.items) |index| { + const object = self.file(index).?.object; + try object.initOutputSections(self); + try object.initRelaSections(self); + } + + const needs_eh_frame = for (self.objects.items) |index| { + if (self.file(index).?.object.cies.items.len > 0) break true; + } else false; + if (needs_eh_frame) { + self.eh_frame_section_index = try self.addSection(.{ + .name = ".eh_frame", + .type = elf.SHT_PROGBITS, + .flags = elf.SHF_ALLOC, + .addralign = ptr_size, + .offset = std.math.maxInt(u64), + }); + } + + try self.initSymtab(); + try self.initShStrtab(); +} + fn initSymtab(self: *Elf) !void { const small_ptr = switch (self.ptr_width) { .p32 => true, @@ -3982,10 +3975,6 @@ fn updateSectionSizes(self: *Elf) !void { } } - if (self.zigObjectPtr()) |zig_object| { - zig_object.updateRelaSectionSizes(self); - } - if (self.eh_frame_section_index) |index| { self.shdrs.items[index].sh_size = try eh_frame.calcEhFrameSize(self); } @@ -4065,6 +4054,37 @@ fn updateSectionSizes(self: *Elf) !void { self.updateShStrtabSize(); } +fn updateSectionSizesObject(self: *Elf) !void { + for (self.output_sections.keys(), self.output_sections.values()) |shndx, atom_list| { + if (atom_list.items.len == 0) continue; + const shdr = &self.shdrs.items[shndx]; + for (atom_list.items) |atom_index| { + const atom_ptr = self.atom(atom_index) orelse continue; + if (!atom_ptr.flags.alive) continue; + const offset = atom_ptr.alignment.forward(shdr.sh_size); + const padding = offset - shdr.sh_size; + atom_ptr.value = offset; + shdr.sh_size += padding + atom_ptr.size; + shdr.sh_addralign = @max(shdr.sh_addralign, atom_ptr.alignment.toByteUnits(1)); + } + } + + if (self.zigObjectPtr()) |zig_object| { + zig_object.updateRelaSectionsSizes(self); + } + + for (self.objects.items) |index| { + self.file(index).?.object.updateRelaSectionsSizes(self); + } + + if (self.eh_frame_section_index) |index| { + self.shdrs.items[index].sh_size = try eh_frame.calcEhFrameSize(self); + } + + self.updateSymtabSize(); + self.updateShStrtabSize(); +} + fn updateShStrtabSize(self: *Elf) void { if (self.shstrtab_section_index) |index| { self.shdrs.items[index].sh_size = self.shstrtab.items.len; @@ -4294,6 +4314,12 @@ fn allocateAllocSections(self: *Elf) error{OutOfMemory}!void { } } +/// Allocates alloc sections when merging relocatable objects files together. +fn allocateAllocSectionsObject(self: *Elf) !void { + _ = self; + @panic("TODO"); +} + /// Allocates non-alloc sections (debug info, symtabs, etc.). fn allocateNonAllocSections(self: *Elf) !void { for (self.shdrs.items, 0..) |*shdr, shndx| { @@ -4484,6 +4510,67 @@ fn writeAtoms(self: *Elf) !void { try self.reportUndefined(&undefs); } +fn writeAtomsObject(self: *Elf) !void { + const gpa = self.base.allocator; + + // TODO iterate over `output_sections` directly + for (self.shdrs.items, 0..) |shdr, shndx| { + if (shdr.sh_type == elf.SHT_NULL) continue; + if (shdr.sh_type == elf.SHT_NOBITS) continue; + + const atom_list = self.output_sections.get(@intCast(shndx)) orelse continue; + + log.debug("writing atoms in '{s}' section", .{self.getShString(shdr.sh_name)}); + + // TODO really, really handle debug section separately + const base_offset = if (self.isDebugSection(@intCast(shndx))) blk: { + const zig_object = self.zigObjectPtr().?; + if (shndx == self.debug_info_section_index.?) + break :blk zig_object.debug_info_section_zig_size; + if (shndx == self.debug_abbrev_section_index.?) + break :blk zig_object.debug_abbrev_section_zig_size; + if (shndx == self.debug_str_section_index.?) + break :blk zig_object.debug_str_section_zig_size; + if (shndx == self.debug_aranges_section_index.?) + break :blk zig_object.debug_aranges_section_zig_size; + if (shndx == self.debug_line_section_index.?) + break :blk zig_object.debug_line_section_zig_size; + unreachable; + } else 0; + const sh_offset = shdr.sh_offset + base_offset; + const sh_size = math.cast(usize, shdr.sh_size - base_offset) orelse return error.Overflow; + + const buffer = try gpa.alloc(u8, sh_size); + defer gpa.free(buffer); + const padding_byte: u8 = if (shdr.sh_type == elf.SHT_PROGBITS and + shdr.sh_flags & elf.SHF_EXECINSTR != 0) + 0xcc // int3 + else + 0; + @memset(buffer, padding_byte); + + for (atom_list.items) |atom_index| { + const atom_ptr = self.atom(atom_index).?; + assert(atom_ptr.flags.alive); + + const object = atom_ptr.file(self).?.object; + const offset = math.cast(usize, atom_ptr.value - shdr.sh_addr - base_offset) orelse + return error.Overflow; + const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow; + + log.debug("writing atom({d}) at 0x{x}", .{ atom_index, sh_offset + offset }); + + // TODO decompress directly into provided buffer + const out_code = buffer[offset..][0..size]; + const in_code = try object.codeDecompressAlloc(self, atom_index); + defer gpa.free(in_code); + @memcpy(out_code, in_code); + } + + try self.base.file.?.pwriteAll(buffer, sh_offset); + } +} + fn updateSymtabSize(self: *Elf) void { var sizes = SymtabSize{}; @@ -4567,10 +4654,6 @@ fn updateSymtabSizeObject(self: *Elf, zig_object: *ZigObject) void { fn writeSyntheticSections(self: *Elf) !void { const gpa = self.base.allocator; - if (self.zigObjectPtr()) |zig_object| { - try zig_object.writeRelaSections(self); - } - if (self.interp_section_index) |shndx| { const shdr = self.shdrs.items[shndx]; const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow; @@ -4698,6 +4781,30 @@ fn writeSyntheticSections(self: *Elf) !void { try self.writeShStrtab(); } +fn writeSyntheticSectionsObject(self: *Elf) !void { + const gpa = self.base.allocator; + + if (self.zigObjectPtr()) |zig_object| { + try zig_object.writeRelaSections(self); + } + + for (self.objects.items) |index| { + try self.file(index).?.object.writeRelaSections(self); + } + + if (self.eh_frame_section_index) |shndx| { + const shdr = self.shdrs.items[shndx]; + const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow; + var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size); + defer buffer.deinit(); + try eh_frame.writeEhFrame(self, buffer.writer()); + try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); + } + + try self.writeSymtab(); + try self.writeShStrtab(); +} + fn writeShStrtab(self: *Elf) !void { if (self.shstrtab_section_index) |index| { const shdr = self.shdrs.items[index]; diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index e710a81ad3..cb258a4c22 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -654,6 +654,21 @@ pub fn allocateAtoms(self: Object, elf_file: *Elf) void { } } +pub fn initRelaSections(self: Object, elf_file: *Elf) !void { + _ = self; + _ = elf_file; +} + +pub fn updateRelaSectionsSizes(self: Object, elf_file: *Elf) void { + _ = self; + _ = elf_file; +} + +pub fn writeRelaSections(self: Object, elf_file: *Elf) !void { + _ = self; + _ = elf_file; +} + pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) !void { const gpa = elf_file.base.allocator; const start = self.first_global orelse self.symtab.items.len; diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index dd2126a99e..4b7e97ed13 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -549,7 +549,7 @@ pub fn writeAr(self: ZigObject, elf_file: *Elf, writer: anytype) !void { try writer.writeAll(contents); } -pub fn updateRelaSectionSizes(self: ZigObject, elf_file: *Elf) void { +pub fn updateRelaSectionsSizes(self: ZigObject, elf_file: *Elf) void { _ = self; for (&[_]?u16{ From c7ed7c4690dfa297c15b94fe7acba77c52d89e68 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 7 Nov 2023 13:12:26 +0100 Subject: [PATCH 02/29] elf: generate section symbols when writing symtab --- src/link/Elf.zig | 110 ++++++++++++++++++++++++++++--------- src/link/Elf/Atom.zig | 10 +++- src/link/Elf/Object.zig | 15 ++++- src/link/Elf/Symbol.zig | 2 - src/link/Elf/ZigObject.zig | 16 ------ src/link/Elf/file.zig | 3 +- 6 files changed, 106 insertions(+), 50 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 300f4b9501..f38152fa27 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -597,7 +597,6 @@ 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.isRelocatable()) { - try zig_object.addSectionSymbol(self.zig_text_section_index.?, self); self.zig_text_rela_section_index = try self.addRelaShdr( ".rela.text.zig", self.zig_text_section_index.?, @@ -609,6 +608,7 @@ pub fn initMetadata(self: *Elf) !void { self.phdr_zig_load_re_index.?, ); } + try self.output_sections.putNoClobber(gpa, self.zig_text_section_index.?, .{}); try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_text_section_index.?, .{}); } @@ -644,7 +644,6 @@ 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.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", self.zig_data_rel_ro_section_index.?, @@ -656,6 +655,7 @@ pub fn initMetadata(self: *Elf) !void { self.phdr_zig_load_ro_index.?, ); } + try self.output_sections.putNoClobber(gpa, self.zig_data_rel_ro_section_index.?, .{}); try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_data_rel_ro_section_index.?, .{}); } @@ -670,7 +670,6 @@ 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.isRelocatable()) { - try zig_object.addSectionSymbol(self.zig_data_section_index.?, self); self.zig_data_rela_section_index = try self.addRelaShdr( ".rela.data.zig", self.zig_data_section_index.?, @@ -682,6 +681,7 @@ pub fn initMetadata(self: *Elf) !void { self.phdr_zig_load_rw_index.?, ); } + try self.output_sections.putNoClobber(gpa, self.zig_data_section_index.?, .{}); try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_data_section_index.?, .{}); } @@ -700,9 +700,9 @@ pub fn initMetadata(self: *Elf) !void { shdr.sh_size = phdr.p_memsz; try self.phdr_to_shdr_table.putNoClobber(gpa, self.zig_bss_section_index.?, phndx); } else { - try zig_object.addSectionSymbol(self.zig_bss_section_index.?, self); shdr.sh_size = 1024; } + try self.output_sections.putNoClobber(gpa, self.zig_bss_section_index.?, .{}); try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_bss_section_index.?, .{}); } @@ -724,6 +724,7 @@ pub fn initMetadata(self: *Elf) !void { shdr.sh_offset = off; shdr.sh_size = size; zig_object.debug_strtab_dirty = true; + try self.output_sections.putNoClobber(gpa, self.debug_str_section_index.?, .{}); } if (self.debug_info_section_index == null) { @@ -739,6 +740,7 @@ pub fn initMetadata(self: *Elf) !void { shdr.sh_offset = off; shdr.sh_size = size; zig_object.debug_info_header_dirty = true; + try self.output_sections.putNoClobber(gpa, self.debug_info_section_index.?, .{}); } if (self.debug_abbrev_section_index == null) { @@ -754,6 +756,7 @@ pub fn initMetadata(self: *Elf) !void { shdr.sh_offset = off; shdr.sh_size = size; zig_object.debug_abbrev_section_dirty = true; + try self.output_sections.putNoClobber(gpa, self.debug_abbrev_section_index.?, .{}); } if (self.debug_aranges_section_index == null) { @@ -769,6 +772,7 @@ pub fn initMetadata(self: *Elf) !void { shdr.sh_offset = off; shdr.sh_size = size; zig_object.debug_aranges_section_dirty = true; + try self.output_sections.putNoClobber(gpa, self.debug_aranges_section_index.?, .{}); } if (self.debug_line_section_index == null) { @@ -784,6 +788,7 @@ pub fn initMetadata(self: *Elf) !void { shdr.sh_offset = off; shdr.sh_size = size; zig_object.debug_line_header_dirty = true; + try self.output_sections.putNoClobber(gpa, self.debug_line_section_index.?, .{}); } } } @@ -1510,14 +1515,14 @@ pub fn flushStaticLib(self: *Elf) link.File.FlushError!void { try self.initShStrtab(); try self.sortShdrs(); zig_object.updateRelaSectionsSizes(self); - self.updateSymtabSizeObject(zig_object); + self.updateSymtabSizeZigObject(zig_object); self.updateShStrtabSize(); try self.allocateNonAllocSections(); try self.writeShdrTable(); try zig_object.writeRelaSections(self); - try self.writeSymtabObject(zig_object); + try self.writeSymtabZigObject(zig_object); try self.writeShStrtab(); try self.writeElfHeader(); } @@ -3579,7 +3584,8 @@ fn sortInitFini(self: *Elf) !void { if (!is_init_fini and !is_ctor_dtor) continue; - const atom_list = self.output_sections.getPtr(@intCast(shndx)) orelse continue; + const atom_list = self.output_sections.getPtr(@intCast(shndx)).?; + if (atom_list.items.len == 0) continue; var entries = std.ArrayList(Entry).init(gpa); try entries.ensureTotalCapacityPrecise(atom_list.items.len); @@ -3909,6 +3915,20 @@ fn sortShdrs(self: *Elf) !void { shdr.sh_info = backlinks[shdr.sh_info]; } + { + var output_sections = try self.output_sections.clone(gpa); + defer output_sections.deinit(gpa); + + self.output_sections.clearRetainingCapacity(); + + var it = output_sections.iterator(); + while (it.next()) |entry| { + const shndx = entry.key_ptr.*; + const meta = entry.value_ptr.*; + self.output_sections.putAssumeCapacityNoClobber(backlinks[shndx], meta); + } + } + { var last_atom_and_free_list_table = try self.last_atom_and_free_list_table.clone(gpa); defer last_atom_and_free_list_table.deinit(gpa); @@ -3962,7 +3982,6 @@ fn sortShdrs(self: *Elf) !void { fn updateSectionSizes(self: *Elf) !void { for (self.output_sections.keys(), self.output_sections.values()) |shndx, atom_list| { - if (atom_list.items.len == 0) continue; const shdr = &self.shdrs.items[shndx]; for (atom_list.items) |atom_index| { const atom_ptr = self.atom(atom_index) orelse continue; @@ -4056,7 +4075,6 @@ fn updateSectionSizes(self: *Elf) !void { fn updateSectionSizesObject(self: *Elf) !void { for (self.output_sections.keys(), self.output_sections.values()) |shndx, atom_list| { - if (atom_list.items.len == 0) continue; const shdr = &self.shdrs.items[shndx]; for (atom_list.items) |atom_index| { const atom_ptr = self.atom(atom_index) orelse continue; @@ -4317,7 +4335,6 @@ fn allocateAllocSections(self: *Elf) error{OutOfMemory}!void { /// Allocates alloc sections when merging relocatable objects files together. fn allocateAllocSectionsObject(self: *Elf) !void { _ = self; - @panic("TODO"); } /// Allocates non-alloc sections (debug info, symtabs, etc.). @@ -4444,6 +4461,7 @@ fn writeAtoms(self: *Elf) !void { if (shdr.sh_type == elf.SHT_NOBITS) continue; const atom_list = self.output_sections.get(@intCast(shndx)) orelse continue; + if (atom_list.items.len == 0) continue; log.debug("writing atoms in '{s}' section", .{self.getShString(shdr.sh_name)}); @@ -4519,6 +4537,7 @@ fn writeAtomsObject(self: *Elf) !void { if (shdr.sh_type == elf.SHT_NOBITS) continue; const atom_list = self.output_sections.get(@intCast(shndx)) orelse continue; + if (atom_list.items.len == 0) continue; log.debug("writing atoms in '{s}' section", .{self.getShString(shdr.sh_name)}); @@ -4617,6 +4636,11 @@ fn updateSymtabSize(self: *Elf) void { sizes.add(file_ptr.linker_defined.output_symtab_size); } + // Section symbols + for (self.output_sections.keys()) |_| { + sizes.nlocals += 1; + } + const symtab_shdr = &self.shdrs.items[self.symtab_section_index.?]; symtab_shdr.sh_info = sizes.nlocals + 1; symtab_shdr.sh_link = self.strtab_section_index.?; @@ -4632,9 +4656,16 @@ fn updateSymtabSize(self: *Elf) void { strtab.sh_size = sizes.strsize + 1; } -fn updateSymtabSizeObject(self: *Elf, zig_object: *ZigObject) void { +fn updateSymtabSizeZigObject(self: *Elf, zig_object: *ZigObject) void { + var sizes = SymtabSize{}; + zig_object.asFile().updateSymtabSize(self); - const sizes = zig_object.output_symtab_size; + sizes.add(zig_object.output_symtab_size); + + // Section symbols + for (self.output_sections.keys()) |_| { + sizes.nlocals += 1; + } const symtab_shdr = &self.shdrs.items[self.symtab_section_index.?]; symtab_shdr.sh_info = sizes.nlocals + 1; @@ -4798,7 +4829,7 @@ fn writeSyntheticSectionsObject(self: *Elf) !void { var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size); defer buffer.deinit(); try eh_frame.writeEhFrame(self, buffer.writer()); - try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); + // try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); } try self.writeSymtab(); @@ -4812,6 +4843,16 @@ fn writeShStrtab(self: *Elf) !void { } } +const WriteSymtabCtx = struct { + ilocal: usize, + iglobal: usize, + + fn incr(this: *@This(), ss: SymtabSize) void { + this.ilocal += ss.nlocals; + this.iglobal += ss.nglobals; + } +}; + fn writeSymtab(self: *Elf) !void { const gpa = self.base.allocator; const symtab_shdr = self.shdrs.items[self.symtab_section_index.?]; @@ -4828,20 +4869,13 @@ fn writeSymtab(self: *Elf) !void { const needed_strtab_size = math.cast(usize, strtab_shdr.sh_size - 1) orelse return error.Overflow; try self.strtab.ensureUnusedCapacity(gpa, needed_strtab_size); - const Ctx = struct { - ilocal: usize, - iglobal: usize, - - fn incr(this: *@This(), ss: SymtabSize) void { - this.ilocal += ss.nlocals; - this.iglobal += ss.nglobals; - } - }; - var ctx: Ctx = .{ + var ctx: WriteSymtabCtx = .{ .ilocal = 1, .iglobal = symtab_shdr.sh_info, }; + ctx.incr(self.writeSectionSymbols(ctx)); + if (self.zigObjectPtr()) |zig_object| { zig_object.asFile().writeSymtab(self, ctx); ctx.incr(zig_object.output_symtab_size); @@ -4915,7 +4949,7 @@ fn writeSymtab(self: *Elf) !void { try self.base.file.?.pwriteAll(self.strtab.items, strtab_shdr.sh_offset); } -fn writeSymtabObject(self: *Elf, zig_object: *ZigObject) !void { +fn writeSymtabZigObject(self: *Elf, zig_object: *ZigObject) !void { const gpa = self.base.allocator; const symtab_shdr = self.shdrs.items[self.symtab_section_index.?]; const strtab_shdr = self.shdrs.items[self.strtab_section_index.?]; @@ -4931,7 +4965,15 @@ fn writeSymtabObject(self: *Elf, zig_object: *ZigObject) !void { const needed_strtab_size = math.cast(usize, strtab_shdr.sh_size - 1) orelse return error.Overflow; try self.strtab.ensureUnusedCapacity(gpa, needed_strtab_size); - zig_object.asFile().writeSymtab(self, .{ .ilocal = 1, .iglobal = symtab_shdr.sh_info }); + var ctx: WriteSymtabCtx = .{ + .ilocal = 1, + .iglobal = symtab_shdr.sh_info, + }; + + ctx.incr(self.writeSectionSymbols(ctx)); + + zig_object.asFile().writeSymtab(self, ctx); + ctx.incr(zig_object.output_symtab_size); const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian(); switch (self.ptr_width) { @@ -4963,6 +5005,24 @@ fn writeSymtabObject(self: *Elf, zig_object: *ZigObject) !void { try self.base.file.?.pwriteAll(self.strtab.items, strtab_shdr.sh_offset); } +fn writeSectionSymbols(self: *Elf, ctx: WriteSymtabCtx) SymtabSize { + var ilocal = ctx.ilocal; + for (self.output_sections.keys()) |shndx| { + const shdr = self.shdrs.items[shndx]; + const out_sym = &self.symtab.items[ilocal]; + out_sym.* = .{ + .st_name = 0, + .st_value = shdr.sh_addr, + .st_info = elf.STT_SECTION, + .st_shndx = @intCast(shndx), + .st_size = 0, + .st_other = 0, + }; + ilocal += 1; + } + return .{ .nlocals = @intCast(ilocal - ctx.ilocal) }; +} + /// Always 4 or 8 depending on whether this is 32-bit ELF or 64-bit ELF. fn ptrWidthBytes(self: Elf) u8 { return switch (self.ptr_width) { diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 1f6f77cc4b..98a9f13771 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -60,6 +60,11 @@ pub fn inputShdr(self: Atom, elf_file: *Elf) Object.ElfShdr { }; } +pub fn relocsShndx(self: Atom) ?u32 { + if (self.relocs_section_index == 0) return null; + return self.relocs_section_index; +} + pub fn outputShndx(self: Atom) ?u16 { if (self.output_section_index == 0) return null; return self.output_section_index; @@ -280,9 +285,10 @@ pub fn free(self: *Atom, elf_file: *Elf) void { } pub fn relocs(self: Atom, elf_file: *Elf) []align(1) const elf.Elf64_Rela { + const shndx = self.relocsShndx() orelse return &[0]elf.Elf64_Rela{}; return switch (self.file(elf_file).?) { - .zig_object => |x| x.relocs.items[self.relocs_section_index].items, - .object => |x| x.getRelocs(self.relocs_section_index), + .zig_object => |x| x.relocs.items[shndx].items, + .object => |x| x.getRelocs(shndx), else => unreachable, }; } diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index cb258a4c22..73bd3a7fb3 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -211,6 +211,7 @@ fn addAtom(self: *Object, shdr: ElfShdr, shndx: u16, elf_file: *Elf) error{OutOf fn initOutputSection(self: Object, elf_file: *Elf, shdr: ElfShdr) error{OutOfMemory}!u16 { const name = blk: { const name = self.getString(shdr.sh_name); + if (elf_file.isRelocatable()) break :blk name; if (shdr.sh_flags & elf.SHF_MERGE != 0) break :blk name; const sh_name_prefixes: []const [:0]const u8 = &.{ ".text", ".data.rel.ro", ".data", ".rodata", ".bss.rel.ro", ".bss", @@ -237,7 +238,10 @@ fn initOutputSection(self: Object, elf_file: *Elf, shdr: ElfShdr) error{OutOfMem else => shdr.sh_type, }; const flags = blk: { - const flags = shdr.sh_flags & ~@as(u64, elf.SHF_COMPRESSED | elf.SHF_GROUP | elf.SHF_GNU_RETAIN); + var flags = shdr.sh_flags; + if (!elf_file.isRelocatable()) { + flags &= ~@as(u64, elf.SHF_COMPRESSED | elf.SHF_GROUP | elf.SHF_GNU_RETAIN); + } break :blk switch (@"type") { elf.SHT_INIT_ARRAY, elf.SHT_FINI_ARRAY => flags | elf.SHF_WRITE, else => flags, @@ -655,8 +659,13 @@ pub fn allocateAtoms(self: Object, elf_file: *Elf) void { } pub fn initRelaSections(self: Object, elf_file: *Elf) !void { - _ = self; - _ = elf_file; + for (self.atoms.items) |atom_index| { + const atom = elf_file.atom(atom_index) orelse continue; + if (!atom.flags.alive) continue; + const shndx = atom.relocsShndx() orelse continue; + const shdr = self.shdrs.items[shndx]; + _ = try self.initOutputSection(elf_file, shdr); + } } pub fn updateRelaSectionsSizes(self: Object, elf_file: *Elf) void { diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index 13d9041813..9a9daff358 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -219,8 +219,6 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void { const st_shndx = blk: { if (symbol.flags.has_copy_rel) break :blk elf_file.copy_rel_section_index.?; if (file_ptr == .shared_object or esym.st_shndx == elf.SHN_UNDEF) break :blk elf.SHN_UNDEF; - // TODO I think this is wrong and obsolete - if (elf_file.isRelocatable() and st_type == elf.STT_SECTION) break :blk symbol.outputShndx().?; if (symbol.atom(elf_file) == null and file_ptr != .linker_defined) break :blk elf.SHN_ABS; break :blk symbol.outputShndx() orelse elf.SHN_UNDEF; diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 4b7e97ed13..3c5174b86e 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -287,22 +287,6 @@ pub fn addAtom(self: *ZigObject, elf_file: *Elf) !Symbol.Index { return symbol_index; } -pub fn addSectionSymbol(self: *ZigObject, shndx: u16, elf_file: *Elf) !void { - 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); - const symbol_ptr = elf_file.symbol(symbol_index); - symbol_ptr.file_index = self.index; - symbol_ptr.output_section_index = shndx; - - const esym_index = try self.addLocalEsym(gpa); - const esym = &self.local_esyms.items(.elf_sym)[esym_index]; - esym.st_info = elf.STT_SECTION; - esym.st_shndx = shndx; - symbol_ptr.esym_index = esym_index; -} - /// TODO actually create fake input shdrs and return that instead. pub fn inputShdr(self: ZigObject, atom_index: Atom.Index, elf_file: *Elf) Object.ElfShdr { _ = self; diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig index cc0b486692..0fae6bc9f4 100644 --- a/src/link/Elf/file.zig +++ b/src/link/Elf/file.zig @@ -136,8 +136,7 @@ pub const File = union(enum) { if (local.atom(elf_file)) |atom| if (!atom.flags.alive) continue; const esym = local.elfSym(elf_file); switch (esym.st_type()) { - elf.STT_SECTION => if (!elf_file.isRelocatable()) continue, - elf.STT_NOTYPE => continue, + elf.STT_SECTION, elf.STT_NOTYPE => continue, else => {}, } local.flags.output_symtab = true; From e22b3595c1af232372ebf3c70922e86e6f9e9076 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 7 Nov 2023 14:29:44 +0100 Subject: [PATCH 03/29] elf: update .rela section sizes --- src/link/Elf/Object.zig | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index 73bd3a7fb3..a2a551ed6a 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -664,13 +664,24 @@ pub fn initRelaSections(self: Object, elf_file: *Elf) !void { if (!atom.flags.alive) continue; const shndx = atom.relocsShndx() orelse continue; const shdr = self.shdrs.items[shndx]; - _ = try self.initOutputSection(elf_file, shdr); + const out_shndx = try self.initOutputSection(elf_file, shdr); + const out_shdr = &elf_file.shdrs.items[out_shndx]; + out_shdr.sh_addralign = @alignOf(elf.Elf64_Rela); + out_shdr.sh_entsize = @sizeOf(elf.Elf64_Rela); } } pub fn updateRelaSectionsSizes(self: Object, elf_file: *Elf) void { - _ = self; - _ = elf_file; + for (self.atoms.items) |atom_index| { + const atom = elf_file.atom(atom_index) orelse continue; + if (!atom.flags.alive) continue; + const shndx = atom.relocsShndx() orelse continue; + const shdr = self.shdrs.items[shndx]; + const out_shndx = self.initOutputSection(elf_file, shdr) catch unreachable; + const out_shdr = &elf_file.shdrs.items[out_shndx]; + const relocs = atom.relocs(elf_file); + out_shdr.sh_size += out_shdr.sh_entsize * relocs.len; + } } pub fn writeRelaSections(self: Object, elf_file: *Elf) !void { From 0211d6bf4f0da9ebea91fe8378181f354b89f073 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 7 Nov 2023 14:42:27 +0100 Subject: [PATCH 04/29] elf: create link between .rela and output section --- src/link/Elf.zig | 14 +++++--------- src/link/Elf/Object.zig | 1 + 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index f38152fa27..0f03e91668 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -3904,15 +3904,11 @@ fn sortShdrs(self: *Elf) !void { shdr.sh_info = self.plt_section_index.?; } - for (&[_]?u16{ - self.zig_text_rela_section_index, - self.zig_data_rel_ro_rela_section_index, - self.zig_data_rela_section_index, - }) |maybe_index| { - const index = maybe_index orelse continue; - const shdr = &self.shdrs.items[index]; - shdr.sh_link = self.symtab_section_index.?; - shdr.sh_info = backlinks[shdr.sh_info]; + for (self.shdrs.items) |*shdr| { + if (shdr.sh_type == elf.SHT_RELA and shdr.sh_flags & elf.SHF_INFO_LINK != 0) { + shdr.sh_link = self.symtab_section_index.?; + shdr.sh_info = backlinks[shdr.sh_info]; + } } { diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index a2a551ed6a..fa9aac3c43 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -668,6 +668,7 @@ pub fn initRelaSections(self: Object, elf_file: *Elf) !void { const out_shdr = &elf_file.shdrs.items[out_shndx]; out_shdr.sh_addralign = @alignOf(elf.Elf64_Rela); out_shdr.sh_entsize = @sizeOf(elf.Elf64_Rela); + out_shdr.sh_info = self.initOutputSection(elf_file, atom.inputShdr(elf_file)) catch unreachable; } } From 31b280c78f41fd36caa2d1ba194b36dc9ec56a28 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 7 Nov 2023 14:47:11 +0100 Subject: [PATCH 05/29] elf: emit .rela.eh_frame section header if required --- src/link/Elf.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 0f03e91668..b26041e989 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -3452,6 +3452,7 @@ fn initSectionsObject(self: *Elf) !void { .addralign = ptr_size, .offset = std.math.maxInt(u64), }); + _ = try self.addRelaShdr(".rela.eh_frame", self.eh_frame_section_index.?); } try self.initSymtab(); From ecf6ed7d9bc9bcd61d619fae985ce9ebdc4d5562 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 7 Nov 2023 15:04:57 +0100 Subject: [PATCH 06/29] elf: allocate alloc sections when emitting relocatable --- src/link/Elf.zig | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index b26041e989..da5690c4de 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -4331,7 +4331,17 @@ fn allocateAllocSections(self: *Elf) error{OutOfMemory}!void { /// Allocates alloc sections when merging relocatable objects files together. fn allocateAllocSectionsObject(self: *Elf) !void { - _ = self; + for (self.shdrs.items) |*shdr| { + if (shdr.sh_type == elf.SHT_NULL) continue; + if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue; + const needed_size = shdr.sh_size; + if (needed_size > self.allocatedSize(shdr.sh_offset)) { + shdr.sh_size = 0; + const new_offset = self.findFreeSpace(needed_size, shdr.sh_addralign); + shdr.sh_offset = new_offset; + shdr.sh_size = needed_size; + } + } } /// Allocates non-alloc sections (debug info, symtabs, etc.). From 5e78600f0f5e790c0429817d8249020fd65b49f8 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 7 Nov 2023 22:48:02 +0100 Subject: [PATCH 07/29] elf: actually track output symtab index of symbols --- src/link/Elf.zig | 251 +++++++++++++++------------- src/link/Elf/Atom.zig | 26 +++ src/link/Elf/LinkerDefined.zig | 2 +- src/link/Elf/Object.zig | 17 +- src/link/Elf/SharedObject.zig | 2 +- src/link/Elf/Symbol.zig | 19 +++ src/link/Elf/ZigObject.zig | 99 ++--------- src/link/Elf/file.zig | 42 ++--- src/link/Elf/synthetic_sections.zig | 40 ++--- 9 files changed, 234 insertions(+), 264 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index da5690c4de..fd52a4327e 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -24,6 +24,7 @@ shdr_table_offset: ?u64 = null, /// Table of lists of atoms per output section. /// This table is not used to track incrementally generated atoms. output_sections: std.AutoArrayHashMapUnmanaged(u16, std.ArrayListUnmanaged(Atom.Index)) = .{}, +output_rela_sections: std.AutoArrayHashMapUnmanaged(u16, std.ArrayListUnmanaged(Atom.Index)) = .{}, /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. /// Same order as in the file. @@ -105,11 +106,8 @@ zig_got: ZigGotSection = .{}, /// Tracked section headers with incremental updates to Zig object. /// .rela.* sections are only used when emitting a relocatable object file. zig_text_section_index: ?u16 = null, -zig_text_rela_section_index: ?u16 = null, zig_data_rel_ro_section_index: ?u16 = null, -zig_data_rel_ro_rela_section_index: ?u16 = null, zig_data_section_index: ?u16 = null, -zig_data_rela_section_index: ?u16 = null, zig_bss_section_index: ?u16 = null, zig_got_section_index: ?u16 = null, @@ -357,6 +355,10 @@ pub fn deinit(self: *Elf) void { list.deinit(gpa); } self.output_sections.deinit(gpa); + for (self.output_rela_sections.values()) |*list| { + list.deinit(gpa); + } + self.output_rela_sections.deinit(gpa); self.shstrtab.deinit(gpa); self.symtab.deinit(gpa); self.strtab.deinit(gpa); @@ -597,10 +599,8 @@ 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.isRelocatable()) { - self.zig_text_rela_section_index = try self.addRelaShdr( - ".rela.text.zig", - self.zig_text_section_index.?, - ); + const rela_shndx = try self.addRelaShdr(".rela.text.zig", self.zig_text_section_index.?); + try self.output_rela_sections.putNoClobber(gpa, rela_shndx, .{}); } else { try self.phdr_to_shdr_table.putNoClobber( gpa, @@ -644,10 +644,11 @@ 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.isRelocatable()) { - self.zig_data_rel_ro_rela_section_index = try self.addRelaShdr( + const rela_shndx = try self.addRelaShdr( ".rela.data.rel.ro.zig", self.zig_data_rel_ro_section_index.?, ); + try self.output_rela_sections.putNoClobber(gpa, rela_shndx, .{}); } else { try self.phdr_to_shdr_table.putNoClobber( gpa, @@ -670,10 +671,11 @@ 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.isRelocatable()) { - self.zig_data_rela_section_index = try self.addRelaShdr( + const rela_shndx = try self.addRelaShdr( ".rela.data.zig", self.zig_data_section_index.?, ); + try self.output_rela_sections.putNoClobber(gpa, rela_shndx, .{}); } else { try self.phdr_to_shdr_table.putNoClobber( gpa, @@ -1514,16 +1516,13 @@ pub fn flushStaticLib(self: *Elf) link.File.FlushError!void { try self.initSymtab(); try self.initShStrtab(); try self.sortShdrs(); - zig_object.updateRelaSectionsSizes(self); - self.updateSymtabSizeZigObject(zig_object); - self.updateShStrtabSize(); + try zig_object.addAtomsToRelaSections(self); + try self.updateSectionSizesObject(); try self.allocateNonAllocSections(); try self.writeShdrTable(); - try zig_object.writeRelaSections(self); - try self.writeSymtabZigObject(zig_object); - try self.writeShStrtab(); + try self.writeSyntheticSectionsObject(); try self.writeElfHeader(); } @@ -1620,7 +1619,9 @@ pub fn flushObject(self: *Elf) link.File.FlushError!void { try self.initSectionsObject(); try self.sortShdrs(); for (self.objects.items) |index| { - try self.file(index).?.object.addAtomsToOutputSections(self); + const object = self.file(index).?.object; + try object.addAtomsToOutputSections(self); + try object.addAtomsToRelaSections(self); } try self.updateSectionSizesObject(); @@ -3452,7 +3453,7 @@ fn initSectionsObject(self: *Elf) !void { .addralign = ptr_size, .offset = std.math.maxInt(u64), }); - _ = try self.addRelaShdr(".rela.eh_frame", self.eh_frame_section_index.?); + // _ = try self.addRelaShdr(".rela.eh_frame", self.eh_frame_section_index.?); } try self.initSymtab(); @@ -3841,12 +3842,9 @@ fn sortShdrs(self: *Elf) !void { &self.versym_section_index, &self.verneed_section_index, &self.zig_text_section_index, - &self.zig_text_rela_section_index, &self.zig_got_section_index, &self.zig_data_rel_ro_section_index, - &self.zig_data_rel_ro_rela_section_index, &self.zig_data_section_index, - &self.zig_data_rela_section_index, &self.zig_bss_section_index, &self.debug_str_section_index, &self.debug_info_section_index, @@ -4066,7 +4064,7 @@ fn updateSectionSizes(self: *Elf) !void { self.shdrs.items[index].sh_size = self.verneed.size(); } - self.updateSymtabSize(); + try self.updateSymtabSize(); self.updateShStrtabSize(); } @@ -4084,19 +4082,21 @@ fn updateSectionSizesObject(self: *Elf) !void { } } - if (self.zigObjectPtr()) |zig_object| { - zig_object.updateRelaSectionsSizes(self); - } - - for (self.objects.items) |index| { - self.file(index).?.object.updateRelaSectionsSizes(self); + for (self.output_rela_sections.keys(), self.output_rela_sections.values()) |shndx, atom_list| { + const shdr = &self.shdrs.items[shndx]; + for (atom_list.items) |atom_index| { + const atom_ptr = self.atom(atom_index) orelse continue; + if (!atom_ptr.flags.alive) continue; + const relocs = atom_ptr.relocs(self); + shdr.sh_size += shdr.sh_entsize * relocs.len; + } } if (self.eh_frame_section_index) |index| { self.shdrs.items[index].sh_size = try eh_frame.calcEhFrameSize(self); } - self.updateSymtabSize(); + try self.updateSymtabSize(); self.updateShStrtabSize(); } @@ -4597,96 +4597,123 @@ fn writeAtomsObject(self: *Elf) !void { } } -fn updateSymtabSize(self: *Elf) void { - var sizes = SymtabSize{}; +fn updateSymtabSize(self: *Elf) !void { + var nlocals: u32 = 0; + var nglobals: u32 = 0; + var strsize: u32 = 0; - if (self.zigObjectPtr()) |zig_object| { - zig_object.asFile().updateSymtabSize(self); - sizes.add(zig_object.output_symtab_size); - } + const gpa = self.base.allocator; + var files = std.ArrayList(File.Index).init(gpa); + defer files.deinit(); + try files.ensureTotalCapacityPrecise(self.objects.items.len + self.shared_objects.items.len + 1); + if (self.zig_object_index) |index| files.appendAssumeCapacity(index); for (self.objects.items) |index| { - const file_ptr = self.file(index).?; - file_ptr.updateSymtabSize(self); - sizes.add(file_ptr.object.output_symtab_size); + files.appendAssumeCapacity(index); + } + for (self.shared_objects.items) |index| { + files.appendAssumeCapacity(index); } - for (self.shared_objects.items) |index| { + // Section symbols + for (self.output_sections.keys()) |_| { + nlocals += 1; + } + + for (files.items) |index| { const file_ptr = self.file(index).?; - file_ptr.updateSymtabSize(self); - sizes.add(file_ptr.shared_object.output_symtab_size); + const ctx = switch (file_ptr) { + inline else => |x| &x.output_symtab_ctx, + }; + ctx.ilocal = nlocals + 1; + ctx.iglobal = nglobals + 1; + try file_ptr.updateSymtabSize(self); + nlocals += ctx.nlocals; + nglobals += ctx.nglobals; + strsize += ctx.strsize; } if (self.zig_got_section_index) |_| { + self.zig_got.output_symtab_ctx.ilocal = nlocals + 1; self.zig_got.updateSymtabSize(self); - sizes.add(self.zig_got.output_symtab_size); + nlocals += self.zig_got.output_symtab_ctx.nlocals; + strsize += self.zig_got.output_symtab_ctx.strsize; } if (self.got_section_index) |_| { + self.got.output_symtab_ctx.ilocal = nlocals + 1; self.got.updateSymtabSize(self); - sizes.add(self.got.output_symtab_size); + nlocals += self.got.output_symtab_ctx.nlocals; + strsize += self.got.output_symtab_ctx.strsize; } if (self.plt_section_index) |_| { + self.plt.output_symtab_ctx.ilocal = nlocals + 1; self.plt.updateSymtabSize(self); - sizes.add(self.plt.output_symtab_size); + nlocals += self.plt.output_symtab_ctx.nlocals; + strsize += self.plt.output_symtab_ctx.strsize; } if (self.plt_got_section_index) |_| { + self.plt_got.output_symtab_ctx.ilocal = nlocals + 1; self.plt_got.updateSymtabSize(self); - sizes.add(self.plt_got.output_symtab_size); + nlocals += self.plt_got.output_symtab_ctx.nlocals; + strsize += self.plt_got.output_symtab_ctx.strsize; } - if (self.linker_defined_index) |index| { + for (files.items) |index| { const file_ptr = self.file(index).?; - file_ptr.updateSymtabSize(self); - sizes.add(file_ptr.linker_defined.output_symtab_size); - } - - // Section symbols - for (self.output_sections.keys()) |_| { - sizes.nlocals += 1; + const ctx = switch (file_ptr) { + inline else => |x| &x.output_symtab_ctx, + }; + ctx.iglobal += nlocals; } const symtab_shdr = &self.shdrs.items[self.symtab_section_index.?]; - symtab_shdr.sh_info = sizes.nlocals + 1; + symtab_shdr.sh_info = nlocals + 1; symtab_shdr.sh_link = self.strtab_section_index.?; const sym_size: u64 = switch (self.ptr_width) { .p32 => @sizeOf(elf.Elf32_Sym), .p64 => @sizeOf(elf.Elf64_Sym), }; - const needed_size = (sizes.nlocals + sizes.nglobals + 1) * sym_size; + const needed_size = (nlocals + nglobals + 1) * sym_size; symtab_shdr.sh_size = needed_size; const strtab = &self.shdrs.items[self.strtab_section_index.?]; - strtab.sh_size = sizes.strsize + 1; + strtab.sh_size = strsize + 1; } fn updateSymtabSizeZigObject(self: *Elf, zig_object: *ZigObject) void { - var sizes = SymtabSize{}; - - zig_object.asFile().updateSymtabSize(self); - sizes.add(zig_object.output_symtab_size); + var nlocals: u32 = 0; + var nglobals: u32 = 0; + var strsize: u32 = 0; // Section symbols for (self.output_sections.keys()) |_| { - sizes.nlocals += 1; + nlocals += 1; } + zig_object.output_symtab_ctx.ilocal = nlocals + 1; + try zig_object.asFile().updateSymtabSize(self); + nlocals += zig_object.output_symtab_ctx.nlocals; + nglobals += zig_object.output_symtab_ctx.nglobals; + strsize += zig_object.output_symtab_ctx.strsize; + zig_object.output_symtab_ctx.iglobal = nlocals + 1; + const symtab_shdr = &self.shdrs.items[self.symtab_section_index.?]; - symtab_shdr.sh_info = sizes.nlocals + 1; + symtab_shdr.sh_info = nlocals + 1; symtab_shdr.sh_link = self.strtab_section_index.?; const sym_size: u64 = switch (self.ptr_width) { .p32 => @sizeOf(elf.Elf32_Sym), .p64 => @sizeOf(elf.Elf64_Sym), }; - const needed_size = (sizes.nlocals + sizes.nglobals + 1) * sym_size; + const needed_size = (nlocals + nglobals + 1) * sym_size; symtab_shdr.sh_size = needed_size; const strtab = &self.shdrs.items[self.strtab_section_index.?]; - strtab.sh_size = sizes.strsize + 1; + strtab.sh_size = strsize + 1; } fn writeSyntheticSections(self: *Elf) !void { @@ -4822,12 +4849,31 @@ fn writeSyntheticSections(self: *Elf) !void { fn writeSyntheticSectionsObject(self: *Elf) !void { const gpa = self.base.allocator; - if (self.zigObjectPtr()) |zig_object| { - try zig_object.writeRelaSections(self); - } + for (self.output_rela_sections.keys(), self.output_rela_sections.values()) |shndx, atom_list| { + if (atom_list.items.len == 0) continue; - for (self.objects.items) |index| { - try self.file(index).?.object.writeRelaSections(self); + const shdr = self.shdrs.items[shndx]; + + const num_relocs = @divExact(shdr.sh_size, shdr.sh_entsize); + var relocs = try std.ArrayList(elf.Elf64_Rela).initCapacity(gpa, num_relocs); + defer relocs.deinit(); + + for (atom_list.items) |atom_index| { + const atom_ptr = self.atom(atom_index) orelse continue; + if (!atom_ptr.flags.alive) continue; + try atom_ptr.writeRelocs(self, &relocs); + } + + const SortRelocs = struct { + pub fn lessThan(ctx: void, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool { + _ = ctx; + return lhs.r_offset < rhs.r_offset; + } + }; + + mem.sort(elf.Elf64_Rela, relocs.items, {}, SortRelocs.lessThan); + + try self.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), shdr.sh_offset); } if (self.eh_frame_section_index) |shndx| { @@ -4836,7 +4882,7 @@ fn writeSyntheticSectionsObject(self: *Elf) !void { var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size); defer buffer.deinit(); try eh_frame.writeEhFrame(self, buffer.writer()); - // try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); + try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); } try self.writeSymtab(); @@ -4850,16 +4896,6 @@ fn writeShStrtab(self: *Elf) !void { } } -const WriteSymtabCtx = struct { - ilocal: usize, - iglobal: usize, - - fn incr(this: *@This(), ss: SymtabSize) void { - this.ilocal += ss.nlocals; - this.iglobal += ss.nglobals; - } -}; - fn writeSymtab(self: *Elf) !void { const gpa = self.base.allocator; const symtab_shdr = self.shdrs.items[self.symtab_section_index.?]; @@ -4876,54 +4912,41 @@ fn writeSymtab(self: *Elf) !void { const needed_strtab_size = math.cast(usize, strtab_shdr.sh_size - 1) orelse return error.Overflow; try self.strtab.ensureUnusedCapacity(gpa, needed_strtab_size); - var ctx: WriteSymtabCtx = .{ - .ilocal = 1, - .iglobal = symtab_shdr.sh_info, - }; - - ctx.incr(self.writeSectionSymbols(ctx)); + self.writeSectionSymbols(); if (self.zigObjectPtr()) |zig_object| { - zig_object.asFile().writeSymtab(self, ctx); - ctx.incr(zig_object.output_symtab_size); + zig_object.asFile().writeSymtab(self); } for (self.objects.items) |index| { const file_ptr = self.file(index).?; - file_ptr.writeSymtab(self, ctx); - ctx.incr(file_ptr.object.output_symtab_size); + file_ptr.writeSymtab(self); } for (self.shared_objects.items) |index| { const file_ptr = self.file(index).?; - file_ptr.writeSymtab(self, ctx); - ctx.incr(file_ptr.shared_object.output_symtab_size); + file_ptr.writeSymtab(self); } if (self.zig_got_section_index) |_| { - self.zig_got.writeSymtab(self, ctx); - ctx.incr(self.zig_got.output_symtab_size); + self.zig_got.writeSymtab(self); } if (self.got_section_index) |_| { - self.got.writeSymtab(self, ctx); - ctx.incr(self.got.output_symtab_size); + self.got.writeSymtab(self); } if (self.plt_section_index) |_| { - self.plt.writeSymtab(self, ctx); - ctx.incr(self.plt.output_symtab_size); + self.plt.writeSymtab(self); } if (self.plt_got_section_index) |_| { - self.plt_got.writeSymtab(self, ctx); - ctx.incr(self.plt_got.output_symtab_size); + self.plt_got.writeSymtab(self); } if (self.linker_defined_index) |index| { const file_ptr = self.file(index).?; - file_ptr.writeSymtab(self, ctx); - ctx.incr(file_ptr.linker_defined.output_symtab_size); + file_ptr.writeSymtab(self); } const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian(); @@ -4972,15 +4995,8 @@ fn writeSymtabZigObject(self: *Elf, zig_object: *ZigObject) !void { const needed_strtab_size = math.cast(usize, strtab_shdr.sh_size - 1) orelse return error.Overflow; try self.strtab.ensureUnusedCapacity(gpa, needed_strtab_size); - var ctx: WriteSymtabCtx = .{ - .ilocal = 1, - .iglobal = symtab_shdr.sh_info, - }; - - ctx.incr(self.writeSectionSymbols(ctx)); - - zig_object.asFile().writeSymtab(self, ctx); - ctx.incr(zig_object.output_symtab_size); + self.writeSectionSymbols(); + zig_object.asFile().writeSymtab(self); const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian(); switch (self.ptr_width) { @@ -5012,8 +5028,8 @@ fn writeSymtabZigObject(self: *Elf, zig_object: *ZigObject) !void { try self.base.file.?.pwriteAll(self.strtab.items, strtab_shdr.sh_offset); } -fn writeSectionSymbols(self: *Elf, ctx: WriteSymtabCtx) SymtabSize { - var ilocal = ctx.ilocal; +fn writeSectionSymbols(self: *Elf) void { + var ilocal: u32 = 1; for (self.output_sections.keys()) |shndx| { const shdr = self.shdrs.items[shndx]; const out_sym = &self.symtab.items[ilocal]; @@ -5027,7 +5043,6 @@ fn writeSectionSymbols(self: *Elf, ctx: WriteSymtabCtx) SymtabSize { }; ilocal += 1; } - return .{ .nlocals = @intCast(ilocal - ctx.ilocal) }; } /// Always 4 or 8 depending on whether this is 32-bit ELF or 64-bit ELF. @@ -6056,16 +6071,12 @@ pub const ComdatGroup = struct { pub const Index = u32; }; -pub const SymtabSize = struct { +pub const SymtabCtx = struct { + ilocal: u32 = 0, + iglobal: u32 = 0, nlocals: u32 = 0, nglobals: u32 = 0, strsize: u32 = 0, - - fn add(ss: *SymtabSize, other: SymtabSize) void { - ss.nlocals += other.nlocals; - ss.nglobals += other.nglobals; - ss.strsize += other.strsize; - } }; pub const null_sym = elf.Elf64_Sym{ diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 98a9f13771..95202a3d4c 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -293,6 +293,32 @@ pub fn relocs(self: Atom, elf_file: *Elf) []align(1) const elf.Elf64_Rela { }; } +pub fn writeRelocs(self: Atom, elf_file: *Elf, out_relocs: *std.ArrayList(elf.Elf64_Rela)) !void { + const file_ptr = self.file(elf_file).?; + for (self.relocs(elf_file)) |rel| { + const target_index = switch (file_ptr) { + .zig_object => |x| x.symbol(rel.r_sym()), + .object => |x| x.symbols.items[rel.r_sym()], + else => unreachable, + }; + const target = elf_file.symbol(target_index); + const r_sym = target.outputSymtabIndex(elf_file); + const r_offset = self.value + rel.r_offset; + const r_addend = rel.r_addend; + const r_type = switch (rel.r_type()) { + Elf.R_X86_64_ZIG_GOT32, + Elf.R_X86_64_ZIG_GOTPCREL, + => unreachable, // Sanity check if we accidentally emitted those. + else => |r_type| r_type, + }; + out_relocs.appendAssumeCapacity(.{ + .r_offset = r_offset, + .r_addend = r_addend, + .r_info = (@as(u64, @intCast(r_sym)) << 32) | r_type, + }); + } +} + pub fn fdes(self: Atom, elf_file: *Elf) []Fde { if (self.fde_start == self.fde_end) return &[0]Fde{}; const object = self.file(elf_file).?.object; diff --git a/src/link/Elf/LinkerDefined.zig b/src/link/Elf/LinkerDefined.zig index 49e0e8f71d..938f22985f 100644 --- a/src/link/Elf/LinkerDefined.zig +++ b/src/link/Elf/LinkerDefined.zig @@ -3,7 +3,7 @@ symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, strtab: std.ArrayListUnmanaged(u8) = .{}, symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, -output_symtab_size: Elf.SymtabSize = .{}, +output_symtab_ctx: Elf.SymtabCtx = .{}, pub fn deinit(self: *LinkerDefined, allocator: Allocator) void { self.symtab.deinit(allocator); diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index fa9aac3c43..637dc6114e 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -19,7 +19,7 @@ cies: std.ArrayListUnmanaged(Cie) = .{}, alive: bool = true, num_dynrelocs: u32 = 0, -output_symtab_size: Elf.SymtabSize = .{}, +output_symtab_ctx: Elf.SymtabCtx = .{}, output_ar_state: Archive.ArState = .{}, pub fn isObject(path: []const u8) !bool { @@ -672,22 +672,19 @@ pub fn initRelaSections(self: Object, elf_file: *Elf) !void { } } -pub fn updateRelaSectionsSizes(self: Object, elf_file: *Elf) void { +pub fn addAtomsToRelaSections(self: Object, elf_file: *Elf) !void { for (self.atoms.items) |atom_index| { const atom = elf_file.atom(atom_index) orelse continue; if (!atom.flags.alive) continue; const shndx = atom.relocsShndx() orelse continue; const shdr = self.shdrs.items[shndx]; const out_shndx = self.initOutputSection(elf_file, shdr) catch unreachable; - const out_shdr = &elf_file.shdrs.items[out_shndx]; - const relocs = atom.relocs(elf_file); - out_shdr.sh_size += out_shdr.sh_entsize * relocs.len; - } -} -pub fn writeRelaSections(self: Object, elf_file: *Elf) !void { - _ = self; - _ = elf_file; + const gpa = elf_file.base.allocator; + const gop = try elf_file.output_rela_sections.getOrPut(gpa, out_shndx); + if (!gop.found_existing) gop.value_ptr.* = .{}; + try gop.value_ptr.append(gpa, atom_index); + } } pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) !void { diff --git a/src/link/Elf/SharedObject.zig b/src/link/Elf/SharedObject.zig index 1785232625..2317719012 100644 --- a/src/link/Elf/SharedObject.zig +++ b/src/link/Elf/SharedObject.zig @@ -21,7 +21,7 @@ verdef_sect_index: ?u16 = null, needed: bool, alive: bool, -output_symtab_size: Elf.SymtabSize = .{}, +output_symtab_ctx: Elf.SymtabCtx = .{}, pub fn isSharedObject(path: []const u8) !bool { const file = try std.fs.cwd().openFile(path, .{}); diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index 9a9daff358..ed3260b77a 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -107,6 +107,24 @@ pub fn address(symbol: Symbol, opts: struct { plt: bool = true }, elf_file: *Elf return symbol.value; } +pub fn outputSymtabIndex(symbol: Symbol, elf_file: *Elf) u32 { + assert(symbol.flags.output_symtab); + const file_ptr = symbol.file(elf_file).?; + const symtab_ctx = switch (file_ptr) { + inline else => |x| x.output_symtab_ctx, + }; + const idx = symbol.extra(elf_file).?.symtab; + return if (symbol.isLocal(elf_file)) idx + symtab_ctx.ilocal else idx + symtab_ctx.iglobal; +} + +pub fn setOutputSymtabIndex(symbol: *Symbol, index: u32, elf_file: *Elf) !void { + if (symbol.extra(elf_file)) |extras| { + var new_extras = extras; + new_extras.symtab = index; + symbol.setExtra(new_extras, elf_file); + } else try symbol.addExtra(.{ .symtab = index }, elf_file); +} + pub fn gotAddress(symbol: Symbol, elf_file: *Elf) u64 { if (!symbol.flags.has_got) return 0; const extras = symbol.extra(elf_file).?; @@ -388,6 +406,7 @@ pub const Extra = struct { plt: u32 = 0, plt_got: u32 = 0, dynamic: u32 = 0, + symtab: u32 = 0, copy_rel: u32 = 0, tlsgd: u32 = 0, gottp: u32 = 0, diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 3c5174b86e..0ade656a3d 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -18,7 +18,7 @@ relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(elf.Elf64_Rela)) = .{}, num_dynrelocs: u32 = 0, -output_symtab_size: Elf.SymtabSize = .{}, +output_symtab_ctx: Elf.SymtabCtx = .{}, output_ar_state: Archive.ArState = .{}, dwarf: ?Dwarf = null, @@ -533,94 +533,17 @@ pub fn writeAr(self: ZigObject, elf_file: *Elf, writer: anytype) !void { try writer.writeAll(contents); } -pub fn updateRelaSectionsSizes(self: ZigObject, elf_file: *Elf) void { - _ = self; +pub fn addAtomsToRelaSections(self: ZigObject, elf_file: *Elf) !void { + for (self.atoms.items) |atom_index| { + const atom = elf_file.atom(atom_index) orelse continue; + if (!atom.flags.alive) continue; + _ = atom.relocsShndx() orelse continue; + const out_shndx = atom.outputShndx().?; - for (&[_]?u16{ - elf_file.zig_text_rela_section_index, - elf_file.zig_data_rel_ro_rela_section_index, - elf_file.zig_data_rela_section_index, - }) |maybe_index| { - const index = maybe_index orelse continue; - const shdr = &elf_file.shdrs.items[index]; - const meta = elf_file.last_atom_and_free_list_table.get(@intCast(shdr.sh_info)).?; - const last_atom_index = meta.last_atom_index; - - var atom = elf_file.atom(last_atom_index) orelse continue; - while (true) { - const relocs = atom.relocs(elf_file); - shdr.sh_size += relocs.len * shdr.sh_entsize; - if (elf_file.atom(atom.prev_index)) |prev| { - atom = prev; - } else break; - } - } - - for (&[_]?u16{ - elf_file.zig_text_rela_section_index, - elf_file.zig_data_rel_ro_rela_section_index, - elf_file.zig_data_rela_section_index, - }) |maybe_index| { - const index = maybe_index orelse continue; - const shdr = &elf_file.shdrs.items[index]; - if (shdr.sh_size == 0) shdr.sh_offset = 0; - } -} - -pub fn writeRelaSections(self: ZigObject, elf_file: *Elf) !void { - const gpa = elf_file.base.allocator; - - for (&[_]?u16{ - elf_file.zig_text_rela_section_index, - elf_file.zig_data_rel_ro_rela_section_index, - elf_file.zig_data_rela_section_index, - }) |maybe_index| { - const index = maybe_index orelse continue; - const shdr = elf_file.shdrs.items[index]; - const meta = elf_file.last_atom_and_free_list_table.get(@intCast(shdr.sh_info)).?; - const last_atom_index = meta.last_atom_index; - - var atom = elf_file.atom(last_atom_index) orelse continue; - - var relocs = std.ArrayList(elf.Elf64_Rela).init(gpa); - defer relocs.deinit(); - try relocs.ensureTotalCapacityPrecise(@intCast(@divExact(shdr.sh_size, shdr.sh_entsize))); - - while (true) { - for (atom.relocs(elf_file)) |rel| { - const target = elf_file.symbol(self.symbol(rel.r_sym())); - const r_offset = atom.value + rel.r_offset; - const r_sym: u32 = if (target.flags.global) - (target.esym_index & symbol_mask) + @as(u32, @intCast(self.local_esyms.slice().len)) - else - target.esym_index; - const r_type = switch (rel.r_type()) { - Elf.R_X86_64_ZIG_GOT32, - Elf.R_X86_64_ZIG_GOTPCREL, - => unreachable, // Sanity check if we accidentally emitted those. - else => |r_type| r_type, - }; - relocs.appendAssumeCapacity(.{ - .r_offset = r_offset, - .r_addend = rel.r_addend, - .r_info = (@as(u64, @intCast(r_sym + 1)) << 32) | r_type, - }); - } - if (elf_file.atom(atom.prev_index)) |prev| { - atom = prev; - } else break; - } - - const SortRelocs = struct { - pub fn lessThan(ctx: void, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool { - _ = ctx; - return lhs.r_offset < rhs.r_offset; - } - }; - - mem.sort(elf.Elf64_Rela, relocs.items, {}, SortRelocs.lessThan); - - try elf_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), shdr.sh_offset); + const gpa = elf_file.base.allocator; + const gop = try elf_file.output_rela_sections.getOrPut(gpa, out_shndx); + if (!gop.found_existing) gop.value_ptr.* = .{}; + try gop.value_ptr.append(gpa, atom_index); } } diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig index 0fae6bc9f4..357e1ee12d 100644 --- a/src/link/Elf/file.zig +++ b/src/link/Elf/file.zig @@ -127,9 +127,9 @@ pub const File = union(enum) { }; } - pub fn updateSymtabSize(file: File, elf_file: *Elf) void { - const output_symtab_size = switch (file) { - inline else => |x| &x.output_symtab_size, + pub fn updateSymtabSize(file: File, elf_file: *Elf) !void { + const output_symtab_ctx = switch (file) { + inline else => |x| &x.output_symtab_ctx, }; for (file.locals()) |local_index| { const local = elf_file.symbol(local_index); @@ -140,8 +140,9 @@ pub const File = union(enum) { else => {}, } local.flags.output_symtab = true; - output_symtab_size.nlocals += 1; - output_symtab_size.strsize += @as(u32, @intCast(local.name(elf_file).len)) + 1; + try local.setOutputSymtabIndex(output_symtab_ctx.nlocals, elf_file); + output_symtab_ctx.nlocals += 1; + output_symtab_ctx.strsize += @as(u32, @intCast(local.name(elf_file).len)) + 1; } for (file.globals()) |global_index| { @@ -151,28 +152,28 @@ pub const File = union(enum) { if (global.atom(elf_file)) |atom| if (!atom.flags.alive) continue; global.flags.output_symtab = true; if (global.isLocal(elf_file)) { - output_symtab_size.nlocals += 1; + try global.setOutputSymtabIndex(output_symtab_ctx.nlocals, elf_file); + output_symtab_ctx.nlocals += 1; } else { - output_symtab_size.nglobals += 1; + try global.setOutputSymtabIndex(output_symtab_ctx.nglobals, elf_file); + output_symtab_ctx.nglobals += 1; } - output_symtab_size.strsize += @as(u32, @intCast(global.name(elf_file).len)) + 1; + output_symtab_ctx.strsize += @as(u32, @intCast(global.name(elf_file).len)) + 1; } } - pub fn writeSymtab(file: File, elf_file: *Elf, ctx: anytype) void { - var ilocal: usize = ctx.ilocal; + pub fn writeSymtab(file: File, elf_file: *Elf) void { for (file.locals()) |local_index| { const local = elf_file.symbol(local_index); if (!local.flags.output_symtab) continue; - const out_sym = &elf_file.symtab.items[ilocal]; + const idx = local.outputSymtabIndex(elf_file); + const out_sym = &elf_file.symtab.items[idx]; out_sym.st_name = @intCast(elf_file.strtab.items.len); elf_file.strtab.appendSliceAssumeCapacity(local.name(elf_file)); elf_file.strtab.appendAssumeCapacity(0); local.setOutputSym(elf_file, out_sym); - ilocal += 1; } - var iglobal: usize = ctx.iglobal; for (file.globals()) |global_index| { const global = elf_file.symbol(global_index); const file_ptr = global.file(elf_file) orelse continue; @@ -181,17 +182,10 @@ pub const File = union(enum) { const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); elf_file.strtab.appendSliceAssumeCapacity(global.name(elf_file)); elf_file.strtab.appendAssumeCapacity(0); - if (global.isLocal(elf_file)) { - const out_sym = &elf_file.symtab.items[ilocal]; - out_sym.st_name = st_name; - global.setOutputSym(elf_file, out_sym); - ilocal += 1; - } else { - const out_sym = &elf_file.symtab.items[iglobal]; - out_sym.st_name = st_name; - global.setOutputSym(elf_file, out_sym); - iglobal += 1; - } + const idx = global.outputSymtabIndex(elf_file); + const out_sym = &elf_file.symtab.items[idx]; + out_sym.st_name = st_name; + global.setOutputSym(elf_file, out_sym); } } diff --git a/src/link/Elf/synthetic_sections.zig b/src/link/Elf/synthetic_sections.zig index 2602940d41..0c9f94b5b9 100644 --- a/src/link/Elf/synthetic_sections.zig +++ b/src/link/Elf/synthetic_sections.zig @@ -222,7 +222,7 @@ pub const DynamicSection = struct { pub const ZigGotSection = struct { entries: std.ArrayListUnmanaged(Symbol.Index) = .{}, - output_symtab_size: Elf.SymtabSize = .{}, + output_symtab_ctx: Elf.SymtabCtx = .{}, flags: Flags = .{}, const Flags = packed struct { @@ -359,15 +359,15 @@ pub const ZigGotSection = struct { } pub fn updateSymtabSize(zig_got: *ZigGotSection, elf_file: *Elf) void { - zig_got.output_symtab_size.nlocals = @as(u32, @intCast(zig_got.entries.items.len)); + zig_got.output_symtab_ctx.nlocals = @as(u32, @intCast(zig_got.entries.items.len)); for (zig_got.entries.items) |entry| { const name = elf_file.symbol(entry).name(elf_file); - zig_got.output_symtab_size.strsize += @as(u32, @intCast(name.len + "$ziggot".len)) + 1; + zig_got.output_symtab_ctx.strsize += @as(u32, @intCast(name.len + "$ziggot".len)) + 1; } } - pub fn writeSymtab(zig_got: ZigGotSection, elf_file: *Elf, ctx: anytype) void { - for (zig_got.entries.items, ctx.ilocal.., 0..) |entry, ilocal, index| { + pub fn writeSymtab(zig_got: ZigGotSection, elf_file: *Elf) void { + for (zig_got.entries.items, zig_got.output_symtab_ctx.ilocal.., 0..) |entry, ilocal, index| { const symbol = elf_file.symbol(entry); const symbol_name = symbol.name(elf_file); const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); @@ -420,7 +420,7 @@ pub const ZigGotSection = struct { pub const GotSection = struct { entries: std.ArrayListUnmanaged(Entry) = .{}, - output_symtab_size: Elf.SymtabSize = .{}, + output_symtab_ctx: Elf.SymtabCtx = .{}, tlsld_index: ?u32 = null, flags: Flags = .{}, @@ -760,18 +760,18 @@ pub const GotSection = struct { } pub fn updateSymtabSize(got: *GotSection, elf_file: *Elf) void { - got.output_symtab_size.nlocals = @as(u32, @intCast(got.entries.items.len)); + got.output_symtab_ctx.nlocals = @as(u32, @intCast(got.entries.items.len)); for (got.entries.items) |entry| { const symbol_name = switch (entry.tag) { .tlsld => "", inline else => elf_file.symbol(entry.symbol_index).name(elf_file), }; - got.output_symtab_size.strsize += @as(u32, @intCast(symbol_name.len + @tagName(entry.tag).len)) + 1 + 1; + got.output_symtab_ctx.strsize += @as(u32, @intCast(symbol_name.len + @tagName(entry.tag).len)) + 1 + 1; } } - pub fn writeSymtab(got: GotSection, elf_file: *Elf, ctx: anytype) void { - for (got.entries.items, ctx.ilocal..) |entry, ilocal| { + pub fn writeSymtab(got: GotSection, elf_file: *Elf) void { + for (got.entries.items, got.output_symtab_ctx.ilocal..) |entry, ilocal| { const symbol = switch (entry.tag) { .tlsld => null, inline else => elf_file.symbol(entry.symbol_index), @@ -831,7 +831,7 @@ pub const GotSection = struct { pub const PltSection = struct { symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, - output_symtab_size: Elf.SymtabSize = .{}, + output_symtab_ctx: Elf.SymtabCtx = .{}, pub const preamble_size = 32; @@ -909,15 +909,15 @@ pub const PltSection = struct { } pub fn updateSymtabSize(plt: *PltSection, elf_file: *Elf) void { - plt.output_symtab_size.nlocals = @as(u32, @intCast(plt.symbols.items.len)); + plt.output_symtab_ctx.nlocals = @as(u32, @intCast(plt.symbols.items.len)); for (plt.symbols.items) |sym_index| { const name = elf_file.symbol(sym_index).name(elf_file); - plt.output_symtab_size.strsize += @as(u32, @intCast(name.len + "$plt".len)) + 1; + plt.output_symtab_ctx.strsize += @as(u32, @intCast(name.len + "$plt".len)) + 1; } } - pub fn writeSymtab(plt: PltSection, elf_file: *Elf, ctx: anytype) void { - var ilocal = ctx.ilocal; + pub fn writeSymtab(plt: PltSection, elf_file: *Elf) void { + var ilocal = plt.output_symtab_ctx.ilocal; for (plt.symbols.items) |sym_index| { const sym = elf_file.symbol(sym_index); const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); @@ -968,7 +968,7 @@ pub const GotPltSection = struct { pub const PltGotSection = struct { symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, - output_symtab_size: Elf.SymtabSize = .{}, + output_symtab_ctx: Elf.SymtabCtx = .{}, pub fn deinit(plt_got: *PltGotSection, allocator: Allocator) void { plt_got.symbols.deinit(allocator); @@ -1008,15 +1008,15 @@ pub const PltGotSection = struct { } pub fn updateSymtabSize(plt_got: *PltGotSection, elf_file: *Elf) void { - plt_got.output_symtab_size.nlocals = @as(u32, @intCast(plt_got.symbols.items.len)); + plt_got.output_symtab_ctx.nlocals = @as(u32, @intCast(plt_got.symbols.items.len)); for (plt_got.symbols.items) |sym_index| { const name = elf_file.symbol(sym_index).name(elf_file); - plt_got.output_symtab_size.strsize += @as(u32, @intCast(name.len + "$pltgot".len)) + 1; + plt_got.output_symtab_ctx.strsize += @as(u32, @intCast(name.len + "$pltgot".len)) + 1; } } - pub fn writeSymtab(plt_got: PltGotSection, elf_file: *Elf, ctx: anytype) void { - var ilocal = ctx.ilocal; + pub fn writeSymtab(plt_got: PltGotSection, elf_file: *Elf) void { + var ilocal = plt_got.output_symtab_ctx.ilocal; for (plt_got.symbols.items) |sym_index| { const sym = elf_file.symbol(sym_index); const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); From e87c751558ec1b81bab09f40959a58d250a35a41 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 8 Nov 2023 10:57:34 +0100 Subject: [PATCH 08/29] elf: reference .rela sections via output section index --- src/link/Elf.zig | 77 +++++++++++++++++++++++++++++--------- src/link/Elf/Object.zig | 8 ++-- src/link/Elf/ZigObject.zig | 5 +-- 3 files changed, 67 insertions(+), 23 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index fd52a4327e..9a52d6fe58 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -18,13 +18,13 @@ shared_objects: std.ArrayListUnmanaged(File.Index) = .{}, /// Same order as in the file. shdrs: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{}, /// Given index to a section, pulls index of containing phdr if any. -phdr_to_shdr_table: std.AutoHashMapUnmanaged(u16, u16) = .{}, +phdr_to_shdr_table: std.AutoHashMapUnmanaged(u32, u32) = .{}, /// File offset into the shdr table. shdr_table_offset: ?u64 = null, /// Table of lists of atoms per output section. /// This table is not used to track incrementally generated atoms. -output_sections: std.AutoArrayHashMapUnmanaged(u16, std.ArrayListUnmanaged(Atom.Index)) = .{}, -output_rela_sections: std.AutoArrayHashMapUnmanaged(u16, std.ArrayListUnmanaged(Atom.Index)) = .{}, +output_sections: std.AutoArrayHashMapUnmanaged(u32, std.ArrayListUnmanaged(Atom.Index)) = .{}, +output_rela_sections: std.AutoArrayHashMapUnmanaged(u32, RelaSection) = .{}, /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. /// Same order as in the file. @@ -355,8 +355,8 @@ pub fn deinit(self: *Elf) void { list.deinit(gpa); } self.output_sections.deinit(gpa); - for (self.output_rela_sections.values()) |*list| { - list.deinit(gpa); + for (self.output_rela_sections.values()) |*sec| { + sec.atom_list.deinit(gpa); } self.output_rela_sections.deinit(gpa); self.shstrtab.deinit(gpa); @@ -600,7 +600,9 @@ pub fn initMetadata(self: *Elf) !void { fillSection(self, shdr, self.base.options.program_code_size_hint, self.phdr_zig_load_re_index); if (self.isRelocatable()) { const rela_shndx = try self.addRelaShdr(".rela.text.zig", self.zig_text_section_index.?); - try self.output_rela_sections.putNoClobber(gpa, rela_shndx, .{}); + try self.output_rela_sections.putNoClobber(gpa, self.zig_text_section_index.?, .{ + .shndx = rela_shndx, + }); } else { try self.phdr_to_shdr_table.putNoClobber( gpa, @@ -648,7 +650,9 @@ pub fn initMetadata(self: *Elf) !void { ".rela.data.rel.ro.zig", self.zig_data_rel_ro_section_index.?, ); - try self.output_rela_sections.putNoClobber(gpa, rela_shndx, .{}); + try self.output_rela_sections.putNoClobber(gpa, self.zig_data_rel_ro_section_index.?, .{ + .shndx = rela_shndx, + }); } else { try self.phdr_to_shdr_table.putNoClobber( gpa, @@ -675,7 +679,9 @@ pub fn initMetadata(self: *Elf) !void { ".rela.data.zig", self.zig_data_section_index.?, ); - try self.output_rela_sections.putNoClobber(gpa, rela_shndx, .{}); + try self.output_rela_sections.putNoClobber(gpa, self.zig_data_section_index.?, .{ + .shndx = rela_shndx, + }); } else { try self.phdr_to_shdr_table.putNoClobber( gpa, @@ -793,6 +799,14 @@ pub fn initMetadata(self: *Elf) !void { try self.output_sections.putNoClobber(gpa, self.debug_line_section_index.?, .{}); } } + + // We need to find current max assumed file offset, and actually write to file to make it a reality. + var end_pos: u64 = 0; + for (self.shdrs.items) |shdr| { + if (shdr.sh_offset == std.math.maxInt(u64)) continue; + end_pos = @max(end_pos, shdr.sh_offset + shdr.sh_size); + } + try self.base.file.?.pwriteAll(&[1]u8{0}, end_pos); } pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void { @@ -1521,8 +1535,12 @@ pub fn flushStaticLib(self: *Elf) link.File.FlushError!void { try self.allocateNonAllocSections(); - try self.writeShdrTable(); + if (build_options.enable_logging) { + state_log.debug("{}", .{self.dumpState()}); + } + try self.writeSyntheticSectionsObject(); + try self.writeShdrTable(); try self.writeElfHeader(); } @@ -1618,6 +1636,9 @@ pub fn flushObject(self: *Elf) link.File.FlushError!void { try self.initSectionsObject(); try self.sortShdrs(); + if (self.zigObjectPtr()) |zig_object| { + try zig_object.addAtomsToRelaSections(self); + } for (self.objects.items) |index| { const object = self.file(index).?.object; try object.addAtomsToOutputSections(self); @@ -3924,6 +3945,21 @@ fn sortShdrs(self: *Elf) !void { } } + { + var output_rela_sections = try self.output_rela_sections.clone(gpa); + defer output_rela_sections.deinit(gpa); + + self.output_rela_sections.clearRetainingCapacity(); + + var it = output_rela_sections.iterator(); + while (it.next()) |entry| { + const shndx = entry.key_ptr.*; + var meta = entry.value_ptr.*; + meta.shndx = backlinks[meta.shndx]; + self.output_rela_sections.putAssumeCapacityNoClobber(backlinks[shndx], meta); + } + } + { var last_atom_and_free_list_table = try self.last_atom_and_free_list_table.clone(gpa); defer last_atom_and_free_list_table.deinit(gpa); @@ -4082,14 +4118,16 @@ fn updateSectionSizesObject(self: *Elf) !void { } } - for (self.output_rela_sections.keys(), self.output_rela_sections.values()) |shndx, atom_list| { - const shdr = &self.shdrs.items[shndx]; - for (atom_list.items) |atom_index| { + for (self.output_rela_sections.values()) |sec| { + const shdr = &self.shdrs.items[sec.shndx]; + for (sec.atom_list.items) |atom_index| { const atom_ptr = self.atom(atom_index) orelse continue; if (!atom_ptr.flags.alive) continue; const relocs = atom_ptr.relocs(self); shdr.sh_size += shdr.sh_entsize * relocs.len; } + + if (shdr.sh_size == 0) shdr.sh_offset = 0; } if (self.eh_frame_section_index) |index| { @@ -4849,16 +4887,16 @@ fn writeSyntheticSections(self: *Elf) !void { fn writeSyntheticSectionsObject(self: *Elf) !void { const gpa = self.base.allocator; - for (self.output_rela_sections.keys(), self.output_rela_sections.values()) |shndx, atom_list| { - if (atom_list.items.len == 0) continue; + for (self.output_rela_sections.values()) |sec| { + if (sec.atom_list.items.len == 0) continue; - const shdr = self.shdrs.items[shndx]; + const shdr = self.shdrs.items[sec.shndx]; const num_relocs = @divExact(shdr.sh_size, shdr.sh_entsize); var relocs = try std.ArrayList(elf.Elf64_Rela).initCapacity(gpa, num_relocs); defer relocs.deinit(); - for (atom_list.items) |atom_index| { + for (sec.atom_list.items) |atom_index| { const atom_ptr = self.atom(atom_index) orelse continue; if (!atom_ptr.flags.alive) continue; try atom_ptr.writeRelocs(self, &relocs); @@ -6127,8 +6165,13 @@ const LastAtomAndFreeList = struct { /// by 1 byte. It will then have -1 overcapacity. free_list: std.ArrayListUnmanaged(Atom.Index) = .{}, }; +const LastAtomAndFreeListTable = std.AutoArrayHashMapUnmanaged(u32, LastAtomAndFreeList); -const LastAtomAndFreeListTable = std.AutoArrayHashMapUnmanaged(u16, LastAtomAndFreeList); +const RelaSection = struct { + shndx: u32, + atom_list: std.ArrayListUnmanaged(Atom.Index) = .{}, +}; +const RelaSectionTable = std.AutoArrayHashMapUnmanaged(u32, RelaSection); pub const R_X86_64_ZIG_GOT32 = elf.R_X86_64_NUM + 1; pub const R_X86_64_ZIG_GOTPCREL = elf.R_X86_64_NUM + 2; diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index 637dc6114e..8a402ad7d2 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -681,9 +681,9 @@ pub fn addAtomsToRelaSections(self: Object, elf_file: *Elf) !void { const out_shndx = self.initOutputSection(elf_file, shdr) catch unreachable; const gpa = elf_file.base.allocator; - const gop = try elf_file.output_rela_sections.getOrPut(gpa, out_shndx); - if (!gop.found_existing) gop.value_ptr.* = .{}; - try gop.value_ptr.append(gpa, atom_index); + const gop = try elf_file.output_rela_sections.getOrPut(gpa, atom.outputShndx().?); + if (!gop.found_existing) gop.value_ptr.* = .{ .shndx = out_shndx }; + try gop.value_ptr.atom_list.append(gpa, atom_index); } } @@ -718,11 +718,13 @@ pub fn writeAr(self: Object, writer: anytype) !void { } pub fn locals(self: Object) []const Symbol.Index { + if (self.symbols.items.len == 0) return &[0]Symbol.Index{}; const end = self.first_global orelse self.symbols.items.len; return self.symbols.items[0..end]; } pub fn globals(self: Object) []const Symbol.Index { + if (self.symbols.items.len == 0) return &[0]Symbol.Index{}; const start = self.first_global orelse self.symbols.items.len; return self.symbols.items[start..]; } diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 0ade656a3d..07c492431c 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -541,9 +541,8 @@ pub fn addAtomsToRelaSections(self: ZigObject, elf_file: *Elf) !void { const out_shndx = atom.outputShndx().?; const gpa = elf_file.base.allocator; - const gop = try elf_file.output_rela_sections.getOrPut(gpa, out_shndx); - if (!gop.found_existing) gop.value_ptr.* = .{}; - try gop.value_ptr.append(gpa, atom_index); + const sec = elf_file.output_rela_sections.getPtr(out_shndx).?; + try sec.atom_list.append(gpa, atom_index); } } From ae08f9bfe9c2ab5488b375ffd949609016658450 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 8 Nov 2023 11:51:11 +0100 Subject: [PATCH 09/29] elf: claim unresolved dangling symbols as undef externs in -r mode --- src/link/Elf.zig | 10 ++++++++-- src/link/Elf/Atom.zig | 20 +++++++++++++++++--- src/link/Elf/Object.zig | 19 +++++++++++++++++++ src/link/Elf/ZigObject.zig | 3 +-- 4 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 9a52d6fe58..fe282b4f02 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -2041,8 +2041,7 @@ fn claimUnresolved(self: *Elf) void { zig_object.claimUnresolved(self); } for (self.objects.items) |index| { - const object = self.file(index).?.object; - object.claimUnresolved(self); + self.file(index).?.object.claimUnresolved(self); } } @@ -2050,6 +2049,9 @@ fn claimUnresolvedObject(self: *Elf) void { if (self.zigObjectPtr()) |zig_object| { zig_object.claimUnresolvedObject(self); } + for (self.objects.items) |index| { + self.file(index).?.object.claimUnresolvedObject(self); + } } /// In scanRelocs we will go over all live atoms and scan their relocs. @@ -5083,6 +5085,10 @@ fn writeSectionSymbols(self: *Elf) void { } } +pub fn sectionSymbolOutputSymtabIndex(self: Elf, shndx: u32) u32 { + return @intCast(self.output_sections.getIndex(shndx).? + 1); +} + /// Always 4 or 8 depending on whether this is 32-bit ELF or 64-bit ELF. fn ptrWidthBytes(self: Elf) u8 { return switch (self.ptr_width) { diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 95202a3d4c..09a16e4240 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -294,6 +294,8 @@ pub fn relocs(self: Atom, elf_file: *Elf) []align(1) const elf.Elf64_Rela { } pub fn writeRelocs(self: Atom, elf_file: *Elf, out_relocs: *std.ArrayList(elf.Elf64_Rela)) !void { + relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) }); + const file_ptr = self.file(elf_file).?; for (self.relocs(elf_file)) |rel| { const target_index = switch (file_ptr) { @@ -302,15 +304,27 @@ pub fn writeRelocs(self: Atom, elf_file: *Elf, out_relocs: *std.ArrayList(elf.El else => unreachable, }; const target = elf_file.symbol(target_index); - const r_sym = target.outputSymtabIndex(elf_file); - const r_offset = self.value + rel.r_offset; - const r_addend = rel.r_addend; const r_type = switch (rel.r_type()) { Elf.R_X86_64_ZIG_GOT32, Elf.R_X86_64_ZIG_GOTPCREL, => unreachable, // Sanity check if we accidentally emitted those. else => |r_type| r_type, }; + const r_offset = self.value + rel.r_offset; + const r_addend = rel.r_addend; + const r_sym = switch (target.type(elf_file)) { + elf.STT_SECTION => elf_file.sectionSymbolOutputSymtabIndex(target.outputShndx().?), + else => target.outputSymtabIndex(elf_file), + }; + + relocs_log.debug(" {s}: [{x} => {d}({s})] + {x}", .{ + fmtRelocType(r_type), + r_offset, + r_sym, + target.name(elf_file), + r_addend, + }); + out_relocs.appendAssumeCapacity(.{ .r_offset = r_offset, .r_addend = r_addend, diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index 8a402ad7d2..0a4232d3b1 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -491,6 +491,25 @@ pub fn claimUnresolved(self: *Object, elf_file: *Elf) void { } } +pub fn claimUnresolvedObject(self: *Object, elf_file: *Elf) void { + const first_global = self.first_global orelse return; + for (self.globals(), 0..) |index, i| { + const esym_index = @as(u32, @intCast(first_global + i)); + const esym = self.symtab.items[esym_index]; + if (esym.st_shndx != elf.SHN_UNDEF) continue; + + const global = elf_file.symbol(index); + if (global.file(elf_file)) |file| { + if (global.elfSym(elf_file).st_shndx != elf.SHN_UNDEF or file.index() <= self.index) continue; + } + + global.value = 0; + global.atom_index = 0; + global.esym_index = esym_index; + global.file_index = self.index; + } +} + pub fn markLive(self: *Object, elf_file: *Elf) void { const first_global = self.first_global orelse return; for (self.globals(), 0..) |index, i| { diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 07c492431c..798ced73a5 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -377,8 +377,7 @@ pub fn claimUnresolvedObject(self: ZigObject, elf_file: *Elf) void { const global = elf_file.symbol(index); if (global.file(elf_file)) |file| { - if (global.elfSym(elf_file).st_shndx != elf.SHN_UNDEF or - file.index() <= self.index) continue; + if (global.elfSym(elf_file).st_shndx != elf.SHN_UNDEF or file.index() <= self.index) continue; } global.value = 0; From d8b1ef9430e0e383769ffa0a43750515aaa32280 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 8 Nov 2023 12:11:55 +0100 Subject: [PATCH 10/29] elf: adjust r_addend with offset of the original input section ref'd via STT_SECTION --- src/link/Elf/Atom.zig | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 09a16e4240..0b1de1feeb 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -311,11 +311,17 @@ pub fn writeRelocs(self: Atom, elf_file: *Elf, out_relocs: *std.ArrayList(elf.El else => |r_type| r_type, }; const r_offset = self.value + rel.r_offset; - const r_addend = rel.r_addend; - const r_sym = switch (target.type(elf_file)) { - elf.STT_SECTION => elf_file.sectionSymbolOutputSymtabIndex(target.outputShndx().?), - else => target.outputSymtabIndex(elf_file), - }; + var r_addend = rel.r_addend; + var r_sym: u32 = 0; + switch (target.type(elf_file)) { + elf.STT_SECTION => { + r_addend += @intCast(target.value); + r_sym = elf_file.sectionSymbolOutputSymtabIndex(target.outputShndx().?); + }, + else => { + r_sym = target.outputSymtabIndex(elf_file); + }, + } relocs_log.debug(" {s}: [{x} => {d}({s})] + {x}", .{ fmtRelocType(r_type), From a16e6706b337e2c7247238d08f205594d777a63a Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 8 Nov 2023 15:10:28 +0100 Subject: [PATCH 11/29] elf: LLVM emits relocs to undef local symbols - color me surprised! --- src/link/Elf/Atom.zig | 2 +- src/link/Elf/Symbol.zig | 4 ++-- src/link/Elf/ZigObject.zig | 2 ++ src/link/Elf/file.zig | 6 ++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 0b1de1feeb..ec12f17721 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -319,7 +319,7 @@ pub fn writeRelocs(self: Atom, elf_file: *Elf, out_relocs: *std.ArrayList(elf.El r_sym = elf_file.sectionSymbolOutputSymtabIndex(target.outputShndx().?); }, else => { - r_sym = target.outputSymtabIndex(elf_file); + r_sym = target.outputSymtabIndex(elf_file) orelse 0; }, } diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index ed3260b77a..a2ffc62014 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -107,8 +107,8 @@ pub fn address(symbol: Symbol, opts: struct { plt: bool = true }, elf_file: *Elf return symbol.value; } -pub fn outputSymtabIndex(symbol: Symbol, elf_file: *Elf) u32 { - assert(symbol.flags.output_symtab); +pub fn outputSymtabIndex(symbol: Symbol, elf_file: *Elf) ?u32 { + if (!symbol.flags.output_symtab) return null; const file_ptr = symbol.file(elf_file).?; const symtab_ctx = switch (file_ptr) { inline else => |x| x.output_symtab_ctx, diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 798ced73a5..03b2572549 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -538,6 +538,8 @@ pub fn addAtomsToRelaSections(self: ZigObject, elf_file: *Elf) !void { if (!atom.flags.alive) continue; _ = atom.relocsShndx() orelse continue; const out_shndx = atom.outputShndx().?; + const out_shdr = elf_file.shdrs.items[out_shndx]; + if (out_shdr.sh_type == elf.SHT_NOBITS) continue; const gpa = elf_file.base.allocator; const sec = elf_file.output_rela_sections.getPtr(out_shndx).?; diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig index 357e1ee12d..29a76daad9 100644 --- a/src/link/Elf/file.zig +++ b/src/link/Elf/file.zig @@ -165,8 +165,7 @@ pub const File = union(enum) { pub fn writeSymtab(file: File, elf_file: *Elf) void { for (file.locals()) |local_index| { const local = elf_file.symbol(local_index); - if (!local.flags.output_symtab) continue; - const idx = local.outputSymtabIndex(elf_file); + const idx = local.outputSymtabIndex(elf_file) orelse continue; const out_sym = &elf_file.symtab.items[idx]; out_sym.st_name = @intCast(elf_file.strtab.items.len); elf_file.strtab.appendSliceAssumeCapacity(local.name(elf_file)); @@ -178,11 +177,10 @@ pub const File = union(enum) { const global = elf_file.symbol(global_index); const file_ptr = global.file(elf_file) orelse continue; if (file_ptr.index() != file.index()) continue; - if (!global.flags.output_symtab) continue; + const idx = global.outputSymtabIndex(elf_file) orelse continue; const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); elf_file.strtab.appendSliceAssumeCapacity(global.name(elf_file)); elf_file.strtab.appendAssumeCapacity(0); - const idx = global.outputSymtabIndex(elf_file); const out_sym = &elf_file.symtab.items[idx]; out_sym.st_name = st_name; global.setOutputSym(elf_file, out_sym); From 0b4d398c40d4dde23c1770f7a9a0c22417f6d51f Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 8 Nov 2023 17:35:56 +0100 Subject: [PATCH 12/29] elf: streamline codepaths for different linker modes (object, ar, exe/dyn) --- src/link/Elf.zig | 607 ++++++++++++++++++++++++++++------------------- 1 file changed, 358 insertions(+), 249 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index fe282b4f02..650d4074a5 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -959,6 +959,9 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node } else null; const gc_sections = self.base.options.gc_sections orelse false; + // --verbose-link + if (self.base.options.verbose_link) try self.dumpArgv(comp); + var csu = try CsuObjects.init(arena, self.base.options, comp); const compiler_rt_path: ?[]const u8 = blk: { if (comp.compiler_rt_lib) |x| break :blk x.full_object_path; @@ -966,247 +969,9 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node break :blk null; }; - // --verbose-link - if (self.base.options.verbose_link) { - var argv = std.ArrayList([]const u8).init(arena); - - try argv.append("zig"); - try argv.append("ld"); - - try argv.append("-o"); - try argv.append(full_out_path); - - if (self.base.options.entry) |entry| { - try argv.append("--entry"); - try argv.append(entry); - } - - if (self.base.options.dynamic_linker) |path| { - try argv.append("-dynamic-linker"); - try argv.append(path); - } - - if (self.base.options.soname) |name| { - try argv.append("-soname"); - try argv.append(name); - } - - for (self.base.options.rpath_list) |rpath| { - try argv.append("-rpath"); - try argv.append(rpath); - } - - if (self.base.options.each_lib_rpath) { - for (self.base.options.lib_dirs) |lib_dir_path| { - try argv.append("-rpath"); - try argv.append(lib_dir_path); - } - for (self.base.options.objects) |obj| { - if (Compilation.classifyFileExt(obj.path) == .shared_library) { - const lib_dir_path = std.fs.path.dirname(obj.path) orelse continue; - if (obj.loption) continue; - - try argv.append("-rpath"); - try argv.append(lib_dir_path); - } - } - } - - if (self.base.options.stack_size_override) |ss| { - try argv.append("-z"); - try argv.append(try std.fmt.allocPrint(arena, "stack-size={d}", .{ss})); - } - - if (self.base.options.image_base_override) |image_base| { - try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{image_base})); - } - - if (gc_sections) { - try argv.append("--gc-sections"); - } - - if (self.base.options.print_gc_sections) { - try argv.append("--print-gc-sections"); - } - - if (self.base.options.eh_frame_hdr) { - try argv.append("--eh-frame-hdr"); - } - - if (self.base.options.rdynamic) { - try argv.append("--export-dynamic"); - } - - if (self.base.options.strip) { - try argv.append("-s"); - } - - if (self.base.options.z_notext) { - try argv.append("-z"); - try argv.append("notext"); - } - - if (self.base.options.z_nocopyreloc) { - try argv.append("-z"); - try argv.append("nocopyreloc"); - } - - if (self.base.options.z_now) { - try argv.append("-z"); - try argv.append("now"); - } - - if (self.isStatic()) { - try argv.append("-static"); - } else if (self.isDynLib()) { - try argv.append("-shared"); - } - - if (self.base.options.pie and self.isExe()) { - try argv.append("-pie"); - } - - // csu prelude - if (csu.crt0) |v| try argv.append(v); - if (csu.crti) |v| try argv.append(v); - if (csu.crtbegin) |v| try argv.append(v); - - for (self.base.options.lib_dirs) |lib_dir| { - try argv.append("-L"); - try argv.append(lib_dir); - } - - if (self.base.options.link_libc) { - if (self.base.options.libc_installation) |libc_installation| { - try argv.append("-L"); - try argv.append(libc_installation.crt_dir.?); - } - } - - var whole_archive = false; - for (self.base.options.objects) |obj| { - if (obj.must_link and !whole_archive) { - try argv.append("-whole-archive"); - whole_archive = true; - } else if (!obj.must_link and whole_archive) { - try argv.append("-no-whole-archive"); - whole_archive = false; - } - - if (obj.loption) { - assert(obj.path[0] == ':'); - try argv.append("-l"); - } - try argv.append(obj.path); - } - if (whole_archive) { - try argv.append("-no-whole-archive"); - whole_archive = false; - } - - for (comp.c_object_table.keys()) |key| { - try argv.append(key.status.success.object_path); - } - - if (module_obj_path) |p| { - try argv.append(p); - } - - // TSAN - if (self.base.options.tsan) { - try argv.append(comp.tsan_static_lib.?.full_object_path); - } - - // libc - if (!self.base.options.skip_linker_dependencies and - !self.base.options.link_libc) - { - if (comp.libc_static_lib) |lib| { - try argv.append(lib.full_object_path); - } - } - - // stack-protector. - // Related: https://github.com/ziglang/zig/issues/7265 - if (comp.libssp_static_lib) |ssp| { - try argv.append(ssp.full_object_path); - } - - // Shared libraries. - // Worst-case, we need an --as-needed argument for every lib, as well - // as one before and one after. - try argv.ensureUnusedCapacity(self.base.options.system_libs.keys().len * 2 + 2); - argv.appendAssumeCapacity("--as-needed"); - var as_needed = true; - - for (self.base.options.system_libs.values()) |lib_info| { - const lib_as_needed = !lib_info.needed; - switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) { - 0b00, 0b11 => {}, - 0b01 => { - argv.appendAssumeCapacity("--no-as-needed"); - as_needed = false; - }, - 0b10 => { - argv.appendAssumeCapacity("--as-needed"); - as_needed = true; - }, - } - argv.appendAssumeCapacity(lib_info.path.?); - } - - if (!as_needed) { - argv.appendAssumeCapacity("--as-needed"); - as_needed = true; - } - - // libc++ dep - if (self.base.options.link_libcpp) { - try argv.append(comp.libcxxabi_static_lib.?.full_object_path); - try argv.append(comp.libcxx_static_lib.?.full_object_path); - } - - // libunwind dep - if (self.base.options.link_libunwind) { - try argv.append(comp.libunwind_static_lib.?.full_object_path); - } - - // libc dep - if (self.base.options.link_libc) { - if (self.base.options.libc_installation != null) { - const needs_grouping = self.base.options.link_mode == .Static; - if (needs_grouping) try argv.append("--start-group"); - try argv.appendSlice(target_util.libcFullLinkFlags(target)); - if (needs_grouping) try argv.append("--end-group"); - } else if (target.isGnuLibC()) { - for (glibc.libs) |lib| { - const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{ - comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, - }); - try argv.append(lib_path); - } - try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a")); - } else if (target.isMusl()) { - try argv.append(try comp.get_libc_crt_file(arena, switch (self.base.options.link_mode) { - .Static => "libc.a", - .Dynamic => "libc.so", - })); - } - } - - // compiler-rt - if (compiler_rt_path) |p| { - try argv.append(p); - } - - // crt postlude - if (csu.crtend) |v| try argv.append(v); - if (csu.crtn) |v| try argv.append(v); - - Compilation.dump_argv(argv.items); - } - if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self); + if (self.isStaticLib()) return self.flushStaticLib(comp, module_obj_path); + if (self.isObject()) return self.flushObject(comp, module_obj_path); // Here we will parse input positional and library files (if referenced). // This will roughly match in any linker backend we support. @@ -1232,6 +997,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node // rpaths var rpath_table = std.StringArrayHashMap(void).init(self.base.allocator); defer rpath_table.deinit(); + for (self.base.options.rpath_list) |rpath| { _ = try rpath_table.put(rpath, {}); } @@ -1380,8 +1146,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node try self.handleAndReportParseError(obj.path, err, &parse_ctx); } - if (self.isStaticLib()) return self.flushStaticLib(); - // Init all objects for (self.objects.items) |index| { try self.file(index).?.object.init(self); @@ -1410,7 +1174,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node // If we haven't already, create a linker-generated input file comprising of // linker-defined synthetic symbols only such as `_DYNAMIC`, etc. - if (self.linker_defined_index == null and !self.isRelocatable()) { + if (self.linker_defined_index == null) { const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); self.files.set(index, .{ .linker_defined = .{ .index = index } }); self.linker_defined_index = index; @@ -1424,8 +1188,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node self.resolveSymbols(); self.markEhFrameAtomsDead(); - if (self.isObject()) return self.flushObject(); - try self.convertCommonSymbols(); self.markImportsExports(); @@ -1519,9 +1281,30 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node } } -pub fn flushStaticLib(self: *Elf) link.File.FlushError!void { +pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void { const gpa = self.base.allocator; + var positionals = std.ArrayList(Compilation.LinkObject).init(gpa); + defer positionals.deinit(); + + try positionals.ensureUnusedCapacity(self.base.options.objects.len); + positionals.appendSliceAssumeCapacity(self.base.options.objects); + + // This is a set of object files emitted by clang in a single `build-exe` invocation. + // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up + // in this set. + for (comp.c_object_table.keys()) |key| { + try positionals.append(.{ .path = key.status.success.object_path }); + } + + if (module_obj_path) |path| try positionals.append(.{ .path = path }); + + for (positionals.items) |obj| { + var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined }; + self.parsePositional(obj.path, obj.must_link, &parse_ctx) catch |err| + try self.handleAndReportParseError(obj.path, err, &parse_ctx); + } + // First, we flush relocatable object file generated with our backends. if (self.zigObjectPtr()) |zig_object| { zig_object.resolveSymbols(self); @@ -1631,7 +1414,41 @@ pub fn flushStaticLib(self: *Elf) link.File.FlushError!void { try self.base.file.?.pwriteAll(buffer.items, 0); } -pub fn flushObject(self: *Elf) link.File.FlushError!void { +pub fn flushObject(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void { + const gpa = self.base.allocator; + + var positionals = std.ArrayList(Compilation.LinkObject).init(gpa); + defer positionals.deinit(); + try positionals.ensureUnusedCapacity(self.base.options.objects.len); + positionals.appendSliceAssumeCapacity(self.base.options.objects); + + // This is a set of object files emitted by clang in a single `build-exe` invocation. + // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up + // in this set. + for (comp.c_object_table.keys()) |key| { + try positionals.append(.{ .path = key.status.success.object_path }); + } + + if (module_obj_path) |path| try positionals.append(.{ .path = path }); + + for (positionals.items) |obj| { + var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined }; + self.parsePositional(obj.path, obj.must_link, &parse_ctx) catch |err| + try self.handleAndReportParseError(obj.path, err, &parse_ctx); + } + + // Init all objects + for (self.objects.items) |index| { + try self.file(index).?.object.init(self); + } + + // Now, we are ready to resolve the symbols across all input files. + // We will first resolve the files in the ZigObject, next in the parsed + // input Object files. + // Any qualifing unresolved symbol will be upgraded to an absolute, weak + // symbol for potential resolution at load-time. + self.resolveSymbols(); + self.markEhFrameAtomsDead(); self.claimUnresolvedObject(); try self.initSectionsObject(); @@ -1660,6 +1477,296 @@ pub fn flushObject(self: *Elf) link.File.FlushError!void { try self.writeElfHeader(); } +/// --verbose-link output +fn dumpArgv(self: *Elf, comp: *Compilation) !void { + var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + + const target = self.base.options.target; + const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type. + const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); + const module_obj_path: ?[]const u8 = if (self.base.intermediary_basename) |path| blk: { + if (fs.path.dirname(full_out_path)) |dirname| { + break :blk try fs.path.join(arena, &.{ dirname, path }); + } else { + break :blk path; + } + } else null; + const gc_sections = self.base.options.gc_sections orelse false; + + var csu = try CsuObjects.init(arena, self.base.options, comp); + const compiler_rt_path: ?[]const u8 = blk: { + if (comp.compiler_rt_lib) |x| break :blk x.full_object_path; + if (comp.compiler_rt_obj) |x| break :blk x.full_object_path; + break :blk null; + }; + + var argv = std.ArrayList([]const u8).init(arena); + + try argv.append("zig"); + + if (self.isStaticLib()) { + try argv.append("ar"); + } else { + try argv.append("ld"); + } + + if (self.isObject()) { + try argv.append("-r"); + } + + try argv.append("-o"); + try argv.append(full_out_path); + + if (self.isRelocatable()) { + for (self.base.options.objects) |obj| { + try argv.append(obj.path); + } + + for (comp.c_object_table.keys()) |key| { + try argv.append(key.status.success.object_path); + } + + if (module_obj_path) |p| { + try argv.append(p); + } + } else { + if (!self.isStatic()) { + if (self.base.options.dynamic_linker) |path| { + try argv.append("-dynamic-linker"); + try argv.append(path); + } + } + + if (self.isDynLib()) { + if (self.base.options.soname) |name| { + try argv.append("-soname"); + try argv.append(name); + } + } + + if (self.base.options.entry) |entry| { + try argv.append("--entry"); + try argv.append(entry); + } + + for (self.base.options.rpath_list) |rpath| { + try argv.append("-rpath"); + try argv.append(rpath); + } + + if (self.base.options.each_lib_rpath) { + for (self.base.options.lib_dirs) |lib_dir_path| { + try argv.append("-rpath"); + try argv.append(lib_dir_path); + } + for (self.base.options.objects) |obj| { + if (Compilation.classifyFileExt(obj.path) == .shared_library) { + const lib_dir_path = std.fs.path.dirname(obj.path) orelse continue; + if (obj.loption) continue; + + try argv.append("-rpath"); + try argv.append(lib_dir_path); + } + } + } + + if (self.base.options.stack_size_override) |ss| { + try argv.append("-z"); + try argv.append(try std.fmt.allocPrint(arena, "stack-size={d}", .{ss})); + } + + if (self.base.options.image_base_override) |image_base| { + try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{image_base})); + } + + if (gc_sections) { + try argv.append("--gc-sections"); + } + + if (self.base.options.print_gc_sections) { + try argv.append("--print-gc-sections"); + } + + if (self.base.options.eh_frame_hdr) { + try argv.append("--eh-frame-hdr"); + } + + if (self.base.options.rdynamic) { + try argv.append("--export-dynamic"); + } + + if (self.base.options.z_notext) { + try argv.append("-z"); + try argv.append("notext"); + } + + if (self.base.options.z_nocopyreloc) { + try argv.append("-z"); + try argv.append("nocopyreloc"); + } + + if (self.base.options.z_now) { + try argv.append("-z"); + try argv.append("now"); + } + + if (self.isStatic()) { + try argv.append("-static"); + } else if (self.isDynLib()) { + try argv.append("-shared"); + } + + if (self.base.options.pie and self.isExe()) { + try argv.append("-pie"); + } + + if (self.base.options.strip) { + try argv.append("-s"); + } + + // csu prelude + if (csu.crt0) |v| try argv.append(v); + if (csu.crti) |v| try argv.append(v); + if (csu.crtbegin) |v| try argv.append(v); + + for (self.base.options.lib_dirs) |lib_dir| { + try argv.append("-L"); + try argv.append(lib_dir); + } + + if (self.base.options.link_libc) { + if (self.base.options.libc_installation) |libc_installation| { + try argv.append("-L"); + try argv.append(libc_installation.crt_dir.?); + } + } + + var whole_archive = false; + for (self.base.options.objects) |obj| { + if (obj.must_link and !whole_archive) { + try argv.append("-whole-archive"); + whole_archive = true; + } else if (!obj.must_link and whole_archive) { + try argv.append("-no-whole-archive"); + whole_archive = false; + } + + if (obj.loption) { + assert(obj.path[0] == ':'); + try argv.append("-l"); + } + try argv.append(obj.path); + } + if (whole_archive) { + try argv.append("-no-whole-archive"); + whole_archive = false; + } + + for (comp.c_object_table.keys()) |key| { + try argv.append(key.status.success.object_path); + } + + if (module_obj_path) |p| { + try argv.append(p); + } + + // TSAN + if (self.base.options.tsan) { + try argv.append(comp.tsan_static_lib.?.full_object_path); + } + + // libc + if (!self.base.options.skip_linker_dependencies and + !self.base.options.link_libc) + { + if (comp.libc_static_lib) |lib| { + try argv.append(lib.full_object_path); + } + } + + // stack-protector. + // Related: https://github.com/ziglang/zig/issues/7265 + if (comp.libssp_static_lib) |ssp| { + try argv.append(ssp.full_object_path); + } + + // Shared libraries. + // Worst-case, we need an --as-needed argument for every lib, as well + // as one before and one after. + try argv.ensureUnusedCapacity(self.base.options.system_libs.keys().len * 2 + 2); + argv.appendAssumeCapacity("--as-needed"); + var as_needed = true; + + for (self.base.options.system_libs.values()) |lib_info| { + const lib_as_needed = !lib_info.needed; + switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) { + 0b00, 0b11 => {}, + 0b01 => { + argv.appendAssumeCapacity("--no-as-needed"); + as_needed = false; + }, + 0b10 => { + argv.appendAssumeCapacity("--as-needed"); + as_needed = true; + }, + } + argv.appendAssumeCapacity(lib_info.path.?); + } + + if (!as_needed) { + argv.appendAssumeCapacity("--as-needed"); + as_needed = true; + } + + // libc++ dep + if (self.base.options.link_libcpp) { + try argv.append(comp.libcxxabi_static_lib.?.full_object_path); + try argv.append(comp.libcxx_static_lib.?.full_object_path); + } + + // libunwind dep + if (self.base.options.link_libunwind) { + try argv.append(comp.libunwind_static_lib.?.full_object_path); + } + + // libc dep + if (self.base.options.link_libc) { + if (self.base.options.libc_installation != null) { + const needs_grouping = self.base.options.link_mode == .Static; + if (needs_grouping) try argv.append("--start-group"); + try argv.appendSlice(target_util.libcFullLinkFlags(target)); + if (needs_grouping) try argv.append("--end-group"); + } else if (target.isGnuLibC()) { + for (glibc.libs) |lib| { + const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{ + comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, + }); + try argv.append(lib_path); + } + try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a")); + } else if (target.isMusl()) { + try argv.append(try comp.get_libc_crt_file(arena, switch (self.base.options.link_mode) { + .Static => "libc.a", + .Dynamic => "libc.so", + })); + } + } + + // compiler-rt + if (compiler_rt_path) |p| { + try argv.append(p); + } + + // crt postlude + if (csu.crtend) |v| try argv.append(v); + if (csu.crtn) |v| try argv.append(v); + } + + Compilation.dump_argv(argv.items); +} + const ParseError = error{ UnknownFileType, InvalidCpuArch, @@ -3651,8 +3758,10 @@ fn setDynamicSection(self: *Elf, rpaths: []const []const u8) !void { try self.dynamic.addNeeded(shared_object, self); } - if (self.base.options.soname) |soname| { - try self.dynamic.setSoname(soname, self); + if (self.isDynLib()) { + if (self.base.options.soname) |soname| { + try self.dynamic.setSoname(soname, self); + } } try self.dynamic.setRpath(rpaths, self); From 29d77272549658489b97aa51e772ccba03f3347d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 8 Nov 2023 18:15:41 +0100 Subject: [PATCH 13/29] elf: emit SHN_COMMON symbols in -r mode --- src/link/Elf/Symbol.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index a2ffc62014..1040515de5 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -237,8 +237,8 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void { const st_shndx = blk: { if (symbol.flags.has_copy_rel) break :blk elf_file.copy_rel_section_index.?; if (file_ptr == .shared_object or esym.st_shndx == elf.SHN_UNDEF) break :blk elf.SHN_UNDEF; - if (symbol.atom(elf_file) == null and file_ptr != .linker_defined) - break :blk elf.SHN_ABS; + if (elf_file.isRelocatable() and esym.st_shndx == elf.SHN_COMMON) break :blk elf.SHN_COMMON; + if (symbol.atom(elf_file) == null and file_ptr != .linker_defined) break :blk elf.SHN_ABS; break :blk symbol.outputShndx() orelse elf.SHN_UNDEF; }; const st_value = blk: { @@ -247,7 +247,7 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void { if (symbol.flags.is_canonical) break :blk symbol.address(.{}, elf_file); break :blk 0; } - if (st_shndx == elf.SHN_ABS) break :blk symbol.value; + if (st_shndx == elf.SHN_ABS or st_shndx == elf.SHN_COMMON) break :blk symbol.value; const shdr = &elf_file.shdrs.items[st_shndx]; if (shdr.sh_flags & elf.SHF_TLS != 0 and file_ptr != .linker_defined) break :blk symbol.value - elf_file.tlsAddress(); From 9bcb432a0efc47b76e6110813119c8b899020301 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 8 Nov 2023 18:41:09 +0100 Subject: [PATCH 14/29] elf: test emitting relocatable --- test/link/elf.zig | 52 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/test/link/elf.zig b/test/link/elf.zig index 02eae58a06..24a4b941fb 100644 --- a/test/link/elf.zig +++ b/test/link/elf.zig @@ -21,6 +21,10 @@ pub fn build(b: *Build) void { .abi = .gnu, }; + // Exercise linker in -r mode + elf_step.dependOn(testEmitRelocatable(b, .{ .use_llvm = false, .target = musl_target })); + elf_step.dependOn(testEmitRelocatable(b, .{ .target = musl_target })); + // Exercise linker in ar mode elf_step.dependOn(testEmitStaticLib(b, .{ .target = musl_target })); @@ -629,6 +633,53 @@ fn testDsoUndef(b: *Build, opts: Options) *Step { return test_step; } +fn testEmitRelocatable(b: *Build, opts: Options) *Step { + const test_step = addTestStep(b, "emit-relocatable", opts); + + const obj1 = addObject(b, "obj1", opts); + addZigSourceBytes(obj1, + \\const std = @import("std"); + \\extern var bar: i32; + \\export fn foo() i32 { + \\ return bar; + \\} + \\export fn printFoo() void { + \\ std.debug.print("foo={d}\n", .{foo()}); + \\} + ); + addCSourceBytes(obj1, + \\#include + \\int bar = 42; + \\void printBar() { + \\ fprintf(stderr, "bar=%d\n", bar); + \\} + , &.{}); + obj1.linkLibC(); + + const exe = addExecutable(b, "test", opts); + addZigSourceBytes(exe, + \\const std = @import("std"); + \\extern fn printFoo() void; + \\extern fn printBar() void; + \\pub fn main() void { + \\ printFoo(); + \\ printBar(); + \\} + ); + exe.addObject(obj1); + exe.linkLibC(); + + const run = addRunArtifact(exe); + run.expectStdErrEqual( + \\foo=42 + \\bar=42 + \\ + ); + test_step.dependOn(&run.step); + + return test_step; +} + fn testEmitStaticLib(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "emit-static-lib", opts); @@ -951,7 +1002,6 @@ fn testGcSectionsZig(b: *Build, opts: Options) *Step { const obj = addObject(b, "obj", .{ .target = opts.target, .use_llvm = true, - .use_lld = true, }); addCSourceBytes(obj, \\int live_var1 = 1; From 0299ed50361fbe3b279f9894119ded14439b7dc4 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 8 Nov 2023 18:45:43 +0100 Subject: [PATCH 15/29] elf: fix 32bit build --- src/link/Elf.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 650d4074a5..a4995a6aef 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -5003,7 +5003,8 @@ fn writeSyntheticSectionsObject(self: *Elf) !void { const shdr = self.shdrs.items[sec.shndx]; - const num_relocs = @divExact(shdr.sh_size, shdr.sh_entsize); + const num_relocs = math.cast(usize, @divExact(shdr.sh_size, shdr.sh_entsize)) orelse + return error.Overflow; var relocs = try std.ArrayList(elf.Elf64_Rela).initCapacity(gpa, num_relocs); defer relocs.deinit(); From 0de5dd2ef144a5a53c5bdbf1d8e45c1d2becd47b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 9 Nov 2023 10:32:39 +0100 Subject: [PATCH 16/29] elf: misc fixes --- src/link/Elf.zig | 33 +-------------------------------- src/link/Elf/ZigObject.zig | 1 + 2 files changed, 2 insertions(+), 32 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index a4995a6aef..a6a6c590a5 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -4483,6 +4483,7 @@ fn allocateAllocSectionsObject(self: *Elf) !void { for (self.shdrs.items) |*shdr| { if (shdr.sh_type == elf.SHT_NULL) continue; if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue; + if (shdr.sh_type == elf.SHT_NOBITS) continue; const needed_size = shdr.sh_size; if (needed_size > self.allocatedSize(shdr.sh_offset)) { shdr.sh_size = 0; @@ -4833,38 +4834,6 @@ fn updateSymtabSize(self: *Elf) !void { strtab.sh_size = strsize + 1; } -fn updateSymtabSizeZigObject(self: *Elf, zig_object: *ZigObject) void { - var nlocals: u32 = 0; - var nglobals: u32 = 0; - var strsize: u32 = 0; - - // Section symbols - for (self.output_sections.keys()) |_| { - nlocals += 1; - } - - zig_object.output_symtab_ctx.ilocal = nlocals + 1; - try zig_object.asFile().updateSymtabSize(self); - nlocals += zig_object.output_symtab_ctx.nlocals; - nglobals += zig_object.output_symtab_ctx.nglobals; - strsize += zig_object.output_symtab_ctx.strsize; - zig_object.output_symtab_ctx.iglobal = nlocals + 1; - - const symtab_shdr = &self.shdrs.items[self.symtab_section_index.?]; - symtab_shdr.sh_info = nlocals + 1; - symtab_shdr.sh_link = self.strtab_section_index.?; - - const sym_size: u64 = switch (self.ptr_width) { - .p32 => @sizeOf(elf.Elf32_Sym), - .p64 => @sizeOf(elf.Elf64_Sym), - }; - const needed_size = (nlocals + nglobals + 1) * sym_size; - symtab_shdr.sh_size = needed_size; - - const strtab = &self.shdrs.items[self.strtab_section_index.?]; - strtab.sh_size = strsize + 1; -} - fn writeSyntheticSections(self: *Elf) !void { const gpa = self.base.allocator; diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 03b2572549..f08e5bacbb 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -75,6 +75,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void { const gpa = elf_file.base.allocator; try self.atoms.append(gpa, 0); // null input section + try self.relocs.append(gpa, .{}); // null relocs section try self.strtab.buffer.append(gpa, 0); const name_off = try self.strtab.insert(gpa, self.path); From f607126614ab249dbf8f965ca0911fda560bc580 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 9 Nov 2023 11:49:04 +0100 Subject: [PATCH 17/29] test/link/elf: verify we can output a valid relocatable with .eh_frame section --- test/link/elf.zig | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/test/link/elf.zig b/test/link/elf.zig index 24a4b941fb..4ab7df288c 100644 --- a/test/link/elf.zig +++ b/test/link/elf.zig @@ -24,6 +24,7 @@ pub fn build(b: *Build) void { // Exercise linker in -r mode elf_step.dependOn(testEmitRelocatable(b, .{ .use_llvm = false, .target = musl_target })); elf_step.dependOn(testEmitRelocatable(b, .{ .target = musl_target })); + elf_step.dependOn(testRelocatableEhFrame(b, .{ .target = musl_target })); // Exercise linker in ar mode elf_step.dependOn(testEmitStaticLib(b, .{ .target = musl_target })); @@ -2139,6 +2140,48 @@ fn testPreinitArray(b: *Build, opts: Options) *Step { return test_step; } +fn testRelocatableEhFrame(b: *Build, opts: Options) *Step { + const test_step = addTestStep(b, "relocatable-eh-frame", opts); + + const obj = addObject(b, "obj", opts); + addCppSourceBytes(obj, + \\#include + \\int try_me() { + \\ throw std::runtime_error("Oh no!"); + \\} + , &.{}); + addCppSourceBytes(obj, + \\extern int try_me(); + \\int try_again() { + \\ return try_me(); + \\} + , &.{}); + obj.linkLibCpp(); + + const exe = addExecutable(b, "test", opts); + addCppSourceBytes(exe, + \\#include + \\#include + \\extern int try_again(); + \\int main() { + \\ try { + \\ try_again(); + \\ } catch (const std::exception &e) { + \\ std::cout << "exception=" << e.what() << std::endl; + \\ } + \\ return 0; + \\} + , &.{}); + exe.addObject(obj); + exe.linkLibCpp(); + + const run = addRunArtifact(exe); + run.expectStdOutEqual("exception=Oh no!"); + test_step.dependOn(&run.step); + + return test_step; +} + fn testSharedAbsSymbol(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "shared-abs-symbol", opts); From 666ac6bf9be68a96d15085c016d123404e256d32 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 9 Nov 2023 11:49:32 +0100 Subject: [PATCH 18/29] elf: track .rela.eh_frame section and emit .eh_frame section symbol --- src/link/Elf.zig | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index a6a6c590a5..13ea069a3a 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -122,6 +122,7 @@ dynamic_section_index: ?u16 = null, dynstrtab_section_index: ?u16 = null, dynsymtab_section_index: ?u16 = null, eh_frame_section_index: ?u16 = null, +eh_frame_rela_section_index: ?u16 = null, eh_frame_hdr_section_index: ?u16 = null, hash_section_index: ?u16 = null, gnu_hash_section_index: ?u16 = null, @@ -3583,7 +3584,7 @@ fn initSectionsObject(self: *Elf) !void { .addralign = ptr_size, .offset = std.math.maxInt(u64), }); - // _ = try self.addRelaShdr(".rela.eh_frame", self.eh_frame_section_index.?); + self.eh_frame_rela_section_index = try self.addRelaShdr(".rela.eh_frame", self.eh_frame_section_index.?); } try self.initSymtab(); @@ -3954,6 +3955,7 @@ fn sortShdrs(self: *Elf) !void { for (&[_]*?u16{ &self.eh_frame_section_index, + &self.eh_frame_rela_section_index, &self.eh_frame_hdr_section_index, &self.got_section_index, &self.symtab_section_index, @@ -4769,6 +4771,9 @@ fn updateSymtabSize(self: *Elf) !void { for (self.output_sections.keys()) |_| { nlocals += 1; } + if (self.eh_frame_section_index) |_| { + nlocals += 1; + } for (files.items) |index| { const file_ptr = self.file(index).?; @@ -5162,9 +5167,26 @@ fn writeSectionSymbols(self: *Elf) void { }; ilocal += 1; } + + if (self.eh_frame_section_index) |shndx| { + const shdr = self.shdrs.items[shndx]; + const out_sym = &self.symtab.items[ilocal]; + out_sym.* = .{ + .st_name = 0, + .st_value = shdr.sh_addr, + .st_info = elf.STT_SECTION, + .st_shndx = @intCast(shndx), + .st_size = 0, + .st_other = 0, + }; + ilocal += 1; + } } pub fn sectionSymbolOutputSymtabIndex(self: Elf, shndx: u32) u32 { + if (self.eh_frame_section_index) |index| { + if (index == shndx) return @intCast(self.output_sections.keys().len + 1); + } return @intCast(self.output_sections.getIndex(shndx).? + 1); } From 0efc4711220d98d8c0178dfe9d64dc897c6dee70 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 9 Nov 2023 12:02:20 +0100 Subject: [PATCH 19/29] elf: calculate required size for .rela.eh_frame --- src/link/Elf.zig | 5 +++++ src/link/Elf/eh_frame.zig | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 13ea069a3a..009207ba09 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -4245,6 +4245,11 @@ fn updateSectionSizesObject(self: *Elf) !void { if (self.eh_frame_section_index) |index| { self.shdrs.items[index].sh_size = try eh_frame.calcEhFrameSize(self); + + if (self.eh_frame_rela_section_index) |rela_index| { + const shdr = &self.shdrs.items[rela_index]; + shdr.sh_size = eh_frame.calcEhFrameRelocs(self) * shdr.sh_entsize; + } } try self.updateSymtabSize(); diff --git a/src/link/Elf/eh_frame.zig b/src/link/Elf/eh_frame.zig index 8c0fa0f769..d15af3489a 100644 --- a/src/link/Elf/eh_frame.zig +++ b/src/link/Elf/eh_frame.zig @@ -282,6 +282,22 @@ pub fn calcEhFrameHdrSize(elf_file: *Elf) usize { return eh_frame_hdr_header_size + count * 8; } +pub fn calcEhFrameRelocs(elf_file: *Elf) usize { + var count: usize = 0; + for (elf_file.objects.items) |index| { + const object = elf_file.file(index).?.object; + for (object.cies.items) |cie| { + if (!cie.alive) continue; + count += cie.relocs(elf_file).len; + } + for (object.fdes.items) |fde| { + if (!fde.alive) continue; + count += fde.relocs(elf_file).len; + } + } + return count; +} + fn resolveReloc(rec: anytype, sym: *const Symbol, rel: elf.Elf64_Rela, elf_file: *Elf, contents: []u8) !void { const offset = std.math.cast(usize, rel.r_offset - rec.offset) orelse return error.Overflow; const P = @as(i64, @intCast(rec.address(elf_file) + offset)); From b1fcf0ed8f435b834f21a3d57c03f5c0333c6fd9 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 9 Nov 2023 12:24:49 +0100 Subject: [PATCH 20/29] elf: emit .rela.eh_frame section contents --- src/link/Elf.zig | 17 ++++++++---- src/link/Elf/eh_frame.zig | 56 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 5 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 009207ba09..e490e6091e 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -4245,11 +4245,10 @@ fn updateSectionSizesObject(self: *Elf) !void { if (self.eh_frame_section_index) |index| { self.shdrs.items[index].sh_size = try eh_frame.calcEhFrameSize(self); - - if (self.eh_frame_rela_section_index) |rela_index| { - const shdr = &self.shdrs.items[rela_index]; - shdr.sh_size = eh_frame.calcEhFrameRelocs(self) * shdr.sh_entsize; - } + } + if (self.eh_frame_rela_section_index) |index| { + const shdr = &self.shdrs.items[index]; + shdr.sh_size = eh_frame.calcEhFrameRelocs(self) * shdr.sh_entsize; } try self.updateSymtabSize(); @@ -5013,6 +5012,14 @@ fn writeSyntheticSectionsObject(self: *Elf) !void { try eh_frame.writeEhFrame(self, buffer.writer()); try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); } + if (self.eh_frame_rela_section_index) |shndx| { + const shdr = self.shdrs.items[shndx]; + const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow; + var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size); + defer buffer.deinit(); + try eh_frame.writeEhFrameRelocs(self, buffer.writer()); + try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); + } try self.writeSymtab(); try self.writeShStrtab(); diff --git a/src/link/Elf/eh_frame.zig b/src/link/Elf/eh_frame.zig index d15af3489a..3657a3f5b7 100644 --- a/src/link/Elf/eh_frame.zig +++ b/src/link/Elf/eh_frame.zig @@ -373,6 +373,62 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void { try writer.writeInt(u32, 0, .little); } +fn emitReloc(elf_file: *Elf, rec: anytype, sym: *const Symbol, rel: elf.Elf64_Rela) elf.Elf64_Rela { + const r_offset = rec.address(elf_file) + rel.r_offset - rec.offset; + const r_type = rel.r_type(); + var r_addend = rel.r_addend; + var r_sym: u32 = 0; + switch (sym.type(elf_file)) { + elf.STT_SECTION => { + r_addend += @intCast(sym.value); + r_sym = elf_file.sectionSymbolOutputSymtabIndex(sym.outputShndx().?); + }, + else => { + r_sym = sym.outputSymtabIndex(elf_file) orelse 0; + }, + } + + relocs_log.debug(" {s}: [{x} => {d}({s})] + {x}", .{ + Atom.fmtRelocType(r_type), + r_offset, + r_sym, + sym.name(elf_file), + r_addend, + }); + + return .{ + .r_offset = r_offset, + .r_addend = r_addend, + .r_info = (@as(u64, @intCast(r_sym)) << 32) | r_type, + }; +} + +pub fn writeEhFrameRelocs(elf_file: *Elf, writer: anytype) !void { + relocs_log.debug("{x}: .eh_frame", .{elf_file.shdrs.items[elf_file.eh_frame_section_index.?].sh_addr}); + + for (elf_file.objects.items) |index| { + const object = elf_file.file(index).?.object; + + for (object.cies.items) |cie| { + if (!cie.alive) continue; + for (cie.relocs(elf_file)) |rel| { + const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]); + const out_rel = emitReloc(elf_file, cie, sym, rel); + try writer.writeStruct(out_rel); + } + } + + for (object.fdes.items) |fde| { + if (!fde.alive) continue; + for (fde.relocs(elf_file)) |rel| { + const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]); + const out_rel = emitReloc(elf_file, fde, sym, rel); + try writer.writeStruct(out_rel); + } + } + } +} + pub fn writeEhFrameHdr(elf_file: *Elf, writer: anytype) !void { try writer.writeByte(1); // version try writer.writeByte(EH_PE.pcrel | EH_PE.sdata4); From 1f8dd27e40bb455da85bfb1ae655ad63bc05ea08 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 9 Nov 2023 14:46:28 +0100 Subject: [PATCH 21/29] elf: correctly format output .eh_frame when emitting relocatable --- src/link/Elf.zig | 19 +++++++++++-------- src/link/Elf/Object.zig | 14 +++++++++----- src/link/Elf/eh_frame.zig | 39 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 58 insertions(+), 14 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index e490e6091e..ce90efbd23 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1446,8 +1446,6 @@ pub fn flushObject(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) // Now, we are ready to resolve the symbols across all input files. // We will first resolve the files in the ZigObject, next in the parsed // input Object files. - // Any qualifing unresolved symbol will be upgraded to an absolute, weak - // symbol for potential resolution at load-time. self.resolveSymbols(); self.markEhFrameAtomsDead(); self.claimUnresolvedObject(); @@ -4037,11 +4035,10 @@ fn sortShdrs(self: *Elf) !void { shdr.sh_info = self.plt_section_index.?; } - for (self.shdrs.items) |*shdr| { - if (shdr.sh_type == elf.SHT_RELA and shdr.sh_flags & elf.SHF_INFO_LINK != 0) { - shdr.sh_link = self.symtab_section_index.?; - shdr.sh_info = backlinks[shdr.sh_info]; - } + if (self.eh_frame_rela_section_index) |index| { + const shdr = &self.shdrs.items[index]; + shdr.sh_link = self.symtab_section_index.?; + shdr.sh_info = self.eh_frame_section_index.?; } { @@ -4122,6 +4119,12 @@ fn sortShdrs(self: *Elf) !void { global.output_section_index = backlinks[out_shndx]; } } + + for (self.output_rela_sections.keys(), self.output_rela_sections.values()) |shndx, sec| { + const shdr = &self.shdrs.items[sec.shndx]; + shdr.sh_link = self.symtab_section_index.?; + shdr.sh_info = shndx; + } } fn updateSectionSizes(self: *Elf) !void { @@ -5009,7 +5012,7 @@ fn writeSyntheticSectionsObject(self: *Elf) !void { const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow; var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size); defer buffer.deinit(); - try eh_frame.writeEhFrame(self, buffer.writer()); + try eh_frame.writeEhFrameObject(self, buffer.writer()); try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); } if (self.eh_frame_rela_section_index) |shndx| { diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index 0a4232d3b1..ce9b83c9bb 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -687,7 +687,6 @@ pub fn initRelaSections(self: Object, elf_file: *Elf) !void { const out_shdr = &elf_file.shdrs.items[out_shndx]; out_shdr.sh_addralign = @alignOf(elf.Elf64_Rela); out_shdr.sh_entsize = @sizeOf(elf.Elf64_Rela); - out_shdr.sh_info = self.initOutputSection(elf_file, atom.inputShdr(elf_file)) catch unreachable; } } @@ -695,13 +694,18 @@ pub fn addAtomsToRelaSections(self: Object, elf_file: *Elf) !void { for (self.atoms.items) |atom_index| { const atom = elf_file.atom(atom_index) orelse continue; if (!atom.flags.alive) continue; - const shndx = atom.relocsShndx() orelse continue; - const shdr = self.shdrs.items[shndx]; - const out_shndx = self.initOutputSection(elf_file, shdr) catch unreachable; + const shndx = blk: { + const shndx = atom.relocsShndx() orelse continue; + const shdr = self.shdrs.items[shndx]; + break :blk self.initOutputSection(elf_file, shdr) catch unreachable; + }; + const shdr = &elf_file.shdrs.items[shndx]; + shdr.sh_info = atom.outputShndx().?; + shdr.sh_link = elf_file.symtab_section_index.?; const gpa = elf_file.base.allocator; const gop = try elf_file.output_rela_sections.getOrPut(gpa, atom.outputShndx().?); - if (!gop.found_existing) gop.value_ptr.* = .{ .shndx = out_shndx }; + if (!gop.found_existing) gop.value_ptr.* = .{ .shndx = shndx }; try gop.value_ptr.atom_list.append(gpa, atom_index); } } diff --git a/src/link/Elf/eh_frame.zig b/src/link/Elf/eh_frame.zig index 3657a3f5b7..73857d03d2 100644 --- a/src/link/Elf/eh_frame.zig +++ b/src/link/Elf/eh_frame.zig @@ -268,7 +268,11 @@ pub fn calcEhFrameSize(elf_file: *Elf) !usize { } } - return offset + 4; // NULL terminator + if (!elf_file.isRelocatable()) { + offset += 4; // NULL terminator + } + + return offset; } pub fn calcEhFrameHdrSize(elf_file: *Elf) usize { @@ -373,6 +377,39 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void { try writer.writeInt(u32, 0, .little); } +pub fn writeEhFrameObject(elf_file: *Elf, writer: anytype) !void { + const gpa = elf_file.base.allocator; + + for (elf_file.objects.items) |index| { + const object = elf_file.file(index).?.object; + + for (object.cies.items) |cie| { + if (!cie.alive) continue; + try writer.writeAll(cie.data(elf_file)); + } + } + + for (elf_file.objects.items) |index| { + const object = elf_file.file(index).?.object; + + for (object.fdes.items) |fde| { + if (!fde.alive) continue; + + const contents = try gpa.dupe(u8, fde.data(elf_file)); + defer gpa.free(contents); + + std.mem.writeInt( + i32, + contents[4..8], + @truncate(@as(i64, @intCast(fde.out_offset + 4)) - @as(i64, @intCast(fde.cie(elf_file).out_offset))), + .little, + ); + + try writer.writeAll(contents); + } + } +} + fn emitReloc(elf_file: *Elf, rec: anytype, sym: *const Symbol, rel: elf.Elf64_Rela) elf.Elf64_Rela { const r_offset = rec.address(elf_file) + rel.r_offset - rec.offset; const r_type = rel.r_type(); From 50f48022f04b747bb2624858df89366dfc63b0a4 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 9 Nov 2023 14:51:00 +0100 Subject: [PATCH 22/29] test/link/elf: fix .eh_frame test --- test/link/elf.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/link/elf.zig b/test/link/elf.zig index 4ab7df288c..f458fbb8de 100644 --- a/test/link/elf.zig +++ b/test/link/elf.zig @@ -2167,7 +2167,7 @@ fn testRelocatableEhFrame(b: *Build, opts: Options) *Step { \\ try { \\ try_again(); \\ } catch (const std::exception &e) { - \\ std::cout << "exception=" << e.what() << std::endl; + \\ std::cout << "exception=" << e.what(); \\ } \\ return 0; \\} From 031d9faf022eb58c688033148f5a1fa145c6d84c Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 9 Nov 2023 15:05:29 +0100 Subject: [PATCH 23/29] elf: separate logic for reseting shdr indexes into a separate fn --- src/link/Elf.zig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index ce90efbd23..0753869671 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -3951,6 +3951,12 @@ fn sortShdrs(self: *Elf) !void { self.shdrs.appendAssumeCapacity(slice[sorted.shndx]); } + try self.resetShdrIndexes(backlinks); +} + +fn resetShdrIndexes(self: *Elf, backlinks: []const u16) !void { + const gpa = self.base.allocator; + for (&[_]*?u16{ &self.eh_frame_section_index, &self.eh_frame_rela_section_index, From acd7cbf0b51dbc8039387de285e2b391a78f59ae Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 9 Nov 2023 17:41:14 +0100 Subject: [PATCH 24/29] elf: init output COMDAT group sections --- lib/std/elf.zig | 2 ++ src/link/Elf.zig | 48 +++++++++++++++++++++++++++-- src/link/Elf/Object.zig | 7 +++-- src/link/Elf/synthetic_sections.zig | 24 +++++++++++++++ 4 files changed, 76 insertions(+), 5 deletions(-) diff --git a/lib/std/elf.zig b/lib/std/elf.zig index 4c53898df8..a72c96c51e 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -1664,6 +1664,8 @@ pub const EM = enum(u16) { } }; +pub const GRP_COMDAT = 1; + /// Section data should be writable during execution. pub const SHF_WRITE = 0x1; diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 0753869671..678844aa81 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -102,6 +102,9 @@ copy_rel: CopyRelSection = .{}, rela_plt: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{}, /// .got.zig section zig_got: ZigGotSection = .{}, +/// SHT_GROUP sections +/// Applies only to a relocatable. +comdat_group_sections: std.ArrayListUnmanaged(ComdatGroupSection) = .{}, /// Tracked section headers with incremental updates to Zig object. /// .rela.* sections are only used when emitting a relocatable object file. @@ -394,6 +397,7 @@ pub fn deinit(self: *Elf) void { self.rela_dyn.deinit(gpa); self.rela_plt.deinit(gpa); self.zig_got.deinit(gpa); + self.comdat_group_sections.deinit(gpa); } pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo) !u64 { @@ -3585,10 +3589,35 @@ fn initSectionsObject(self: *Elf) !void { self.eh_frame_rela_section_index = try self.addRelaShdr(".rela.eh_frame", self.eh_frame_section_index.?); } + try self.initComdatGroups(); try self.initSymtab(); try self.initShStrtab(); } +fn initComdatGroups(self: *Elf) !void { + const gpa = self.base.allocator; + + for (self.objects.items) |index| { + const object = self.file(index).?.object; + + for (object.comdat_groups.items) |cg_index| { + const cg = self.comdatGroup(cg_index); + const cg_owner = self.comdatGroupOwner(cg.owner); + if (cg_owner.file != index) continue; + + const cg_sec = try self.comdat_group_sections.addOne(gpa); + cg_sec.* = .{ + .shndx = try self.addSection(.{ + .name = ".group", + .type = elf.SHT_GROUP, + .entsize = @sizeOf(u32), + }), + .cg_index = cg_index, + }; + } + } +} + fn initSymtab(self: *Elf) !void { const small_ptr = switch (self.ptr_width) { .p32 => true, @@ -3892,7 +3921,7 @@ fn shdrRank(self: *Elf, shndx: u16) u8 { elf.SHT_DYNAMIC => return 0xf3, - elf.SHT_RELA => return 0xf, + elf.SHT_RELA, elf.SHT_GROUP => return 0xf, elf.SHT_PROGBITS => if (flags & elf.SHF_ALLOC != 0) { if (flags & elf.SHF_EXECINSTR != 0) { @@ -4131,6 +4160,10 @@ fn resetShdrIndexes(self: *Elf, backlinks: []const u16) !void { shdr.sh_link = self.symtab_section_index.?; shdr.sh_info = shndx; } + + for (self.comdat_group_sections.items) |*cg| { + cg.shndx = backlinks[cg.shndx]; + } } fn updateSectionSizes(self: *Elf) !void { @@ -4260,10 +4293,15 @@ fn updateSectionSizesObject(self: *Elf) !void { shdr.sh_size = eh_frame.calcEhFrameRelocs(self) * shdr.sh_entsize; } + self.updateComdatGroupsSizes(); try self.updateSymtabSize(); self.updateShStrtabSize(); } +fn updateComdatGroupsSizes(self: *Elf) void { + _ = self; +} + fn updateShStrtabSize(self: *Elf) void { if (self.shstrtab_section_index) |index| { self.shdrs.items[index].sh_size = self.shstrtab.items.len; @@ -6168,7 +6206,12 @@ fn fmtDumpState( try writer.print("{}\n", .{self.got.fmt(self)}); try writer.print("{}\n", .{self.zig_got.fmt(self)}); - try writer.writeAll("Output shdrs\n"); + try writer.writeAll("Output COMDAT groups\n"); + for (self.comdat_group_sections.items) |cg| { + try writer.print("shdr({d}) : COMDAT({d})\n", .{ cg.shndx, cg.cg_index }); + } + + try writer.writeAll("\nOutput shdrs\n"); for (self.shdrs.items, 0..) |shdr, shndx| { try writer.print("shdr({d}) : phdr({?d}) : {}\n", .{ shndx, @@ -6332,6 +6375,7 @@ const Archive = @import("Elf/Archive.zig"); pub const Atom = @import("Elf/Atom.zig"); const Cache = std.Build.Cache; const Compilation = @import("../Compilation.zig"); +const ComdatGroupSection = synthetic_sections.ComdatGroupSection; const CopyRelSection = synthetic_sections.CopyRelSection; const DynamicSection = synthetic_sections.DynamicSection; const DynsymSection = synthetic_sections.DynsymSection; diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index ce9b83c9bb..7b4add7f53 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -142,7 +142,7 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void { const group_nmembers = @divExact(group_raw_data.len, @sizeOf(u32)); const group_members = @as([*]align(1) const u32, @ptrCast(group_raw_data.ptr))[0..group_nmembers]; - if (group_members[0] != 0x1) { // GRP_COMDAT + if (group_members[0] != elf.GRP_COMDAT) { // TODO convert into an error log.debug("{}: unknown SHT_GROUP format", .{self.fmtPath()}); continue; @@ -939,16 +939,17 @@ fn formatComdatGroups( _ = options; const object = ctx.object; const elf_file = ctx.elf_file; - try writer.writeAll(" comdat groups\n"); + try writer.writeAll(" COMDAT groups\n"); for (object.comdat_groups.items) |cg_index| { const cg = elf_file.comdatGroup(cg_index); const cg_owner = elf_file.comdatGroupOwner(cg.owner); if (cg_owner.file != object.index) continue; + try writer.print(" COMDAT({d})\n", .{cg_index}); const cg_members = object.comdatGroupMembers(cg.shndx); for (cg_members) |shndx| { const atom_index = object.atoms.items[shndx]; const atom = elf_file.atom(atom_index) orelse continue; - try writer.print(" atom({d}) : {s}\n", .{ atom_index, atom.name(elf_file) }); + try writer.print(" atom({d}) : {s}\n", .{ atom_index, atom.name(elf_file) }); } } } diff --git a/src/link/Elf/synthetic_sections.zig b/src/link/Elf/synthetic_sections.zig index 0c9f94b5b9..7fbc75d80a 100644 --- a/src/link/Elf/synthetic_sections.zig +++ b/src/link/Elf/synthetic_sections.zig @@ -1499,6 +1499,30 @@ pub const VerneedSection = struct { } }; +pub const ComdatGroupSection = struct { + shndx: u32, + cg_index: u32, + + // pub fn size(cg: ComdatGroupSection) usize { + // return cg.members.items.len + 1; + // } + + // pub fn write(cg: ComdatGroupSection, elf_file: *Elf, writer: anytype) !void { + // try writeInt(@as(u32, elf.GRP_COMDAT), elf_file, writer); + // for (cg.members.items) |atom_index| { + // const atom = elf_file.atom(atom_index); + // const input_shdr = atom.inputShdr(elf_file); + // switch (input_shdr.sh_type) { + // elf.SHT_RELA => { + + // }, + // else => {}, + // } + // } + // try writer.writeAll(mem.sliceAsBytes(cg.members.items)); + // } +}; + fn writeInt(value: anytype, elf_file: *Elf, writer: anytype) !void { const entry_size = elf_file.archPtrWidthBytes(); const endian = elf_file.base.options.target.cpu.arch.endian(); From 03c3a85ea36d8d6a18437b8d9c8a5fc714e4a81e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 9 Nov 2023 18:29:58 +0100 Subject: [PATCH 25/29] elf: write out COMDAT groups to file --- src/link/Elf.zig | 28 +++++++++++++-- src/link/Elf/synthetic_sections.zig | 56 ++++++++++++++++++++--------- 2 files changed, 66 insertions(+), 18 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 678844aa81..ce7f2fc40e 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -3611,6 +3611,8 @@ fn initComdatGroups(self: *Elf) !void { .name = ".group", .type = elf.SHT_GROUP, .entsize = @sizeOf(u32), + .addralign = @alignOf(u32), + .offset = std.math.maxInt(u64), }), .cg_index = cg_index, }; @@ -4293,13 +4295,22 @@ fn updateSectionSizesObject(self: *Elf) !void { shdr.sh_size = eh_frame.calcEhFrameRelocs(self) * shdr.sh_entsize; } - self.updateComdatGroupsSizes(); try self.updateSymtabSize(); + self.updateComdatGroupsSizes(); self.updateShStrtabSize(); } fn updateComdatGroupsSizes(self: *Elf) void { - _ = self; + for (self.comdat_group_sections.items) |cg| { + const shdr = &self.shdrs.items[cg.shndx]; + shdr.sh_size = cg.size(self); + shdr.sh_link = self.symtab_section_index.?; + + const sym = self.symbol(cg.symbol(self)); + std.debug.print("{s}\n", .{sym.name(self)}); + shdr.sh_info = sym.outputSymtabIndex(self) orelse + self.sectionSymbolOutputSymtabIndex(sym.outputShndx().?); + } } fn updateShStrtabSize(self: *Elf) void { @@ -5068,10 +5079,23 @@ fn writeSyntheticSectionsObject(self: *Elf) !void { try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); } + try self.writeComdatGroups(); try self.writeSymtab(); try self.writeShStrtab(); } +fn writeComdatGroups(self: *Elf) !void { + const gpa = self.base.allocator; + for (self.comdat_group_sections.items) |cgs| { + const shdr = self.shdrs.items[cgs.shndx]; + const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow; + var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size); + defer buffer.deinit(); + try cgs.write(self, buffer.writer()); + try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); + } +} + fn writeShStrtab(self: *Elf) !void { if (self.shstrtab_section_index) |index| { const shdr = self.shdrs.items[index]; diff --git a/src/link/Elf/synthetic_sections.zig b/src/link/Elf/synthetic_sections.zig index 7fbc75d80a..67acba9dd2 100644 --- a/src/link/Elf/synthetic_sections.zig +++ b/src/link/Elf/synthetic_sections.zig @@ -1503,24 +1503,48 @@ pub const ComdatGroupSection = struct { shndx: u32, cg_index: u32, - // pub fn size(cg: ComdatGroupSection) usize { - // return cg.members.items.len + 1; - // } + fn file(cgs: ComdatGroupSection, elf_file: *Elf) ?File { + const cg = elf_file.comdatGroup(cgs.cg_index); + const cg_owner = elf_file.comdatGroupOwner(cg.owner); + return elf_file.file(cg_owner.file); + } - // pub fn write(cg: ComdatGroupSection, elf_file: *Elf, writer: anytype) !void { - // try writeInt(@as(u32, elf.GRP_COMDAT), elf_file, writer); - // for (cg.members.items) |atom_index| { - // const atom = elf_file.atom(atom_index); - // const input_shdr = atom.inputShdr(elf_file); - // switch (input_shdr.sh_type) { - // elf.SHT_RELA => { + pub fn symbol(cgs: ComdatGroupSection, elf_file: *Elf) Symbol.Index { + const cg = elf_file.comdatGroup(cgs.cg_index); + const object = cgs.file(elf_file).?.object; + const shdr = object.shdrs.items[cg.shndx]; + return object.symbols.items[shdr.sh_info]; + } - // }, - // else => {}, - // } - // } - // try writer.writeAll(mem.sliceAsBytes(cg.members.items)); - // } + pub fn size(cgs: ComdatGroupSection, elf_file: *Elf) usize { + const cg = elf_file.comdatGroup(cgs.cg_index); + const object = cgs.file(elf_file).?.object; + const members = object.comdatGroupMembers(cg.shndx); + return (members.len + 1) * @sizeOf(u32); + } + + pub fn write(cgs: ComdatGroupSection, elf_file: *Elf, writer: anytype) !void { + const cg = elf_file.comdatGroup(cgs.cg_index); + const object = cgs.file(elf_file).?.object; + const members = object.comdatGroupMembers(cg.shndx); + try writeInt(@as(u32, elf.GRP_COMDAT), elf_file, writer); + for (members) |shndx| { + const shdr = object.shdrs.items[shndx]; + switch (shdr.sh_type) { + elf.SHT_RELA => { + const atom_index = object.atoms.items[shdr.sh_info]; + const atom = elf_file.atom(atom_index).?; + const rela = elf_file.output_rela_sections.get(atom.outputShndx().?).?; + try writeInt(rela.shndx, elf_file, writer); + }, + else => { + const atom_index = object.atoms.items[shndx]; + const atom = elf_file.atom(atom_index).?; + try writeInt(atom.outputShndx().?, elf_file, writer); + }, + } + } + } }; fn writeInt(value: anytype, elf_file: *Elf, writer: anytype) !void { From 27970bab0739232ff7331887f16ec1ee9bc8a9eb Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 9 Nov 2023 18:49:19 +0100 Subject: [PATCH 26/29] elf: format shdr flags when dumping state --- src/link/Elf.zig | 60 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index ce7f2fc40e..1cb6fc02b1 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -6124,13 +6124,69 @@ fn formatShdr( _ = options; _ = unused_fmt_string; const shdr = ctx.shdr; - try writer.print("{s} : @{x} ({x}) : align({x}) : size({x})", .{ + try writer.print("{s} : @{x} ({x}) : align({x}) : size({x}) : flags({})", .{ ctx.elf_file.getShString(shdr.sh_name), shdr.sh_offset, shdr.sh_addr, shdr.sh_addralign, - shdr.sh_size, + shdr.sh_size, fmtShdrFlags(shdr.sh_flags), }); } +fn fmtShdrFlags(sh_flags: u64) std.fmt.Formatter(formatShdrFlags) { + return .{ .data = sh_flags }; +} + +fn formatShdrFlags( + sh_flags: u64, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) !void { + _ = unused_fmt_string; + _ = options; + if (elf.SHF_WRITE & sh_flags != 0) { + try writer.writeAll("W"); + } + if (elf.SHF_ALLOC & sh_flags != 0) { + try writer.writeAll("A"); + } + if (elf.SHF_EXECINSTR & sh_flags != 0) { + try writer.writeAll("X"); + } + if (elf.SHF_MERGE & sh_flags != 0) { + try writer.writeAll("M"); + } + if (elf.SHF_STRINGS & sh_flags != 0) { + try writer.writeAll("S"); + } + if (elf.SHF_INFO_LINK & sh_flags != 0) { + try writer.writeAll("I"); + } + if (elf.SHF_LINK_ORDER & sh_flags != 0) { + try writer.writeAll("L"); + } + if (elf.SHF_EXCLUDE & sh_flags != 0) { + try writer.writeAll("E"); + } + if (elf.SHF_COMPRESSED & sh_flags != 0) { + try writer.writeAll("C"); + } + if (elf.SHF_GROUP & sh_flags != 0) { + try writer.writeAll("G"); + } + if (elf.SHF_OS_NONCONFORMING & sh_flags != 0) { + try writer.writeAll("O"); + } + if (elf.SHF_TLS & sh_flags != 0) { + try writer.writeAll("T"); + } + if (elf.SHF_X86_64_LARGE & sh_flags != 0) { + try writer.writeAll("l"); + } + if (elf.SHF_MIPS_ADDR & sh_flags != 0 or elf.SHF_ARM_PURECODE & sh_flags != 0) { + try writer.writeAll("p"); + } +} + const FormatPhdrCtx = struct { elf_file: *Elf, phdr: elf.Elf64_Phdr, From 6e797d864871575e6c1c4bec37f1e9eea4c79678 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 9 Nov 2023 19:41:50 +0100 Subject: [PATCH 27/29] elf: add SHF_INFO_LINK flag to any emitted SHT_RELA section --- src/link/Elf.zig | 9 ++++----- src/link/Elf/Object.zig | 1 + 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 1cb6fc02b1..43b481af59 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -4307,7 +4307,6 @@ fn updateComdatGroupsSizes(self: *Elf) void { shdr.sh_link = self.symtab_section_index.?; const sym = self.symbol(cg.symbol(self)); - std.debug.print("{s}\n", .{sym.name(self)}); shdr.sh_info = sym.outputSymtabIndex(self) orelse self.sectionSymbolOutputSymtabIndex(sym.outputShndx().?); } @@ -6131,7 +6130,7 @@ fn formatShdr( }); } -fn fmtShdrFlags(sh_flags: u64) std.fmt.Formatter(formatShdrFlags) { +pub fn fmtShdrFlags(sh_flags: u64) std.fmt.Formatter(formatShdrFlags) { return .{ .data = sh_flags }; } @@ -6288,12 +6287,12 @@ fn fmtDumpState( try writer.writeAll("Output COMDAT groups\n"); for (self.comdat_group_sections.items) |cg| { - try writer.print("shdr({d}) : COMDAT({d})\n", .{ cg.shndx, cg.cg_index }); + try writer.print(" shdr({d}) : COMDAT({d})\n", .{ cg.shndx, cg.cg_index }); } try writer.writeAll("\nOutput shdrs\n"); for (self.shdrs.items, 0..) |shdr, shndx| { - try writer.print("shdr({d}) : phdr({?d}) : {}\n", .{ + try writer.print(" shdr({d}) : phdr({?d}) : {}\n", .{ shndx, self.phdr_to_shdr_table.get(@intCast(shndx)), self.fmtShdr(shdr), @@ -6301,7 +6300,7 @@ fn fmtDumpState( } try writer.writeAll("\nOutput phdrs\n"); for (self.phdrs.items, 0..) |phdr, phndx| { - try writer.print("phdr{d} : {}\n", .{ phndx, self.fmtPhdr(phdr) }); + try writer.print(" phdr{d} : {}\n", .{ phndx, self.fmtPhdr(phdr) }); } } diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index 7b4add7f53..f595988e82 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -687,6 +687,7 @@ pub fn initRelaSections(self: Object, elf_file: *Elf) !void { const out_shdr = &elf_file.shdrs.items[out_shndx]; out_shdr.sh_addralign = @alignOf(elf.Elf64_Rela); out_shdr.sh_entsize = @sizeOf(elf.Elf64_Rela); + out_shdr.sh_flags |= elf.SHF_INFO_LINK; } } From 08882234d17a93dd44181323db697db993aebe3d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 9 Nov 2023 23:16:41 +0100 Subject: [PATCH 28/29] elf: fix overflowing designated capacity when writing COMDAT groups --- src/link/Elf.zig | 88 ++++++++++++----------------- src/link/Elf/synthetic_sections.zig | 6 +- 2 files changed, 40 insertions(+), 54 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 43b481af59..d210292bf9 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -4797,7 +4797,11 @@ fn writeAtomsObject(self: *Elf) !void { return error.Overflow; const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow; - log.debug("writing atom({d}) at 0x{x}", .{ atom_index, sh_offset + offset }); + log.debug("writing atom({d}) from 0x{x} to 0x{x}", .{ + atom_index, + sh_offset + offset, + sh_offset + offset + size, + }); // TODO decompress directly into provided buffer const out_code = buffer[offset..][0..size]; @@ -5048,6 +5052,7 @@ fn writeSyntheticSectionsObject(self: *Elf) !void { if (!atom_ptr.flags.alive) continue; try atom_ptr.writeRelocs(self, &relocs); } + assert(relocs.items.len == num_relocs); const SortRelocs = struct { pub fn lessThan(ctx: void, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool { @@ -5058,6 +5063,12 @@ fn writeSyntheticSectionsObject(self: *Elf) !void { mem.sort(elf.Elf64_Rela, relocs.items, {}, SortRelocs.lessThan); + log.debug("writing {s} from 0x{x} to 0x{x}", .{ + self.getShString(shdr.sh_name), + shdr.sh_offset, + shdr.sh_offset + shdr.sh_size, + }); + try self.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), shdr.sh_offset); } @@ -5067,6 +5078,11 @@ fn writeSyntheticSectionsObject(self: *Elf) !void { var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size); defer buffer.deinit(); try eh_frame.writeEhFrameObject(self, buffer.writer()); + log.debug("writing .eh_frame from 0x{x} to 0x{x}", .{ + shdr.sh_offset, + shdr.sh_offset + shdr.sh_size, + }); + assert(buffer.items.len == sh_size); try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); } if (self.eh_frame_rela_section_index) |shndx| { @@ -5075,6 +5091,11 @@ fn writeSyntheticSectionsObject(self: *Elf) !void { var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size); defer buffer.deinit(); try eh_frame.writeEhFrameRelocs(self, buffer.writer()); + assert(buffer.items.len == sh_size); + log.debug("writing .rela.eh_frame from 0x{x} to 0x{x}", .{ + shdr.sh_offset, + shdr.sh_offset + shdr.sh_size, + }); try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); } @@ -5091,6 +5112,11 @@ fn writeComdatGroups(self: *Elf) !void { var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size); defer buffer.deinit(); try cgs.write(self, buffer.writer()); + assert(buffer.items.len == sh_size); + log.debug("writing COMDAT group from 0x{x} to 0x{x}", .{ + shdr.sh_offset, + shdr.sh_offset + shdr.sh_size, + }); try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); } } @@ -5098,6 +5124,7 @@ fn writeComdatGroups(self: *Elf) !void { fn writeShStrtab(self: *Elf) !void { if (self.shstrtab_section_index) |index| { const shdr = self.shdrs.items[index]; + log.debug("writing .shstrtab from 0x{x} to 0x{x}", .{ shdr.sh_offset, shdr.sh_offset + shdr.sh_size }); try self.base.file.?.pwriteAll(self.shstrtab.items, shdr.sh_offset); } } @@ -5112,7 +5139,15 @@ fn writeSymtab(self: *Elf) !void { }; const nsyms = math.cast(usize, @divExact(symtab_shdr.sh_size, sym_size)) orelse return error.Overflow; - log.debug("writing {d} symbols at 0x{x}", .{ nsyms, symtab_shdr.sh_offset }); + log.debug("writing {d} symbols in .symtab from 0x{x} to 0x{x}", .{ + nsyms, + symtab_shdr.sh_offset, + symtab_shdr.sh_offset + symtab_shdr.sh_size, + }); + log.debug("writing .strtab from 0x{x} to 0x{x}", .{ + strtab_shdr.sh_offset, + strtab_shdr.sh_offset + strtab_shdr.sh_size, + }); try self.symtab.resize(gpa, nsyms); const needed_strtab_size = math.cast(usize, strtab_shdr.sh_size - 1) orelse return error.Overflow; @@ -5185,55 +5220,6 @@ fn writeSymtab(self: *Elf) !void { try self.base.file.?.pwriteAll(self.strtab.items, strtab_shdr.sh_offset); } -fn writeSymtabZigObject(self: *Elf, zig_object: *ZigObject) !void { - const gpa = self.base.allocator; - const symtab_shdr = self.shdrs.items[self.symtab_section_index.?]; - const strtab_shdr = self.shdrs.items[self.strtab_section_index.?]; - const sym_size: u64 = switch (self.ptr_width) { - .p32 => @sizeOf(elf.Elf32_Sym), - .p64 => @sizeOf(elf.Elf64_Sym), - }; - const nsyms = math.cast(usize, @divExact(symtab_shdr.sh_size, sym_size)) orelse return error.Overflow; - - log.debug("writing {d} symbols at 0x{x}", .{ nsyms, symtab_shdr.sh_offset }); - - try self.symtab.resize(gpa, nsyms); - const needed_strtab_size = math.cast(usize, strtab_shdr.sh_size - 1) orelse return error.Overflow; - try self.strtab.ensureUnusedCapacity(gpa, needed_strtab_size); - - self.writeSectionSymbols(); - zig_object.asFile().writeSymtab(self); - - const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian(); - switch (self.ptr_width) { - .p32 => { - const buf = try gpa.alloc(elf.Elf32_Sym, self.symtab.items.len); - defer gpa.free(buf); - - for (buf, self.symtab.items) |*out, sym| { - out.* = .{ - .st_name = sym.st_name, - .st_info = sym.st_info, - .st_other = sym.st_other, - .st_shndx = sym.st_shndx, - .st_value = @as(u32, @intCast(sym.st_value)), - .st_size = @as(u32, @intCast(sym.st_size)), - }; - if (foreign_endian) mem.byteSwapAllFields(elf.Elf32_Sym, out); - } - try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), symtab_shdr.sh_offset); - }, - .p64 => { - if (foreign_endian) { - for (self.symtab.items) |*sym| mem.byteSwapAllFields(elf.Elf64_Sym, sym); - } - try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.symtab.items), symtab_shdr.sh_offset); - }, - } - - try self.base.file.?.pwriteAll(self.strtab.items, strtab_shdr.sh_offset); -} - fn writeSectionSymbols(self: *Elf) void { var ilocal: u32 = 1; for (self.output_sections.keys()) |shndx| { diff --git a/src/link/Elf/synthetic_sections.zig b/src/link/Elf/synthetic_sections.zig index 67acba9dd2..7419709afc 100644 --- a/src/link/Elf/synthetic_sections.zig +++ b/src/link/Elf/synthetic_sections.zig @@ -1527,7 +1527,7 @@ pub const ComdatGroupSection = struct { const cg = elf_file.comdatGroup(cgs.cg_index); const object = cgs.file(elf_file).?.object; const members = object.comdatGroupMembers(cg.shndx); - try writeInt(@as(u32, elf.GRP_COMDAT), elf_file, writer); + try writer.writeInt(u32, elf.GRP_COMDAT, .little); for (members) |shndx| { const shdr = object.shdrs.items[shndx]; switch (shdr.sh_type) { @@ -1535,12 +1535,12 @@ pub const ComdatGroupSection = struct { const atom_index = object.atoms.items[shdr.sh_info]; const atom = elf_file.atom(atom_index).?; const rela = elf_file.output_rela_sections.get(atom.outputShndx().?).?; - try writeInt(rela.shndx, elf_file, writer); + try writer.writeInt(u32, rela.shndx, .little); }, else => { const atom_index = object.atoms.items[shndx]; const atom = elf_file.atom(atom_index).?; - try writeInt(atom.outputShndx().?, elf_file, writer); + try writer.writeInt(u32, atom.outputShndx().?, .little); }, } } From 73fd4ed54b639bfd4d8a142102b54762d290763e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 9 Nov 2023 23:22:47 +0100 Subject: [PATCH 29/29] test/link/elf: make .eh_frame relocatable test also verify COMDATs we emit --- test/link/elf.zig | 107 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 74 insertions(+), 33 deletions(-) diff --git a/test/link/elf.zig b/test/link/elf.zig index f458fbb8de..09847e382c 100644 --- a/test/link/elf.zig +++ b/test/link/elf.zig @@ -2143,41 +2143,82 @@ fn testPreinitArray(b: *Build, opts: Options) *Step { fn testRelocatableEhFrame(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "relocatable-eh-frame", opts); - const obj = addObject(b, "obj", opts); - addCppSourceBytes(obj, - \\#include - \\int try_me() { - \\ throw std::runtime_error("Oh no!"); - \\} - , &.{}); - addCppSourceBytes(obj, - \\extern int try_me(); - \\int try_again() { - \\ return try_me(); - \\} - , &.{}); - obj.linkLibCpp(); + { + const obj = addObject(b, "obj1", opts); + addCppSourceBytes(obj, + \\#include + \\int try_me() { + \\ throw std::runtime_error("Oh no!"); + \\} + , &.{}); + addCppSourceBytes(obj, + \\extern int try_me(); + \\int try_again() { + \\ return try_me(); + \\} + , &.{}); + obj.linkLibCpp(); - const exe = addExecutable(b, "test", opts); - addCppSourceBytes(exe, - \\#include - \\#include - \\extern int try_again(); - \\int main() { - \\ try { - \\ try_again(); - \\ } catch (const std::exception &e) { - \\ std::cout << "exception=" << e.what(); - \\ } - \\ return 0; - \\} - , &.{}); - exe.addObject(obj); - exe.linkLibCpp(); + const exe = addExecutable(b, "test1", opts); + addCppSourceBytes(exe, + \\#include + \\#include + \\extern int try_again(); + \\int main() { + \\ try { + \\ try_again(); + \\ } catch (const std::exception &e) { + \\ std::cout << "exception=" << e.what(); + \\ } + \\ return 0; + \\} + , &.{}); + exe.addObject(obj); + exe.linkLibCpp(); - const run = addRunArtifact(exe); - run.expectStdOutEqual("exception=Oh no!"); - test_step.dependOn(&run.step); + const run = addRunArtifact(exe); + run.expectStdOutEqual("exception=Oh no!"); + test_step.dependOn(&run.step); + } + + { + // Let's make the object file COMDAT group heavy! + const obj = addObject(b, "obj2", opts); + addCppSourceBytes(obj, + \\#include + \\int try_me() { + \\ throw std::runtime_error("Oh no!"); + \\} + , &.{}); + addCppSourceBytes(obj, + \\extern int try_me(); + \\int try_again() { + \\ return try_me(); + \\} + , &.{}); + addCppSourceBytes(obj, + \\#include + \\#include + \\extern int try_again(); + \\int main() { + \\ try { + \\ try_again(); + \\ } catch (const std::exception &e) { + \\ std::cout << "exception=" << e.what(); + \\ } + \\ return 0; + \\} + , &.{}); + obj.linkLibCpp(); + + const exe = addExecutable(b, "test2", opts); + exe.addObject(obj); + exe.linkLibCpp(); + + const run = addRunArtifact(exe); + run.expectStdOutEqual("exception=Oh no!"); + test_step.dependOn(&run.step); + } return test_step; }