Commit 1448d6b77c

Jakub Konka <kubkon@jakubkonka.com>
2023-10-18 18:33:30
elf: store shndx to Zig module atoms out-of-band
This allows us to increase the effective resolution of `st_shndx` from `u8` to `u32`.
1 parent 9ced0ec
Changed files (2)
src/link/Elf/ZigModule.zig
@@ -7,8 +7,8 @@
 path: []const u8,
 index: File.Index,
 
-local_esyms: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
-global_esyms: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
+local_esyms: std.MultiArrayList(ElfSym) = .{},
+global_esyms: std.MultiArrayList(ElfSym) = .{},
 local_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
 global_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
 globals_lookup: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .{},
@@ -20,6 +20,10 @@ num_dynrelocs: u32 = 0,
 
 output_symtab_size: Elf.SymtabSize = .{},
 
+pub const global_symbol_bit: u32 = 0x80000000;
+pub const symbol_mask: u32 = 0x7fffffff;
+pub const SHN_ATOM: u16 = 0x100;
+
 pub fn deinit(self: *ZigModule, allocator: Allocator) void {
     self.local_esyms.deinit(allocator);
     self.global_esyms.deinit(allocator);
@@ -35,20 +39,20 @@ pub fn deinit(self: *ZigModule, allocator: Allocator) void {
 
 pub fn addLocalEsym(self: *ZigModule, allocator: Allocator) !Symbol.Index {
     try self.local_esyms.ensureUnusedCapacity(allocator, 1);
-    const index = @as(Symbol.Index, @intCast(self.local_esyms.items.len));
-    const esym = self.local_esyms.addOneAssumeCapacity();
-    esym.* = Elf.null_sym;
-    esym.st_info = elf.STB_LOCAL << 4;
+    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);
     return index;
 }
 
 pub fn addGlobalEsym(self: *ZigModule, allocator: Allocator) !Symbol.Index {
     try self.global_esyms.ensureUnusedCapacity(allocator, 1);
-    const index = @as(Symbol.Index, @intCast(self.global_esyms.items.len));
-    const esym = self.global_esyms.addOneAssumeCapacity();
-    esym.* = Elf.null_sym;
-    esym.st_info = elf.STB_GLOBAL << 4;
-    return index | 0x10000000;
+    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;
 }
 
 pub fn addAtom(self: *ZigModule, elf_file: *Elf) !Symbol.Index {
@@ -58,7 +62,7 @@ pub fn addAtom(self: *ZigModule, elf_file: *Elf) !Symbol.Index {
     const symbol_index = try elf_file.addSymbol();
     const esym_index = try self.addLocalEsym(gpa);
 
-    const shndx = @as(u16, @intCast(self.atoms.items.len));
+    const shndx = @as(u32, @intCast(self.atoms.items.len));
     try self.atoms.append(gpa, atom_index);
     try self.local_symbols.append(gpa, symbol_index);
 
@@ -69,8 +73,8 @@ pub fn addAtom(self: *ZigModule, elf_file: *Elf) !Symbol.Index {
     symbol_ptr.file_index = self.index;
     symbol_ptr.atom_index = atom_index;
 
-    const esym = &self.local_esyms.items[esym_index];
-    esym.st_shndx = shndx;
+    self.local_esyms.items(.shndx)[esym_index] = shndx;
+    self.local_esyms.items(.elf_sym)[esym_index].st_shndx = SHN_ATOM;
     symbol_ptr.esym_index = esym_index;
 
     const relocs_index = @as(u32, @intCast(self.relocs.items.len));
@@ -99,13 +103,15 @@ pub fn inputShdr(self: ZigModule, atom_index: Atom.Index, elf_file: *Elf) Object
 
 pub fn resolveSymbols(self: *ZigModule, elf_file: *Elf) void {
     for (self.globals(), 0..) |index, i| {
-        const esym_index = @as(Symbol.Index, @intCast(i)) | 0x10000000;
-        const esym = self.global_esyms.items[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;
 
         if (esym.st_shndx != elf.SHN_ABS and esym.st_shndx != elf.SHN_COMMON) {
-            const atom_index = self.atoms.items[esym.st_shndx];
+            assert(esym.st_shndx == SHN_ATOM);
+            const atom_index = self.atoms.items[shndx];
             const atom = elf_file.atom(atom_index) orelse continue;
             if (!atom.flags.alive) continue;
         }
@@ -114,7 +120,8 @@ pub fn resolveSymbols(self: *ZigModule, elf_file: *Elf) void {
         if (self.asFile().symbolRank(esym, false) < global.symbolRank(elf_file)) {
             const atom_index = switch (esym.st_shndx) {
                 elf.SHN_ABS, elf.SHN_COMMON => 0,
-                else => self.atoms.items[esym.st_shndx],
+                SHN_ATOM => self.atoms.items[shndx],
+                else => unreachable,
             };
             const output_section_index = if (elf_file.atom(atom_index)) |atom|
                 atom.outputShndx().?
@@ -133,8 +140,8 @@ pub fn resolveSymbols(self: *ZigModule, elf_file: *Elf) void {
 
 pub fn claimUnresolved(self: *ZigModule, elf_file: *Elf) void {
     for (self.globals(), 0..) |index, i| {
-        const esym_index = @as(Symbol.Index, @intCast(i)) | 0x10000000;
-        const esym = self.global_esyms.items[i];
+        const esym_index = @as(Symbol.Index, @intCast(i)) | global_symbol_bit;
+        const esym = self.global_esyms.items(.elf_sym)[i];
 
         if (esym.st_shndx != elf.SHN_UNDEF) continue;
 
@@ -187,7 +194,7 @@ pub fn resetGlobals(self: *ZigModule, elf_file: *Elf) void {
 
 pub fn markLive(self: *ZigModule, elf_file: *Elf) void {
     for (self.globals(), 0..) |index, i| {
-        const esym = self.global_esyms.items[i];
+        const esym = self.global_esyms.items(.elf_sym)[i];
         if (esym.st_bind() == elf.STB_WEAK) continue;
 
         const global = elf_file.symbol(index);
@@ -256,17 +263,17 @@ pub fn writeSymtab(self: *ZigModule, elf_file: *Elf, ctx: anytype) void {
 }
 
 pub fn symbol(self: *ZigModule, index: Symbol.Index) Symbol.Index {
-    const is_global = index & 0x10000000 != 0;
-    const actual_index = index & 0x0fffffff;
+    const is_global = index & global_symbol_bit != 0;
+    const actual_index = index & symbol_mask;
     if (is_global) return self.global_symbols.items[actual_index];
     return self.local_symbols.items[actual_index];
 }
 
 pub fn elfSym(self: *ZigModule, index: Symbol.Index) *elf.Elf64_Sym {
-    const is_global = index & 0x10000000 != 0;
-    const actual_index = index & 0x0fffffff;
-    if (is_global) return &self.global_esyms.items[actual_index];
-    return &self.local_esyms.items[actual_index];
+    const is_global = index & global_symbol_bit != 0;
+    const actual_index = index & symbol_mask;
+    if (is_global) return &self.global_esyms.items(.elf_sym)[actual_index];
+    return &self.local_esyms.items(.elf_sym)[actual_index];
 }
 
 pub fn locals(self: *ZigModule) []const Symbol.Index {
@@ -354,6 +361,11 @@ fn formatAtoms(
     }
 }
 
+const ElfSym = struct {
+    elf_sym: elf.Elf64_Sym,
+    shndx: u32 = elf.SHN_UNDEF,
+};
+
 const assert = std.debug.assert;
 const std = @import("std");
 const elf = std.elf;
src/link/Elf.zig
@@ -331,7 +331,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
         symbol_ptr.name_offset = name_off;
 
         const esym_index = try zig_module.addLocalEsym(allocator);
-        const esym = &zig_module.local_esyms.items[esym_index];
+        const esym = &zig_module.local_esyms.items(.elf_sym)[esym_index];
         esym.st_name = name_off;
         esym.st_info |= elf.STT_FILE;
         esym.st_shndx = elf.SHN_ABS;
@@ -3172,7 +3172,7 @@ fn updateDeclCode(
     const required_alignment = decl.getAlignment(mod);
 
     const sym = self.symbol(sym_index);
-    const esym = &zig_module.local_esyms.items[sym.esym_index];
+    const esym = &zig_module.local_esyms.items(.elf_sym)[sym.esym_index];
     const atom_ptr = sym.atom(self).?;
 
     const shdr_index = self.getDeclShdrIndex(decl_index, code);
@@ -3438,7 +3438,7 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol.
     const phdr_index = self.phdr_to_shdr_table.get(output_section_index).?;
     local_sym.name_offset = name_str_index;
     local_sym.output_section_index = output_section_index;
-    const local_esym = &zig_module.local_esyms.items[local_sym.esym_index];
+    const local_esym = &zig_module.local_esyms.items(.elf_sym)[local_sym.esym_index];
     local_esym.st_name = name_str_index;
     local_esym.st_info |= elf.STT_OBJECT;
     local_esym.st_size = code.len;
@@ -3527,7 +3527,7 @@ fn lowerConst(
     const name_str_index = try self.strtab.insert(gpa, name);
     local_sym.name_offset = name_str_index;
     local_sym.output_section_index = output_section_index;
-    const local_esym = &zig_module.local_esyms.items[local_sym.esym_index];
+    const local_esym = &zig_module.local_esyms.items(.elf_sym)[local_sym.esym_index];
     local_esym.st_name = name_str_index;
     local_esym.st_info |= elf.STT_OBJECT;
     local_esym.st_size = code.len;
@@ -3573,7 +3573,9 @@ pub fn updateDeclExports(
     const zig_module = self.file(self.zig_module_index.?).?.zig_module;
     const decl = mod.declPtr(decl_index);
     const decl_sym_index = try self.getOrCreateMetadataForDecl(decl_index);
-    const decl_esym = zig_module.local_esyms.items[self.symbol(decl_sym_index).esym_index];
+    const decl_esym_index = self.symbol(decl_sym_index).esym_index;
+    const decl_esym = zig_module.local_esyms.items(.elf_sym)[decl_esym_index];
+    const decl_esym_shndx = zig_module.local_esyms.items(.shndx)[decl_esym_index];
     const decl_metadata = self.decls.getPtr(decl_index).?;
 
     for (exports) |exp| {
@@ -3615,11 +3617,13 @@ pub fn updateDeclExports(
             try zig_module.global_symbols.append(gpa, gop.index);
             break :blk sym_index;
         };
-        const esym = &zig_module.global_esyms.items[sym_index & 0x0fffffff];
-        esym.st_value = self.symbol(decl_sym_index).value;
-        esym.st_shndx = decl_esym.st_shndx;
-        esym.st_info = (stb_bits << 4) | stt_bits;
-        esym.st_name = name_off;
+        const global_esym_index = sym_index & ZigModule.symbol_mask;
+        const global_esym = &zig_module.global_esyms.items(.elf_sym)[global_esym_index];
+        global_esym.st_value = self.symbol(decl_sym_index).value;
+        global_esym.st_shndx = decl_esym.st_shndx;
+        global_esym.st_info = (stb_bits << 4) | stt_bits;
+        global_esym.st_name = name_off;
+        zig_module.global_esyms.items(.shndx)[global_esym_index] = decl_esym_shndx;
     }
 }
 
@@ -3651,7 +3655,7 @@ pub fn deleteDeclExport(
     const exp_name = mod.intern_pool.stringToSlice(name);
     const esym_index = metadata.@"export"(self, exp_name) orelse return;
     log.debug("deleting export '{s}'", .{exp_name});
-    const esym = &zig_module.global_esyms.items[esym_index.*];
+    const esym = &zig_module.global_esyms.items(.elf_sym)[esym_index.*];
     _ = zig_module.globals_lookup.remove(esym.st_name);
     const sym_index = self.resolver.get(esym.st_name).?;
     const sym = self.symbol(sym_index);
@@ -3660,6 +3664,7 @@ pub fn deleteDeclExport(
         sym.* = .{};
     }
     esym.* = null_sym;
+    zig_module.global_esyms.items(.shndx)[esym_index.*] = elf.SHN_UNDEF;
 }
 
 fn addLinkerDefinedSymbols(self: *Elf) !void {