diff --git a/src/link/Elf.zig b/src/link/Elf.zig index dbf0756edd..3893dc947d 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1043,7 +1043,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node } } - try self.writeSymbols(); + try self.updateSymtabSize(); + try self.writeSymtab(); if (build_options.enable_logging) { state_log.debug("{}", .{self.dumpState()}); @@ -2662,14 +2663,7 @@ pub fn deleteDeclExport( assert(self.resolver.fetchSwapRemove(sym.name_offset) != null); // TODO don't delete it if it's not dominant sym.* = .{}; // TODO free list for esym! - esym.* = .{ - .st_name = 0, - .st_info = 0, - .st_other = 0, - .st_shndx = 0, - .st_value = 0, - .st_size = 0, - }; + esym.* = null_sym; self.symbols_free_list.append(gpa, sym_index.*) catch {}; sym_index.* = 0; } @@ -2773,19 +2767,20 @@ fn writeOffsetTableEntry(self: *Elf, index: @TypeOf(self.got_table).Index) !void } } -fn elf32SymFromSym(sym: elf.Elf64_Sym, out: *elf.Elf32_Sym) void { - out.* = .{ - .st_name = sym.st_name, - .st_value = @as(u32, @intCast(sym.st_value)), - .st_size = @as(u32, @intCast(sym.st_size)), - .st_info = sym.st_info, - .st_other = sym.st_other, - .st_shndx = sym.st_shndx, - }; -} +fn updateSymtabSize(self: *Elf) !void { + var sizes = SymtabSize{}; + + if (self.zig_module_index) |index| { + const zig_module = self.file(index).?.zig_module; + zig_module.updateSymtabSize(self); + sizes.nlocals += zig_module.output_symtab_size.nlocals; + sizes.nglobals += zig_module.output_symtab_size.nglobals; + } + + const shdr = &self.sections.items(.shdr)[self.symtab_section_index.?]; + shdr.sh_info = sizes.nlocals + 1; + self.markDirty(self.symtab_section_index.?, null); -fn writeSymbols(self: *Elf) !void { - const gpa = self.base.allocator; const sym_size: u64 = switch (self.ptr_width) { .p32 => @sizeOf(elf.Elf32_Sym), .p64 => @sizeOf(elf.Elf64_Sym), @@ -2794,59 +2789,61 @@ fn writeSymbols(self: *Elf) !void { .p32 => @alignOf(elf.Elf32_Sym), .p64 => @alignOf(elf.Elf64_Sym), }; - const zig_module = self.file(self.zig_module_index.?).?.zig_module; - - const shdr = &self.sections.items(.shdr)[self.symtab_section_index.?]; - shdr.sh_info = @intCast(zig_module.locals().len); - self.markDirty(self.symtab_section_index.?, null); - - const nsyms = zig_module.locals().len + zig_module.globals().len; - const needed_size = nsyms * sym_size; + const needed_size = (sizes.nlocals + sizes.nglobals + 1) * sym_size; try self.growNonAllocSection(self.symtab_section_index.?, needed_size, sym_align, true); +} + +fn writeSymtab(self: *Elf) !void { + const gpa = self.base.allocator; + const shdr = &self.sections.items(.shdr)[self.symtab_section_index.?]; + const sym_size: u64 = switch (self.ptr_width) { + .p32 => @sizeOf(elf.Elf32_Sym), + .p64 => @sizeOf(elf.Elf64_Sym), + }; + const nsyms = @divExact(shdr.sh_size, sym_size); + + log.debug("writing {d} symbols at 0x{x}", .{ nsyms, shdr.sh_offset }); + + const symtab = try gpa.alloc(elf.Elf64_Sym, nsyms); + defer gpa.free(symtab); + + var ctx: struct { ilocal: usize, iglobal: usize, symtab: []elf.Elf64_Sym } = .{ + .ilocal = 1, + .iglobal = shdr.sh_info, + .symtab = symtab, + }; + + if (self.zig_module_index) |index| { + const zig_module = self.file(index).?.zig_module; + zig_module.writeSymtab(self, ctx); + ctx.ilocal += zig_module.output_symtab_size.nlocals; + ctx.iglobal += zig_module.output_symtab_size.nglobals; + } const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian(); - log.debug("writing {d} symbols at 0x{x}", .{ nsyms, shdr.sh_offset }); switch (self.ptr_width) { .p32 => { - const buf = try gpa.alloc(elf.Elf32_Sym, nsyms); + const buf = try gpa.alloc(elf.Elf32_Sym, symtab.len); defer gpa.free(buf); - for (buf[0..zig_module.locals().len], zig_module.locals()) |*sym, local_index| { - const local = self.symbol(local_index); - elf32SymFromSym(local.sourceSymbol(self).*, sym); - if (foreign_endian) { - mem.byteSwapAllFields(elf.Elf32_Sym, sym); - } - } - - for (buf[zig_module.locals().len..], zig_module.globals()) |*sym, global_index| { - const global = self.symbol(global_index); - elf32SymFromSym(global.sourceSymbol(self).*, sym); - if (foreign_endian) { - mem.byteSwapAllFields(elf.Elf32_Sym, sym); - } + for (buf, symtab) |*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), shdr.sh_offset); }, .p64 => { - const buf = try gpa.alloc(elf.Elf64_Sym, nsyms); - defer gpa.free(buf); - for (buf[0..zig_module.locals().len], zig_module.locals()) |*sym, local_index| { - const local = self.symbol(local_index); - sym.* = local.sourceSymbol(self).*; - if (foreign_endian) { - mem.byteSwapAllFields(elf.Elf64_Sym, sym); - } + if (foreign_endian) { + for (symtab) |*sym| mem.byteSwapAllFields(elf.Elf64_Sym, sym); } - - for (buf[zig_module.locals().len..], zig_module.globals()) |*sym, global_index| { - const global = self.symbol(global_index); - sym.* = global.sourceSymbol(self).*; - if (foreign_endian) { - mem.byteSwapAllFields(elf.Elf64_Sym, sym); - } - } - try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), shdr.sh_offset); + try self.base.file.?.pwriteAll(mem.sliceAsBytes(symtab), shdr.sh_offset); }, } } @@ -3282,7 +3279,19 @@ const DeclMetadata = struct { } }; -const Elf = @This(); +pub const SymtabSize = struct { + nlocals: u32 = 0, + nglobals: u32 = 0, +}; + +pub const null_sym = elf.Elf64_Sym{ + .st_name = 0, + .st_info = 0, + .st_other = 0, + .st_shndx = 0, + .st_value = 0, + .st_size = 0, +}; const std = @import("std"); const build_options = @import("build_options"); @@ -3309,6 +3318,7 @@ pub const Atom = @import("Elf/Atom.zig"); const Cache = std.Build.Cache; const Compilation = @import("../Compilation.zig"); const Dwarf = @import("Dwarf.zig"); +const Elf = @This(); const File = @import("Elf/file.zig").File; const LinkerDefined = @import("Elf/LinkerDefined.zig"); const Liveness = @import("../Liveness.zig"); diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index 0418aa59db..33b159e700 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -35,7 +35,8 @@ extra_index: u32 = 0, pub fn isAbs(symbol: Symbol, elf_file: *Elf) bool { const file_ptr = symbol.file(elf_file).?; // if (file_ptr == .shared) return symbol.sourceSymbol(elf_file).st_shndx == elf.SHN_ABS; - return !symbol.flags.import and symbol.atom(elf_file) == null and symbol.output_section_index == 0 and file_ptr != .linker_defined and file_ptr != .zig_module; + return !symbol.flags.import and symbol.atom(elf_file) == null and symbol.output_section_index == 0 and + file_ptr != .linker_defined and file_ptr != .zig_module; } pub fn isLocal(symbol: Symbol) bool { @@ -169,7 +170,7 @@ pub fn setExtra(symbol: Symbol, extras: Extra, elf_file: *Elf) void { elf_file.setSymbolExtra(symbol.extra_index, extras); } -pub fn asElfSym(symbol: Symbol, st_name: u32, elf_file: *Elf) elf.Elf64_Sym { +pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void { const file_ptr = symbol.file(elf_file).?; const s_sym = symbol.sourceSymbol(elf_file); const st_type = symbol.type(elf_file); @@ -184,7 +185,7 @@ pub fn asElfSym(symbol: Symbol, st_name: u32, elf_file: *Elf) elf.Elf64_Sym { // if (file_ptr == .shared or s_sym.st_shndx == elf.SHN_UNDEF) break :blk elf.SHN_UNDEF; if (symbol.atom(elf_file) == null and file_ptr != .linker_defined and file_ptr != .zig_module) break :blk elf.SHN_ABS; - break :blk symbol.shndx; + break :blk symbol.output_section_index; }; const st_value = blk: { // if (symbol.flags.copy_rel) break :blk symbol.address(.{}, elf_file); @@ -197,8 +198,8 @@ pub fn asElfSym(symbol: Symbol, st_name: u32, elf_file: *Elf) elf.Elf64_Sym { // if (Elf.shdrIsTls(shdr)) break :blk symbol.value - elf_file.getTlsAddress(); break :blk symbol.value; }; - return elf.Elf64_Sym{ - .st_name = st_name, + out.* = .{ + .st_name = symbol.name_offset, .st_info = (st_bind << 4) | st_type, .st_other = s_sym.st_other, .st_shndx = st_shndx, @@ -343,9 +344,9 @@ pub const Extra = struct { pub const Index = u32; -const std = @import("std"); const assert = std.debug.assert; const elf = std.elf; +const std = @import("std"); const Atom = @import("Atom.zig"); const Elf = @import("../Elf.zig"); diff --git a/src/link/Elf/ZigModule.zig b/src/link/Elf/ZigModule.zig index 6cf918c8a1..0ac9d05b81 100644 --- a/src/link/Elf/ZigModule.zig +++ b/src/link/Elf/ZigModule.zig @@ -10,7 +10,7 @@ atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, alive: bool = true, -// output_symtab_size: Elf.SymtabSize = .{}, +output_symtab_size: Elf.SymtabSize = .{}, pub fn deinit(self: *ZigModule, allocator: Allocator) void { self.elf_local_symbols.deinit(allocator); @@ -36,14 +36,9 @@ pub fn createAtom(self: *ZigModule, output_section_index: u16, elf_file: *Elf) ! symbol_ptr.esym_index = @as(Symbol.Index, @intCast(self.elf_local_symbols.items.len)); const local_esym = try self.elf_local_symbols.addOne(gpa); - local_esym.* = .{ - .st_name = 0, - .st_info = elf.STB_LOCAL << 4, - .st_other = 0, - .st_shndx = output_section_index, - .st_value = 0, - .st_size = 0, - }; + local_esym.* = Elf.null_sym; + local_esym.st_info = elf.STB_LOCAL << 4; + local_esym.st_shndx = output_section_index; try self.atoms.append(gpa, atom_index); try self.local_symbols.putNoClobber(gpa, symbol_index, {}); @@ -57,22 +52,73 @@ pub fn addGlobal(self: *ZigModule, name: [:0]const u8, elf_file: *Elf) !Symbol.I try self.global_symbols.ensureUnusedCapacity(gpa, 1); const off = try elf_file.strtab.insert(gpa, name); const esym_index = @as(Symbol.Index, @intCast(self.elf_global_symbols.items.len)); - self.elf_global_symbols.appendAssumeCapacity(.{ - .st_name = off, - .st_info = elf.STB_GLOBAL << 4, - .st_other = 0, - .st_shndx = 0, - .st_value = 0, - .st_size = 0, - }); + const esym = self.elf_global_symbols.addOneAssumeCapacity(); + esym.* = Elf.null_sym; + esym.st_name = off; + esym.st_info = elf.STB_GLOBAL << 4; const gop = try elf_file.getOrPutGlobal(off); const sym = elf_file.symbol(gop.index); sym.file_index = self.index; sym.esym_index = esym_index; + sym.flags.@"export" = true; self.global_symbols.putAssumeCapacityNoClobber(gop.index, {}); return gop.index; } +pub fn updateSymtabSize(self: *ZigModule, elf_file: *Elf) void { + for (self.locals()) |local_index| { + const local = elf_file.symbol(local_index); + const esym = self.sourceSymbol(local_index, elf_file); + switch (esym.st_type()) { + elf.STT_SECTION, elf.STT_NOTYPE => { + local.flags.output_symtab = false; + continue; + }, + else => {}, + } + local.flags.output_symtab = true; + self.output_symtab_size.nlocals += 1; + } + + for (self.globals()) |global_index| { + const global = elf_file.symbol(global_index); + if (global.file(elf_file)) |file| if (file.index() != self.index) { + global.flags.output_symtab = false; + continue; + }; + global.flags.output_symtab = true; + if (global.isLocal()) { + self.output_symtab_size.nlocals += 1; + } else { + self.output_symtab_size.nglobals += 1; + } + } +} + +pub fn writeSymtab(self: *ZigModule, elf_file: *Elf, ctx: anytype) void { + var ilocal = ctx.ilocal; + for (self.locals()) |local_index| { + const local = elf_file.symbol(local_index); + if (!local.flags.output_symtab) continue; + local.setOutputSym(elf_file, &ctx.symtab[ilocal]); + ilocal += 1; + } + + var iglobal = ctx.iglobal; + for (self.globals()) |global_index| { + const global = elf_file.symbol(global_index); + if (global.file(elf_file)) |file| if (file.index() != self.index) continue; + if (!global.flags.output_symtab) continue; + if (global.isLocal()) { + global.setOutputSym(elf_file, &ctx.symtab[ilocal]); + ilocal += 1; + } else { + global.setOutputSym(elf_file, &ctx.symtab[iglobal]); + iglobal += 1; + } + } +} + pub fn sourceSymbol(self: *ZigModule, symbol_index: Symbol.Index, elf_file: *Elf) *elf.Elf64_Sym { const sym = elf_file.symbol(symbol_index); if (self.local_symbols.get(symbol_index)) |_| return &self.elf_local_symbols.items[sym.esym_index]; @@ -134,5 +180,4 @@ const Elf = @import("../Elf.zig"); const File = @import("file.zig").File; const Module = @import("../../Module.zig"); const ZigModule = @This(); -// const Object = @import("Object.zig"); const Symbol = @import("Symbol.zig");