Commit 7c82079d2c

Jakub Konka <kubkon@jakubkonka.com>
2021-07-03 14:54:53
zld: allocate symbols using the new scheme
1 parent ceb4315
Changed files (1)
src
link
MachO
src/link/MachO/Zld.zig
@@ -108,6 +108,7 @@ globals: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
 /// Offset into __DATA,__common section.
 /// Set if the linker found tentative definitions in any of the objects.
 tentative_defs_offset: u64 = 0,
+has_tentative_defs: bool = false,
 
 threadlocal_offsets: std.ArrayListUnmanaged(TlvOffset) = .{}, // TODO merge with Symbol abstraction
 local_rebases: std.ArrayListUnmanaged(Pointer) = .{},
@@ -222,20 +223,33 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg
     try self.parseInputFiles(files, args.syslibroot);
     try self.parseLibs(args.libs, args.syslibroot);
     try self.resolveSymbols();
+    try self.resolveStubsAndGotEntries();
+    try self.updateMetadata();
+    try self.sortSections();
+    try self.addRpaths(args.rpaths);
+    try self.addDataInCodeLC();
+    try self.addCodeSignatureLC();
+    try self.allocateTextSegment();
+    try self.allocateDataConstSegment();
+    try self.allocateDataSegment();
+    self.allocateLinkeditSegment();
+    try self.allocateSymbols();
+    try self.allocateTentativeSymbols();
+    try self.allocateProxyBindAddresses();
+
+    log.warn("globals", .{});
+    for (self.globals.values()) |value| {
+        log.warn("  | {s}: {}", .{ value.name, value.payload });
+    }
+
+    for (self.objects.items) |object| {
+        log.warn("object {s}", .{object.name.?});
+        for (object.symbols.items) |sym| {
+            log.warn("  | {s}: {}", .{ sym.name, sym.payload });
+        }
+    }
+
     return error.TODO;
-    // try self.resolveStubsAndGotEntries();
-    // try self.updateMetadata();
-    // try self.sortSections();
-    // try self.addRpaths(args.rpaths);
-    // try self.addDataInCodeLC();
-    // try self.addCodeSignatureLC();
-    // try self.allocateTextSegment();
-    // try self.allocateDataConstSegment();
-    // try self.allocateDataSegment();
-    // self.allocateLinkeditSegment();
-    // try self.allocateSymbols();
-    // try self.allocateTentativeSymbols();
-    // try self.allocateProxyBindAddresses();
     // try self.flush();
 }
 
@@ -351,7 +365,7 @@ fn updateMetadata(self: *Zld) !void {
 
     // Ensure we have __DATA,__common section if we have tentative definitions.
     // Update size and alignment of __DATA,__common section.
-    if (self.tentatives.values().len > 0) {
+    if (self.has_tentative_defs) {
         const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
         const common_section_index = self.common_section_index orelse ind: {
             self.common_section_index = @intCast(u16, data_seg.sections.items.len);
@@ -364,10 +378,10 @@ fn updateMetadata(self: *Zld) !void {
 
         var max_align: u16 = 0;
         var added_size: u64 = 0;
-        for (self.tentatives.values()) |sym| {
-            const tent = sym.cast(Symbol.Tentative) orelse unreachable;
-            max_align = math.max(max_align, tent.alignment);
-            added_size += tent.size;
+        for (self.globals.values()) |sym| {
+            if (sym.payload != .tentative) continue;
+            max_align = math.max(max_align, sym.payload.tentative.alignment);
+            added_size += sym.payload.tentative.size;
         }
 
         common_sect.@"align" = math.max(common_sect.@"align", max_align);
@@ -1069,47 +1083,55 @@ fn allocateSegment(self: *Zld, index: u16, offset: u64) !void {
     seg.inner.vmsize = seg_size_aligned;
 }
 
-fn allocateSymbols(self: *Zld) !void {
-    for (self.objects.items) |object| {
-        for (object.symbols.items) |sym| {
-            const reg = sym.cast(Symbol.Regular) orelse continue;
+fn allocateSymbol(self: *Zld, symbol: *Symbol) !void {
+    const reg = &symbol.payload.regular;
+    const object = reg.file orelse return;
+    const source_sect = &object.sections.items[reg.section];
+    const target_map = source_sect.target_map orelse {
+        log.debug("section '{s},{s}' not mapped for symbol '{s}'", .{
+            parseName(&source_sect.inner.segname),
+            parseName(&source_sect.inner.sectname),
+            symbol.name,
+        });
+        return;
+    };
 
-            const source_sect = &object.sections.items[reg.section];
-            const target_map = source_sect.target_map orelse {
-                log.debug("section '{s},{s}' not mapped for symbol '{s}'", .{
-                    parseName(&source_sect.inner.segname),
-                    parseName(&source_sect.inner.sectname),
-                    sym.name,
-                });
-                continue;
-            };
+    const target_seg = self.load_commands.items[target_map.segment_id].Segment;
+    const target_sect = target_seg.sections.items[target_map.section_id];
+    const target_addr = target_sect.addr + target_map.offset;
+    const address = reg.address - source_sect.inner.addr + target_addr;
 
-            const target_seg = self.load_commands.items[target_map.segment_id].Segment;
-            const target_sect = target_seg.sections.items[target_map.section_id];
-            const target_addr = target_sect.addr + target_map.offset;
-            const address = reg.address - source_sect.inner.addr + target_addr;
-
-            log.debug("resolving symbol '{s}' at 0x{x}", .{ sym.name, address });
-
-            // TODO there might be a more generic way of doing this.
-            var section: u8 = 0;
-            for (self.load_commands.items) |cmd, cmd_id| {
-                if (cmd != .Segment) break;
-                if (cmd_id == target_map.segment_id) {
-                    section += @intCast(u8, target_map.section_id) + 1;
-                    break;
-                }
-                section += @intCast(u8, cmd.Segment.sections.items.len);
-            }
+    log.debug("resolving symbol '{s}' at 0x{x}", .{ symbol.name, address });
 
-            reg.address = address;
-            reg.section = section;
+    // TODO there might be a more generic way of doing this.
+    var section: u8 = 0;
+    for (self.load_commands.items) |cmd, cmd_id| {
+        if (cmd != .Segment) break;
+        if (cmd_id == target_map.segment_id) {
+            section += @intCast(u8, target_map.section_id) + 1;
+            break;
         }
+        section += @intCast(u8, cmd.Segment.sections.items.len);
+    }
+
+    reg.address = address;
+    reg.section = section;
+}
+
+fn allocateSymbols(self: *Zld) !void {
+    for (self.locals.items) |symbol| {
+        if (symbol.payload != .regular) continue;
+        try self.allocateSymbol(symbol);
+    }
+
+    for (self.globals.values()) |symbol| {
+        if (symbol.payload != .regular) continue;
+        try self.allocateSymbol(symbol);
     }
 }
 
 fn allocateTentativeSymbols(self: *Zld) !void {
-    if (self.tentatives.values().len == 0) return;
+    if (!self.has_tentative_defs) return;
 
     const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
     const common_sect = &data_seg.sections.items[self.common_section_index.?];
@@ -1131,36 +1153,21 @@ fn allocateTentativeSymbols(self: *Zld) !void {
     }
 
     // Convert tentative definitions into regular symbols.
-    for (self.tentatives.values()) |sym| {
-        const tent = sym.cast(Symbol.Tentative) orelse unreachable;
-        const reg = try Symbol.Regular.new(self.allocator, tent.base.name, .{
-            .linkage = .global,
-            .address = base_address,
-            .section = section,
-            .weak_ref = false,
-            .file = tent.file,
-        });
-        reg.got_index = tent.base.got_index;
-        reg.stubs_index = tent.base.stubs_index;
-
-        try self.globals.putNoClobber(self.allocator, reg.name, reg);
-        tent.base.alias = reg;
-
-        if (tent.base.got_index) |idx| {
-            self.got_entries.items[idx] = reg;
-        }
-        if (tent.base.stubs_index) |idx| {
-            self.stubs.items[idx] = reg;
-        }
+    for (self.globals.values()) |sym| {
+        if (sym.payload != .tentative) continue;
 
-        const address = mem.alignForwardGeneric(u64, base_address + tent.size, alignment);
+        const address = mem.alignForwardGeneric(u64, base_address + sym.payload.tentative.size, alignment);
 
-        log.debug("tentative definition '{s}' allocated from 0x{x} to 0x{x}", .{
-            tent.base.name,
-            base_address,
-            address,
-        });
+        log.debug("tentative definition '{s}' allocated from 0x{x} to 0x{x}", .{ sym.name, base_address, address });
 
+        sym.payload = .{
+            .regular = .{
+                .linkage = .global,
+                .address = base_address,
+                .section = section,
+                .weak_ref = false,
+            },
+        };
         base_address = address;
     }
 }
@@ -1174,17 +1181,17 @@ fn allocateProxyBindAddresses(self: *Zld) !void {
                 if (rel.@"type" != .unsigned) continue; // GOT is currently special-cased
                 if (rel.target != .symbol) continue;
 
-                const sym = object.symbols.items[rel.target.symbol].getTopmostAlias();
-                if (sym.cast(Symbol.Proxy)) |proxy| {
-                    const target_map = sect.target_map orelse continue;
-                    const target_seg = self.load_commands.items[target_map.segment_id].Segment;
-                    const target_sect = target_seg.sections.items[target_map.section_id];
+                const sym = object.symbols.items[rel.target.symbol];
+                if (sym.payload != .proxy) continue;
 
-                    try proxy.bind_info.append(self.allocator, .{
-                        .segment_id = target_map.segment_id,
-                        .address = target_sect.addr + target_map.offset + rel.offset,
-                    });
-                }
+                const target_map = sect.target_map orelse continue;
+                const target_seg = self.load_commands.items[target_map.segment_id].Segment;
+                const target_sect = target_seg.sections.items[target_map.section_id];
+
+                try sym.payload.proxy.bind_info.append(self.allocator, .{
+                    .segment_id = target_map.segment_id,
+                    .address = target_sect.addr + target_map.offset + rel.offset,
+                });
             }
         }
     }
@@ -1586,6 +1593,14 @@ fn resolveSymbols(self: *Zld) !void {
         }
     }
 
+    // Mark if we need to allocate zerofill section for tentative definitions
+    for (self.globals.values()) |symbol| {
+        if (symbol.payload == .tentative) {
+            self.has_tentative_defs = true;
+            break;
+        }
+    }
+
     // Third pass, resolve symbols in dynamic libraries.
     {
         // Put dyld_stub_binder as an undefined special symbol.
@@ -1651,18 +1666,6 @@ fn resolveSymbols(self: *Zld) !void {
     }
 
     if (has_undefined) return error.UndefinedSymbolReference;
-
-    log.warn("globals", .{});
-    for (self.globals.values()) |value| {
-        log.warn("  | {s}: {}", .{ value.name, value.payload });
-    }
-
-    for (self.objects.items) |object| {
-        log.warn("object {s}", .{object.name.?});
-        for (object.symbols.items) |sym| {
-            log.warn("  | {s}: {}", .{ sym.name, sym.payload });
-        }
-    }
 }
 
 fn resolveStubsAndGotEntries(self: *Zld) !void {
@@ -1675,7 +1678,7 @@ fn resolveStubsAndGotEntries(self: *Zld) !void {
                 switch (rel.@"type") {
                     .unsigned => continue,
                     .got_page, .got_page_off, .got_load, .got, .pointer_to_got => {
-                        const sym = object.symbols.items[rel.target.symbol].getTopmostAlias();
+                        const sym = object.symbols.items[rel.target.symbol];
                         if (sym.got_index != null) continue;
 
                         const index = @intCast(u32, self.got_entries.items.len);
@@ -1687,11 +1690,11 @@ fn resolveStubsAndGotEntries(self: *Zld) !void {
                     else => {
                         if (rel.target != .symbol) continue;
 
-                        const sym = object.symbols.items[rel.target.symbol].getTopmostAlias();
-                        assert(sym.@"type" != .unresolved);
+                        const sym = object.symbols.items[rel.target.symbol];
+                        assert(sym.payload != .undef);
 
                         if (sym.stubs_index != null) continue;
-                        if (sym.@"type" != .proxy) continue;
+                        if (sym.payload != .proxy) continue;
 
                         const index = @intCast(u32, self.stubs.items.len);
                         sym.stubs_index = index;
@@ -1705,7 +1708,7 @@ fn resolveStubsAndGotEntries(self: *Zld) !void {
     }
 
     // Finally, put dyld_stub_binder as the final GOT entry
-    const sym = self.imports.get("dyld_stub_binder") orelse unreachable;
+    const sym = self.globals.get("dyld_stub_binder") orelse unreachable;
     const index = @intCast(u32, self.got_entries.items.len);
     sym.got_index = index;
     try self.got_entries.append(self.allocator, sym);