diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2024-08-01 08:50:01 +0200 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2024-08-07 10:21:02 +0200 |
| commit | deeaa1bb0cb8a8c7ccebb23cc68be64e4b013ab2 (patch) | |
| tree | 0b1e93cb77cff05de14485b646e985f7dcba9ecf /src | |
| parent | de80e4fec2a29c5aac70c8d72b11a90cb96feeaf (diff) | |
| download | zig-deeaa1bb0cb8a8c7ccebb23cc68be64e4b013ab2.tar.gz zig-deeaa1bb0cb8a8c7ccebb23cc68be64e4b013ab2.zip | |
elf: redo symbol mgmt and ownership in ZigObject
Diffstat (limited to 'src')
| -rw-r--r-- | src/link/Elf.zig | 78 | ||||
| -rw-r--r-- | src/link/Elf/LinkerDefined.zig | 8 | ||||
| -rw-r--r-- | src/link/Elf/Object.zig | 27 | ||||
| -rw-r--r-- | src/link/Elf/SharedObject.zig | 16 | ||||
| -rw-r--r-- | src/link/Elf/Symbol.zig | 14 | ||||
| -rw-r--r-- | src/link/Elf/ZigObject.zig | 457 | ||||
| -rw-r--r-- | src/link/Elf/file.zig | 13 |
7 files changed, 335 insertions, 278 deletions
diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 1e68374502..f2dc7b563f 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -173,11 +173,7 @@ shstrtab_section_index: ?u32 = null, strtab_section_index: ?u32 = null, symtab_section_index: ?u32 = null, -/// An array of symbols parsed across all input files. -symbols: std.ArrayListUnmanaged(Symbol) = .{}, -symbols_extra: std.ArrayListUnmanaged(u32) = .{}, - -resolver: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{}, +resolver: SymbolResolver = .{}, has_text_reloc: bool = false, num_ifunc_dynrelocs: usize = 0, @@ -191,10 +187,6 @@ merge_sections: std.ArrayListUnmanaged(MergeSection) = .{}, /// Table of last atom index in a section and matching atom free list if any. last_atom_and_free_list_table: LastAtomAndFreeListTable = .{}, -/// Global string table used to provide quick access to global symbol resolvers -/// such as `resolver`. -strings: StringTable = .{}, - first_eflags: ?elf.Elf64_Word = null, /// When allocating, the ideal_capacity is calculated by @@ -455,8 +447,6 @@ pub fn deinit(self: *Elf) void { self.shstrtab.deinit(gpa); self.symtab.deinit(gpa); self.strtab.deinit(gpa); - self.symbols.deinit(gpa); - self.symbols_extra.deinit(gpa); self.resolver.deinit(gpa); for (self.thunks.items) |*th| { @@ -472,8 +462,6 @@ pub fn deinit(self: *Elf) void { } self.last_atom_and_free_list_table.deinit(gpa); - self.strings.deinit(gpa); - self.got.deinit(gpa); self.plt.deinit(gpa); self.plt_got.deinit(gpa); @@ -1916,20 +1904,17 @@ fn accessLibPath( /// 6. Re-run symbol resolution on pruned objects and shared objects sets. pub fn resolveSymbols(self: *Elf) !void { // Resolve symbols in the ZigObject. For now, we assume that it's always live. - if (self.zigObjectPtr()) |zig_object| zig_object.asFile().resolveSymbols(self); + if (self.zigObjectPtr()) |zo| try zo.asFile().resolveSymbols(self); // Resolve symbols on the set of all objects and shared objects (even if some are unneeded). - for (self.objects.items) |index| self.file(index).?.resolveSymbols(self); - for (self.shared_objects.items) |index| self.file(index).?.resolveSymbols(self); - if (self.linkerDefinedPtr()) |obj| obj.asFile().resolveSymbols(self); + for (self.objects.items) |index| try self.file(index).?.resolveSymbols(self); + for (self.shared_objects.items) |index| try self.file(index).?.resolveSymbols(self); + if (self.linkerDefinedPtr()) |obj| try obj.asFile().resolveSymbols(self); // Mark live objects. self.markLive(); // Reset state of all globals after marking live objects. - if (self.zigObjectPtr()) |zig_object| zig_object.asFile().resetGlobals(self); - for (self.objects.items) |index| self.file(index).?.resetGlobals(self); - for (self.shared_objects.items) |index| self.file(index).?.resetGlobals(self); - if (self.linkerDefinedPtr()) |obj| obj.asFile().resetGlobals(self); + self.resolver.reset(); // Prune dead objects and shared objects. var i: usize = 0; @@ -1962,10 +1947,10 @@ pub fn resolveSymbols(self: *Elf) !void { } // Re-resolve the symbols. - if (self.zigObjectPtr()) |zig_object| zig_object.asFile().resolveSymbols(self); - for (self.objects.items) |index| self.file(index).?.resolveSymbols(self); - for (self.shared_objects.items) |index| self.file(index).?.resolveSymbols(self); - if (self.linkerDefinedPtr()) |obj| obj.asFile().resolveSymbols(self); + if (self.zigObjectPtr()) |zo| try zo.asFile().resolveSymbols(self); + for (self.objects.items) |index| try self.file(index).?.resolveSymbols(self); + for (self.shared_objects.items) |index| try self.file(index).?.resolveSymbols(self); + if (self.linkerDefinedPtr()) |obj| try obj.asFile().resolveSymbols(self); } /// Traverses all objects and shared objects marking any object referenced by @@ -1999,46 +1984,17 @@ fn convertCommonSymbols(self: *Elf) !void { } fn markImportsExports(self: *Elf) void { - const mark = struct { - fn mark(elf_file: *Elf, file_index: File.Index) void { - for (elf_file.file(file_index).?.globals()) |global_index| { - const global = elf_file.symbol(global_index); - if (global.version_index == elf.VER_NDX_LOCAL) continue; - const file_ptr = global.file(elf_file) orelse continue; - const vis = @as(elf.STV, @enumFromInt(global.elfSym(elf_file).st_other)); - if (vis == .HIDDEN) continue; - if (file_ptr == .shared_object and !global.isAbs(elf_file)) { - global.flags.import = true; - continue; - } - if (file_ptr.index() == file_index) { - global.flags.@"export" = true; - if (elf_file.isEffectivelyDynLib() and vis != .PROTECTED) { - global.flags.import = true; - } - } - } - } - }.mark; - + if (self.zigObjectPtr()) |zo| { + zo.markImportsExports(self); + } + for (self.objects.items) |index| { + self.file(index).?.object.markImportsExports(self); + } if (!self.isEffectivelyDynLib()) { for (self.shared_objects.items) |index| { - for (self.file(index).?.globals()) |global_index| { - const global = self.symbol(global_index); - const file_ptr = global.file(self) orelse continue; - const vis = @as(elf.STV, @enumFromInt(global.elfSym(self).st_other)); - if (file_ptr != .shared_object and vis != .HIDDEN) global.flags.@"export" = true; - } + self.file(index).?.shared_object.markImportExports(self); } } - - if (self.zig_object_index) |index| { - mark(self, index); - } - - for (self.objects.items) |index| { - mark(self, index); - } } fn claimUnresolved(self: *Elf) void { diff --git a/src/link/Elf/LinkerDefined.zig b/src/link/Elf/LinkerDefined.zig index 1247a08ef1..89e56a7e16 100644 --- a/src/link/Elf/LinkerDefined.zig +++ b/src/link/Elf/LinkerDefined.zig @@ -302,12 +302,8 @@ pub fn allocateSymbols(self: *LinkerDefined, elf_file: *Elf) void { } } -pub fn globals(self: *LinkerDefined) []Symbol { - return self.symbols.items; -} - pub fn updateSymtabSize(self: *LinkerDefined, elf_file: *Elf) void { - for (self.globals(), self.symbols_resolver.items) |*global, resolv| { + for (self.symbols.items, self.symbols_resolver.items) |*global, resolv| { const ref = elf_file.resolver.get(resolv).?; const ref_sym = elf_file.symbol(ref) orelse continue; if (ref_sym.file(elf_file).?.index() != self.index) continue; @@ -324,7 +320,7 @@ pub fn updateSymtabSize(self: *LinkerDefined, elf_file: *Elf) void { } pub fn writeSymtab(self: *LinkerDefined, elf_file: *Elf) void { - for (self.globals(), self.symbols_resolver.items) |global, resolv| { + for (self.symbols.items, self.symbols_resolver.items) |global, resolv| { const ref = elf_file.resolver.get(resolv).?; const ref_sym = elf_file.symbol(ref) orelse continue; if (ref_sym.file(elf_file).?.index() != self.index) continue; diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index fa912c0119..feb70776d5 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -676,6 +676,29 @@ pub fn markEhFrameAtomsDead(self: *Object, elf_file: *Elf) void { } } +pub fn markImportsExports(self: *Object, elf_file: *Elf) void { + const first_global = self.first_global orelse return; + for (0..self.globals().len) |i| { + const idx = first_global + i; + const ref = self.resolveSymbol(@intCast(idx), elf_file); + const sym = elf_file.symbol(ref) orelse continue; + const file = sym.file(elf_file).?; + if (sym.version_index == elf.VER_NDX_LOCAL) continue; + const vis = @as(elf.STV, @enumFromInt(sym.elfSym(elf_file).st_other)); + if (vis == .HIDDEN) continue; + if (file == .shared_object and !sym.isAbs(elf_file)) { + sym.flags.import = true; + continue; + } + if (file.index() == self.index) { + sym.flags.@"export" = true; + if (elf_file.isEffectivelyDynLib() and vis != .PROTECTED) { + sym.flags.import = true; + } + } + } +} + pub fn checkDuplicates(self: *Object, dupes: anytype, elf_file: *Elf) error{OutOfMemory}!void { const first_global = self.first_global orelse return; for (0..self.globals().len) |i| { @@ -1169,14 +1192,14 @@ pub fn codeDecompressAlloc(self: *Object, elf_file: *Elf, atom_index: Atom.Index return data; } -pub fn locals(self: *Object) []Symbol { +fn locals(self: *Object) []Symbol { if (self.symbols.items.len == 0) return &[0]Symbol{}; assert(self.symbols.items.len >= self.symtab.items.len); const end = self.first_global orelse self.symtab.items.len; return self.symbols.items[0..end]; } -pub fn globals(self: *Object) []Symbol { +fn globals(self: *Object) []Symbol { if (self.symbols.items.len == 0) return &[0]Symbol{}; assert(self.symbols.items.len >= self.symtab.items.len); const start = self.first_global orelse self.symtab.items.len; diff --git a/src/link/Elf/SharedObject.zig b/src/link/Elf/SharedObject.zig index 245805e59e..aff07e4469 100644 --- a/src/link/Elf/SharedObject.zig +++ b/src/link/Elf/SharedObject.zig @@ -282,12 +282,18 @@ pub fn markLive(self: *SharedObject, elf_file: *Elf) void { } } -pub fn globals(self: *SharedObject) []Symbol { - return self.symbols.items; +pub fn markImportExports(self: *SharedObject, elf_file: *Elf) void { + for (0..self.symbols.items.len) |i| { + const ref = self.resolveSymbol(@intCast(i), elf_file); + const ref_sym = elf_file.symbol(ref) orelse continue; + const ref_file = ref_sym.file(self).?; + const vis = @as(elf.STV, @enumFromInt(ref_sym.elfSym(self).st_other)); + if (ref_file != .shared_object and vis != .HIDDEN) ref_sym.flags.@"export" = true; + } } pub fn updateSymtabSize(self: *SharedObject, elf_file: *Elf) void { - for (self.globals(), self.symbols_resolver.items) |*global, resolv| { + for (self.symbols.items, self.symbols_resolver.items) |*global, resolv| { const ref = elf_file.resolver.get(resolv).?; const ref_sym = elf_file.symbol(ref) orelse continue; if (ref_sym.file(elf_file).?.index() != self.index) continue; @@ -300,7 +306,7 @@ pub fn updateSymtabSize(self: *SharedObject, elf_file: *Elf) void { } pub fn writeSymtab(self: *SharedObject, elf_file: *Elf) void { - for (self.globals(), self.symbols_resolver.items) |global, resolv| { + for (self.symbols.items, self.symbols_resolver.items) |global, resolv| { const ref = elf_file.resolver.get(resolv).?; const ref_sym = elf_file.symbol(ref) orelse continue; if (ref_sym.file(elf_file).?.index() != self.index) continue; @@ -354,7 +360,7 @@ pub fn initSymbolAliases(self: *SharedObject, elf_file: *Elf) !void { const gpa = comp.gpa; var aliases = std.ArrayList(Symbol.Index).init(gpa); defer aliases.deinit(); - try aliases.ensureTotalCapacityPrecise(self.globals().len); + try aliases.ensureTotalCapacityPrecise(self.symbols.items.len); for (self.symbols_resolvers.items, 0..) |resolv, index| { const ref = elf_file.resolver.get(resolv).?; diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index 2c1ce0a104..ba5c8f4986 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -63,9 +63,7 @@ pub fn @"type"(symbol: Symbol, elf_file: *Elf) u4 { } pub fn name(symbol: Symbol, elf_file: *Elf) [:0]const u8 { - if (symbol.flags.global) return elf_file.strings.getAssumeExists(symbol.name_offset); - const file_ptr = symbol.file(elf_file).?; - return switch (file_ptr) { + return switch (symbol.file(elf_file).?) { inline else => |x| x.getString(symbol.name_offset), }; } @@ -87,9 +85,7 @@ pub fn file(symbol: Symbol, elf_file: *Elf) ?File { } pub fn elfSym(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym { - const file_ptr = symbol.file(elf_file).?; - return switch (file_ptr) { - .zig_object => |x| x.elfSym(symbol.esym_index).*, + return switch (symbol.file(elf_file).?) { inline else => |x| x.symtab.items[symbol.esym_index], }; } @@ -423,12 +419,6 @@ pub const Flags = packed struct { /// Whether this symbol is weak. weak: bool = false, - /// Whether the symbol has its name interned in global symbol - /// resolver table. - /// This happens for any symbol that is considered a global - /// symbol, but is not necessarily an import or export. - global: bool = false, - /// Whether the symbol makes into the output symtab. output_symtab: bool = false, diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 554e038d95..fb41cd2c97 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -8,9 +8,11 @@ data: std.ArrayListUnmanaged(u8) = .{}, path: []const u8, index: File.Index, -local_esyms: std.MultiArrayList(ElfSym) = .{}, -global_esyms: std.MultiArrayList(ElfSym) = .{}, +symtab: std.MultiArrayList(ElfSym) = .{}, strtab: StringTable = .{}, +symbols: std.ArrayListUnmanaged(Symbol) = .{}, +symbols_extra: std.ArrayListUnmanaged(u32) = .{}, +symbols_resolver: std.ArrayListUnmanaged(Elf.SymbolResolver.Index) = .{}, local_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, global_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, globals_lookup: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .{}, @@ -113,9 +115,11 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void { pub fn deinit(self: *ZigObject, allocator: Allocator) void { self.data.deinit(allocator); - self.local_esyms.deinit(allocator); - self.global_esyms.deinit(allocator); + self.symtab.deinit(allocator); self.strtab.deinit(allocator); + self.symbols.deinit(allocator); + self.symbols_extra.deinit(allocator); + self.symbols_resolver.deinit(allocator); self.local_symbols.deinit(allocator); self.global_symbols.deinit(allocator); self.globals_lookup.deinit(allocator); @@ -263,51 +267,73 @@ fn saveDebugSectionsSizes(self: *ZigObject, elf_file: *Elf) void { } } -pub fn addLocalEsym(self: *ZigObject, allocator: Allocator) !Symbol.Index { - try self.local_esyms.ensureUnusedCapacity(allocator, 1); - const index = @as(Symbol.Index, @intCast(self.local_esyms.addOneAssumeCapacity())); - var esym = ElfSym{ .elf_sym = Elf.null_sym }; - esym.elf_sym.st_info = elf.STB_LOCAL << 4; - self.local_esyms.set(index, esym); +fn newSymbol(self: *ZigObject, allocator: Allocator, name_off: u32, st_bind: u4) !Symbol.Index { + try self.symtab.ensureUnusedCapacity(allocator, 1); + try self.symbols.ensureUnusedCapacity(allocator, 1); + try self.symbols_extra.ensureUnusedCapacity(allocator, @sizeOf(Symbol.Extra)); + + const index = self.addSymbolAssumeCapacity(); + const sym = &self.symbols.items[index]; + sym.name_offset = name_off; + sym.extra = self.addSymbolExtraAssumeCapacity(.{}); + + const esym_idx: u32 = @intCast(self.symtab.addOneAssumeCapacity()); + const esym = ElfSym{ .elf_sym = .{ + .st_value = 0, + .st_name = name_off, + .st_info = @as(u8, @intCast(st_bind)) << 4, + .st_other = 0, + .st_size = 0, + .st_shndx = 0, + } }; + self.symtab.set(index, esym); + sym.esym_index = esym_idx; + return index; } -pub fn addGlobalEsym(self: *ZigObject, allocator: Allocator) !Symbol.Index { - try self.global_esyms.ensureUnusedCapacity(allocator, 1); - const index = @as(Symbol.Index, @intCast(self.global_esyms.addOneAssumeCapacity())); - var esym = ElfSym{ .elf_sym = Elf.null_sym }; - esym.elf_sym.st_info = elf.STB_GLOBAL << 4; - self.global_esyms.set(index, esym); - return index | global_symbol_bit; +fn newLocalSymbol(self: *ZigObject, allocator: Allocator, name_off: u32) !Symbol.Index { + try self.local_symbols.ensureUnusedCapacity(allocator, 1); + const fake_index: Symbol.Index = @intCast(self.local_symbols.items.len); + const index = try self.newSymbol(allocator, name_off, elf.STB_LOCAL); + self.local_symbols.appendAssumeCapacity(index); + return fake_index; } -pub fn newAtom(self: *ZigObject, elf_file: *Elf) !Symbol.Index { - const gpa = elf_file.base.comp.gpa; - const atom_index = try self.addAtom(gpa); - const symbol_index = try elf_file.addSymbol(); - const esym_index = try self.addLocalEsym(gpa); - - try self.atoms_indexes.append(gpa, atom_index); - try self.local_symbols.append(gpa, symbol_index); - - const symbol_ptr = elf_file.symbol(symbol_index); - symbol_ptr.file_index = self.index; - symbol_ptr.ref = .{ .index = atom_index, .file = self.index }; - symbol_ptr.extra_index = try elf_file.addSymbolExtra(.{}); +fn newGlobalSymbol(self: *ZigObject, allocator: Allocator, name_off: u32) !Symbol.Index { + try self.global_symbols.ensureUnusedCapacity(allocator, 1); + const fake_index: Symbol.Index = @intCast(self.global_symbols.items.len); + const index = try self.newSymbol(allocator, name_off, elf.STB_GLOBAL); + self.global_symbols.appendAssumeCapacity(index); + return fake_index | global_symbol_bit; +} - self.local_esyms.items(.shndx)[esym_index] = atom_index; - self.local_esyms.items(.elf_sym)[esym_index].st_shndx = SHN_ATOM; - symbol_ptr.esym_index = esym_index; +fn newAtom(self: *ZigObject, allocator: Allocator, name_off: u32) !Atom.Index { + try self.atoms.ensureUnusedCapacity(allocator, 1); + try self.atoms_extra.ensureUnusedCapacity(allocator, @sizeOf(Atom.Extra)); + try self.atoms_indexes.ensureUnusedCapacity(allocator, 1); + try self.relocs.ensureUnusedCapacity(allocator, 1); - // TODO I'm thinking that maybe we shouldn' set this value unless it's actually needed? - const relocs_index = @as(u32, @intCast(self.relocs.items.len)); - const relocs = try self.relocs.addOne(gpa); - relocs.* = .{}; + const index = self.addAtomAssumeCapacity(); + self.atoms_indexes.appendAssumeCapacity(index); + const atom_ptr = self.atom(index).?; + atom_ptr.name_offset = name_off; - const atom_ptr = self.atom(atom_index).?; + const relocs_index: u32 = @intCast(self.relocs.items.len); + self.relocs.addOneAssumeCapacity().* = .{}; atom_ptr.relocs_section_index = relocs_index; - return symbol_index; + return index; +} + +fn newSymbolWithAtom(self: *ZigObject, allocator: Allocator, name_off: u32) !Symbol.Index { + const atom_index = try self.newAtom(allocator, name_off); + const sym_index = try self.newLocalSymbol(allocator, name_off); + const sym = self.symbol(sym_index); + sym.ref = .{ .index = atom_index, .file = self.index }; + self.symtab.items(.shndx)[sym.esym_index] = atom_index; + self.symtab.items(.elf_sym)[sym.esym_index].st_shndx = SHN_ATOM; + return sym_index; } /// TODO actually create fake input shdrs and return that instead. @@ -322,48 +348,47 @@ pub fn inputShdr(self: *ZigObject, atom_index: Atom.Index, elf_file: *Elf) elf.E return shdr; } -pub fn resolveSymbols(self: *ZigObject, elf_file: *Elf) void { - for (self.globals(), 0..) |index, i| { - const esym_index = @as(Symbol.Index, @intCast(i)) | global_symbol_bit; - const esym = self.global_esyms.items(.elf_sym)[i]; - const shndx = self.global_esyms.items(.shndx)[i]; - - if (esym.st_shndx == elf.SHN_UNDEF) continue; +pub fn resolveSymbols(self: *ZigObject, elf_file: *Elf) !void { + const gpa = elf_file.base.comp.gpa; + for (self.global_symbols.items, 0..) |index, i| { + const global = &self.symbols.items[index]; + const esym = global.elfSym(elf_file); + const shndx = self.symtab.items(.shndx)[global.esym_index]; if (esym.st_shndx != elf.SHN_ABS and esym.st_shndx != elf.SHN_COMMON) { assert(esym.st_shndx == SHN_ATOM); const atom_ptr = self.atom(shndx) orelse continue; if (!atom_ptr.alive) continue; } - const global = elf_file.symbol(index); - if (self.asFile().symbolRank(esym, false) < global.symbolRank(elf_file)) { - const atom_index = switch (esym.st_shndx) { - elf.SHN_ABS, elf.SHN_COMMON => 0, - SHN_ATOM => shndx, - else => unreachable, - }; - global.value = @intCast(esym.st_value); - global.ref = .{ .index = atom_index, .file = self.index }; - global.esym_index = esym_index; - global.file_index = self.index; - global.version_index = elf_file.default_sym_version; - if (esym.st_bind() == elf.STB_WEAK) global.flags.weak = true; + const resolv = &self.symbols_resolver.items[i]; + const gop = try elf_file.resolver.getOrPut(gpa, .{ + .index = @intCast(i | global_symbol_bit), + .file = self.index, + }, elf_file); + if (!gop.found_existing) { + gop.ref.* = .{ .index = 0, .file = 0 }; + } + resolv.* = gop.index; + + if (esym.st_shndx == elf.SHN_UNDEF) continue; + if (elf_file.symbol(gop.ref.*) == null) { + gop.ref.* = .{ .index = @intCast(i | global_symbol_bit), .file = self.index }; + continue; + } + + if (self.asFile().symbolRank(esym, !self.alive) < elf_file.symbol(gop.ref.*).?.symbolRank(elf_file)) { + gop.ref.* = .{ .index = @intCast(i | global_symbol_bit), .file = self.index }; } } } -pub fn claimUnresolved(self: ZigObject, elf_file: *Elf) void { - for (self.globals(), 0..) |index, i| { - const esym_index = @as(Symbol.Index, @intCast(i)) | global_symbol_bit; - const esym = self.global_esyms.items(.elf_sym)[i]; - +pub fn claimUnresolved(self: *ZigObject, elf_file: *Elf) void { + for (self.global_symbols.items, 0..) |index, i| { + const global = &self.symbols.items[index]; + const esym = self.symtab.items(.elf_sym)[index]; if (esym.st_shndx != elf.SHN_UNDEF) continue; - - const global = elf_file.symbol(index); - if (global.file(elf_file)) |_| { - if (global.elfSym(elf_file).st_shndx != elf.SHN_UNDEF) continue; - } + if (elf_file.symbol(self.resolveSymbol(@intCast(i | global_symbol_bit), elf_file)) != null) continue; const is_import = blk: { if (!elf_file.isEffectivelyDynLib()) break :blk false; @@ -374,29 +399,36 @@ pub fn claimUnresolved(self: ZigObject, elf_file: *Elf) void { global.value = 0; global.ref = .{ .index = 0, .file = 0 }; - global.esym_index = esym_index; + global.esym_index = @intCast(index); global.file_index = self.index; global.version_index = if (is_import) elf.VER_NDX_LOCAL else elf_file.default_sym_version; global.flags.import = is_import; + + const idx = self.symbols_resolver.items[i]; + elf_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i | global_symbol_bit), .file = self.index }; } } pub fn claimUnresolvedObject(self: ZigObject, elf_file: *Elf) void { - for (self.globals(), 0..) |index, i| { - const esym_index = @as(Symbol.Index, @intCast(i)) | global_symbol_bit; - const esym = self.global_esyms.items(.elf_sym)[i]; - + for (self.global_symbols.items, 0..) |index, i| { + const global = &self.symbols.items[index]; + const esym = self.symtab.items(.elf_sym)[index]; if (esym.st_shndx != elf.SHN_UNDEF) continue; + if (elf_file.symbol(self.resolveSymbol(@intCast(i | global_symbol_bit), elf_file)) != null) 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; - } + // TODO: audit this + // 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.ref = .{ .index = 0, .file = 0 }; - global.esym_index = esym_index; + global.esym_index = @intCast(index); global.file_index = self.index; + + const idx = self.symbols_resolver.items[i]; + elf_file.resolver.items[idx - 1] = .{ .index = @intCast(i | global_symbol_bit), .file = self.index }; } } @@ -419,12 +451,14 @@ pub fn scanRelocs(self: *ZigObject, elf_file: *Elf, undefs: anytype) !void { } pub fn markLive(self: *ZigObject, elf_file: *Elf) void { - for (self.globals(), 0..) |index, i| { - const esym = self.global_esyms.items(.elf_sym)[i]; + for (self.global_symbols.items, 0..) |index, i| { + const global = self.symbols.items[index]; + const esym = self.symtab.items(.elf_sym)[index]; if (esym.st_bind() == elf.STB_WEAK) continue; - const global = elf_file.symbol(index); - const file = global.file(elf_file) orelse continue; + const ref = self.resolveSymbol(@intCast(i | global_symbol_bit), elf_file); + const sym = elf_file.symbol(ref) orelse continue; + const file = sym.file(elf_file).?; const should_keep = esym.st_shndx == elf.SHN_UNDEF or (esym.st_shndx == elf.SHN_COMMON and global.elfSym(elf_file).st_shndx != elf.SHN_COMMON); if (should_keep and !file.isAlive()) { @@ -434,14 +468,36 @@ pub fn markLive(self: *ZigObject, elf_file: *Elf) void { } } -pub fn checkDuplicates(self: *ZigObject, dupes: anytype, elf_file: *Elf) error{OutOfMemory}!void { - for (self.globals(), 0..) |index, i| { - const esym = self.global_esyms.items(.elf_sym)[i]; - const shndx = self.global_esyms.items(.shndx)[i]; - const global = elf_file.symbol(index); - const global_file = global.file(elf_file) orelse continue; +pub fn markImportsExports(self: *Object, elf_file: *Elf) void { + for (0..self.global_symbols.items.len) |i| { + const ref = self.resolveSymbol(@intCast(i | global_symbol_bit), elf_file); + const sym = elf_file.symbol(ref) orelse continue; + const file = sym.file(elf_file).?; + if (sym.version_index == elf.VER_NDX_LOCAL) continue; + const vis = @as(elf.STV, @enumFromInt(sym.elfSym(elf_file).st_other)); + if (vis == .HIDDEN) continue; + if (file == .shared_object and !sym.isAbs(elf_file)) { + sym.flags.import = true; + continue; + } + if (file.index() == self.index) { + sym.flags.@"export" = true; + if (elf_file.isEffectivelyDynLib() and vis != .PROTECTED) { + sym.flags.import = true; + } + } + } +} - if (self.index == global_file.index() or +pub fn checkDuplicates(self: *ZigObject, dupes: anytype, elf_file: *Elf) error{OutOfMemory}!void { + for (self.global_symbols.items, 0..) |index, i| { + const esym = self.symtab.items(.elf_sym)[index]; + const shndx = self.symtab.items(.shndx)[index]; + const ref = self.resolveSymbol(@intCast(i | global_symbol_bit), elf_file); + const ref_sym = elf_file.symbol(ref) orelse continue; + const ref_file = ref_sym.file(elf_file).?; + + if (self.index == ref_file.index() or esym.st_shndx == elf.SHN_UNDEF or esym.st_bind() == elf.STB_WEAK or esym.st_shndx == elf.SHN_COMMON) continue; @@ -451,7 +507,7 @@ pub fn checkDuplicates(self: *ZigObject, dupes: anytype, elf_file: *Elf) error{O if (!atom_ptr.alive) continue; } - const gop = try dupes.getOrPut(index); + const gop = try dupes.getOrPut(self.symbols_resolver.items[i]); if (!gop.found_existing) { gop.value_ptr.* = .{}; } @@ -483,12 +539,13 @@ pub fn readFileContents(self: *ZigObject, elf_file: *Elf) !void { pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) error{OutOfMemory}!void { const gpa = elf_file.base.comp.gpa; - try ar_symtab.symtab.ensureUnusedCapacity(gpa, self.globals().len); + try ar_symtab.symtab.ensureUnusedCapacity(gpa, self.global_symbols.items.len); - for (self.globals()) |global_index| { - const global = elf_file.symbol(global_index); - const file_ptr = global.file(elf_file).?; - assert(file_ptr.index() == self.index); + for (self.global_symbols.items, 0..) |index, i| { + const global = self.symbols.items[index]; + const ref = self.resolveSymbol(@intCast(i | global_symbol_bit), elf_file); + const sym = elf_file.symbol(ref).?; + assert(sym.file(elf_file).?.index() == self.index); if (global.outputShndx(elf_file) == null) continue; const off = try ar_symtab.strtab.insert(gpa, global.name(elf_file)); @@ -530,33 +587,9 @@ pub fn addAtomsToRelaSections(self: *ZigObject, elf_file: *Elf) !void { } } -inline fn isGlobal(index: Symbol.Index) bool { - return index & global_symbol_bit != 0; -} - -pub fn symbol(self: ZigObject, index: Symbol.Index) Symbol.Index { - const actual_index = index & symbol_mask; - if (isGlobal(index)) return self.globals()[actual_index]; - return self.locals()[actual_index]; -} - -pub fn elfSym(self: *ZigObject, index: Symbol.Index) *elf.Elf64_Sym { - const actual_index = index & symbol_mask; - if (isGlobal(index)) return &self.global_esyms.items(.elf_sym)[actual_index]; - return &self.local_esyms.items(.elf_sym)[actual_index]; -} - -pub fn locals(self: ZigObject) []const Symbol.Index { - return self.local_symbols.items; -} - -pub fn globals(self: ZigObject) []const Symbol.Index { - return self.global_symbols.items; -} - pub fn updateSymtabSize(self: *ZigObject, elf_file: *Elf) !void { - for (self.locals()) |local_index| { - const local = elf_file.symbol(local_index); + for (self.local_symbols.items) |index| { + const local = &self.symbols.items[index]; if (local.atom(elf_file)) |atom_ptr| if (!atom_ptr.alive) continue; const esym = local.elfSym(elf_file); switch (esym.st_type()) { @@ -564,22 +597,23 @@ pub fn updateSymtabSize(self: *ZigObject, elf_file: *Elf) !void { else => {}, } local.flags.output_symtab = true; - try local.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file); + local.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file); self.output_symtab_ctx.nlocals += 1; self.output_symtab_ctx.strsize += @as(u32, @intCast(local.name(elf_file).len)) + 1; } - for (self.globals()) |global_index| { - const global = elf_file.symbol(global_index); - const file_ptr = global.file(elf_file) orelse continue; - if (file_ptr.index() != self.index) continue; + for (self.global_symbols.items, self.symbols_resolver.items) |index, resolv| { + const global = &self.symbols.items[index]; + const ref = elf_file.resolver.items[resolv]; + const ref_sym = elf_file.symbol(ref) orelse continue; + if (ref_sym.file(elf_file).?.index() != self.index) continue; if (global.atom(elf_file)) |atom_ptr| if (!atom_ptr.alive) continue; global.flags.output_symtab = true; if (global.isLocal(elf_file)) { - try global.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file); + global.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file); self.output_symtab_ctx.nlocals += 1; } else { - try global.addExtra(.{ .symtab = self.output_symtab_ctx.nglobals }, elf_file); + global.addExtra(.{ .symtab = self.output_symtab_ctx.nglobals }, elf_file); self.output_symtab_ctx.nglobals += 1; } self.output_symtab_ctx.strsize += @as(u32, @intCast(global.name(elf_file).len)) + 1; @@ -587,8 +621,8 @@ pub fn updateSymtabSize(self: *ZigObject, elf_file: *Elf) !void { } pub fn writeSymtab(self: ZigObject, elf_file: *Elf) void { - for (self.locals()) |local_index| { - const local = elf_file.symbol(local_index); + for (self.local_symbols.items) |index| { + const local = &self.symbols.items[index]; 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); @@ -597,10 +631,11 @@ pub fn writeSymtab(self: ZigObject, elf_file: *Elf) void { local.setOutputSym(elf_file, out_sym); } - for (self.globals()) |global_index| { - const global = elf_file.symbol(global_index); - const file_ptr = global.file(elf_file) orelse continue; - if (file_ptr.index() != self.index) continue; + for (self.global_symbols.items, self.symbols_resolver.items) |index, resolv| { + const global = self.symbols.items[index]; + const ref = elf_file.resolver.items[resolv]; + const ref_sym = elf_file.symbol(ref) orelse continue; + if (ref_sym.file(elf_file).?.index() != self.index) 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)); @@ -611,10 +646,6 @@ pub fn writeSymtab(self: ZigObject, elf_file: *Elf) void { } } -pub fn asFile(self: *ZigObject) File { - return .{ .zig_object = self }; -} - /// Returns atom's code. /// Caller owns the memory. pub fn codeAlloc(self: *ZigObject, elf_file: *Elf, atom_index: Atom.Index) ![]u8 { @@ -755,8 +786,8 @@ pub fn getOrCreateMetadataForLazySymbol( }; switch (metadata.state.*) { .unused => { - const symbol_index = try self.newAtom(elf_file); - const sym = elf_file.symbol(symbol_index); + const symbol_index = try self.newSymbolWithAtom(gpa, 0); + const sym = self.symbol(symbol_index); sym.flags.needs_zig_got = true; metadata.symbol_index.* = symbol_index; }, @@ -817,10 +848,10 @@ pub fn getOrCreateMetadataForDecl( const gop = try self.decls.getOrPut(gpa, decl_index); if (!gop.found_existing) { const any_non_single_threaded = elf_file.base.comp.config.any_non_single_threaded; - const symbol_index = try self.newAtom(elf_file); + const symbol_index = try self.newSymbolWithAtom(gpa, 0); const mod = elf_file.base.comp.module.?; const decl = mod.declPtr(decl_index); - const sym = elf_file.symbol(symbol_index); + const sym = self.symbol(symbol_index); if (decl.getOwnedVariable(mod)) |variable| { if (variable.is_threadlocal and any_non_single_threaded) { sym.flags.is_tls = true; @@ -1064,7 +1095,7 @@ pub fn updateFunc( const sym_index = try self.getOrCreateMetadataForDecl(elf_file, decl_index); self.freeUnnamedConsts(elf_file, decl_index); - elf_file.symbol(sym_index).atom(elf_file).?.freeRelocs(elf_file); + self.symbol(sym_index).atom(elf_file).?.freeRelocs(elf_file); var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); @@ -1096,7 +1127,7 @@ pub fn updateFunc( try self.updateDeclCode(elf_file, pt, decl_index, sym_index, shndx, code, elf.STT_FUNC); if (decl_state) |*ds| { - const sym = elf_file.symbol(sym_index); + const sym = self.symbol(sym_index); try self.dwarf.?.commitDeclState( pt, decl_index, @@ -1130,13 +1161,13 @@ pub fn updateDecl( const variable = decl.getOwnedVariable(mod).?; const name = decl.name.toSlice(&mod.intern_pool); const lib_name = variable.lib_name.toSlice(&mod.intern_pool); - const esym_index = try self.getGlobalSymbol(elf_file, name, lib_name); - elf_file.symbol(self.symbol(esym_index)).flags.needs_got = true; + const sym_index = try self.getGlobalSymbol(elf_file, name, lib_name); + self.symbol(sym_index).flags.needs_got = true; return; } const sym_index = try self.getOrCreateMetadataForDecl(elf_file, decl_index); - elf_file.symbol(sym_index).atom(elf_file).?.freeRelocs(elf_file); + self.symbol(sym_index).atom(elf_file).?.freeRelocs(elf_file); const gpa = elf_file.base.comp.gpa; var code_buffer = std.ArrayList(u8).init(gpa); @@ -1174,7 +1205,7 @@ pub fn updateDecl( try self.updateDeclCode(elf_file, pt, decl_index, sym_index, shndx, code, elf.STT_OBJECT); if (decl_state) |*ds| { - const sym = elf_file.symbol(sym_index); + const sym = self.symbol(sym_index); try self.dwarf.?.commitDeclState( pt, decl_index, @@ -1323,7 +1354,8 @@ fn lowerConst( var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); - const sym_index = try self.newAtom(elf_file); + const name_off = try self.addString(gpa, name); + const sym_index = try self.newSymbolWithAtom(gpa, name_off); const res = try codegen.generateSymbol( &elf_file.base, @@ -1339,27 +1371,19 @@ fn lowerConst( .fail => |em| return .{ .fail = em }, }; - const local_sym = elf_file.symbol(sym_index); - const name_str_index = try self.strtab.insert(gpa, name); - local_sym.name_offset = name_str_index; - const local_esym = &self.local_esyms.items(.elf_sym)[local_sym.esym_index]; - local_esym.st_name = name_str_index; + const local_sym = self.symbol(sym_index); + const local_esym = local_sym.elfSym(elf_file); local_esym.st_info |= elf.STT_OBJECT; local_esym.st_size = code.len; const atom_ptr = local_sym.atom(elf_file).?; atom_ptr.alive = true; - atom_ptr.name_offset = name_str_index; atom_ptr.alignment = required_alignment; atom_ptr.size = code.len; atom_ptr.output_section_index = output_section_index; try atom_ptr.allocate(elf_file); - // TODO rename and re-audit this method errdefer self.freeDeclMetadata(elf_file, sym_index); - local_sym.value = 0; - local_esym.st_value = 0; - const shdr = elf_file.shdrs.items[output_section_index]; const file_offset = shdr.sh_offset + @as(u64, @intCast(atom_ptr.value)); try elf_file.base.file.?.pwriteAll(code, file_offset); @@ -1401,9 +1425,9 @@ pub fn updateExports( }, }; const sym_index = metadata.symbol_index; - const esym_index = elf_file.symbol(sym_index).esym_index; - const esym = self.local_esyms.items(.elf_sym)[esym_index]; - const esym_shndx = self.local_esyms.items(.shndx)[esym_index]; + const esym_index = self.symbol(sym_index).esym_index; + const esym = self.symtab.items(.elf_sym)[esym_index]; + const esym_shndx = self.symtab.items(.shndx)[esym_index]; for (export_indices) |export_idx| { const exp = mod.all_exports.items[export_idx]; @@ -1437,22 +1461,27 @@ pub fn updateExports( const stt_bits: u8 = @as(u4, @truncate(esym.st_info)); const exp_name = exp.opts.name.toSlice(&mod.intern_pool); const name_off = try self.strtab.insert(gpa, exp_name); - const global_esym_index = if (metadata.@"export"(self, exp_name)) |exp_index| + const global_sym_index = if (metadata.@"export"(self, exp_name)) |exp_index| exp_index.* else blk: { - const global_esym_index = try self.getGlobalSymbol(elf_file, exp_name, null); - try metadata.exports.append(gpa, global_esym_index); - break :blk global_esym_index; + const global_sym_index = try self.getGlobalSymbol(elf_file, exp_name, null); + try metadata.exports.append(gpa, global_sym_index); + break :blk global_sym_index; }; - const actual_esym_index = global_esym_index & symbol_mask; - const global_esym = &self.global_esyms.items(.elf_sym)[actual_esym_index]; - global_esym.st_value = @intCast(elf_file.symbol(sym_index).value); + const value = self.symbol(sym_index).value; + const global_sym = self.symbol(global_sym_index); + global_sym.value = value; + global_sym.flags.weak = exp.opts.linkage == .weak; + global_sym.version_index = elf_file.default_version_index; + global_sym.ref = .{ .index = esym_shndx, .file = self.index }; + const global_esym = global_sym.elfSym(elf_file); + global_esym.st_value = @intCast(value); global_esym.st_shndx = esym.st_shndx; global_esym.st_info = (stb_bits << 4) | stt_bits; global_esym.st_name = name_off; global_esym.st_size = esym.st_size; - self.global_esyms.items(.shndx)[actual_esym_index] = esym_shndx; + self.symtab.items(.shndx)[global_sym.esym_index] = esym_shndx; } } @@ -1506,16 +1535,19 @@ pub fn getGlobalSymbol(self: *ZigObject, elf_file: *Elf, name: []const u8, lib_n const off = try self.strtab.insert(gpa, name); const lookup_gop = try self.globals_lookup.getOrPut(gpa, off); if (!lookup_gop.found_existing) { - const esym_index = try self.addGlobalEsym(gpa); - const esym = self.elfSym(esym_index); - esym.st_name = off; - lookup_gop.value_ptr.* = esym_index; - const gop = try elf_file.getOrPutGlobal(name); - try self.global_symbols.append(gpa, gop.index); + lookup_gop.value_ptr.* = try self.newSymbol(gpa, off); } return lookup_gop.value_ptr.*; } +pub fn asFile(self: *ZigObject) File { + return .{ .zig_object = self }; +} + +fn addString(self: *ZigObject, allocator: Allocator, string: []const u8) !u32 { + return self.strtab.insert(allocator, string); +} + pub fn getString(self: ZigObject, off: u32) [:0]const u8 { return self.strtab.getAssumeExists(off); } @@ -1586,6 +1618,73 @@ pub fn setAtomExtra(self: *ZigObject, index: u32, extra: Atom.Extra) void { } } +inline fn isGlobal(index: Symbol.Index) bool { + return index & global_symbol_bit != 0; +} + +pub fn symbol(self: *ZigObject, index: Symbol.Index) *Symbol { + const actual_index = index & symbol_mask; + if (isGlobal(index)) return &self.symbols.items[self.global_symbols.items[actual_index]]; + return &self.symbols.items[self.local_symbols.items[actual_index]]; +} + +pub fn resolveSymbol(self: ZigObject, index: Symbol.Index, elf_file: *Elf) Elf.Ref { + if (isGlobal(index)) { + const resolv = self.symbols_resolver.items[index & symbol_mask]; + return elf_file.resolver.get(resolv).?; + } + return .{ .index = index, .file = self.index }; +} + +pub fn addSymbol(self: *ZigObject, allocator: Allocator) !Symbol.Index { + try self.symbols.ensureUnusedCapacity(allocator, 1); + const index: Symbol.Index = @intCast(self.symbols.items.len); + self.symbols.appendAssumeCapacity(.{ .file_index = self.index }); + return index; +} + +pub fn addSymbolExtra(self: *ZigObject, allocator: Allocator, extra: Symbol.Extra) !u32 { + const fields = @typeInfo(Symbol.Extra).Struct.fields; + try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len); + return self.addSymbolExtraAssumeCapacity(extra); +} + +pub fn addSymbolExtraAssumeCapacity(self: *ZigObject, extra: Symbol.Extra) u32 { + const index = @as(u32, @intCast(self.symbols_extra.items.len)); + const fields = @typeInfo(Symbol.Extra).Struct.fields; + inline for (fields) |field| { + self.symbols_extra.appendAssumeCapacity(switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }); + } + return index; +} + +pub fn symbolExtra(self: *ZigObject, index: u32) Symbol.Extra { + const fields = @typeInfo(Symbol.Extra).Struct.fields; + var i: usize = index; + var result: Symbol.Extra = undefined; + inline for (fields) |field| { + @field(result, field.name) = switch (field.type) { + u32 => self.symbols_extra.items[i], + else => @compileError("bad field type"), + }; + i += 1; + } + return result; +} + +pub fn setSymbolExtra(self: *ZigObject, index: u32, extra: Symbol.Extra) void { + const fields = @typeInfo(Symbol.Extra).Struct.fields; + inline for (fields, 0..) |field, i| { + self.symbols_extra.items[index + i] = switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }; + } +} + pub fn fmtSymtab(self: *ZigObject, elf_file: *Elf) std.fmt.Formatter(formatSymtab) { return .{ .data = .{ .self = self, @@ -1658,9 +1757,9 @@ const DeclMetadata = struct { /// A list of all exports aliases of this Decl. exports: std.ArrayListUnmanaged(Symbol.Index) = .{}, - fn @"export"(m: DeclMetadata, zig_object: *ZigObject, name: []const u8) ?*u32 { + fn @"export"(m: DeclMetadata, zo: *ZigObject, name: []const u8) ?*u32 { for (m.exports.items) |*exp| { - const exp_name = zig_object.getString(zig_object.elfSym(exp.*).st_name); + const exp_name = zo.getString(zo.symbol(exp.*).name_off); if (mem.eql(u8, name, exp_name)) return exp; } return null; diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig index 346f83d10b..9c7cd27626 100644 --- a/src/link/Elf/file.zig +++ b/src/link/Elf/file.zig @@ -153,19 +153,6 @@ pub const File = union(enum) { }; } - pub fn locals(file: File) []const Symbol.Index { - return switch (file) { - .linker_defined, .shared_object => &[0]Symbol.Index{}, - inline else => |x| x.locals(), - }; - } - - pub fn globals(file: File) []const Symbol.Index { - return switch (file) { - inline else => |x| x.globals(), - }; - } - pub fn getString(file: File, off: u32) [:0]const u8 { return switch (file) { inline else => |x| x.getString(off), |
