diff --git a/src/Elf.zig b/src/Elf.zig index a9532de9..95c60614 100644 --- a/src/Elf.zig +++ b/src/Elf.zig @@ -91,10 +91,6 @@ thunks: std.ArrayListUnmanaged(Thunk) = .{}, merge_sections: std.ArrayListUnmanaged(MergeSection) = .{}, merge_subsections: std.ArrayListUnmanaged(MergeSubsection) = .{}, -comdat_groups: std.ArrayListUnmanaged(ComdatGroup) = .{}, -comdat_groups_owners: std.ArrayListUnmanaged(ComdatGroupOwner) = .{}, -comdat_groups_table: std.AutoHashMapUnmanaged(u32, ComdatGroupOwner.Index) = .{}, - has_text_reloc: bool = false, num_ifunc_dynrelocs: usize = 0, default_sym_version: elf.Elf64_Versym, @@ -154,9 +150,6 @@ pub fn deinit(self: *Elf) void { } self.merge_sections.deinit(gpa); self.merge_subsections.deinit(gpa); - self.comdat_groups.deinit(gpa); - self.comdat_groups_owners.deinit(gpa); - self.comdat_groups_table.deinit(gpa); self.symbols.deinit(gpa); self.symbols_extra.deinit(gpa); self.globals.deinit(gpa); @@ -2046,34 +2039,17 @@ fn resolveSymbols(self: *Elf) !void { } else i += 1; } - // Dedup comdat groups. - for (self.objects.items) |index| { - const object = self.getFile(index).?.object; - for (object.comdat_groups.items) |cg_index| { - const cg = self.getComdatGroup(cg_index); - const cg_owner = self.getComdatGroupOwner(cg.owner); - const owner_file_index = if (self.getFile(cg_owner.file)) |file| - file.object.index - else - std.math.maxInt(File.Index); - cg_owner.file = @min(owner_file_index, index); + { + // Dedup comdat groups. + var table = std.StringHashMap(Ref).init(self.base.allocator); + defer table.deinit(); + + for (self.objects.items) |index| { + try self.getFile(index).?.object.resolveComdatGroups(self, &table); } - } - for (self.objects.items) |index| { - const object = self.getFile(index).?.object; - for (object.comdat_groups.items) |cg_index| { - const cg = self.getComdatGroup(cg_index); - const cg_owner = self.getComdatGroupOwner(cg.owner); - if (cg_owner.file != index) { - for (cg.getComdatGroupMembers(self)) |shndx| { - const atom_index = object.atoms.items[shndx]; - if (self.getAtom(atom_index)) |atom| { - atom.flags.alive = false; - atom.markFdesDead(self); - } - } - } + for (self.objects.items) |index| { + self.getFile(index).?.object.markComdatGroupsDead(self); } } @@ -3019,40 +2995,8 @@ pub fn getMergeSection(self: *Elf, index: MergeSection.Index) *MergeSection { return &self.merge_sections.items[index]; } -const GetOrCreateComdatGroupOwnerResult = struct { - found_existing: bool, - index: ComdatGroupOwner.Index, -}; - -pub fn getOrCreateComdatGroupOwner(self: *Elf, off: u32) !GetOrCreateComdatGroupOwnerResult { - const gpa = self.base.allocator; - const gop = try self.comdat_groups_table.getOrPut(gpa, off); - if (!gop.found_existing) { - const index = @as(ComdatGroupOwner.Index, @intCast(self.comdat_groups_owners.items.len)); - const owner = try self.comdat_groups_owners.addOne(gpa); - owner.* = .{}; - gop.value_ptr.* = index; - } - return .{ - .found_existing = gop.found_existing, - .index = gop.value_ptr.*, - }; -} - -pub fn addComdatGroup(self: *Elf) !ComdatGroup.Index { - const index = @as(ComdatGroup.Index, @intCast(self.comdat_groups.items.len)); - _ = try self.comdat_groups.addOne(self.base.allocator); - return index; -} - -pub inline fn getComdatGroup(self: *Elf, index: ComdatGroup.Index) *ComdatGroup { - assert(index < self.comdat_groups.items.len); - return &self.comdat_groups.items[index]; -} - -pub inline fn getComdatGroupOwner(self: *Elf, index: ComdatGroupOwner.Index) *ComdatGroupOwner { - assert(index < self.comdat_groups_owners.items.len); - return &self.comdat_groups_owners.items[index]; +pub fn getComdatGroup(self: *Elf, ref: Ref) *ComdatGroup { + return self.getFile(ref.file).?.getComdatGroup(ref.index); } const RelaDyn = struct { @@ -3294,7 +3238,12 @@ fn fmtDumpState( try writer.print("merge_sect({d}) : {}\n", .{ index, msec.fmt(self) }); } try writer.writeByte('\n'); - try writer.writeAll("Output sections\n"); + try writer.writeAll("Output COMDAT groups\n"); + for (self.comdat_group_sections.items) |cg| { + try writer.print(" shdr({d}) : COMDAT({})\n", .{ cg.shndx, cg.cg_ref }); + } + try writer.writeByte('\n'); + try writer.writeAll("Output shdrs\n"); try writer.print("{}\n", .{self.fmtSections()}); try writer.writeAll("Output phdrs\n"); try writer.print("{}\n", .{self.fmtPhdrs()}); @@ -3313,21 +3262,44 @@ const Section = struct { sym_index: u32 = 0, }; -const ComdatGroupOwner = struct { - file: File.Index = 0, +pub const Ref = struct { + index: u32 = 0, + file: u32 = 0, + + pub fn eql(ref: Ref, other: Ref) bool { + return ref.index == other.index and ref.file == other.file; + } - const Index = u32; + pub fn format( + ref: Ref, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = unused_fmt_string; + _ = options; + try writer.print("ref({},{})", .{ ref.index, ref.file }); + } }; pub const ComdatGroup = struct { - owner: ComdatGroupOwner.Index, - file: File.Index, + signature_off: u32, + file_index: File.Index, shndx: u32, members_start: u32, members_len: u32, + alive: bool = true, + + pub fn getFile(cg: ComdatGroup, elf_file: *Elf) File { + return elf_file.getFile(cg.file_index).?; + } + + pub fn getSignature(cg: ComdatGroup, elf_file: *Elf) [:0]const u8 { + return cg.getFile(elf_file).object.getString(cg.signature_off); + } pub fn getComdatGroupMembers(cg: ComdatGroup, elf_file: *Elf) []const u32 { - const object = elf_file.getFile(cg.file).?.object; + const object = cg.getFile(elf_file).object; return object.comdat_group_data.items[cg.members_start..][0..cg.members_len]; } diff --git a/src/Elf/Object.zig b/src/Elf/Object.zig index 918cdb7c..e0f4e3ad 100644 --- a/src/Elf/Object.zig +++ b/src/Elf/Object.zig @@ -5,20 +5,20 @@ index: File.Index, header: ?elf.Elf64_Ehdr = null, shdrs: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{}, -shstrtab: StringTable = .{}, symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, strtab: std.ArrayListUnmanaged(u8) = .{}, first_global: ?Symbol.Index = null, symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, -comdat_groups: std.ArrayListUnmanaged(Elf.ComdatGroup.Index) = .{}, -comdat_group_data: std.ArrayListUnmanaged(u32) = .{}, relocs: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{}, merge_sections: std.ArrayListUnmanaged(InputMergeSection) = .{}, merge_sections_indexes: std.ArrayListUnmanaged(InputMergeSection.Index) = .{}, +comdat_groups: std.ArrayListUnmanaged(Elf.ComdatGroup) = .{}, +comdat_group_data: std.ArrayListUnmanaged(u32) = .{}, + fdes: std.ArrayListUnmanaged(Fde) = .{}, cies: std.ArrayListUnmanaged(Cie) = .{}, eh_frame_data: std.ArrayListUnmanaged(u8) = .{}, @@ -41,7 +41,6 @@ pub fn deinit(self: *Object, allocator: Allocator) void { if (self.archive) |*ar| allocator.free(ar.path); allocator.free(self.path); self.shdrs.deinit(allocator); - self.shstrtab.deinit(allocator); self.symtab.deinit(allocator); self.strtab.deinit(allocator); self.symbols.deinit(allocator); @@ -88,7 +87,7 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { const shstrtab = try self.preadShdrContentsAlloc(gpa, file, self.header.?.e_shstrndx); defer gpa.free(shstrtab); - try self.shstrtab.buffer.appendSlice(gpa, shstrtab); + try self.strtab.appendSlice(gpa, shstrtab); const symtab_index = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) { elf.SHT_SYMTAB => break @as(u32, @intCast(i)), @@ -107,9 +106,17 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { }; try self.symtab.appendUnalignedSlice(gpa, @as([*]align(1) const elf.Elf64_Sym, @ptrCast(symtab.ptr))[0..nsyms]); + const strtab_bias = @as(u32, @intCast(self.strtab.items.len)); const strtab = try self.preadShdrContentsAlloc(gpa, file, shdr.sh_link); defer gpa.free(strtab); try self.strtab.appendSlice(gpa, strtab); + + for (self.symtab.items) |*sym| { + sym.st_name = if (sym.st_name == 0 and sym.st_type() == elf.STT_SECTION) + shdrs[sym.st_shndx].sh_name + else + sym.st_name + strtab_bias; + } } // Append null input merge section @@ -152,9 +159,9 @@ fn initAtoms(self: *Object, allocator: Allocator, file: std.fs.File, elf_file: * const group_signature = blk: { if (group_info_sym.st_name == 0 and group_info_sym.st_type() == elf.STT_SECTION) { const sym_shdr = shdrs[group_info_sym.st_shndx]; - break :blk self.getShString(sym_shdr.sh_name); + break :blk sym_shdr.sh_name; } - break :blk self.getString(group_info_sym.st_name); + break :blk group_info_sym.st_name; }; const group_raw_data = try self.preadShdrContentsAlloc(allocator, file, @intCast(i)); @@ -170,18 +177,15 @@ fn initAtoms(self: *Object, allocator: Allocator, file: std.fs.File, elf_file: * const group_start = @as(u32, @intCast(self.comdat_group_data.items.len)); try self.comdat_group_data.appendUnalignedSlice(allocator, group_members[1..]); - const group_signature_off = try elf_file.internString("{s}", .{group_signature}); - const gop = try elf_file.getOrCreateComdatGroupOwner(group_signature_off); - const comdat_group_index = try elf_file.addComdatGroup(); - const comdat_group = elf_file.getComdatGroup(comdat_group_index); + const comdat_group_index = try self.addComdatGroup(allocator); + const comdat_group = self.getComdatGroup(comdat_group_index); comdat_group.* = .{ - .owner = gop.index, - .file = self.index, + .signature_off = group_signature, + .file_index = self.index, .shndx = @intCast(i), .members_start = group_start, .members_len = @intCast(group_nmembers - 1), }; - try self.comdat_groups.append(allocator, comdat_group_index); }, elf.SHT_SYMTAB_SHNDX => @panic("TODO"), @@ -194,7 +198,7 @@ fn initAtoms(self: *Object, allocator: Allocator, file: std.fs.File, elf_file: * => {}, else => { - const name = self.getShString(shdr.sh_name); + const name = self.getString(shdr.sh_name); const shndx = @as(u32, @intCast(i)); if (mem.eql(u8, ".note.GNU-stack", name)) { @@ -258,7 +262,7 @@ fn addAtom(self: *Object, allocator: Allocator, file: std.fs.File, shdr: elf.Elf fn skipShdr(self: *Object, index: u32, elf_file: *Elf) bool { const shdr = self.shdrs.items[index]; - const name = self.getShString(shdr.sh_name); + const name = self.getString(shdr.sh_name); const ignore = blk: { if (mem.startsWith(u8, name, ".note")) break :blk true; if (mem.startsWith(u8, name, ".llvm_addrsig")) break :blk true; @@ -276,7 +280,6 @@ fn initSymtab(self: *Object, allocator: Allocator, elf_file: *Elf) !void { defer tracy.end(); const first_global = self.first_global orelse self.symtab.items.len; - const shdrs = self.shdrs.items; try self.symbols.ensureTotalCapacityPrecise(allocator, self.symtab.items.len); @@ -284,13 +287,7 @@ fn initSymtab(self: *Object, allocator: Allocator, elf_file: *Elf) !void { const index = try elf_file.addSymbol(); self.symbols.appendAssumeCapacity(index); const symbol = elf_file.getSymbol(index); - const name = blk: { - if (sym.st_name == 0 and sym.st_type() == elf.STT_SECTION) { - const shdr = shdrs[sym.st_shndx]; - break :blk self.getShString(shdr.sh_name); - } - break :blk self.getString(sym.st_name); - }; + const name = self.getString(sym.st_name); symbol.* = .{ .value = @intCast(sym.st_value), .name = try elf_file.string_intern.insert(elf_file.base.allocator, name), @@ -310,7 +307,7 @@ fn initSymtab(self: *Object, allocator: Allocator, elf_file: *Elf) !void { pub fn initOutputSection(self: Object, elf_file: *Elf, shdr: elf.Elf64_Shdr) !u32 { const name = blk: { - const name = self.getShString(shdr.sh_name); + const name = self.getString(shdr.sh_name); if (elf_file.options.relocatable) break :blk name; if (shdr.sh_flags & elf.SHF_MERGE != 0) break :blk name; const sh_name_prefixes: []const [:0]const u8 = &.{ @@ -829,7 +826,7 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void { const shndx = @as(u32, @intCast(self.shdrs.items.len)); const shdr = try self.shdrs.addOne(gpa); shdr.* = .{ - .sh_name = try self.shstrtab.insert(gpa, name), + .sh_name = try self.addString(gpa, name), .sh_type = elf.SHT_NOBITS, .sh_flags = sh_flags, .sh_addr = 0, @@ -848,6 +845,37 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void { } } +pub fn resolveComdatGroups(self: *Object, elf_file: *Elf, table: anytype) !void { + for (self.comdat_groups.items, 0..) |*cg, cgi| { + const signature = cg.getSignature(elf_file); + const gop = try table.getOrPut(signature); + if (!gop.found_existing) { + gop.value_ptr.* = .{ .index = @intCast(cgi), .file = self.index }; + continue; + } + const current = elf_file.getComdatGroup(gop.value_ptr.*); + cg.alive = false; + if (self.index < current.file_index) { + current.alive = false; + cg.alive = true; + gop.value_ptr.* = .{ .index = @intCast(cgi), .file = self.index }; + } + } +} + +pub fn markComdatGroupsDead(self: *Object, elf_file: *Elf) void { + for (self.comdat_groups.items) |cg| { + if (cg.alive) continue; + for (cg.getComdatGroupMembers(elf_file)) |shndx| { + const atom_index = self.atoms.items[shndx]; + if (elf_file.getAtom(atom_index)) |atom_ptr| { + atom_ptr.flags.alive = false; + atom_ptr.markFdesDead(elf_file); + } + } + } +} + pub fn calcSymtabSize(self: *Object, elf_file: *Elf) !void { if (elf_file.options.strip_all) return; @@ -953,13 +981,17 @@ fn preadRelocsAlloc(self: Object, allocator: Allocator, file: std.fs.File, shndx return @as([*]align(1) const elf.Elf64_Rela, @ptrCast(raw.ptr))[0..num]; } -inline fn getString(self: Object, off: u32) [:0]const u8 { - assert(off < self.strtab.items.len); - return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0); +fn addString(self: *Object, allocator: Allocator, str: []const u8) !u32 { + const off: u32 = @intCast(self.strtab.items.len); + try self.strtab.ensureUnusedCapacity(allocator, str.len + 1); + self.strtab.appendSliceAssumeCapacity(str); + self.strtab.appendAssumeCapacity(0); + return off; } -inline fn getShString(self: Object, off: u32) [:0]const u8 { - return self.shstrtab.getAssumeExists(off); +pub fn getString(self: Object, off: u32) [:0]const u8 { + assert(off < self.strtab.items.len); + return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0); } pub fn asFile(self: *Object) File { @@ -978,6 +1010,17 @@ fn getInputMergeSection(self: *Object, index: InputMergeSection.Index) ?*InputMe return &self.merge_sections.items[index]; } +fn addComdatGroup(self: *Object, allocator: Allocator) !Elf.ComdatGroup.Index { + const index = @as(Elf.ComdatGroup.Index, @intCast(self.comdat_groups.items.len)); + _ = try self.comdat_groups.addOne(allocator); + return index; +} + +pub fn getComdatGroup(self: *Object, index: Elf.ComdatGroup.Index) *Elf.ComdatGroup { + assert(index < self.comdat_groups.items.len); + return &self.comdat_groups.items[index]; +} + pub fn format( self: *Object, comptime unused_fmt_string: []const u8, @@ -1108,12 +1151,13 @@ fn formatComdatGroups( _ = options; const object = ctx.object; const elf_file = ctx.elf_file; - try writer.writeAll(" comdat groups\n"); - for (object.comdat_groups.items) |cg_index| { - const cg = elf_file.getComdatGroup(cg_index); - const cg_owner = elf_file.getComdatGroupOwner(cg.owner); - if (cg_owner.file != object.index) continue; - for (cg.getComdatGroupMembers(elf_file)) |shndx| { + try writer.writeAll(" COMDAT groups\n"); + for (object.comdat_groups.items, 0..) |cg, cg_index| { + try writer.print(" COMDAT({d})", .{cg_index}); + if (!cg.alive) try writer.writeAll(" : [*]"); + try writer.writeByte('\n'); + const cg_members = cg.getComdatGroupMembers(elf_file); + for (cg_members) |shndx| { const atom_index = object.atoms.items[shndx]; const atom = elf_file.getAtom(atom_index) orelse continue; try writer.print(" atom({d}) : {s}\n", .{ atom_index, atom.getName(elf_file) }); diff --git a/src/Elf/file.zig b/src/Elf/file.zig index 8abe4321..18bb24c1 100644 --- a/src/Elf/file.zig +++ b/src/Elf/file.zig @@ -85,6 +85,13 @@ pub const File = union(enum) { } } + pub fn getComdatGroup(file: File, ind: Elf.ComdatGroup.Index) *Elf.ComdatGroup { + return switch (file) { + .internal, .shared => unreachable, + .object => |x| x.getComdatGroup(ind), + }; + } + pub fn getLocals(file: File) []const Symbol.Index { return switch (file) { .object => |x| x.getLocals(), diff --git a/src/Elf/relocatable.zig b/src/Elf/relocatable.zig index a82338d7..6cfd63f8 100644 --- a/src/Elf/relocatable.zig +++ b/src/Elf/relocatable.zig @@ -122,19 +122,12 @@ fn initSections(elf_file: *Elf) !void { } fn initComdatGroups(elf_file: *Elf) !void { - const tracy = trace(@src()); - defer tracy.end(); - const gpa = elf_file.base.allocator; for (elf_file.objects.items) |index| { const object = elf_file.getFile(index).?.object; - - for (object.comdat_groups.items) |cg_index| { - const cg = elf_file.getComdatGroup(cg_index); - const cg_owner = elf_file.getComdatGroupOwner(cg.owner); - if (cg_owner.file != index) continue; - + for (object.comdat_groups.items, 0..) |cg, cg_index| { + if (!cg.alive) continue; const cg_sec = try elf_file.comdat_group_sections.addOne(gpa); cg_sec.* = .{ .shndx = try elf_file.addSection(.{ @@ -143,7 +136,7 @@ fn initComdatGroups(elf_file: *Elf) !void { .entsize = @sizeOf(u32), .addralign = @alignOf(u32), }), - .cg_index = cg_index, + .cg_ref = .{ .index = @intCast(cg_index), .file = index }, }; } } @@ -205,9 +198,8 @@ fn calcComdatGroupsSizes(elf_file: *Elf) void { shdr.sh_size = cg.size(elf_file); shdr.sh_link = elf_file.symtab_sect_index.?; - const sym = elf_file.getSymbol(cg.getSymbol(elf_file)); - shdr.sh_info = sym.getOutputSymtabIndex(elf_file) orelse - elf_file.sections.items(.sym_index)[sym.shndx]; + const sym = cg.getSymbol(elf_file); + shdr.sh_info = sym.getOutputSymtabIndex(elf_file) orelse sym.shndx; } } diff --git a/src/Elf/synthetic.zig b/src/Elf/synthetic.zig index 73b5add2..a825e692 100644 --- a/src/Elf/synthetic.zig +++ b/src/Elf/synthetic.zig @@ -1409,30 +1409,30 @@ pub const CopyRelSection = struct { pub const ComdatGroupSection = struct { shndx: u32, - cg_index: u32, + cg_ref: Elf.Ref, - fn getFile(cgs: ComdatGroupSection, elf_file: *Elf) ?File { - const cg = elf_file.getComdatGroup(cgs.cg_index); - const cg_owner = elf_file.getComdatGroupOwner(cg.owner); - return elf_file.getFile(cg_owner.file); + fn getComdatGroup(cgs: ComdatGroupSection, elf_file: *Elf) *Elf.ComdatGroup { + const cg_file = elf_file.getFile(cgs.cg_ref.file).?; + return cg_file.object.getComdatGroup(cgs.cg_ref.index); } - pub fn getSymbol(cgs: ComdatGroupSection, elf_file: *Elf) Symbol.Index { - const cg = elf_file.getComdatGroup(cgs.cg_index); - const object = cgs.getFile(elf_file).?.object; + pub fn getSymbol(cgs: ComdatGroupSection, elf_file: *Elf) *Symbol { + const cg = cgs.getComdatGroup(elf_file); + const object = cg.getFile(elf_file).object; const shdr = object.shdrs.items[cg.shndx]; - return object.symbols.items[shdr.sh_info]; + const sym_index = object.symbols.items[shdr.sh_info]; + return elf_file.getSymbol(sym_index); } pub fn size(cgs: ComdatGroupSection, elf_file: *Elf) usize { - const cg = elf_file.getComdatGroup(cgs.cg_index); + const cg = cgs.getComdatGroup(elf_file); const members = cg.getComdatGroupMembers(elf_file); return (members.len + 1) * @sizeOf(u32); } pub fn write(cgs: ComdatGroupSection, elf_file: *Elf, writer: anytype) !void { - const cg = elf_file.getComdatGroup(cgs.cg_index); - const object = cgs.getFile(elf_file).?.object; + const cg = cgs.getComdatGroup(elf_file); + const object = cg.getFile(elf_file).object; const members = cg.getComdatGroupMembers(elf_file); try writer.writeInt(u32, elf.GRP_COMDAT, .little); for (members) |shndx| { @@ -1441,8 +1441,12 @@ pub const ComdatGroupSection = struct { elf.SHT_RELA => { const atom_index = object.atoms.items[shdr.sh_info]; const atom = elf_file.getAtom(atom_index).?; - const rela_shndx = elf_file.sections.items(.rela_shndx)[atom.out_shndx]; - try writer.writeInt(u32, rela_shndx, .little); + const rela_shndx = for (elf_file.sections.items(.shdr), 0..) |rela_shdr, rela_shndx| { + if (rela_shdr.sh_type == elf.SHT_RELA and + atom.out_shndx == rela_shdr.sh_info) + break rela_shndx; + } else unreachable; + try writer.writeInt(u32, @intCast(rela_shndx), .little); }, else => { const atom_index = object.atoms.items[shndx];