| ... | ... | @@ -86,9 +86,9 @@ imports: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, |
| 86 | 86 | unresolved: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, |
| 87 | 87 | tentatives: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, |
| 88 | 88 | |
| 89 | | // /// Offset into __DATA,__common section. |
| 90 | | // /// Set if the linker found tentative definitions in any of the objects. |
| 91 | | // tentative_defs_offset: u32 = 0, |
| 89 | /// Offset into __DATA,__common section. |
| 90 | /// Set if the linker found tentative definitions in any of the objects. |
| 91 | tentative_defs_offset: u64 = 0, |
| 92 | 92 | |
| 93 | 93 | strtab: std.ArrayListUnmanaged(u8) = .{}, |
| 94 | 94 | strtab_dir: std.StringHashMapUnmanaged(u32) = .{}, |
| ... | ... | @@ -227,6 +227,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L |
| 227 | 227 | try self.allocateDataSegment(); |
| 228 | 228 | self.allocateLinkeditSegment(); |
| 229 | 229 | try self.allocateSymbols(); |
| 230 | try self.allocateTentativeSymbols(); |
| 230 | 231 | try self.flush(); |
| 231 | 232 | } |
| 232 | 233 | |
| ... | ... | @@ -691,6 +692,49 @@ fn updateMetadata(self: *Zld) !void { |
| 691 | 692 | } |
| 692 | 693 | } |
| 693 | 694 | |
| 695 | // Ensure we have __DATA,__common section if we have tentative definitions. |
| 696 | // Update size and alignment of __DATA,__common section. |
| 697 | if (self.tentatives.values().len > 0) { |
| 698 | const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; |
| 699 | const common_section_index = self.common_section_index orelse ind: { |
| 700 | self.common_section_index = @intCast(u16, data_seg.sections.items.len); |
| 701 | try data_seg.addSection(self.allocator, .{ |
| 702 | .sectname = makeStaticString("__common"), |
| 703 | .segname = makeStaticString("__DATA"), |
| 704 | .addr = 0, |
| 705 | .size = 0, |
| 706 | .offset = 0, |
| 707 | .@"align" = 0, |
| 708 | .reloff = 0, |
| 709 | .nreloc = 0, |
| 710 | .flags = macho.S_ZEROFILL, |
| 711 | .reserved1 = 0, |
| 712 | .reserved2 = 0, |
| 713 | .reserved3 = 0, |
| 714 | }); |
| 715 | break :ind self.common_section_index.?; |
| 716 | }; |
| 717 | const common_sect = &data_seg.sections.items[common_section_index]; |
| 718 | |
| 719 | var max_align: u16 = 0; |
| 720 | var added_size: u64 = 0; |
| 721 | for (self.tentatives.values()) |sym| { |
| 722 | const tent = sym.cast(Symbol.Tentative) orelse unreachable; |
| 723 | if (max_align > tent.alignment) continue; |
| 724 | max_align = tent.alignment; |
| 725 | added_size += tent.size; |
| 726 | } |
| 727 | |
| 728 | common_sect.@"align" = math.max(common_sect.@"align", max_align); |
| 729 | |
| 730 | const alignment = try math.powi(u32, 2, common_sect.@"align"); |
| 731 | const offset = mem.alignForwardGeneric(u64, common_sect.size, alignment); |
| 732 | const size = mem.alignForwardGeneric(u64, added_size, alignment); |
| 733 | |
| 734 | common_sect.size = offset + size; |
| 735 | self.tentative_defs_offset = offset; |
| 736 | } |
| 737 | |
| 694 | 738 | tlv_align: { |
| 695 | 739 | const has_tlv = |
| 696 | 740 | self.tlv_section_index != null or |
| ... | ... | @@ -1110,6 +1154,70 @@ fn allocateSymbols(self: *Zld) !void { |
| 1110 | 1154 | } |
| 1111 | 1155 | } |
| 1112 | 1156 | |
| 1157 | fn allocateTentativeSymbols(self: *Zld) !void { |
| 1158 | if (self.tentatives.values().len == 0) return; |
| 1159 | |
| 1160 | const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; |
| 1161 | const common_sect = &data_seg.sections.items[self.common_section_index.?]; |
| 1162 | |
| 1163 | const alignment = try math.powi(u32, 2, common_sect.@"align"); |
| 1164 | var base_address: u64 = common_sect.addr + self.tentative_defs_offset; |
| 1165 | |
| 1166 | log.debug("base address for tentative definitions 0x{x}", .{base_address}); |
| 1167 | |
| 1168 | // TODO there might be a more generic way of doing this. |
| 1169 | var section: u8 = 0; |
| 1170 | for (self.load_commands.items) |cmd, cmd_id| { |
| 1171 | if (cmd != .Segment) break; |
| 1172 | if (cmd_id == self.data_segment_cmd_index.?) { |
| 1173 | section += @intCast(u8, self.common_section_index.?) + 1; |
| 1174 | break; |
| 1175 | } |
| 1176 | section += @intCast(u8, cmd.Segment.sections.items.len); |
| 1177 | } |
| 1178 | |
| 1179 | // Convert tentative definitions into regular symbols. |
| 1180 | for (self.tentatives.values()) |sym, i| { |
| 1181 | const tent = sym.cast(Symbol.Tentative) orelse unreachable; |
| 1182 | const reg = try self.allocator.create(Symbol.Regular); |
| 1183 | errdefer self.allocator.destroy(reg); |
| 1184 | |
| 1185 | reg.* = .{ |
| 1186 | .base = .{ |
| 1187 | .@"type" = .regular, |
| 1188 | .name = try self.allocator.dupe(u8, tent.base.name), |
| 1189 | .got_index = tent.base.got_index, |
| 1190 | .stubs_index = tent.base.stubs_index, |
| 1191 | }, |
| 1192 | .linkage = .global, |
| 1193 | .address = base_address, |
| 1194 | .section = section, |
| 1195 | .weak_ref = false, |
| 1196 | .file = tent.file, |
| 1197 | }; |
| 1198 | |
| 1199 | try self.globals.putNoClobber(self.allocator, reg.base.name, &reg.base); |
| 1200 | tent.base.alias = &reg.base; |
| 1201 | |
| 1202 | if (tent.base.got_index) |idx| { |
| 1203 | self.got_entries.items[idx] = &reg.base; |
| 1204 | } |
| 1205 | if (tent.base.stubs_index) |idx| { |
| 1206 | self.stubs.items[idx] = &reg.base; |
| 1207 | } |
| 1208 | |
| 1209 | const address = mem.alignForwardGeneric(u64, base_address + tent.size, alignment); |
| 1210 | |
| 1211 | log.debug("tentative definition '{s}' allocated from 0x{x} to 0x{x}", .{ |
| 1212 | tent.base.name, |
| 1213 | base_address, |
| 1214 | address, |
| 1215 | }); |
| 1216 | |
| 1217 | base_address = address; |
| 1218 | } |
| 1219 | } |
| 1220 | |
| 1113 | 1221 | fn writeStubHelperCommon(self: *Zld) !void { |
| 1114 | 1222 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1115 | 1223 | const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?]; |
| ... | ... | @@ -1432,14 +1540,25 @@ fn resolveSymbolsInObject(self: *Zld, object: *Object) !void { |
| 1432 | 1540 | kv.value.alias = sym; |
| 1433 | 1541 | } |
| 1434 | 1542 | |
| 1435 | | const t_sym = self.tentatives.get(sym.name) orelse { |
| 1543 | const sym_ptr = self.tentatives.getPtr(sym.name) orelse { |
| 1436 | 1544 | // Put new tentative definition symbol into symbol table. |
| 1437 | 1545 | try self.tentatives.putNoClobber(self.allocator, sym.name, sym); |
| 1438 | 1546 | continue; |
| 1439 | 1547 | }; |
| 1440 | 1548 | |
| 1441 | | // TODO compare by size and pick the largest. |
| 1442 | | return error.TODOResolveTentatives; |
| 1549 | // Compare by size and pick the largest tentative definition. |
| 1550 | // We model this like a heap where the tentative definition with the |
| 1551 | // largest size always washes up on top. |
| 1552 | const t_sym = sym_ptr.*; |
| 1553 | const t_tent = t_sym.cast(Symbol.Tentative) orelse unreachable; |
| 1554 | |
| 1555 | if (tent.size < t_tent.size) { |
| 1556 | sym.alias = t_sym; |
| 1557 | continue; |
| 1558 | } |
| 1559 | |
| 1560 | t_sym.alias = sym; |
| 1561 | sym_ptr.* = sym; |
| 1443 | 1562 | } else if (sym.cast(Symbol.Unresolved)) |und| { |
| 1444 | 1563 | if (self.globals.get(sym.name)) |g_sym| { |
| 1445 | 1564 | sym.alias = g_sym; |
| ... | ... | @@ -1453,6 +1572,7 @@ fn resolveSymbolsInObject(self: *Zld, object: *Object) !void { |
| 1453 | 1572 | sym.alias = u_sym; |
| 1454 | 1573 | continue; |
| 1455 | 1574 | } |
| 1575 | |
| 1456 | 1576 | try self.unresolved.putNoClobber(self.allocator, sym.name, sym); |
| 1457 | 1577 | } else unreachable; |
| 1458 | 1578 | } |
| ... | ... | @@ -1494,7 +1614,6 @@ fn resolveSymbols(self: *Zld) !void { |
| 1494 | 1614 | next_sym += 1; |
| 1495 | 1615 | } |
| 1496 | 1616 | } |
| 1497 | | |
| 1498 | 1617 | // Third pass, resolve symbols in dynamic libraries. |
| 1499 | 1618 | // TODO Implement libSystem as a hard-coded library, or ship with |
| 1500 | 1619 | // a libSystem.B.tbd definition file? |