diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2021-06-10 17:37:38 +0200 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2021-06-10 17:49:14 +0200 |
| commit | c2086efb412f17bb52514e931016e79c65bed442 (patch) | |
| tree | f1aa2c933f77c3b14eac48180ccc4a9f7ee765af /src | |
| parent | 03cda80a634d82e02e641fe04fa2abda4455966a (diff) | |
| download | zig-c2086efb412f17bb52514e931016e79c65bed442.tar.gz zig-c2086efb412f17bb52514e931016e79c65bed442.zip | |
zld: synthetise regular from tentative definition
Diffstat (limited to 'src')
| -rw-r--r-- | src/link/MachO/Zld.zig | 133 |
1 files changed, 126 insertions, 7 deletions
diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 017b3f7d4e..191f63fe52 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -86,9 +86,9 @@ imports: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, unresolved: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, tentatives: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, -// /// Offset into __DATA,__common section. -// /// Set if the linker found tentative definitions in any of the objects. -// tentative_defs_offset: u32 = 0, +/// Offset into __DATA,__common section. +/// Set if the linker found tentative definitions in any of the objects. +tentative_defs_offset: u64 = 0, strtab: std.ArrayListUnmanaged(u8) = .{}, strtab_dir: std.StringHashMapUnmanaged(u32) = .{}, @@ -227,6 +227,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L try self.allocateDataSegment(); self.allocateLinkeditSegment(); try self.allocateSymbols(); + try self.allocateTentativeSymbols(); try self.flush(); } @@ -691,6 +692,49 @@ 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) { + 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); + try data_seg.addSection(self.allocator, .{ + .sectname = makeStaticString("__common"), + .segname = makeStaticString("__DATA"), + .addr = 0, + .size = 0, + .offset = 0, + .@"align" = 0, + .reloff = 0, + .nreloc = 0, + .flags = macho.S_ZEROFILL, + .reserved1 = 0, + .reserved2 = 0, + .reserved3 = 0, + }); + break :ind self.common_section_index.?; + }; + const common_sect = &data_seg.sections.items[common_section_index]; + + var max_align: u16 = 0; + var added_size: u64 = 0; + for (self.tentatives.values()) |sym| { + const tent = sym.cast(Symbol.Tentative) orelse unreachable; + if (max_align > tent.alignment) continue; + max_align = tent.alignment; + added_size += tent.size; + } + + common_sect.@"align" = math.max(common_sect.@"align", max_align); + + const alignment = try math.powi(u32, 2, common_sect.@"align"); + const offset = mem.alignForwardGeneric(u64, common_sect.size, alignment); + const size = mem.alignForwardGeneric(u64, added_size, alignment); + + common_sect.size = offset + size; + self.tentative_defs_offset = offset; + } + tlv_align: { const has_tlv = self.tlv_section_index != null or @@ -1110,6 +1154,70 @@ fn allocateSymbols(self: *Zld) !void { } } +fn allocateTentativeSymbols(self: *Zld) !void { + if (self.tentatives.values().len == 0) 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.?]; + + const alignment = try math.powi(u32, 2, common_sect.@"align"); + var base_address: u64 = common_sect.addr + self.tentative_defs_offset; + + log.debug("base address for tentative definitions 0x{x}", .{base_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 == self.data_segment_cmd_index.?) { + section += @intCast(u8, self.common_section_index.?) + 1; + break; + } + section += @intCast(u8, cmd.Segment.sections.items.len); + } + + // Convert tentative definitions into regular symbols. + for (self.tentatives.values()) |sym, i| { + const tent = sym.cast(Symbol.Tentative) orelse unreachable; + const reg = try self.allocator.create(Symbol.Regular); + errdefer self.allocator.destroy(reg); + + reg.* = .{ + .base = .{ + .@"type" = .regular, + .name = try self.allocator.dupe(u8, tent.base.name), + .got_index = tent.base.got_index, + .stubs_index = tent.base.stubs_index, + }, + .linkage = .global, + .address = base_address, + .section = section, + .weak_ref = false, + .file = tent.file, + }; + + try self.globals.putNoClobber(self.allocator, reg.base.name, ®.base); + tent.base.alias = ®.base; + + if (tent.base.got_index) |idx| { + self.got_entries.items[idx] = ®.base; + } + if (tent.base.stubs_index) |idx| { + self.stubs.items[idx] = ®.base; + } + + const address = mem.alignForwardGeneric(u64, base_address + tent.size, alignment); + + log.debug("tentative definition '{s}' allocated from 0x{x} to 0x{x}", .{ + tent.base.name, + base_address, + address, + }); + + base_address = address; + } +} + fn writeStubHelperCommon(self: *Zld) !void { const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?]; @@ -1432,14 +1540,25 @@ fn resolveSymbolsInObject(self: *Zld, object: *Object) !void { kv.value.alias = sym; } - const t_sym = self.tentatives.get(sym.name) orelse { + const sym_ptr = self.tentatives.getPtr(sym.name) orelse { // Put new tentative definition symbol into symbol table. try self.tentatives.putNoClobber(self.allocator, sym.name, sym); continue; }; - // TODO compare by size and pick the largest. - return error.TODOResolveTentatives; + // Compare by size and pick the largest tentative definition. + // We model this like a heap where the tentative definition with the + // largest size always washes up on top. + const t_sym = sym_ptr.*; + const t_tent = t_sym.cast(Symbol.Tentative) orelse unreachable; + + if (tent.size < t_tent.size) { + sym.alias = t_sym; + continue; + } + + t_sym.alias = sym; + sym_ptr.* = sym; } else if (sym.cast(Symbol.Unresolved)) |und| { if (self.globals.get(sym.name)) |g_sym| { sym.alias = g_sym; @@ -1453,6 +1572,7 @@ fn resolveSymbolsInObject(self: *Zld, object: *Object) !void { sym.alias = u_sym; continue; } + try self.unresolved.putNoClobber(self.allocator, sym.name, sym); } else unreachable; } @@ -1494,7 +1614,6 @@ fn resolveSymbols(self: *Zld) !void { next_sym += 1; } } - // Third pass, resolve symbols in dynamic libraries. // TODO Implement libSystem as a hard-coded library, or ship with // a libSystem.B.tbd definition file? |
