| ... | @@ -86,9 +86,9 @@ imports: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, | ... | @@ -86,9 +86,9 @@ imports: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, |
| 86 | unresolved: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, | 86 | unresolved: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, |
| 87 | tentatives: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, | 87 | tentatives: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, |
| 88 | | 88 | |
| 89 | // /// Offset into __DATA,__common section. | 89 | /// Offset into __DATA,__common section. |
| 90 | // /// Set if the linker found tentative definitions in any of the objects. | 90 | /// Set if the linker found tentative definitions in any of the objects. |
| 91 | // tentative_defs_offset: u32 = 0, | 91 | tentative_defs_offset: u64 = 0, |
| 92 | | 92 | |
| 93 | strtab: std.ArrayListUnmanaged(u8) = .{}, | 93 | strtab: std.ArrayListUnmanaged(u8) = .{}, |
| 94 | strtab_dir: std.StringHashMapUnmanaged(u32) = .{}, | 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,6 +227,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L |
| 227 | try self.allocateDataSegment(); | 227 | try self.allocateDataSegment(); |
| 228 | self.allocateLinkeditSegment(); | 228 | self.allocateLinkeditSegment(); |
| 229 | try self.allocateSymbols(); | 229 | try self.allocateSymbols(); |
| | 230 | try self.allocateTentativeSymbols(); |
| 230 | try self.flush(); | 231 | try self.flush(); |
| 231 | } | 232 | } |
| 232 | | 233 | |
| ... | @@ -691,6 +692,49 @@ fn updateMetadata(self: *Zld) !void { | ... | @@ -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 | tlv_align: { | 738 | tlv_align: { |
| 695 | const has_tlv = | 739 | const has_tlv = |
| 696 | self.tlv_section_index != null or | 740 | self.tlv_section_index != null or |
| ... | @@ -1110,6 +1154,70 @@ fn allocateSymbols(self: *Zld) !void { | ... | @@ -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 | fn writeStubHelperCommon(self: *Zld) !void { | 1221 | fn writeStubHelperCommon(self: *Zld) !void { |
| 1114 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | 1222 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1115 | const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?]; | 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,14 +1540,25 @@ fn resolveSymbolsInObject(self: *Zld, object: *Object) !void { |
| 1432 | kv.value.alias = sym; | 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 | // Put new tentative definition symbol into symbol table. | 1544 | // Put new tentative definition symbol into symbol table. |
| 1437 | try self.tentatives.putNoClobber(self.allocator, sym.name, sym); | 1545 | try self.tentatives.putNoClobber(self.allocator, sym.name, sym); |
| 1438 | continue; | 1546 | continue; |
| 1439 | }; | 1547 | }; |
| 1440 | | 1548 | |
| 1441 | // TODO compare by size and pick the largest. | 1549 | // Compare by size and pick the largest tentative definition. |
| 1442 | return error.TODOResolveTentatives; | 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 | } else if (sym.cast(Symbol.Unresolved)) |und| { | 1562 | } else if (sym.cast(Symbol.Unresolved)) |und| { |
| 1444 | if (self.globals.get(sym.name)) |g_sym| { | 1563 | if (self.globals.get(sym.name)) |g_sym| { |
| 1445 | sym.alias = g_sym; | 1564 | sym.alias = g_sym; |
| ... | @@ -1453,6 +1572,7 @@ fn resolveSymbolsInObject(self: *Zld, object: *Object) !void { | ... | @@ -1453,6 +1572,7 @@ fn resolveSymbolsInObject(self: *Zld, object: *Object) !void { |
| 1453 | sym.alias = u_sym; | 1572 | sym.alias = u_sym; |
| 1454 | continue; | 1573 | continue; |
| 1455 | } | 1574 | } |
| | 1575 | |
| 1456 | try self.unresolved.putNoClobber(self.allocator, sym.name, sym); | 1576 | try self.unresolved.putNoClobber(self.allocator, sym.name, sym); |
| 1457 | } else unreachable; | 1577 | } else unreachable; |
| 1458 | } | 1578 | } |
| ... | @@ -1494,7 +1614,6 @@ fn resolveSymbols(self: *Zld) !void { | ... | @@ -1494,7 +1614,6 @@ fn resolveSymbols(self: *Zld) !void { |
| 1494 | next_sym += 1; | 1614 | next_sym += 1; |
| 1495 | } | 1615 | } |
| 1496 | } | 1616 | } |
| 1497 | | | |
| 1498 | // Third pass, resolve symbols in dynamic libraries. | 1617 | // Third pass, resolve symbols in dynamic libraries. |
| 1499 | // TODO Implement libSystem as a hard-coded library, or ship with | 1618 | // TODO Implement libSystem as a hard-coded library, or ship with |
| 1500 | // a libSystem.B.tbd definition file? | 1619 | // a libSystem.B.tbd definition file? |