Commit 9691d1a30f

Jakub Konka <kubkon@jakubkonka.com>
2023-09-07 15:45:58
elf: use zld's update mechanism for symtab for Zig module
1 parent 37e2958
Changed files (3)
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");
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");
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 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.?];
-    shdr.sh_info = @intCast(zig_module.locals().len);
-    self.markDirty(self.symtab_section_index.?, null);
+    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);
 
-    const nsyms = zig_module.locals().len + zig_module.globals().len;
-    const needed_size = nsyms * sym_size;
-    try self.growNonAllocSection(self.symtab_section_index.?, needed_size, sym_align, true);
+    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);
-                }
-            }
-
-            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);
-                }
+            if (foreign_endian) {
+                for (symtab) |*sym| 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");