Commit c654f3b0ee

Jakub Konka <kubkon@jakubkonka.com>
2023-09-12 15:44:16
elf: claim unresolved dangling symbols that can be claimed
1 parent b478a0d
Changed files (4)
src/link/Elf/file.zig
@@ -76,6 +76,12 @@ pub const File = union(enum) {
         }
     }
 
+    pub fn globals(file: File) []const Symbol.Index {
+        return switch (file) {
+            inline else => |x| x.globals(),
+        };
+    }
+
     pub const Index = u32;
 
     pub const Entry = union(enum) {
src/link/Elf/Object.zig
@@ -411,6 +411,34 @@ pub fn resolveSymbols(self: *Object, elf_file: *Elf) void {
     }
 }
 
+pub fn claimUnresolved(self: *Object, elf_file: *Elf) void {
+    const first_global = self.first_global orelse return;
+    for (self.globals(), 0..) |index, i| {
+        const esym_index = @as(u32, @intCast(first_global + i));
+        const esym = self.symtab[esym_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;
+        }
+
+        const is_import = blk: {
+            if (!elf_file.isDynLib()) break :blk false;
+            const vis = @as(elf.STV, @enumFromInt(esym.st_other));
+            if (vis == .HIDDEN) break :blk false;
+            break :blk true;
+        };
+
+        global.value = 0;
+        global.atom_index = 0;
+        global.esym_index = esym_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;
+    }
+}
+
 pub fn resetGlobals(self: *Object, elf_file: *Elf) void {
     for (self.globals()) |index| {
         const global = elf_file.symbol(index);
@@ -576,12 +604,12 @@ pub fn writeSymtab(self: *Object, elf_file: *Elf, ctx: anytype) void {
     }
 }
 
-pub fn locals(self: *Object) []const u32 {
+pub fn locals(self: *Object) []const Symbol.Index {
     const end = self.first_global orelse self.symbols.items.len;
     return self.symbols.items[0..end];
 }
 
-pub fn globals(self: *Object) []const u32 {
+pub fn globals(self: *Object) []const Symbol.Index {
     const start = self.first_global orelse self.symbols.items.len;
     return self.symbols.items[start..];
 }
src/link/Elf/ZigModule.zig
@@ -102,6 +102,34 @@ 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];
+
+        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;
+        }
+
+        const is_import = blk: {
+            if (!elf_file.isDynLib()) break :blk false;
+            const vis = @as(elf.STV, @enumFromInt(esym.st_other));
+            if (vis == .HIDDEN) break :blk false;
+            break :blk true;
+        };
+
+        global.value = 0;
+        global.atom_index = 0;
+        global.esym_index = esym_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;
+    }
+}
+
 pub fn updateSymtabSize(self: *ZigModule, elf_file: *Elf) void {
     for (self.locals()) |local_index| {
         const local = elf_file.symbol(local_index);
src/link/Elf.zig
@@ -1048,6 +1048,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
     // Resolve symbols
     self.resolveSymbols();
     self.markImportsExports();
+    self.claimUnresolved();
 
     if (self.unresolved.keys().len > 0) try self.reportUndefined();
 
@@ -1338,48 +1339,45 @@ fn resolveSymbols(self: *Elf) void {
 }
 
 fn markImportsExports(self: *Elf) void {
-    const is_dyn_lib = self.base.options.output_mode == .Lib and self.base.options.link_mode == .Dynamic;
-
-    if (self.zig_module_index) |index| {
-        const zig_module = self.file(index).?.zig_module;
-        for (zig_module.globals()) |global_index| {
-            const global = self.symbol(global_index);
-            if (global.version_index == elf.VER_NDX_LOCAL) continue;
-            const file_ptr = global.file(self) orelse continue;
-            const vis = @as(elf.STV, @enumFromInt(global.elfSym(self).st_other));
-            if (vis == .HIDDEN) continue;
-            // if (file == .shared and !global.isAbs(self)) {
-            //     global.flags.import = true;
-            //     continue;
-            // }
-            if (file_ptr.index() == index) {
-                global.flags.@"export" = true;
-                if (is_dyn_lib and vis != .PROTECTED) {
-                    global.flags.import = true;
+    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 == .shared and !global.isAbs(self)) {
+                //     global.flags.import = true;
+                //     continue;
+                // }
+                if (file_ptr.index() == file_index) {
+                    global.flags.@"export" = true;
+                    if (elf_file.isDynLib() and vis != .PROTECTED) {
+                        global.flags.import = true;
+                    }
                 }
             }
         }
+    }.mark;
+
+    if (self.zig_module_index) |index| {
+        mark(self, index);
+    }
+
+    for (self.objects.items) |index| {
+        mark(self, index);
     }
+}
 
+fn claimUnresolved(self: *Elf) void {
+    if (self.zig_module_index) |index| {
+        const zig_module = self.file(index).?.zig_module;
+        zig_module.claimUnresolved(self);
+    }
     for (self.objects.items) |index| {
         const object = self.file(index).?.object;
-        for (object.globals()) |global_index| {
-            const global = self.symbol(global_index);
-            if (global.version_index == elf.VER_NDX_LOCAL) continue;
-            const file_ptr = global.file(self) orelse continue;
-            const vis = @as(elf.STV, @enumFromInt(global.elfSym(self).st_other));
-            if (vis == .HIDDEN) continue;
-            // if (file == .shared and !global.isAbs(self)) {
-            //     global.flags.import = true;
-            //     continue;
-            // }
-            if (file_ptr.index() == index) {
-                global.flags.@"export" = true;
-                if (is_dyn_lib and vis != .PROTECTED) {
-                    global.flags.import = true;
-                }
-            }
-        }
+        object.claimUnresolved(self);
     }
 }
 
@@ -3423,6 +3421,10 @@ pub fn defaultEntryAddress(self: Elf) u64 {
     };
 }
 
+pub fn isDynLib(self: Elf) bool {
+    return self.base.options.output_mode == .Lib and self.base.options.link_mode == .Dynamic;
+}
+
 pub fn sectionByName(self: *Elf, name: [:0]const u8) ?u16 {
     for (self.shdrs.items, 0..) |*shdr, i| {
         const this_name = self.shstrtab.getAssumeExists(shdr.sh_name);