From 88aec4a1ee49088685b16d744f35c902c4900a09 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 8 Jun 2021 23:23:31 +0200 Subject: [PATCH 1/9] zld: work out size and alignment of commons --- src/link/MachO/Object.zig | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 2662072758..5241fea582 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -383,11 +383,10 @@ pub fn parseSymbols(self: *Object) !void { } if (sym.n_value != 0) { - log.err("common symbol {s} in {s}", .{ sym_name, self.name.? }); + const comm_size = sym.n_value; + const comm_align = (sym.n_desc >> 8) & 0x0f; + log.err("Common symbol {s} in {s}: size 0x{x}, align 0x{x}", .{ sym_name, self.name.?, comm_size, comm_align }); return error.UnhandledSymbolType; - // const comm_size = sym.n_value; - // const comm_align = (sym.n_desc >> 8) & 0x0f; - // log.warn("Common symbol: size 0x{x}, align 0x{x}", .{ comm_size, comm_align }); } const undef = try self.allocator.create(Symbol.Unresolved); From 96bb81b6ef0896d0b63c54bfd5d1ac0fa267d68a Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 10 Jun 2021 08:30:25 +0200 Subject: [PATCH 2/9] zld: moving target seg,sect mapping into Object.Section --- src/link/MachO/Object.zig | 5 + src/link/MachO/Zld.zig | 207 ++++++++++++++++---------------------- 2 files changed, 94 insertions(+), 118 deletions(-) diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 5241fea582..1277423b09 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -54,6 +54,11 @@ pub const Section = struct { inner: macho.section_64, code: []u8, relocs: ?[]*Relocation, + target_map: ?struct { + segment_id: u16, + section_id: u16, + offset: u32, + } = null, pub fn deinit(self: *Section, allocator: *Allocator) void { allocator.free(self.code); diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index c5362b8d7d..9f676f3fc2 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -95,9 +95,6 @@ got_entries: std.ArrayListUnmanaged(*Symbol) = .{}, stub_helper_stubs_start_off: ?u64 = null, -mappings: std.AutoHashMapUnmanaged(MappingKey, SectionMapping) = .{}, -unhandled_sections: std.AutoHashMapUnmanaged(MappingKey, u0) = .{}, - const TlvOffset = struct { source_addr: u64, offset: u64, @@ -107,18 +104,6 @@ const TlvOffset = struct { } }; -const MappingKey = struct { - object_id: u16, - source_sect_id: u16, -}; - -pub const SectionMapping = struct { - source_sect_id: u16, - target_seg_id: u16, - target_sect_id: u16, - offset: u32, -}; - /// Default path to dyld const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld"; @@ -159,9 +144,6 @@ pub fn deinit(self: *Zld) void { } self.dylibs.deinit(self.allocator); - self.mappings.deinit(self.allocator); - self.unhandled_sections.deinit(self.allocator); - self.globals.deinit(self.allocator); self.imports.deinit(self.allocator); self.unresolved.deinit(self.allocator); @@ -407,46 +389,39 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void { fn mapAndUpdateSections( self: *Zld, - object_id: u16, + object: *Object, source_sect_id: u16, target_seg_id: u16, target_sect_id: u16, ) !void { - const object = self.objects.items[object_id]; - const source_seg = object.load_commands.items[object.segment_cmd_index.?].Segment; - const source_sect = source_seg.sections.items[source_sect_id]; + const source_sect = &object.sections.items[source_sect_id]; const target_seg = &self.load_commands.items[target_seg_id].Segment; const target_sect = &target_seg.sections.items[target_sect_id]; const alignment = try math.powi(u32, 2, target_sect.@"align"); const offset = mem.alignForwardGeneric(u64, target_sect.size, alignment); - const size = mem.alignForwardGeneric(u64, source_sect.size, alignment); - const key = MappingKey{ - .object_id = object_id, - .source_sect_id = source_sect_id, - }; - try self.mappings.putNoClobber(self.allocator, key, .{ - .source_sect_id = source_sect_id, - .target_seg_id = target_seg_id, - .target_sect_id = target_sect_id, - .offset = @intCast(u32, offset), - }); - log.debug("{s}: {s},{s} mapped to {s},{s} from 0x{x} to 0x{x}", .{ - object.name, - parseName(&source_sect.segname), - parseName(&source_sect.sectname), + const size = mem.alignForwardGeneric(u64, source_sect.inner.size, alignment); + + log.debug("{s}: '{s},{s}' mapped to '{s},{s}' from 0x{x} to 0x{x}", .{ + object.name.?, + parseName(&source_sect.inner.segname), + parseName(&source_sect.inner.sectname), parseName(&target_sect.segname), parseName(&target_sect.sectname), offset, offset + size, }); + source_sect.target_map = .{ + .segment_id = target_seg_id, + .section_id = target_sect_id, + .offset = @intCast(u32, offset), + }; target_sect.size = offset + size; } fn updateMetadata(self: *Zld) !void { - for (self.objects.items) |object, id| { - const object_id = @intCast(u16, id); + for (self.objects.items) |object| { const object_seg = object.load_commands.items[object.segment_cmd_index.?].Segment; const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; @@ -699,19 +674,14 @@ fn updateMetadata(self: *Zld) !void { for (object_seg.sections.items) |source_sect, sect_id| { const source_sect_id = @intCast(u16, sect_id); if (self.getMatchingSection(source_sect)) |res| { - try self.mapAndUpdateSections(object_id, source_sect_id, res.seg, res.sect); + try self.mapAndUpdateSections(object, source_sect_id, res.seg, res.sect); continue; } - const segname = parseName(&source_sect.segname); - const sectname = parseName(&source_sect.sectname); - - log.debug("section '{s}/{s}' will be unmapped", .{ segname, sectname }); - - try self.unhandled_sections.putNoClobber(self.allocator, .{ - .object_id = object_id, - .source_sect_id = source_sect_id, - }, 0); + log.debug("section '{s},{s}' will be unmapped", .{ + parseName(&source_sect.segname), + parseName(&source_sect.sectname), + }); } } @@ -954,18 +924,34 @@ fn sortSections(self: *Zld) !void { } } - var it = self.mappings.valueIterator(); - while (it.next()) |mapping| { - if (self.text_segment_cmd_index.? == mapping.target_seg_id) { - const new_index = text_index_mapping.get(mapping.target_sect_id) orelse unreachable; - mapping.target_sect_id = new_index; - } else if (self.data_const_segment_cmd_index.? == mapping.target_seg_id) { - const new_index = data_const_index_mapping.get(mapping.target_sect_id) orelse unreachable; - mapping.target_sect_id = new_index; - } else if (self.data_segment_cmd_index.? == mapping.target_seg_id) { - const new_index = data_index_mapping.get(mapping.target_sect_id) orelse unreachable; - mapping.target_sect_id = new_index; - } else unreachable; + for (self.objects.items) |object| { + for (object.sections.items) |*sect| { + const target_map = sect.target_map orelse continue; + + const new_index = blk: { + if (self.text_segment_cmd_index.? == target_map.segment_id) { + break :blk text_index_mapping.get(target_map.section_id) orelse unreachable; + } else if (self.data_const_segment_cmd_index.? == target_map.segment_id) { + break :blk data_const_index_mapping.get(target_map.section_id) orelse unreachable; + } else if (self.data_segment_cmd_index.? == target_map.segment_id) { + break :blk data_index_mapping.get(target_map.section_id) orelse unreachable; + } else unreachable; + }; + + log.debug("remapping in {s}: '{s},{s}': {} => {}", .{ + object.name.?, + parseName(§.inner.segname), + parseName(§.inner.sectname), + target_map.section_id, + new_index, + }); + + sect.target_map = .{ + .segment_id = target_map.segment_id, + .section_id = new_index, + .offset = target_map.offset, + }; + } } } @@ -1080,30 +1066,24 @@ fn allocateSegment(self: *Zld, index: u16, offset: u64) !void { } fn allocateSymbols(self: *Zld) !void { - for (self.objects.items) |object, object_id| { + for (self.objects.items) |object| { for (object.symbols.items) |sym| { const reg = sym.cast(Symbol.Regular) orelse continue; - // TODO I am more and more convinced we should store the mapping as part of the Object struct. - const target_mapping = self.mappings.get(.{ - .object_id = @intCast(u16, object_id), - .source_sect_id = reg.section, - }) orelse { - if (self.unhandled_sections.get(.{ - .object_id = @intCast(u16, object_id), - .source_sect_id = reg.section, - }) != null) continue; - - log.err("section not mapped for symbol '{s}'", .{sym.name}); - return error.SectionNotMappedForSymbol; + 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 source_seg = object.load_commands.items[object.segment_cmd_index.?].Segment; - const source_sect = source_seg.sections.items[reg.section]; - const target_seg = self.load_commands.items[target_mapping.target_seg_id].Segment; - const target_sect = target_seg.sections.items[target_mapping.target_sect_id]; - const target_addr = target_sect.addr + target_mapping.offset; - const address = reg.address - source_sect.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 }); @@ -1111,8 +1091,8 @@ fn allocateSymbols(self: *Zld) !void { var section: u8 = 0; for (self.load_commands.items) |cmd, cmd_id| { if (cmd != .Segment) break; - if (cmd_id == target_mapping.target_seg_id) { - section += @intCast(u8, target_mapping.target_sect_id) + 1; + if (cmd_id == target_map.segment_id) { + section += @intCast(u8, target_map.section_id) + 1; break; } section += @intCast(u8, cmd.Segment.sections.items.len); @@ -1602,10 +1582,10 @@ fn resolveStubsAndGotEntries(self: *Zld) !void { } fn resolveRelocsAndWriteSections(self: *Zld) !void { - for (self.objects.items) |object, object_id| { + for (self.objects.items) |object| { log.debug("relocating object {s}", .{object.name}); - for (object.sections.items) |sect, source_sect_id| { + for (object.sections.items) |sect| { if (sect.inner.flags == macho.S_MOD_INIT_FUNC_POINTERS or sect.inner.flags == macho.S_MOD_TERM_FUNC_POINTERS) continue; @@ -1614,18 +1594,15 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { log.debug("relocating section '{s},{s}'", .{ segname, sectname }); - // Get mapping - const target_mapping = self.mappings.get(.{ - .object_id = @intCast(u16, object_id), - .source_sect_id = @intCast(u16, source_sect_id), - }) orelse { - log.debug("no mapping for {s},{s}; skipping", .{ segname, sectname }); + // Get target mapping + const target_map = sect.target_map orelse { + log.debug("no mapping for '{s},{s}'; skipping", .{ segname, sectname }); continue; }; - const target_seg = self.load_commands.items[target_mapping.target_seg_id].Segment; - const target_sect = target_seg.sections.items[target_mapping.target_sect_id]; - const target_sect_addr = target_sect.addr + target_mapping.offset; - const target_sect_off = target_sect.offset + target_mapping.offset; + 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_sect_addr = target_sect.addr + target_map.offset; + const target_sect_off = target_sect.offset + target_map.offset; if (sect.relocs) |relocs| { for (relocs) |rel| { @@ -1638,11 +1615,11 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { switch (rel.@"type") { .unsigned => { - args.target_addr = try self.relocTargetAddr(@intCast(u16, object_id), rel.target); + args.target_addr = try self.relocTargetAddr(object, rel.target); const unsigned = rel.cast(reloc.Unsigned) orelse unreachable; if (unsigned.subtractor) |subtractor| { - args.subtractor = try self.relocTargetAddr(@intCast(u16, object_id), subtractor); + args.subtractor = try self.relocTargetAddr(object, subtractor); } if (rel.target == .section) { const source_sect = object.sections.items[rel.target.section]; @@ -1652,14 +1629,14 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { rebases: { var hit: bool = false; - if (target_mapping.target_seg_id == self.data_segment_cmd_index.?) { + if (target_map.segment_id == self.data_segment_cmd_index.?) { if (self.data_section_index) |index| { - if (index == target_mapping.target_sect_id) hit = true; + if (index == target_map.section_id) hit = true; } } - if (target_mapping.target_seg_id == self.data_const_segment_cmd_index.?) { + if (target_map.segment_id == self.data_const_segment_cmd_index.?) { if (self.data_const_section_index) |index| { - if (index == target_mapping.target_sect_id) hit = true; + if (index == target_map.section_id) hit = true; } } @@ -1667,7 +1644,7 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { try self.local_rebases.append(self.allocator, .{ .offset = source_addr - target_seg.inner.vmaddr, - .segment_id = target_mapping.target_seg_id, + .segment_id = target_map.segment_id, }); } // TLV is handled via a separate offset mechanism. @@ -1705,7 +1682,7 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { args.source_source_sect_addr = sect.inner.addr; args.source_target_sect_addr = source_sect.inner.addr; } - args.target_addr = try self.relocTargetAddr(@intCast(u16, object_id), rel.target); + args.target_addr = try self.relocTargetAddr(object, rel.target); }, } @@ -1744,7 +1721,7 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { } } -fn relocTargetAddr(self: *Zld, object_id: u16, target: reloc.Relocation.Target) !u64 { +fn relocTargetAddr(self: *Zld, object: *const Object, target: reloc.Relocation.Target) !u64 { const target_addr = blk: { switch (target) { .symbol => |sym| { @@ -1770,13 +1747,11 @@ fn relocTargetAddr(self: *Zld, object_id: u16, target: reloc.Relocation.Target) } }, .section => |sect_id| { - const target_mapping = self.mappings.get(.{ - .object_id = object_id, - .source_sect_id = sect_id, - }) orelse unreachable; - const target_seg = self.load_commands.items[target_mapping.target_seg_id].Segment; - const target_sect = target_seg.sections.items[target_mapping.target_sect_id]; - break :blk target_sect.addr + target_mapping.offset; + const source_sect = object.sections.items[sect_id]; + const target_map = source_sect.target_map orelse unreachable; + const target_seg = self.load_commands.items[target_map.segment_id].Segment; + const target_sect = target_seg.sections.items[target_map.section_id]; + break :blk target_sect.addr + target_map.offset; }, } }; @@ -2901,20 +2876,16 @@ fn writeDataInCode(self: *Zld) !void { const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; const text_sect = text_seg.sections.items[self.text_section_index.?]; - for (self.objects.items) |object, object_id| { - const source_seg = object.load_commands.items[object.segment_cmd_index.?].Segment; - const source_sect = source_seg.sections.items[object.text_section_index.?]; - const target_mapping = self.mappings.get(.{ - .object_id = @intCast(u16, object_id), - .source_sect_id = object.text_section_index.?, - }) orelse continue; + for (self.objects.items) |object| { + const source_sect = object.sections.items[object.text_section_index.?]; + const target_map = source_sect.target_map orelse continue; try buf.ensureCapacity( buf.items.len + object.data_in_code_entries.items.len * @sizeOf(macho.data_in_code_entry), ); for (object.data_in_code_entries.items) |dice| { const new_dice: macho.data_in_code_entry = .{ - .offset = text_sect.offset + target_mapping.offset + dice.offset, + .offset = text_sect.offset + target_map.offset + dice.offset, .length = dice.length, .kind = dice.kind, }; From 66ff56c58f3ef71f5cc3b8392632b3ec8abec43f Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 10 Jun 2021 10:29:57 +0200 Subject: [PATCH 3/9] zld: add Symbol.Tentative to denote common symbol --- src/link/MachO/Object.zig | 16 +++++++++++++--- src/link/MachO/Symbol.zig | 18 ++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 1277423b09..12ce918a36 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -388,9 +388,19 @@ pub fn parseSymbols(self: *Object) !void { } if (sym.n_value != 0) { - const comm_size = sym.n_value; - const comm_align = (sym.n_desc >> 8) & 0x0f; - log.err("Common symbol {s} in {s}: size 0x{x}, align 0x{x}", .{ sym_name, self.name.?, comm_size, comm_align }); + const tentative = try self.allocator.create(Symbol.Tentative); + errdefer self.allocator.destroy(tentative); + tentative.* = .{ + .base = .{ + .@"type" = .tentative, + .name = name, + }, + .size = sym.n_value, + .alignment = (sym.n_desc >> 8) & 0x0f, + .file = self, + }; + + log.err("Common symbol {s} in {s}: {}", .{ sym_name, self.name.?, tentative }); return error.UnhandledSymbolType; } diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig index 46203b5d0d..632bba12e6 100644 --- a/src/link/MachO/Symbol.zig +++ b/src/link/MachO/Symbol.zig @@ -12,6 +12,7 @@ pub const Type = enum { regular, proxy, unresolved, + tentative, }; /// Symbol type. @@ -94,6 +95,23 @@ pub const Unresolved = struct { pub const base_type: Symbol.Type = .unresolved; }; +pub const Tentative = struct { + base: Symbol, + + /// Symbol size. + size: u64, + + /// Symbol alignment as power of two. + alignment: u16, + + /// File where this symbol was referenced. + file: *Object, + + // TODO debug info? + + pub const base_type: Symbol.Type = .tentative; +}; + pub fn deinit(base: *Symbol, allocator: *Allocator) void { allocator.free(base.name); } From 03cda80a634d82e02e641fe04fa2abda4455966a Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 10 Jun 2021 11:03:14 +0200 Subject: [PATCH 4/9] zld: handle aliasing of tentative into regular global --- src/link/MachO/Object.zig | 10 ++++------ src/link/MachO/Zld.zig | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 12ce918a36..4501d63fff 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -351,15 +351,15 @@ pub fn parseSymbols(self: *Object) !void { const sym_name = mem.spanZ(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx)); if (Symbol.isStab(sym)) { - log.err("stab {s} in {s}", .{ sym_name, self.name.? }); + log.err("unhandled symbol type: stab {s} in {s}", .{ sym_name, self.name.? }); return error.UnhandledSymbolType; } if (Symbol.isIndr(sym)) { - log.err("indirect symbol {s} in {s}", .{ sym_name, self.name.? }); + log.err("unhandled symbol type: indirect {s} in {s}", .{ sym_name, self.name.? }); return error.UnhandledSymbolType; } if (Symbol.isAbs(sym)) { - log.err("absolute symbol {s} in {s}", .{ sym_name, self.name.? }); + log.err("unhandled symbol type: absolute {s} in {s}", .{ sym_name, self.name.? }); return error.UnhandledSymbolType; } @@ -399,9 +399,7 @@ pub fn parseSymbols(self: *Object) !void { .alignment = (sym.n_desc >> 8) & 0x0f, .file = self, }; - - log.err("Common symbol {s} in {s}: {}", .{ sym_name, self.name.?, tentative }); - return error.UnhandledSymbolType; + break :symbol &tentative.base; } const undef = try self.allocator.create(Symbol.Unresolved); diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 9f676f3fc2..017b3f7d4e 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -84,6 +84,11 @@ common_section_index: ?u16 = null, globals: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, 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, strtab: std.ArrayListUnmanaged(u8) = .{}, strtab_dir: std.StringHashMapUnmanaged(u32) = .{}, @@ -144,6 +149,7 @@ pub fn deinit(self: *Zld) void { } self.dylibs.deinit(self.allocator); + self.tentatives.deinit(self.allocator); self.globals.deinit(self.allocator); self.imports.deinit(self.allocator); self.unresolved.deinit(self.allocator); @@ -1379,6 +1385,10 @@ fn resolveSymbolsInObject(self: *Zld, object: *Object) !void { if (sym.cast(Symbol.Regular)) |reg| { if (reg.linkage == .translation_unit) continue; // Symbol local to TU. + if (self.tentatives.fetchSwapRemove(sym.name)) |kv| { + // Create link to the global. + kv.value.alias = sym; + } if (self.unresolved.fetchSwapRemove(sym.name)) |kv| { // Create link to the global. kv.value.alias = sym; @@ -1412,11 +1422,33 @@ fn resolveSymbolsInObject(self: *Zld, object: *Object) !void { g_sym.alias = sym; sym_ptr.* = sym; + } else if (sym.cast(Symbol.Tentative)) |tent| { + if (self.globals.get(sym.name)) |g_sym| { + sym.alias = g_sym; + continue; + } + + if (self.unresolved.fetchSwapRemove(sym.name)) |kv| { + kv.value.alias = sym; + } + + const t_sym = self.tentatives.get(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; } else if (sym.cast(Symbol.Unresolved)) |und| { if (self.globals.get(sym.name)) |g_sym| { sym.alias = g_sym; continue; } + if (self.tentatives.get(sym.name)) |t_sym| { + sym.alias = t_sym; + continue; + } if (self.unresolved.get(sym.name)) |u_sym| { sym.alias = u_sym; continue; From c2086efb412f17bb52514e931016e79c65bed442 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 10 Jun 2021 17:37:38 +0200 Subject: [PATCH 5/9] zld: synthetise regular from tentative definition --- src/link/MachO/Zld.zig | 133 ++++++++++++++++++++++++++++++++++++++--- 1 file 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? From 0e08cd63e236314750f18d28f8ede98a2188c6d2 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 10 Jun 2021 19:28:11 +0200 Subject: [PATCH 6/9] zld: fix debug info for regulars synthed from tentative --- src/link/MachO/Symbol.zig | 4 ++++ src/link/MachO/Zld.zig | 20 ++++++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig index 632bba12e6..2c775824c5 100644 --- a/src/link/MachO/Symbol.zig +++ b/src/link/MachO/Symbol.zig @@ -61,6 +61,10 @@ pub const Regular = struct { size: u64, } = null, + /// True if symbol was already committed into the final + /// symbol table. + visited: bool = false, + pub const base_type: Symbol.Type = .regular; pub const Linkage = enum { diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 191f63fe52..d248d283b6 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -1194,6 +1194,10 @@ fn allocateTentativeSymbols(self: *Zld) !void { .section = section, .weak_ref = false, .file = tent.file, + .stab = .{ + .kind = .global, + .size = 0, + }, }; try self.globals.putNoClobber(self.allocator, reg.base.name, ®.base); @@ -2772,8 +2776,17 @@ fn writeDebugInfo(self: *Zld) !void { }); for (object.symbols.items) |sym| { - if (sym.@"type" != .regular) continue; - const reg = sym.cast(Symbol.Regular) orelse unreachable; + const reg = reg: { + switch (sym.@"type") { + .regular => break :reg sym.cast(Symbol.Regular) orelse unreachable, + .tentative => { + const final = sym.getTopmostAlias().cast(Symbol.Regular) orelse unreachable; + if (object != final.file) continue; + break :reg final; + }, + else => continue, + } + }; if (reg.isTemp() or reg.stab == null) continue; const stab = reg.stab orelse unreachable; @@ -2877,6 +2890,7 @@ fn writeSymbolTable(self: *Zld) !void { const reg = final.cast(Symbol.Regular) orelse unreachable; if (reg.isTemp()) continue; + if (reg.visited) continue; switch (reg.linkage) { .translation_unit => { @@ -2898,6 +2912,8 @@ fn writeSymbolTable(self: *Zld) !void { }); }, } + + reg.visited = true; } } From 183d5f4a5312c090729823dd5ebf4500c0a5a5eb Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 10 Jun 2021 19:51:41 +0200 Subject: [PATCH 7/9] Add standalone test for common symbols --- test/standalone.zig | 1 + test/standalone/link_common_symbols/a.c | 6 ++++++ test/standalone/link_common_symbols/b.c | 6 ++++++ test/standalone/link_common_symbols/build.zig | 16 ++++++++++++++++ test/standalone/link_common_symbols/main.zig | 11 +++++++++++ 5 files changed, 40 insertions(+) create mode 100644 test/standalone/link_common_symbols/a.c create mode 100644 test/standalone/link_common_symbols/b.c create mode 100644 test/standalone/link_common_symbols/build.zig create mode 100644 test/standalone/link_common_symbols/main.zig diff --git a/test/standalone.zig b/test/standalone.zig index 43c68c3686..77f15db286 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -15,6 +15,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void { cases.addBuildFile("test/standalone/static_c_lib/build.zig", .{}); cases.addBuildFile("test/standalone/link_interdependent_static_c_libs/build.zig", .{}); cases.addBuildFile("test/standalone/link_static_lib_as_system_lib/build.zig", .{}); + cases.addBuildFile("test/standalone/link_common_symbols/build.zig", .{}); cases.addBuildFile("test/standalone/issue_339/build.zig", .{}); cases.addBuildFile("test/standalone/issue_8550/build.zig", .{}); cases.addBuildFile("test/standalone/issue_794/build.zig", .{}); diff --git a/test/standalone/link_common_symbols/a.c b/test/standalone/link_common_symbols/a.c new file mode 100644 index 0000000000..829a96e507 --- /dev/null +++ b/test/standalone/link_common_symbols/a.c @@ -0,0 +1,6 @@ +int i; +int j; + +int add_to_i_and_j(int x) { + return x + i + j; +} diff --git a/test/standalone/link_common_symbols/b.c b/test/standalone/link_common_symbols/b.c new file mode 100644 index 0000000000..d3789c0fdf --- /dev/null +++ b/test/standalone/link_common_symbols/b.c @@ -0,0 +1,6 @@ +long i; +int j = 2; + +void incr_i() { + i++; +} diff --git a/test/standalone/link_common_symbols/build.zig b/test/standalone/link_common_symbols/build.zig new file mode 100644 index 0000000000..43bb41fe32 --- /dev/null +++ b/test/standalone/link_common_symbols/build.zig @@ -0,0 +1,16 @@ +const Builder = @import("std").build.Builder; + +pub fn build(b: *Builder) void { + const mode = b.standardReleaseOptions(); + + const lib_a = b.addStaticLibrary("a", null); + lib_a.addCSourceFiles(&.{ "a.c", "b.c" }, &.{"-fcommon"}); + lib_a.setBuildMode(mode); + + const test_exe = b.addTest("main.zig"); + test_exe.setBuildMode(mode); + test_exe.linkLibrary(lib_a); + + const test_step = b.step("test", "Test it"); + test_step.dependOn(&test_exe.step); +} diff --git a/test/standalone/link_common_symbols/main.zig b/test/standalone/link_common_symbols/main.zig new file mode 100644 index 0000000000..9d00d0d4fb --- /dev/null +++ b/test/standalone/link_common_symbols/main.zig @@ -0,0 +1,11 @@ +const std = @import("std"); +const expect = std.testing.expect; + +extern fn incr_i() void; +extern fn add_to_i_and_j(x: c_int) c_int; + +test "import C common symbols" { + incr_i(); + const res = add_to_i_and_j(2); + try expect(res == 5); +} From e1f1f161830282ffa80f9b885fefef62862373a3 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 10 Jun 2021 21:05:20 +0200 Subject: [PATCH 8/9] zld: clean up --- src/link/MachO/Symbol.zig | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig index 2c775824c5..bb97acdf9f 100644 --- a/src/link/MachO/Symbol.zig +++ b/src/link/MachO/Symbol.zig @@ -111,8 +111,6 @@ pub const Tentative = struct { /// File where this symbol was referenced. file: *Object, - // TODO debug info? - pub const base_type: Symbol.Type = .tentative; }; From a8116dcc2727e83a4c0049ca37046e05fe9de0c5 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 11 Jun 2021 08:34:12 +0200 Subject: [PATCH 9/9] zld: fix bug in working out commons total size --- src/link/MachO/Zld.zig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index d248d283b6..e9ed34fce6 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -720,8 +720,7 @@ fn updateMetadata(self: *Zld) !void { 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; + max_align = math.max(max_align, tent.alignment); added_size += tent.size; }