authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-09-13 15:27:25+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-09-18 10:00:04+02:00
loge601969244d9e3da7f6c88792932297d87c821eb
tree272c198c88b1edb5f366d20ffa887f4298bba19e
parent79ab46ec918edc5d31c87a2535a30b8d2207228c

macho: rewrite how we allocate space in incremental context


8 files changed, 533 insertions(+), 850 deletions(-)

lib/std/macho.zig+5
...@@ -798,6 +798,11 @@ pub const section_64 = extern struct {...@@ -798,6 +798,11 @@ pub const section_64 = extern struct {
798 return tt == S_ZEROFILL or tt == S_GB_ZEROFILL or tt == S_THREAD_LOCAL_ZEROFILL;798 return tt == S_ZEROFILL or tt == S_GB_ZEROFILL or tt == S_THREAD_LOCAL_ZEROFILL;
799 }799 }
800800
801 pub fn isSymbolStubs(sect: section_64) bool {
802 const tt = sect.@"type"();
803 return tt == S_SYMBOL_STUBS;
804 }
805
801 pub fn isDebug(sect: section_64) bool {806 pub fn isDebug(sect: section_64) bool {
802 return sect.attrs() & S_ATTR_DEBUG != 0;807 return sect.attrs() & S_ATTR_DEBUG != 0;
803 }808 }
src/link/Dwarf.zig+15-2
...@@ -948,7 +948,7 @@ pub fn commitDeclState(...@@ -948,7 +948,7 @@ pub fn commitDeclState(
948 new_offset,948 new_offset,
949 });949 });
950950
951 try File.MachO.copyRangeAllOverlappingAlloc(951 try copyRangeAllOverlappingAlloc(
952 gpa,952 gpa,
953 d_sym.file,953 d_sym.file,
954 debug_line_sect.offset,954 debug_line_sect.offset,
...@@ -1247,7 +1247,7 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co...@@ -1247,7 +1247,7 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co
1247 new_offset,1247 new_offset,
1248 });1248 });
12491249
1250 try File.MachO.copyRangeAllOverlappingAlloc(1250 try copyRangeAllOverlappingAlloc(
1251 gpa,1251 gpa,
1252 d_sym.file,1252 d_sym.file,
1253 debug_info_sect.offset,1253 debug_info_sect.offset,
...@@ -2338,3 +2338,16 @@ fn addDbgInfoErrorSet(...@@ -2338,3 +2338,16 @@ fn addDbgInfoErrorSet(
2338 // DW.AT.enumeration_type delimit children2338 // DW.AT.enumeration_type delimit children
2339 try dbg_info_buffer.append(0);2339 try dbg_info_buffer.append(0);
2340}2340}
2341
2342fn copyRangeAllOverlappingAlloc(
2343 allocator: Allocator,
2344 file: std.fs.File,
2345 in_offset: u64,
2346 out_offset: u64,
2347 len: usize,
2348) !void {
2349 const buf = try allocator.alloc(u8, len);
2350 defer allocator.free(buf);
2351 const amt = try file.preadAll(buf, in_offset);
2352 try file.pwriteAll(buf[0..amt], out_offset);
2353}
src/link/MachO.zig+480-837
...@@ -115,6 +115,7 @@ segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},...@@ -115,6 +115,7 @@ segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
115sections: std.MultiArrayList(Section) = .{},115sections: std.MultiArrayList(Section) = .{},
116116
117pagezero_segment_cmd_index: ?u8 = null,117pagezero_segment_cmd_index: ?u8 = null,
118header_segment_cmd_index: ?u8 = null,
118text_segment_cmd_index: ?u8 = null,119text_segment_cmd_index: ?u8 = null,
119data_const_segment_cmd_index: ?u8 = null,120data_const_segment_cmd_index: ?u8 = null,
120data_segment_cmd_index: ?u8 = null,121data_segment_cmd_index: ?u8 = null,
...@@ -124,6 +125,7 @@ text_section_index: ?u8 = null,...@@ -124,6 +125,7 @@ text_section_index: ?u8 = null,
124stubs_section_index: ?u8 = null,125stubs_section_index: ?u8 = null,
125stub_helper_section_index: ?u8 = null,126stub_helper_section_index: ?u8 = null,
126got_section_index: ?u8 = null,127got_section_index: ?u8 = null,
128data_const_section_index: ?u8 = null,
127la_symbol_ptr_section_index: ?u8 = null,129la_symbol_ptr_section_index: ?u8 = null,
128data_section_index: ?u8 = null,130data_section_index: ?u8 = null,
129131
...@@ -157,6 +159,8 @@ stubs_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},...@@ -157,6 +159,8 @@ stubs_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
157159
158error_flags: File.ErrorFlags = File.ErrorFlags{},160error_flags: File.ErrorFlags = File.ErrorFlags{},
159161
162segment_table_dirty: bool = false,
163
160/// A helper var to indicate if we are at the start of the incremental updates, or164/// A helper var to indicate if we are at the start of the incremental updates, or
161/// already somewhere further along the update-and-run chain.165/// already somewhere further along the update-and-run chain.
162/// TODO once we add opening a prelinked output binary from file, this will become166/// TODO once we add opening a prelinked output binary from file, this will become
...@@ -1066,136 +1070,6 @@ pub fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs:...@@ -1066,136 +1070,6 @@ pub fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs:
1066 }1070 }
1067}1071}
10681072
1069const GetOutputSectionResult = struct {
1070 found_existing: bool,
1071 sect_id: u8,
1072};
1073
1074pub fn getOutputSection(self: *MachO, sect: macho.section_64) !?GetOutputSectionResult {
1075 const segname = sect.segName();
1076 const sectname = sect.sectName();
1077
1078 var found_existing: bool = true;
1079 const sect_id: u8 = blk: {
1080 if (mem.eql(u8, "__LLVM", segname)) {
1081 log.debug("TODO LLVM section: type 0x{x}, name '{s},{s}'", .{
1082 sect.flags, segname, sectname,
1083 });
1084 return null;
1085 }
1086
1087 if (sect.isCode()) {
1088 if (self.text_section_index == null) {
1089 self.text_section_index = try self.initSection("__TEXT", "__text", .{
1090 .flags = macho.S_REGULAR |
1091 macho.S_ATTR_PURE_INSTRUCTIONS |
1092 macho.S_ATTR_SOME_INSTRUCTIONS,
1093 });
1094 found_existing = false;
1095 }
1096 break :blk self.text_section_index.?;
1097 }
1098
1099 if (sect.isDebug()) {
1100 // TODO debug attributes
1101 if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) {
1102 log.debug("TODO compact unwind section: type 0x{x}, name '{s},{s}'", .{
1103 sect.flags, segname, sectname,
1104 });
1105 }
1106 return null;
1107 }
1108
1109 switch (sect.@"type"()) {
1110 macho.S_4BYTE_LITERALS,
1111 macho.S_8BYTE_LITERALS,
1112 macho.S_16BYTE_LITERALS,
1113 => {
1114 if (self.getSectionByName("__TEXT", "__const")) |sect_id| break :blk sect_id;
1115 found_existing = false;
1116 break :blk try self.initSection("__TEXT", "__const", .{});
1117 },
1118 macho.S_CSTRING_LITERALS => {
1119 if (mem.startsWith(u8, sectname, "__objc")) {
1120 if (self.getSectionByName(segname, sectname)) |sect_id| break :blk sect_id;
1121 found_existing = false;
1122 break :blk try self.initSection(segname, sectname, .{});
1123 }
1124 if (self.getSectionByName("__TEXT", "__cstring")) |sect_id| break :blk sect_id;
1125 found_existing = false;
1126 break :blk try self.initSection("__TEXT", "__cstring", .{
1127 .flags = macho.S_CSTRING_LITERALS,
1128 });
1129 },
1130 macho.S_MOD_INIT_FUNC_POINTERS,
1131 macho.S_MOD_TERM_FUNC_POINTERS,
1132 => {
1133 if (self.getSectionByName("__DATA_CONST", sectname)) |sect_id| break :blk sect_id;
1134 found_existing = false;
1135 break :blk try self.initSection("__DATA_CONST", sectname, .{
1136 .flags = sect.flags,
1137 });
1138 },
1139 macho.S_LITERAL_POINTERS,
1140 macho.S_ZEROFILL,
1141 macho.S_THREAD_LOCAL_VARIABLES,
1142 macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
1143 macho.S_THREAD_LOCAL_REGULAR,
1144 macho.S_THREAD_LOCAL_ZEROFILL,
1145 => {
1146 if (self.getSectionByName(segname, sectname)) |sect_id| break :blk sect_id;
1147 found_existing = false;
1148 break :blk try self.initSection(segname, sectname, .{ .flags = sect.flags });
1149 },
1150 macho.S_COALESCED => {
1151 if (self.getSectionByName(segname, sectname)) |sect_id| break :blk sect_id;
1152 found_existing = false;
1153 break :blk try self.initSection(segname, sectname, .{});
1154 },
1155 macho.S_REGULAR => {
1156 if (mem.eql(u8, segname, "__TEXT")) {
1157 if (mem.eql(u8, sectname, "__rodata") or
1158 mem.eql(u8, sectname, "__typelink") or
1159 mem.eql(u8, sectname, "__itablink") or
1160 mem.eql(u8, sectname, "__gosymtab") or
1161 mem.eql(u8, sectname, "__gopclntab"))
1162 {
1163 if (self.getSectionByName("__DATA_CONST", "__const")) |sect_id| break :blk sect_id;
1164 found_existing = false;
1165 break :blk try self.initSection("__DATA_CONST", "__const", .{});
1166 }
1167 }
1168 if (mem.eql(u8, segname, "__DATA")) {
1169 if (mem.eql(u8, sectname, "__const") or
1170 mem.eql(u8, sectname, "__cfstring") or
1171 mem.eql(u8, sectname, "__objc_classlist") or
1172 mem.eql(u8, sectname, "__objc_imageinfo"))
1173 {
1174 if (self.getSectionByName("__DATA_CONST", sectname)) |sect_id| break :blk sect_id;
1175 found_existing = false;
1176 break :blk try self.initSection("__DATA_CONST", sectname, .{});
1177 } else if (mem.eql(u8, sectname, "__data")) {
1178 if (self.data_section_index == null) {
1179 self.data_section_index = try self.initSection(segname, sectname, .{});
1180 found_existing = false;
1181 }
1182 break :blk self.data_section_index.?;
1183 }
1184 }
1185 if (self.getSectionByName(segname, sectname)) |sect_id| break :blk sect_id;
1186 found_existing = false;
1187 break :blk try self.initSection(segname, sectname, .{});
1188 },
1189 else => return null,
1190 }
1191 };
1192
1193 return GetOutputSectionResult{
1194 .found_existing = found_existing,
1195 .sect_id = sect_id,
1196 };
1197}
1198
1199pub fn createEmptyAtom(gpa: Allocator, sym_index: u32, size: u64, alignment: u32) !*Atom {1073pub fn createEmptyAtom(gpa: Allocator, sym_index: u32, size: u64, alignment: u32) !*Atom {
1200 const size_usize = math.cast(usize, size) orelse return error.Overflow;1074 const size_usize = math.cast(usize, size) orelse return error.Overflow;
1201 const atom = try gpa.create(Atom);1075 const atom = try gpa.create(Atom);
...@@ -1263,7 +1137,11 @@ pub fn allocateSpecialSymbols(self: *MachO) !void {...@@ -1263,7 +1137,11 @@ pub fn allocateSpecialSymbols(self: *MachO) !void {
1263 const global = self.getGlobal(name) orelse continue;1137 const global = self.getGlobal(name) orelse continue;
1264 if (global.file != null) continue;1138 if (global.file != null) continue;
1265 const sym = self.getSymbolPtr(global);1139 const sym = self.getSymbolPtr(global);
1266 const seg = self.segments.items[self.text_segment_cmd_index.?];1140 const seg_id = switch (self.mode) {
1141 .incremental => self.sections.items(.segment_index)[self.text_section_index.?],
1142 .one_shot => self.text_segment_cmd_index.?,
1143 };
1144 const seg = self.segments.items[seg_id];
1267 sym.n_sect = 1;1145 sym.n_sect = 1;
1268 sym.n_value = seg.vmaddr;1146 sym.n_value = seg.vmaddr;
12691147
...@@ -1284,7 +1162,7 @@ pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {...@@ -1284,7 +1162,7 @@ pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
1284 atom.* = Atom.empty;1162 atom.* = Atom.empty;
1285 atom.sym_index = sym_index;1163 atom.sym_index = sym_index;
1286 atom.size = @sizeOf(u64);1164 atom.size = @sizeOf(u64);
1287 atom.alignment = 3;1165 atom.alignment = @alignOf(u64);
1288 break :blk atom;1166 break :blk atom;
1289 },1167 },
1290 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3),1168 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3),
...@@ -1357,39 +1235,6 @@ pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {...@@ -1357,39 +1235,6 @@ pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
1357 return atom;1235 return atom;
1358}1236}
13591237
1360pub fn createTlvPtrAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
1361 assert(self.mode == .one_shot);
1362
1363 const gpa = self.base.allocator;
1364 const sym_index = try self.allocateSymbol();
1365 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);
1366
1367 const target_sym = self.getSymbol(target);
1368 assert(target_sym.undf());
1369
1370 const global = self.getGlobal(self.getSymbolName(target)).?;
1371 try atom.bindings.append(gpa, .{
1372 .target = global,
1373 .offset = 0,
1374 });
1375
1376 try self.managed_atoms.append(gpa, atom);
1377 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
1378
1379 const sym = atom.getSymbolPtr(self);
1380 sym.n_type = macho.N_SECT;
1381 const gop = (try self.getOutputSection(.{
1382 .segname = makeStaticString("__DATA"),
1383 .sectname = makeStaticString("__thread_ptrs"),
1384 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
1385 })).?;
1386 sym.n_sect = gop.sect_id + 1;
1387
1388 try self.addAtomToSection(atom);
1389
1390 return atom;
1391}
1392
1393pub fn createDyldPrivateAtom(self: *MachO) !void {1238pub fn createDyldPrivateAtom(self: *MachO) !void {
1394 if (self.dyld_stub_binder_index == null) return;1239 if (self.dyld_stub_binder_index == null) return;
1395 if (self.dyld_private_atom != null) return;1240 if (self.dyld_private_atom != null) return;
...@@ -1403,7 +1248,7 @@ pub fn createDyldPrivateAtom(self: *MachO) !void {...@@ -1403,7 +1248,7 @@ pub fn createDyldPrivateAtom(self: *MachO) !void {
1403 atom.* = Atom.empty;1248 atom.* = Atom.empty;
1404 atom.sym_index = sym_index;1249 atom.sym_index = sym_index;
1405 atom.size = @sizeOf(u64);1250 atom.size = @sizeOf(u64);
1406 atom.alignment = 3;1251 atom.alignment = @alignOf(u64);
1407 break :blk atom;1252 break :blk atom;
1408 },1253 },
1409 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3),1254 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3),
...@@ -1450,7 +1295,11 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -1450,7 +1295,11 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
1450 atom.* = Atom.empty;1295 atom.* = Atom.empty;
1451 atom.sym_index = sym_index;1296 atom.sym_index = sym_index;
1452 atom.size = size;1297 atom.size = size;
1453 atom.alignment = alignment;1298 atom.alignment = switch (arch) {
1299 .x86_64 => 1,
1300 .aarch64 => @alignOf(u32),
1301 else => unreachable,
1302 };
1454 break :blk atom;1303 break :blk atom;
1455 },1304 },
1456 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, size, alignment),1305 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, size, alignment),
...@@ -1621,7 +1470,7 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -1621,7 +1470,7 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
1621 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);1470 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
16221471
1623 if (self.mode == .incremental) {1472 if (self.mode == .incremental) {
1624 sym.n_value = try self.allocateAtom(atom, size, math.powi(u32, 2, alignment) catch unreachable);1473 sym.n_value = try self.allocateAtom(atom, size, atom.alignment);
1625 log.debug("allocated stub preamble atom at 0x{x}", .{sym.n_value});1474 log.debug("allocated stub preamble atom at 0x{x}", .{sym.n_value});
1626 try self.writeAtom(atom, code);1475 try self.writeAtom(atom, code);
1627 } else {1476 } else {
...@@ -1650,7 +1499,11 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -1650,7 +1499,11 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
1650 atom.* = Atom.empty;1499 atom.* = Atom.empty;
1651 atom.sym_index = sym_index;1500 atom.sym_index = sym_index;
1652 atom.size = size;1501 atom.size = size;
1653 atom.alignment = alignment;1502 atom.alignment = switch (arch) {
1503 .x86_64 => 1,
1504 .aarch64 => @alignOf(u32),
1505 else => unreachable,
1506 };
1654 break :blk atom;1507 break :blk atom;
1655 },1508 },
1656 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, size, alignment),1509 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, size, alignment),
...@@ -1738,7 +1591,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -1738,7 +1591,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
1738 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);1591 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
17391592
1740 if (self.mode == .incremental) {1593 if (self.mode == .incremental) {
1741 sym.n_value = try self.allocateAtom(atom, size, math.powi(u32, 2, alignment) catch unreachable);1594 sym.n_value = try self.allocateAtom(atom, size, atom.alignment);
1742 log.debug("allocated stub helper atom at 0x{x}", .{sym.n_value});1595 log.debug("allocated stub helper atom at 0x{x}", .{sym.n_value});
1743 try self.writeAtom(atom, code);1596 try self.writeAtom(atom, code);
1744 } else {1597 } else {
...@@ -1758,7 +1611,7 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWi...@@ -1758,7 +1611,7 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWi
1758 atom.* = Atom.empty;1611 atom.* = Atom.empty;
1759 atom.sym_index = sym_index;1612 atom.sym_index = sym_index;
1760 atom.size = @sizeOf(u64);1613 atom.size = @sizeOf(u64);
1761 atom.alignment = 3;1614 atom.alignment = @alignOf(u64);
1762 break :blk atom;1615 break :blk atom;
1763 },1616 },
1764 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3),1617 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3),
...@@ -1843,7 +1696,12 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -1843,7 +1696,12 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
1843 atom.* = Atom.empty;1696 atom.* = Atom.empty;
1844 atom.sym_index = sym_index;1697 atom.sym_index = sym_index;
1845 atom.size = size;1698 atom.size = size;
1846 atom.alignment = alignment;1699 atom.alignment = switch (arch) {
1700 .x86_64 => 1,
1701 .aarch64 => @alignOf(u32),
1702 else => unreachable, // unhandled architecture type
1703
1704 };
1847 break :blk atom;1705 break :blk atom;
1848 },1706 },
1849 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, size, alignment),1707 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, size, alignment),
...@@ -1945,7 +1803,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -1945,7 +1803,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
1945 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);1803 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
19461804
1947 if (self.mode == .incremental) {1805 if (self.mode == .incremental) {
1948 sym.n_value = try self.allocateAtom(atom, size, math.powi(u32, 2, alignment) catch unreachable);1806 sym.n_value = try self.allocateAtom(atom, size, atom.alignment);
1949 log.debug("allocated stub atom at 0x{x}", .{sym.n_value});1807 log.debug("allocated stub atom at 0x{x}", .{sym.n_value});
1950 try self.writeAtom(atom, code);1808 try self.writeAtom(atom, code);
1951 } else {1809 } else {
...@@ -1956,7 +1814,41 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -1956,7 +1814,41 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
1956 return atom;1814 return atom;
1957}1815}
19581816
1817pub fn createTlvPtrAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
1818 assert(self.mode == .one_shot);
1819
1820 const gpa = self.base.allocator;
1821 const sym_index = try self.allocateSymbol();
1822 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);
1823
1824 const target_sym = self.getSymbol(target);
1825 assert(target_sym.undf());
1826
1827 const global = self.getGlobal(self.getSymbolName(target)).?;
1828 try atom.bindings.append(gpa, .{
1829 .target = global,
1830 .offset = 0,
1831 });
1832
1833 try self.managed_atoms.append(gpa, atom);
1834 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
1835
1836 const sym = atom.getSymbolPtr(self);
1837 sym.n_type = macho.N_SECT;
1838 const sect_id = (try self.getOutputSection(.{
1839 .segname = makeStaticString("__DATA"),
1840 .sectname = makeStaticString("__thread_ptrs"),
1841 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
1842 })).?;
1843 sym.n_sect = sect_id + 1;
1844
1845 try self.addAtomToSection(atom);
1846
1847 return atom;
1848}
1849
1959pub fn createTentativeDefAtoms(self: *MachO) !void {1850pub fn createTentativeDefAtoms(self: *MachO) !void {
1851 assert(self.mode == .one_shot);
1960 const gpa = self.base.allocator;1852 const gpa = self.base.allocator;
19611853
1962 for (self.globals.items) |global| {1854 for (self.globals.items) |global| {
...@@ -1971,20 +1863,15 @@ pub fn createTentativeDefAtoms(self: *MachO) !void {...@@ -1971,20 +1863,15 @@ pub fn createTentativeDefAtoms(self: *MachO) !void {
1971 // text blocks for each tentative definition.1863 // text blocks for each tentative definition.
1972 const size = sym.n_value;1864 const size = sym.n_value;
1973 const alignment = (sym.n_desc >> 8) & 0x0f;1865 const alignment = (sym.n_desc >> 8) & 0x0f;
1974 const gop = (try self.getOutputSection(.{1866 const sect_id = (try self.getOutputSection(.{
1975 .segname = makeStaticString("__DATA"),1867 .segname = makeStaticString("__DATA"),
1976 .sectname = makeStaticString("__bss"),1868 .sectname = makeStaticString("__bss"),
1977 .flags = macho.S_ZEROFILL,1869 .flags = macho.S_ZEROFILL,
1978 })).?;1870 })).?;
1979 if (self.mode == .incremental and !gop.found_existing) {
1980 // TODO allocate section
1981 try self.allocateSection(gop.sect_id, size, alignment);
1982 }
1983
1984 sym.* = .{1871 sym.* = .{
1985 .n_strx = sym.n_strx,1872 .n_strx = sym.n_strx,
1986 .n_type = macho.N_SECT | macho.N_EXT,1873 .n_type = macho.N_SECT | macho.N_EXT,
1987 .n_sect = gop.sect_id,1874 .n_sect = sect_id + 1,
1988 .n_desc = 0,1875 .n_desc = 0,
1989 .n_value = 0,1876 .n_value = 0,
1990 };1877 };
...@@ -1992,7 +1879,7 @@ pub fn createTentativeDefAtoms(self: *MachO) !void {...@@ -1992,7 +1879,7 @@ pub fn createTentativeDefAtoms(self: *MachO) !void {
1992 const atom = try MachO.createEmptyAtom(gpa, global.sym_index, size, alignment);1879 const atom = try MachO.createEmptyAtom(gpa, global.sym_index, size, alignment);
1993 atom.file = global.file;1880 atom.file = global.file;
19941881
1995 try self.allocateAtomCommon(atom);1882 try self.addAtomToSection(atom);
19961883
1997 if (global.file) |file| {1884 if (global.file) |file| {
1998 const object = &self.objects.items[file];1885 const object = &self.objects.items[file];
...@@ -2350,7 +2237,12 @@ pub fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {...@@ -2350,7 +2237,12 @@ pub fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
23502237
2351pub fn writeMainLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {2238pub fn writeMainLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
2352 if (self.base.options.output_mode != .Exe) return;2239 if (self.base.options.output_mode != .Exe) return;
2353 const seg = self.segments.items[self.text_segment_cmd_index.?];2240 const seg_id = switch (self.mode) {
2241 .incremental => self.header_segment_cmd_index.?,
2242 // .incremental => self.sections.items(.segment_index)[self.text_section_index.?],
2243 .one_shot => self.text_segment_cmd_index.?,
2244 };
2245 const seg = self.segments.items[seg_id];
2354 const global = try self.getEntryPoint();2246 const global = try self.getEntryPoint();
2355 const sym = self.getSymbol(global);2247 const sym = self.getSymbol(global);
2356 try lc_writer.writeStruct(macho.entry_point_command{2248 try lc_writer.writeStruct(macho.entry_point_command{
...@@ -2946,14 +2838,8 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu...@@ -2946,14 +2838,8 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
29462838
2947 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);2839 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
2948 atom.size = code.len;2840 atom.size = code.len;
2949 atom.alignment = math.log2(required_alignment);2841 atom.alignment = required_alignment;
2950 const sect_id = try self.getOutputSectionAtom(2842 const sect_id = self.getDeclOutputSection(decl);
2951 atom,
2952 decl_name,
2953 typed_value.ty,
2954 typed_value.val,
2955 required_alignment,
2956 );
2957 const symbol = atom.getSymbolPtr(self);2843 const symbol = atom.getSymbolPtr(self);
2958 symbol.n_strx = name_str_index;2844 symbol.n_strx = name_str_index;
2959 symbol.n_type = macho.N_SECT;2845 symbol.n_type = macho.N_SECT;
...@@ -3050,85 +2936,16 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)...@@ -3050,85 +2936,16 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
3050 try self.updateDeclExports(module, decl_index, decl_exports);2936 try self.updateDeclExports(module, decl_index, decl_exports);
3051}2937}
30522938
3053/// Checks if the value, or any of its embedded values stores a pointer, and thus requires2939fn getDeclOutputSection(self: *MachO, decl: *Module.Decl) u8 {
3054/// a rebase opcode for the dynamic linker.2940 const ty = decl.ty;
3055fn needsPointerRebase(ty: Type, val: Value, mod: *Module) bool {2941 const val = decl.val;
3056 if (ty.zigTypeTag() == .Fn) {
3057 return false;
3058 }
3059 if (val.pointerDecl()) |_| {
3060 return true;
3061 }
3062
3063 switch (ty.zigTypeTag()) {
3064 .Fn => unreachable,
3065 .Pointer => return true,
3066 .Array, .Vector => {
3067 if (ty.arrayLen() == 0) return false;
3068 const elem_ty = ty.childType();
3069 var elem_value_buf: Value.ElemValueBuffer = undefined;
3070 const elem_val = val.elemValueBuffer(mod, 0, &elem_value_buf);
3071 return needsPointerRebase(elem_ty, elem_val, mod);
3072 },
3073 .Struct => {
3074 const fields = ty.structFields().values();
3075 if (fields.len == 0) return false;
3076 if (val.castTag(.aggregate)) |payload| {
3077 const field_values = payload.data;
3078 for (field_values) |field_val, i| {
3079 if (needsPointerRebase(fields[i].ty, field_val, mod)) return true;
3080 } else return false;
3081 } else return false;
3082 },
3083 .Optional => {
3084 if (val.castTag(.opt_payload)) |payload| {
3085 const sub_val = payload.data;
3086 var buffer: Type.Payload.ElemType = undefined;
3087 const sub_ty = ty.optionalChild(&buffer);
3088 return needsPointerRebase(sub_ty, sub_val, mod);
3089 } else return false;
3090 },
3091 .Union => {
3092 const union_obj = val.cast(Value.Payload.Union).?.data;
3093 const active_field_ty = ty.unionFieldType(union_obj.tag, mod);
3094 return needsPointerRebase(active_field_ty, union_obj.val, mod);
3095 },
3096 .ErrorUnion => {
3097 if (val.castTag(.eu_payload)) |payload| {
3098 const payload_ty = ty.errorUnionPayload();
3099 return needsPointerRebase(payload_ty, payload.data, mod);
3100 } else return false;
3101 },
3102 else => return false,
3103 }
3104}
3105
3106fn getOutputSectionAtom(
3107 self: *MachO,
3108 atom: *Atom,
3109 name: []const u8,
3110 ty: Type,
3111 val: Value,
3112 alignment: u32,
3113) !u8 {
3114 const code = atom.code.items;
3115 const mod = self.base.options.module.?;
3116 const align_log_2 = math.log2(alignment);
3117 const zig_ty = ty.zigTypeTag();2942 const zig_ty = ty.zigTypeTag();
3118 const mode = self.base.options.optimize_mode;2943 const mode = self.base.options.optimize_mode;
3119
3120 const sect_id: u8 = blk: {2944 const sect_id: u8 = blk: {
3121 // TODO finish and audit this function2945 // TODO finish and audit this function
3122 if (val.isUndefDeep()) {2946 if (val.isUndefDeep()) {
3123 if (mode == .ReleaseFast or mode == .ReleaseSmall) {2947 if (mode == .ReleaseFast or mode == .ReleaseSmall) {
3124 const gop = (try self.getOutputSection(.{2948 @panic("TODO __DATA,__bss");
3125 .segname = makeStaticString("__DATA"),
3126 .sectname = makeStaticString("__bss"),
3127 })).?;
3128 if (!gop.found_existing) {
3129 try self.allocateSection(gop.sect_id, code.len, align_log_2);
3130 }
3131 break :blk gop.sect_id;
3132 } else {2949 } else {
3133 break :blk self.data_section_index.?;2950 break :blk self.data_section_index.?;
3134 }2951 }
...@@ -3138,88 +2955,145 @@ fn getOutputSectionAtom(...@@ -3138,88 +2955,145 @@ fn getOutputSectionAtom(
3138 break :blk self.data_section_index.?;2955 break :blk self.data_section_index.?;
3139 }2956 }
31402957
3141 if (needsPointerRebase(ty, val, mod)) {
3142 const gop = (try self.getOutputSection(.{
3143 .segname = makeStaticString("__DATA_CONST"),
3144 .sectname = makeStaticString("__const"),
3145 })).?;
3146 if (!gop.found_existing) {
3147 try self.allocateSection(gop.sect_id, code.len, align_log_2);
3148 }
3149 break :blk gop.sect_id;
3150 }
3151
3152 switch (zig_ty) {2958 switch (zig_ty) {
3153 .Fn => {2959 .Fn => break :blk self.text_section_index.?,
3154 break :blk self.text_section_index.?;2960 else => {
3155 },2961 if (val.castTag(.variable)) |_| {
3156 .Array => {2962 break :blk self.data_section_index.?;
3157 if (val.tag() == .bytes) {
3158 switch (ty.tag()) {
3159 .array_u8_sentinel_0,
3160 .const_slice_u8_sentinel_0,
3161 .manyptr_const_u8_sentinel_0,
3162 => {
3163 const gop = (try self.getOutputSection(.{
3164 .segname = makeStaticString("__TEXT"),
3165 .sectname = makeStaticString("__cstring"),
3166 .flags = macho.S_CSTRING_LITERALS,
3167 })).?;
3168 if (!gop.found_existing) {
3169 try self.allocateSection(gop.sect_id, code.len, align_log_2);
3170 }
3171 break :blk gop.sect_id;
3172 },
3173 else => {},
3174 }
3175 }2963 }
2964 break :blk self.data_const_section_index.?;
3176 },2965 },
3177 else => {},
3178 }2966 }
3179 const gop = (try self.getOutputSection(.{
3180 .segname = makeStaticString("__TEXT"),
3181 .sectname = makeStaticString("__const"),
3182 })).?;
3183 if (!gop.found_existing) {
3184 try self.allocateSection(gop.sect_id, code.len, align_log_2);
3185 }
3186 break :blk gop.sect_id;
3187 };2967 };
3188
3189 const header = self.sections.items(.header)[sect_id];
3190 log.debug(" allocating atom '{s}' in '{s},{s}', ord({d})", .{
3191 name,
3192 header.segName(),
3193 header.sectName(),
3194 sect_id,
3195 });
3196 return sect_id;2968 return sect_id;
3197}2969}
31982970
3199fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8) !u64 {2971pub fn getOutputSection(self: *MachO, sect: macho.section_64) !?u8 {
3200 const gpa = self.base.allocator;2972 const segname = sect.segName();
3201 const mod = self.base.options.module.?;2973 const sectname = sect.sectName();
3202 const decl = mod.declPtr(decl_index);2974 const sect_id: ?u8 = blk: {
32032975 if (mem.eql(u8, "__LLVM", segname)) {
3204 const required_alignment = decl.getAlignment(self.base.options.target);2976 log.debug("TODO LLVM section: type 0x{x}, name '{s},{s}'", .{
3205 assert(decl.link.macho.sym_index != 0); // Caller forgot to call allocateDeclIndexes()2977 sect.flags, segname, sectname,
2978 });
2979 break :blk null;
2980 }
32062981
3207 const sym_name = try decl.getFullyQualifiedName(mod);2982 if (sect.isCode()) {
3208 defer self.base.allocator.free(sym_name);2983 if (self.text_section_index == null) {
2984 self.text_section_index = try self.initSection("__TEXT", "__text", .{
2985 .flags = macho.S_REGULAR |
2986 macho.S_ATTR_PURE_INSTRUCTIONS |
2987 macho.S_ATTR_SOME_INSTRUCTIONS,
2988 });
2989 }
2990 break :blk self.text_section_index.?;
2991 }
32092992
3210 const atom = &decl.link.macho;2993 if (sect.isDebug()) {
3211 const decl_ptr = self.decls.getPtr(decl_index).?;2994 // TODO debug attributes
3212 if (decl_ptr.* == null) {2995 if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) {
3213 decl_ptr.* = try self.getOutputSectionAtom(2996 log.debug("TODO compact unwind section: type 0x{x}, name '{s},{s}'", .{
3214 atom,2997 sect.flags, segname, sectname,
3215 sym_name,2998 });
3216 decl.ty,2999 }
3217 decl.val,3000 break :blk null;
3218 required_alignment,3001 }
3219 );3002
3220 }3003 switch (sect.@"type"()) {
3221 const sect_id = decl_ptr.*.?;3004 macho.S_4BYTE_LITERALS,
3222 const code_len = code.len;3005 macho.S_8BYTE_LITERALS,
3006 macho.S_16BYTE_LITERALS,
3007 => {
3008 if (self.getSectionByName("__TEXT", "__const")) |sect_id| break :blk sect_id;
3009 break :blk try self.initSection("__TEXT", "__const", .{});
3010 },
3011 macho.S_CSTRING_LITERALS => {
3012 if (mem.startsWith(u8, sectname, "__objc")) {
3013 if (self.getSectionByName(segname, sectname)) |sect_id| break :blk sect_id;
3014 break :blk try self.initSection(segname, sectname, .{});
3015 }
3016 if (self.getSectionByName("__TEXT", "__cstring")) |sect_id| break :blk sect_id;
3017 break :blk try self.initSection("__TEXT", "__cstring", .{
3018 .flags = macho.S_CSTRING_LITERALS,
3019 });
3020 },
3021 macho.S_MOD_INIT_FUNC_POINTERS,
3022 macho.S_MOD_TERM_FUNC_POINTERS,
3023 => {
3024 if (self.getSectionByName("__DATA_CONST", sectname)) |sect_id| break :blk sect_id;
3025 break :blk try self.initSection("__DATA_CONST", sectname, .{
3026 .flags = sect.flags,
3027 });
3028 },
3029 macho.S_LITERAL_POINTERS,
3030 macho.S_ZEROFILL,
3031 macho.S_THREAD_LOCAL_VARIABLES,
3032 macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
3033 macho.S_THREAD_LOCAL_REGULAR,
3034 macho.S_THREAD_LOCAL_ZEROFILL,
3035 => {
3036 if (self.getSectionByName(segname, sectname)) |sect_id| break :blk sect_id;
3037 break :blk try self.initSection(segname, sectname, .{ .flags = sect.flags });
3038 },
3039 macho.S_COALESCED => {
3040 if (self.getSectionByName(segname, sectname)) |sect_id| break :blk sect_id;
3041 break :blk try self.initSection(segname, sectname, .{});
3042 },
3043 macho.S_REGULAR => {
3044 if (mem.eql(u8, segname, "__TEXT")) {
3045 if (mem.eql(u8, sectname, "__rodata") or
3046 mem.eql(u8, sectname, "__typelink") or
3047 mem.eql(u8, sectname, "__itablink") or
3048 mem.eql(u8, sectname, "__gosymtab") or
3049 mem.eql(u8, sectname, "__gopclntab"))
3050 {
3051 if (self.getSectionByName("__DATA_CONST", "__const")) |sect_id| break :blk sect_id;
3052 break :blk try self.initSection("__DATA_CONST", "__const", .{});
3053 }
3054 }
3055 if (mem.eql(u8, segname, "__DATA")) {
3056 if (mem.eql(u8, sectname, "__const") or
3057 mem.eql(u8, sectname, "__cfstring") or
3058 mem.eql(u8, sectname, "__objc_classlist") or
3059 mem.eql(u8, sectname, "__objc_imageinfo"))
3060 {
3061 if (self.getSectionByName("__DATA_CONST", sectname)) |sect_id| break :blk sect_id;
3062 break :blk try self.initSection("__DATA_CONST", sectname, .{});
3063 } else if (mem.eql(u8, sectname, "__data")) {
3064 if (self.data_section_index == null) {
3065 self.data_section_index = try self.initSection(segname, sectname, .{});
3066 }
3067 break :blk self.data_section_index.?;
3068 }
3069 }
3070 if (self.getSectionByName(segname, sectname)) |sect_id| break :blk sect_id;
3071 break :blk try self.initSection(segname, sectname, .{});
3072 },
3073 else => break :blk null,
3074 }
3075 };
3076 return sect_id;
3077}
3078
3079fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8) !u64 {
3080 const gpa = self.base.allocator;
3081 const mod = self.base.options.module.?;
3082 const decl = mod.declPtr(decl_index);
3083
3084 const required_alignment = decl.getAlignment(self.base.options.target);
3085 assert(decl.link.macho.sym_index != 0); // Caller forgot to call allocateDeclIndexes()
3086
3087 const sym_name = try decl.getFullyQualifiedName(mod);
3088 defer self.base.allocator.free(sym_name);
3089
3090 const atom = &decl.link.macho;
3091 const decl_ptr = self.decls.getPtr(decl_index).?;
3092 if (decl_ptr.* == null) {
3093 decl_ptr.* = self.getDeclOutputSection(decl);
3094 }
3095 const sect_id = decl_ptr.*.?;
3096 const code_len = code.len;
32233097
3224 if (atom.size != 0) {3098 if (atom.size != 0) {
3225 const sym = atom.getSymbolPtr(self);3099 const sym = atom.getSymbolPtr(self);
...@@ -3536,15 +3410,13 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -3536,15 +3410,13 @@ pub fn populateMissingMetadata(self: *MachO) !void {
3536 }3410 }
3537 }3411 }
35383412
3539 if (self.text_segment_cmd_index == null) {3413 if (self.header_segment_cmd_index == null) {
3540 self.text_segment_cmd_index = @intCast(u8, self.segments.items.len);3414 // The first __TEXT segment is immovable and covers MachO header and load commands.
3541 const headerpad_size = @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size);3415 self.header_segment_cmd_index = @intCast(u8, self.segments.items.len);
3542 const program_code_size_hint = self.base.options.program_code_size_hint;3416 const ideal_size = @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size);
3543 const got_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint;
3544 const ideal_size = headerpad_size + program_code_size_hint + got_size_hint;
3545 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);3417 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
35463418
3547 log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size });3419 log.debug("found __TEXT segment (header-only) free space 0x{x} to 0x{x}", .{ 0, needed_size });
35483420
3549 try self.segments.append(gpa, .{3421 try self.segments.append(gpa, .{
3550 .segname = makeStaticString("__TEXT"),3422 .segname = makeStaticString("__TEXT"),
...@@ -3555,150 +3427,101 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -3555,150 +3427,101 @@ pub fn populateMissingMetadata(self: *MachO) !void {
3555 .initprot = macho.PROT.READ | macho.PROT.EXEC,3427 .initprot = macho.PROT.READ | macho.PROT.EXEC,
3556 .cmdsize = @sizeOf(macho.segment_command_64),3428 .cmdsize = @sizeOf(macho.segment_command_64),
3557 });3429 });
3430 self.segment_table_dirty = true;
3558 }3431 }
35593432
3560 if (self.text_section_index == null) {3433 if (self.text_section_index == null) {
3561 const alignment: u2 = switch (cpu_arch) {3434 self.text_section_index = try self.allocateSection("__TEXT1", "__text", .{
3562 .x86_64 => 0,3435 .size = self.base.options.program_code_size_hint,
3563 .aarch64 => 2,3436 .alignment = switch (cpu_arch) {
3564 else => unreachable, // unhandled architecture type3437 .x86_64 => 1,
3565 };3438 .aarch64 => @sizeOf(u32),
3566 const needed_size = self.base.options.program_code_size_hint;3439 else => unreachable, // unhandled architecture type
3567 self.text_section_index = try self.initSection("__TEXT", "__text", .{3440 },
3568 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,3441 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
3442 .prot = macho.PROT.READ | macho.PROT.EXEC,
3569 });3443 });
3570 try self.allocateSection(self.text_section_index.?, needed_size, alignment);3444 self.segment_table_dirty = true;
3571 }3445 }
35723446
3573 if (self.stubs_section_index == null) {3447 if (self.stubs_section_index == null) {
3574 const alignment: u2 = switch (cpu_arch) {3448 const stub_size: u32 = switch (cpu_arch) {
3575 .x86_64 => 0,
3576 .aarch64 => 2,
3577 else => unreachable, // unhandled architecture type
3578 };
3579 const stub_size: u4 = switch (cpu_arch) {
3580 .x86_64 => 6,3449 .x86_64 => 6,
3581 .aarch64 => 3 * @sizeOf(u32),3450 .aarch64 => 3 * @sizeOf(u32),
3582 else => unreachable, // unhandled architecture type3451 else => unreachable, // unhandled architecture type
3583 };3452 };
3584 const needed_size = stub_size * self.base.options.symbol_count_hint;3453 self.stubs_section_index = try self.allocateSection("__TEXT2", "__stubs", .{
3585 self.stubs_section_index = try self.initSection("__TEXT", "__stubs", .{3454 .size = stub_size,
3455 .alignment = switch (cpu_arch) {
3456 .x86_64 => 1,
3457 .aarch64 => @sizeOf(u32),
3458 else => unreachable, // unhandled architecture type
3459 },
3586 .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,3460 .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
3587 .reserved2 = stub_size,3461 .reserved2 = stub_size,
3462 .prot = macho.PROT.READ | macho.PROT.EXEC,
3588 });3463 });
3589 try self.allocateSection(self.stubs_section_index.?, needed_size, alignment);3464 self.segment_table_dirty = true;
3590 }3465 }
35913466
3592 if (self.stub_helper_section_index == null) {3467 if (self.stub_helper_section_index == null) {
3593 const alignment: u2 = switch (cpu_arch) {3468 self.stub_helper_section_index = try self.allocateSection("__TEXT3", "__stub_helper", .{
3594 .x86_64 => 0,3469 .size = @sizeOf(u32),
3595 .aarch64 => 2,3470 .alignment = switch (cpu_arch) {
3596 else => unreachable, // unhandled architecture type3471 .x86_64 => 1,
3597 };3472 .aarch64 => @sizeOf(u32),
3598 const preamble_size: u6 = switch (cpu_arch) {3473 else => unreachable, // unhandled architecture type
3599 .x86_64 => 15,3474 },
3600 .aarch64 => 6 * @sizeOf(u32),
3601 else => unreachable,
3602 };
3603 const stub_size: u4 = switch (cpu_arch) {
3604 .x86_64 => 10,
3605 .aarch64 => 3 * @sizeOf(u32),
3606 else => unreachable,
3607 };
3608 const needed_size = stub_size * self.base.options.symbol_count_hint + preamble_size;
3609 self.stub_helper_section_index = try self.initSection("__TEXT", "__stub_helper", .{
3610 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,3475 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
3476 .prot = macho.PROT.READ | macho.PROT.EXEC,
3611 });3477 });
3612 try self.allocateSection(self.stub_helper_section_index.?, needed_size, alignment);3478 self.segment_table_dirty = true;
3613 }
3614
3615 if (self.data_const_segment_cmd_index == null) {
3616 self.data_const_segment_cmd_index = @intCast(u8, self.segments.items.len);
3617 const base = self.getSegmentAllocBase(&.{self.text_segment_cmd_index.?});
3618 const vmaddr = base.vmaddr;
3619 const fileoff = base.fileoff;
3620 const ideal_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
3621 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
3622
3623 log.debug("found __DATA_CONST segment free space 0x{x} to 0x{x}", .{
3624 fileoff,
3625 fileoff + needed_size,
3626 });
3627
3628 try self.segments.append(gpa, .{
3629 .segname = makeStaticString("__DATA_CONST"),
3630 .vmaddr = vmaddr,
3631 .vmsize = needed_size,
3632 .fileoff = fileoff,
3633 .filesize = needed_size,
3634 .maxprot = macho.PROT.READ | macho.PROT.WRITE,
3635 .initprot = macho.PROT.READ | macho.PROT.WRITE,
3636 .cmdsize = @sizeOf(macho.segment_command_64),
3637 });
3638 }3479 }
36393480
3640 if (self.got_section_index == null) {3481 if (self.got_section_index == null) {
3641 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;3482 self.got_section_index = try self.allocateSection("__DATA_CONST", "__got", .{
3642 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)3483 .size = @sizeOf(u64) * self.base.options.symbol_count_hint,
3643 self.got_section_index = try self.initSection("__DATA_CONST", "__got", .{3484 .alignment = @alignOf(u64),
3644 .flags = macho.S_NON_LAZY_SYMBOL_POINTERS,3485 .flags = macho.S_NON_LAZY_SYMBOL_POINTERS,
3486 .prot = macho.PROT.READ | macho.PROT.WRITE,
3645 });3487 });
3646 try self.allocateSection(self.got_section_index.?, needed_size, alignment);3488 self.segment_table_dirty = true;
3647 }3489 }
36483490
3649 if (self.data_segment_cmd_index == null) {3491 if (self.data_const_section_index == null) {
3650 self.data_segment_cmd_index = @intCast(u8, self.segments.items.len);3492 self.data_const_section_index = try self.allocateSection("__DATA_CONST1", "__const", .{
3651 const base = self.getSegmentAllocBase(&.{self.data_const_segment_cmd_index.?});3493 .size = @sizeOf(u64),
3652 const vmaddr = base.vmaddr;3494 .alignment = @alignOf(u64),
3653 const fileoff = base.fileoff;3495 .flags = macho.S_REGULAR,
3654 const ideal_size = 2 * @sizeOf(u64) * self.base.options.symbol_count_hint;3496 .prot = macho.PROT.READ | macho.PROT.WRITE,
3655 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
3656
3657 log.debug("found __DATA segment free space 0x{x} to 0x{x}", .{
3658 fileoff,
3659 fileoff + needed_size,
3660 });
3661
3662 try self.segments.append(gpa, .{
3663 .segname = makeStaticString("__DATA"),
3664 .vmaddr = vmaddr,
3665 .vmsize = needed_size,
3666 .fileoff = fileoff,
3667 .filesize = needed_size,
3668 .maxprot = macho.PROT.READ | macho.PROT.WRITE,
3669 .initprot = macho.PROT.READ | macho.PROT.WRITE,
3670 .cmdsize = @sizeOf(macho.segment_command_64),
3671 });3497 });
3498 self.segment_table_dirty = true;
3672 }3499 }
36733500
3674 if (self.la_symbol_ptr_section_index == null) {3501 if (self.la_symbol_ptr_section_index == null) {
3675 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;3502 self.la_symbol_ptr_section_index = try self.allocateSection("__DATA", "__la_symbol_ptr", .{
3676 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)3503 .size = @sizeOf(u64),
3677 self.la_symbol_ptr_section_index = try self.initSection("__DATA", "__la_symbol_ptr", .{3504 .alignment = @alignOf(u64),
3678 .flags = macho.S_LAZY_SYMBOL_POINTERS,3505 .flags = macho.S_LAZY_SYMBOL_POINTERS,
3506 .prot = macho.PROT.READ | macho.PROT.WRITE,
3679 });3507 });
3680 try self.allocateSection(self.la_symbol_ptr_section_index.?, needed_size, alignment);3508 self.segment_table_dirty = true;
3681 }3509 }
36823510
3683 if (self.data_section_index == null) {3511 if (self.data_section_index == null) {
3684 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;3512 self.data_section_index = try self.allocateSection("__DATA1", "__data", .{
3685 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)3513 .size = @sizeOf(u64),
3686 self.data_section_index = try self.initSection("__DATA", "__data", .{});3514 .alignment = @alignOf(u64),
3687 try self.allocateSection(self.data_section_index.?, needed_size, alignment);3515 .flags = macho.S_REGULAR,
3516 .prot = macho.PROT.READ | macho.PROT.WRITE,
3517 });
3518 self.segment_table_dirty = true;
3688 }3519 }
36893520
3690 if (self.linkedit_segment_cmd_index == null) {3521 if (self.linkedit_segment_cmd_index == null) {
3691 self.linkedit_segment_cmd_index = @intCast(u8, self.segments.items.len);3522 self.linkedit_segment_cmd_index = @intCast(u8, self.segments.items.len);
3692 const base = self.getSegmentAllocBase(&.{self.data_segment_cmd_index.?});
3693 const vmaddr = base.vmaddr;
3694 const fileoff = base.fileoff;
3695
3696 log.debug("found __LINKEDIT segment free space at 0x{x}", .{fileoff});
3697
3698 try self.segments.append(gpa, .{3523 try self.segments.append(gpa, .{
3699 .segname = makeStaticString("__LINKEDIT"),3524 .segname = makeStaticString("__LINKEDIT"),
3700 .vmaddr = vmaddr,
3701 .fileoff = fileoff,
3702 .maxprot = macho.PROT.READ,3525 .maxprot = macho.PROT.READ,
3703 .initprot = macho.PROT.READ,3526 .initprot = macho.PROT.READ,
3704 .cmdsize = @sizeOf(macho.segment_command_64),3527 .cmdsize = @sizeOf(macho.segment_command_64),
...@@ -3825,338 +3648,65 @@ pub fn calcMinHeaderPad(self: *MachO) !u64 {...@@ -3825,338 +3648,65 @@ pub fn calcMinHeaderPad(self: *MachO) !u64 {
3825 return offset;3648 return offset;
3826}3649}
38273650
3828fn allocateSection(self: *MachO, sect_id: u8, size: u64, alignment: u32) !void {3651fn allocateSection(self: *MachO, segname: []const u8, sectname: []const u8, opts: struct {
3829 const segment_id = self.sections.items(.segment_index)[sect_id];3652 size: u64 = 0,
3830 const seg = &self.segments.items[segment_id];3653 alignment: u32 = 0,
3831 const header = &self.sections.items(.header)[sect_id];3654 prot: macho.vm_prot_t = macho.PROT.NONE,
3832 header.size = size;
3833 header.@"align" = alignment;
3834
3835 const prev_end_off = if (sect_id > 0) blk: {
3836 const prev_section = self.sections.get(sect_id - 1);
3837 if (prev_section.segment_index == segment_id) {
3838 const prev_header = prev_section.header;
3839 break :blk prev_header.offset + padToIdeal(prev_header.size);
3840 } else break :blk seg.fileoff;
3841 } else 0;
3842 const alignment_pow_2 = try math.powi(u32, 2, alignment);
3843 // TODO better prealloc for __text section
3844 // const padding: u64 = if (sect_id == 0) try self.calcMinHeaderPad() else 0;
3845 const padding: u64 = if (sect_id == 0) 0x1000 else 0;
3846 const off = mem.alignForwardGeneric(u64, padding + prev_end_off, alignment_pow_2);
3847
3848 if (!header.isZerofill()) {
3849 header.offset = @intCast(u32, off);
3850 }
3851 header.addr = seg.vmaddr + off - seg.fileoff;
3852
3853 // TODO Will this break if we are inserting section that is not the last section
3854 // in a segment?
3855 const max_size = self.allocatedSize(segment_id, off);
3856
3857 if (size > max_size) {
3858 try self.growSection(sect_id, @intCast(u32, size));
3859 self.markRelocsDirtyByAddress(header.addr + size);
3860 }
3861
3862 log.debug("allocating {s},{s} section at 0x{x}", .{ header.segName(), header.sectName(), off });
3863
3864 self.updateSectionOrdinals(sect_id + 1);
3865}
3866
3867fn getSectionPrecedence(header: macho.section_64) u4 {
3868 if (header.isCode()) {
3869 if (mem.eql(u8, "__text", header.sectName())) return 0x0;
3870 if (header.@"type"() == macho.S_SYMBOL_STUBS) return 0x1;
3871 return 0x2;
3872 }
3873 switch (header.@"type"()) {
3874 macho.S_NON_LAZY_SYMBOL_POINTERS,
3875 macho.S_LAZY_SYMBOL_POINTERS,
3876 => return 0x0,
3877 macho.S_MOD_INIT_FUNC_POINTERS => return 0x1,
3878 macho.S_MOD_TERM_FUNC_POINTERS => return 0x2,
3879 macho.S_ZEROFILL => return 0xf,
3880 macho.S_THREAD_LOCAL_REGULAR => return 0xd,
3881 macho.S_THREAD_LOCAL_ZEROFILL => return 0xe,
3882 else => if (mem.eql(u8, "__eh_frame", header.sectName()))
3883 return 0xf
3884 else
3885 return 0x3,
3886 }
3887}
3888
3889const InitSectionOpts = struct {
3890 flags: u32 = macho.S_REGULAR,3655 flags: u32 = macho.S_REGULAR,
3891 reserved1: u32 = 0,
3892 reserved2: u32 = 0,3656 reserved2: u32 = 0,
3893};3657}) !u8 {
3658 const gpa = self.base.allocator;
3659 // In incremental context, we create one section per segment pairing. This way,
3660 // we can move the segment in raw file as we please.
3661 const segment_id = @intCast(u8, self.segments.items.len);
3662 const section_id = @intCast(u8, self.sections.slice().len);
3663 const vmaddr = blk: {
3664 const prev_segment = self.segments.items[segment_id - 1];
3665 break :blk mem.alignForwardGeneric(u64, prev_segment.vmaddr + prev_segment.vmsize, self.page_size);
3666 };
3667 // We commit more memory than needed upfront so that we don't have to reallocate too soon.
3668 const vmsize = mem.alignForwardGeneric(u64, opts.size, self.page_size);
3669 const off = self.findFreeSpace(opts.size, self.page_size);
3670
3671 log.debug("found {s},{s} free space 0x{x} to 0x{x} (0x{x} - 0x{x})", .{
3672 segname,
3673 sectname,
3674 off,
3675 off + opts.size,
3676 vmaddr,
3677 vmaddr + vmsize,
3678 });
38943679
3895pub fn initSection(3680 const seg = try self.segments.addOne(gpa);
3896 self: *MachO,3681 seg.* = .{
3897 segname: []const u8,3682 .segname = makeStaticString(segname),
3898 sectname: []const u8,3683 .vmaddr = vmaddr,
3899 opts: InitSectionOpts,3684 .vmsize = vmsize,
3900) !u8 {3685 .fileoff = off,
3901 const segment_id = self.getSegmentByName(segname).?;3686 .filesize = opts.size,
3902 const seg = &self.segments.items[segment_id];3687 .maxprot = opts.prot,
3903 const index = try self.insertSection(segment_id, .{3688 .initprot = opts.prot,
3689 .nsects = 1,
3690 .cmdsize = @sizeOf(macho.segment_command_64) + @sizeOf(macho.section_64),
3691 };
3692
3693 var section = macho.section_64{
3904 .sectname = makeStaticString(sectname),3694 .sectname = makeStaticString(sectname),
3905 .segname = seg.segname,3695 .segname = makeStaticString(segname),
3696 .addr = mem.alignForwardGeneric(u64, vmaddr, opts.alignment),
3697 .offset = mem.alignForwardGeneric(u32, @intCast(u32, off), opts.alignment),
3698 .size = opts.size,
3699 .@"align" = math.log2(opts.alignment),
3906 .flags = opts.flags,3700 .flags = opts.flags,
3907 .reserved1 = opts.reserved1,
3908 .reserved2 = opts.reserved2,3701 .reserved2 = opts.reserved2,
3909 });3702 };
3910 seg.cmdsize += @sizeOf(macho.section_64);3703 assert(!section.isZerofill()); // TODO zerofill sections
3911 seg.nsects += 1;
3912 return index;
3913}
3914
3915fn insertSection(self: *MachO, segment_index: u8, header: macho.section_64) !u8 {
3916 const precedence = getSectionPrecedence(header);
3917 const indexes = self.getSectionIndexes(segment_index);
3918 const insertion_index = for (self.sections.items(.header)[indexes.start..indexes.end]) |hdr, i| {
3919 if (getSectionPrecedence(hdr) > precedence) break @intCast(u8, i + indexes.start);
3920 } else indexes.end;
3921 log.debug("inserting section '{s},{s}' at index {d}", .{
3922 header.segName(),
3923 header.sectName(),
3924 insertion_index,
3925 });
3926 for (&[_]*?u8{
3927 &self.text_section_index,
3928 &self.stubs_section_index,
3929 &self.stub_helper_section_index,
3930 &self.got_section_index,
3931 &self.la_symbol_ptr_section_index,
3932 &self.data_section_index,
3933 }) |maybe_index| {
3934 const index = maybe_index.* orelse continue;
3935 if (insertion_index <= index) maybe_index.* = index + 1;
3936 }
3937 try self.sections.insert(self.base.allocator, insertion_index, .{
3938 .segment_index = segment_index,
3939 .header = header,
3940 });
3941 return insertion_index;
3942}
3943
3944fn updateSectionOrdinals(self: *MachO, start: u8) void {
3945 const tracy = trace(@src());
3946 defer tracy.end();
3947
3948 const slice = self.sections.slice();
3949 for (slice.items(.last_atom)[start..]) |last_atom| {
3950 var atom = last_atom orelse continue;
3951
3952 while (true) {
3953 const sym = atom.getSymbolPtr(self);
3954 sym.n_sect = start + 1;
3955
3956 for (atom.contained.items) |sym_at_off| {
3957 const contained_sym = self.getSymbolPtr(.{
3958 .sym_index = sym_at_off.sym_index,
3959 .file = atom.file,
3960 });
3961 contained_sym.n_sect = start + 1;
3962 }
3963
3964 if (atom.prev) |prev| {
3965 atom = prev;
3966 } else break;
3967 }
3968 }
3969}
3970
3971fn shiftLocalsByOffset(self: *MachO, sect_id: u8, offset: i64) !void {
3972 var atom = self.sections.items(.last_atom)[sect_id] orelse return;
3973
3974 while (true) {
3975 const atom_sym = atom.getSymbolPtr(self);
3976 atom_sym.n_value = @intCast(u64, @intCast(i64, atom_sym.n_value) + offset);
3977
3978 for (atom.contained.items) |sym_at_off| {
3979 const contained_sym = self.getSymbolPtr(.{
3980 .sym_index = sym_at_off.sym_index,
3981 .file = atom.file,
3982 });
3983 contained_sym.n_value = @intCast(u64, @intCast(i64, contained_sym.n_value) + offset);
3984 }
3985
3986 if (atom.prev) |prev| {
3987 atom = prev;
3988 } else break;
3989 }
3990}
39913704
3992fn growSegment(self: *MachO, segment_index: u8, new_size: u64) !void {3705 try self.sections.append(gpa, .{
3993 const segment = &self.segments.items[segment_index];3706 .segment_index = segment_id,
3994 const new_segment_size = mem.alignForwardGeneric(u64, new_size, self.page_size);3707 .header = section,
3995 assert(new_segment_size > segment.filesize);
3996 const offset_amt = new_segment_size - segment.filesize;
3997 log.debug("growing segment {s} from 0x{x} to 0x{x}", .{
3998 segment.segname,
3999 segment.filesize,
4000 new_segment_size,
4001 });
4002 segment.filesize = new_segment_size;
4003 segment.vmsize = new_segment_size;
4004
4005 log.debug(" (new segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
4006 segment.fileoff,
4007 segment.fileoff + segment.filesize,
4008 segment.vmaddr,
4009 segment.vmaddr + segment.vmsize,
4010 });3708 });
40113709 return section_id;
4012 var next: u8 = segment_index + 1;
4013 while (next < self.linkedit_segment_cmd_index.? + 1) : (next += 1) {
4014 const next_segment = &self.segments.items[next];
4015
4016 try MachO.copyRangeAllOverlappingAlloc(
4017 self.base.allocator,
4018 self.base.file.?,
4019 next_segment.fileoff,
4020 next_segment.fileoff + offset_amt,
4021 math.cast(usize, next_segment.filesize) orelse return error.Overflow,
4022 );
4023
4024 next_segment.fileoff += offset_amt;
4025 next_segment.vmaddr += offset_amt;
4026
4027 log.debug(" (new {s} segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
4028 next_segment.segname,
4029 next_segment.fileoff,
4030 next_segment.fileoff + next_segment.filesize,
4031 next_segment.vmaddr,
4032 next_segment.vmaddr + next_segment.vmsize,
4033 });
4034
4035 const indexes = self.getSectionIndexes(next);
4036 for (self.sections.items(.header)[indexes.start..indexes.end]) |*header, i| {
4037 header.offset += @intCast(u32, offset_amt);
4038 header.addr += offset_amt;
4039
4040 log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
4041 header.segName(),
4042 header.sectName(),
4043 header.offset,
4044 header.offset + header.size,
4045 header.addr,
4046 header.addr + header.size,
4047 });
4048
4049 try self.shiftLocalsByOffset(@intCast(u8, i + indexes.start), @intCast(i64, offset_amt));
4050 }
4051 }
4052}
4053
4054fn growSection(self: *MachO, sect_id: u8, new_size: u32) !void {
4055 const tracy = trace(@src());
4056 defer tracy.end();
4057
4058 const section = self.sections.get(sect_id);
4059 const segment_index = section.segment_index;
4060 const header = section.header;
4061 const segment = self.segments.items[segment_index];
4062
4063 const alignment = try math.powi(u32, 2, header.@"align");
4064 const max_size = self.allocatedSize(segment_index, header.offset);
4065 const ideal_size = padToIdeal(new_size);
4066 const needed_size = mem.alignForwardGeneric(u32, ideal_size, alignment);
4067
4068 if (needed_size > max_size) blk: {
4069 log.debug(" (need to grow! needed 0x{x}, max 0x{x})", .{ needed_size, max_size });
4070
4071 const indexes = self.getSectionIndexes(segment_index);
4072 if (sect_id == indexes.end - 1) {
4073 // Last section, just grow segments
4074 try self.growSegment(segment_index, segment.filesize + needed_size - max_size);
4075 break :blk;
4076 }
4077
4078 // Need to move all sections below in file and address spaces.
4079 const offset_amt = offset: {
4080 const max_alignment = try self.getSectionMaxAlignment(sect_id + 1, indexes.end);
4081 break :offset mem.alignForwardGeneric(u64, needed_size - max_size, max_alignment);
4082 };
4083
4084 // Before we commit to this, check if the segment needs to grow too.
4085 // We assume that each section header is growing linearly with the increasing
4086 // file offset / virtual memory address space.
4087 const last_sect_header = self.sections.items(.header)[indexes.end - 1];
4088 const last_sect_off = last_sect_header.offset + last_sect_header.size;
4089 const seg_off = segment.fileoff + segment.filesize;
4090
4091 if (last_sect_off + offset_amt > seg_off) {
4092 // Need to grow segment first.
4093 const spill_size = (last_sect_off + offset_amt) - seg_off;
4094 try self.growSegment(segment_index, segment.filesize + spill_size);
4095 }
4096
4097 // We have enough space to expand within the segment, so move all sections by
4098 // the required amount and update their header offsets.
4099 const next_sect = self.sections.items(.header)[sect_id + 1];
4100 const total_size = last_sect_off - next_sect.offset;
4101
4102 try MachO.copyRangeAllOverlappingAlloc(
4103 self.base.allocator,
4104 self.base.file.?,
4105 next_sect.offset,
4106 next_sect.offset + offset_amt,
4107 math.cast(usize, total_size) orelse return error.Overflow,
4108 );
4109
4110 for (self.sections.items(.header)[sect_id + 1 .. indexes.end]) |*moved_sect, i| {
4111 moved_sect.offset += @intCast(u32, offset_amt);
4112 moved_sect.addr += offset_amt;
4113
4114 log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
4115 moved_sect.segName(),
4116 moved_sect.sectName(),
4117 moved_sect.offset,
4118 moved_sect.offset + moved_sect.size,
4119 moved_sect.addr,
4120 moved_sect.addr + moved_sect.size,
4121 });
4122
4123 try self.shiftLocalsByOffset(@intCast(u8, sect_id + 1 + i), @intCast(i64, offset_amt));
4124 }
4125 }
4126}
4127
4128fn allocatedSize(self: MachO, segment_id: u8, start: u64) u64 {
4129 const segment = self.segments.items[segment_id];
4130 const indexes = self.getSectionIndexes(segment_id);
4131 assert(start >= segment.fileoff);
4132 var min_pos: u64 = segment.fileoff + segment.filesize;
4133 if (start > min_pos) return 0;
4134 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
4135 if (header.offset <= start) continue;
4136 if (header.offset < min_pos) min_pos = header.offset;
4137 }
4138 return min_pos - start;
4139}
4140
4141fn getSectionMaxAlignment(self: *MachO, start: u8, end: u8) !u32 {
4142 var max_alignment: u32 = 1;
4143 const slice = self.sections.slice();
4144 for (slice.items(.header)[start..end]) |header| {
4145 const alignment = try math.powi(u32, 2, header.@"align");
4146 max_alignment = math.max(max_alignment, alignment);
4147 }
4148 return max_alignment;
4149}
4150
4151fn allocateAtomCommon(self: *MachO, atom: *Atom) !void {
4152 if (self.mode == .incremental) {
4153 const sym_name = atom.getName(self);
4154 const size = atom.size;
4155 const alignment = try math.powi(u32, 2, atom.alignment);
4156 const vaddr = try self.allocateAtom(atom, size, alignment);
4157 log.debug("allocated {s} atom at 0x{x}", .{ sym_name, vaddr });
4158 atom.getSymbolPtr(self).n_value = vaddr;
4159 } else try self.addAtomToSection(atom);
4160}3710}
41613711
4162fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !u64 {3712fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !u64 {
...@@ -4164,12 +3714,13 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !...@@ -4164,12 +3714,13 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
4164 defer tracy.end();3714 defer tracy.end();
41653715
4166 const sect_id = atom.getSymbol(self).n_sect - 1;3716 const sect_id = atom.getSymbol(self).n_sect - 1;
3717 const segment = &self.segments.items[self.sections.items(.segment_index)[sect_id]];
4167 const header = &self.sections.items(.header)[sect_id];3718 const header = &self.sections.items(.header)[sect_id];
4168 const free_list = &self.sections.items(.free_list)[sect_id];3719 const free_list = &self.sections.items(.free_list)[sect_id];
4169 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];3720 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];
4170 const requires_padding = blk: {3721 const requires_padding = blk: {
4171 if (!header.isCode()) break :blk false;3722 if (!header.isCode()) break :blk false;
4172 if (mem.eql(u8, "__stubs", header.sectName())) break :blk false;3723 if (header.isSymbolStubs()) break :blk false;
4173 if (mem.eql(u8, "__stub_helper", header.sectName())) break :blk false;3724 if (mem.eql(u8, "__stub_helper", header.sectName())) break :blk false;
4174 break :blk true;3725 break :blk true;
4175 };3726 };
...@@ -4229,24 +3780,58 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !...@@ -4229,24 +3780,58 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
4229 atom_placement = last;3780 atom_placement = last;
4230 break :blk new_start_vaddr;3781 break :blk new_start_vaddr;
4231 } else {3782 } else {
4232 break :blk mem.alignForwardGeneric(u64, header.addr, alignment);3783 break :blk mem.alignForwardGeneric(u64, segment.vmaddr, alignment);
4233 }3784 }
4234 };3785 };
42353786
4236 const expand_section = atom_placement == null or atom_placement.?.next == null;3787 const expand_section = atom_placement == null or atom_placement.?.next == null;
4237 if (expand_section) {3788 if (expand_section) {
4238 const needed_size = @intCast(u32, (vaddr + new_atom_size) - header.addr);3789 const sect_capacity = self.allocatedSize(header.offset);
4239 try self.growSection(sect_id, needed_size);3790 const needed_size = (vaddr + new_atom_size) - segment.vmaddr;
4240 self.markRelocsDirtyByAddress(header.addr + needed_size);3791 if (needed_size > sect_capacity) {
4241 maybe_last_atom.* = atom;3792 const new_offset = self.findFreeSpace(needed_size, self.page_size);
3793 const current_size = if (maybe_last_atom.*) |last_atom| blk: {
3794 const sym = last_atom.getSymbol(self);
3795 break :blk (sym.n_value + last_atom.size) - segment.vmaddr;
3796 } else 0;
3797
3798 log.debug("moving {s},{s} from 0x{x} to 0x{x}", .{
3799 header.segName(),
3800 header.sectName(),
3801 header.offset,
3802 new_offset,
3803 });
3804
3805 const amt = try self.base.file.?.copyRangeAll(
3806 header.offset,
3807 self.base.file.?,
3808 new_offset,
3809 current_size,
3810 );
3811 if (amt != current_size) return error.InputOutput;
3812 header.offset = @intCast(u32, new_offset);
3813 segment.fileoff = new_offset;
3814 }
3815
3816 const sect_vm_capacity = self.allocatedVirtualSize(segment.vmaddr);
3817 if (needed_size > sect_vm_capacity) {
3818 self.markRelocsDirtyByAddress(segment.vmaddr + needed_size);
3819 @panic("TODO grow section in VM");
3820 }
3821
4242 header.size = needed_size;3822 header.size = needed_size;
3823 segment.filesize = needed_size;
3824 segment.vmsize = mem.alignForwardGeneric(u64, needed_size, self.page_size);
3825 log.warn("updating {s},{s}: {x}, {x}", .{ header.segName(), header.sectName(), segment.vmsize, segment.filesize });
3826 maybe_last_atom.* = atom;
3827
3828 self.segment_table_dirty = true;
4243 }3829 }
3830
4244 const align_pow = @intCast(u32, math.log2(alignment));3831 const align_pow = @intCast(u32, math.log2(alignment));
4245 if (header.@"align" < align_pow) {3832 if (header.@"align" < align_pow) {
4246 header.@"align" = align_pow;3833 header.@"align" = align_pow;
4247 }3834 }
4248 atom.size = new_atom_size;
4249 atom.alignment = align_pow;
42503835
4251 if (atom.prev) |prev| {3836 if (atom.prev) |prev| {
4252 prev.next = atom.next;3837 prev.next = atom.next;
...@@ -4270,6 +3855,78 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !...@@ -4270,6 +3855,78 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
4270 return vaddr;3855 return vaddr;
4271}3856}
42723857
3858fn getSectionPrecedence(header: macho.section_64) u4 {
3859 if (header.isCode()) {
3860 if (mem.eql(u8, "__text", header.sectName())) return 0x0;
3861 if (header.@"type"() == macho.S_SYMBOL_STUBS) return 0x1;
3862 return 0x2;
3863 }
3864 switch (header.@"type"()) {
3865 macho.S_NON_LAZY_SYMBOL_POINTERS,
3866 macho.S_LAZY_SYMBOL_POINTERS,
3867 => return 0x0,
3868 macho.S_MOD_INIT_FUNC_POINTERS => return 0x1,
3869 macho.S_MOD_TERM_FUNC_POINTERS => return 0x2,
3870 macho.S_ZEROFILL => return 0xf,
3871 macho.S_THREAD_LOCAL_REGULAR => return 0xd,
3872 macho.S_THREAD_LOCAL_ZEROFILL => return 0xe,
3873 else => if (mem.eql(u8, "__eh_frame", header.sectName()))
3874 return 0xf
3875 else
3876 return 0x3,
3877 }
3878}
3879
3880const InitSectionOpts = struct {
3881 flags: u32 = macho.S_REGULAR,
3882 reserved1: u32 = 0,
3883 reserved2: u32 = 0,
3884};
3885
3886pub fn initSection(self: *MachO, segname: []const u8, sectname: []const u8, opts: InitSectionOpts) !u8 {
3887 const segment_id = self.getSegmentByName(segname).?;
3888 const seg = &self.segments.items[segment_id];
3889 const index = try self.insertSection(segment_id, .{
3890 .sectname = makeStaticString(sectname),
3891 .segname = seg.segname,
3892 .flags = opts.flags,
3893 .reserved1 = opts.reserved1,
3894 .reserved2 = opts.reserved2,
3895 });
3896 seg.cmdsize += @sizeOf(macho.section_64);
3897 seg.nsects += 1;
3898 return index;
3899}
3900
3901fn insertSection(self: *MachO, segment_index: u8, header: macho.section_64) !u8 {
3902 const precedence = getSectionPrecedence(header);
3903 const indexes = self.getSectionIndexes(segment_index);
3904 const insertion_index = for (self.sections.items(.header)[indexes.start..indexes.end]) |hdr, i| {
3905 if (getSectionPrecedence(hdr) > precedence) break @intCast(u8, i + indexes.start);
3906 } else indexes.end;
3907 log.debug("inserting section '{s},{s}' at index {d}", .{
3908 header.segName(),
3909 header.sectName(),
3910 insertion_index,
3911 });
3912 for (&[_]*?u8{
3913 &self.text_section_index,
3914 &self.stubs_section_index,
3915 &self.stub_helper_section_index,
3916 &self.got_section_index,
3917 &self.la_symbol_ptr_section_index,
3918 &self.data_section_index,
3919 }) |maybe_index| {
3920 const index = maybe_index.* orelse continue;
3921 if (insertion_index <= index) maybe_index.* = index + 1;
3922 }
3923 try self.sections.insert(self.base.allocator, insertion_index, .{
3924 .segment_index = segment_index,
3925 .header = header,
3926 });
3927 return insertion_index;
3928}
3929
4273pub fn addAtomToSection(self: *MachO, atom: *Atom) !void {3930pub fn addAtomToSection(self: *MachO, atom: *Atom) !void {
4274 const sect_id = atom.getSymbol(self).n_sect - 1;3931 const sect_id = atom.getSymbol(self).n_sect - 1;
4275 var section = self.sections.get(sect_id);3932 var section = self.sections.get(sect_id);
...@@ -4310,43 +3967,13 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {...@@ -4310,43 +3967,13 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {
4310 return global_index;3967 return global_index;
4311}3968}
43123969
4313pub fn getSegmentAllocBase(self: MachO, indices: []const ?u8) struct { vmaddr: u64, fileoff: u64 } {
4314 for (indices) |maybe_prev_id| {
4315 const prev_id = maybe_prev_id orelse continue;
4316 const prev = self.segments.items[prev_id];
4317 return .{
4318 .vmaddr = prev.vmaddr + prev.vmsize,
4319 .fileoff = prev.fileoff + prev.filesize,
4320 };
4321 }
4322 return .{ .vmaddr = 0, .fileoff = 0 };
4323}
4324
4325fn writeSegmentHeaders(self: *MachO, ncmds: *u32, writer: anytype) !void {3970fn writeSegmentHeaders(self: *MachO, ncmds: *u32, writer: anytype) !void {
4326 for (self.segments.items) |seg, i| {3971 for (self.segments.items) |seg, i| {
4327 const indexes = self.getSectionIndexes(@intCast(u8, i));3972 const indexes = self.getSectionIndexes(@intCast(u8, i));
4328 var out_seg = seg;3973 try writer.writeStruct(seg);
4329 out_seg.cmdsize = @sizeOf(macho.segment_command_64);
4330 out_seg.nsects = 0;
4331
4332 // Update section headers count; any section with size of 0 is excluded
4333 // since it doesn't have any data in the final binary file.
4334 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {3974 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
4335 if (header.size == 0) continue;
4336 out_seg.cmdsize += @sizeOf(macho.section_64);
4337 out_seg.nsects += 1;
4338 }
4339
4340 if (out_seg.nsects == 0 and
4341 (mem.eql(u8, out_seg.segName(), "__DATA_CONST") or
4342 mem.eql(u8, out_seg.segName(), "__DATA"))) continue;
4343
4344 try writer.writeStruct(out_seg);
4345 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
4346 if (header.size == 0) continue;
4347 try writer.writeStruct(header);3975 try writer.writeStruct(header);
4348 }3976 }
4349
4350 ncmds.* += 1;3977 ncmds.* += 1;
4351 }3978 }
4352}3979}
...@@ -4356,6 +3983,24 @@ fn writeLinkeditSegmentData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void...@@ -4356,6 +3983,24 @@ fn writeLinkeditSegmentData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void
4356 seg.filesize = 0;3983 seg.filesize = 0;
4357 seg.vmsize = 0;3984 seg.vmsize = 0;
43583985
3986 for (self.segments.items) |segment, id| {
3987 if (self.linkedit_segment_cmd_index.? == @intCast(u8, id)) continue;
3988 if (seg.vmaddr < segment.vmaddr + segment.vmsize) {
3989 seg.vmaddr = mem.alignForwardGeneric(u64, segment.vmaddr + segment.vmsize, self.page_size);
3990 }
3991 if (seg.fileoff < segment.fileoff + segment.filesize) {
3992 seg.fileoff = mem.alignForwardGeneric(u64, segment.fileoff + segment.filesize, self.page_size);
3993 }
3994 }
3995 // seg.vmaddr = blk: {
3996 // const prev_segment = self.segments.items[self.linkedit_segment_cmd_index.? - 1];
3997 // break :blk mem.alignForwardGeneric(u64, prev_segment.vmaddr + prev_segment.vmsize, self.page_size);
3998 // };
3999 // seg.fileoff = blk: {
4000 // const prev_segment = self.segments.items[self.linkedit_segment_cmd_index.? - 1];
4001 // break :blk mem.alignForwardGeneric(u64, prev_segment.fileoff + prev_segment.filesize, self.page_size);
4002 // };
4003
4359 try self.writeDyldInfoData(ncmds, lc_writer);4004 try self.writeDyldInfoData(ncmds, lc_writer);
4360 try self.writeSymtabs(ncmds, lc_writer);4005 try self.writeSymtabs(ncmds, lc_writer);
43614006
...@@ -4471,7 +4116,7 @@ fn writeDyldInfoData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {...@@ -4471,7 +4116,7 @@ fn writeDyldInfoData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
4471 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.4116 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
4472 log.debug("generating export trie", .{});4117 log.debug("generating export trie", .{});
44734118
4474 const text_segment = self.segments.items[self.text_segment_cmd_index.?];4119 const text_segment = self.segments.items[self.header_segment_cmd_index.?];
4475 const base_address = text_segment.vmaddr;4120 const base_address = text_segment.vmaddr;
44764121
4477 if (self.base.options.output_mode == .Exe) {4122 if (self.base.options.output_mode == .Exe) {
...@@ -4593,7 +4238,8 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -4593,7 +4238,8 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
4593 var stub_atom = last_atom;4238 var stub_atom = last_atom;
4594 var laptr_atom = self.sections.items(.last_atom)[self.la_symbol_ptr_section_index.?].?;4239 var laptr_atom = self.sections.items(.last_atom)[self.la_symbol_ptr_section_index.?].?;
4595 const base_addr = blk: {4240 const base_addr = blk: {
4596 const seg = self.segments.items[self.data_segment_cmd_index.?];4241 const seg_id = self.sections.items(.segment_index)[self.la_symbol_ptr_section_index.?];
4242 const seg = self.segments.items[seg_id];
4597 break :blk seg.vmaddr;4243 break :blk seg.vmaddr;
4598 };4244 };
45994245
...@@ -4932,7 +4578,8 @@ fn writeCodeSignaturePadding(...@@ -4932,7 +4578,8 @@ fn writeCodeSignaturePadding(
4932}4578}
49334579
4934fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature, offset: u32) !void {4580fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature, offset: u32) !void {
4935 const seg = self.segments.items[self.text_segment_cmd_index.?];4581 const seg_id = self.sections.items(.segment_index)[self.text_section_index.?];
4582 const seg = self.segments.items[seg_id];
49364583
4937 var buffer = std.ArrayList(u8).init(self.base.allocator);4584 var buffer = std.ArrayList(u8).init(self.base.allocator);
4938 defer buffer.deinit();4585 defer buffer.deinit();
...@@ -5005,7 +4652,7 @@ pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {...@@ -5005,7 +4652,7 @@ pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
50054652
5006fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {4653fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
5007 // TODO: header and load commands have to be part of the __TEXT segment4654 // TODO: header and load commands have to be part of the __TEXT segment
5008 const header_size = default_headerpad_size;4655 const header_size = self.segments.items[self.header_segment_cmd_index.?].filesize;
5009 if (start < header_size)4656 if (start < header_size)
5010 return header_size;4657 return header_size;
50114658
...@@ -5023,16 +4670,16 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {...@@ -5023,16 +4670,16 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
5023 return null;4670 return null;
5024}4671}
50254672
5026// fn allocatedSize(self: *MachO, start: u64) u64 {4673fn allocatedSize(self: *MachO, start: u64) u64 {
5027// if (start == 0)4674 if (start == 0)
5028// return 0;4675 return 0;
5029// var min_pos: u64 = std.math.maxInt(u64);4676 var min_pos: u64 = std.math.maxInt(u64);
5030// for (self.sections.items(.header)) |header| {4677 for (self.sections.items(.header)) |header| {
5031// if (header.offset <= start) continue;4678 if (header.offset <= start) continue;
5032// if (header.offset < min_pos) min_pos = header.offset;4679 if (header.offset < min_pos) min_pos = header.offset;
5033// }4680 }
5034// return min_pos - start;4681 return min_pos - start;
5035// }4682}
50364683
5037fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 {4684fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 {
5038 var start: u64 = 0;4685 var start: u64 = 0;
...@@ -5042,6 +4689,18 @@ fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 {...@@ -5042,6 +4689,18 @@ fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 {
5042 return start;4689 return start;
5043}4690}
50444691
4692fn allocatedVirtualSize(self: *MachO, start: u64) u64 {
4693 if (start == 0)
4694 return 0;
4695 var min_pos: u64 = std.math.maxInt(u64);
4696 for (self.sections.items(.segment_index)) |seg_id| {
4697 const segment = self.segments.items[seg_id];
4698 if (segment.vmaddr <= start) continue;
4699 if (segment.vmaddr < min_pos) min_pos = segment.vmaddr;
4700 }
4701 return min_pos - start;
4702}
4703
5045pub fn makeStaticString(bytes: []const u8) [16]u8 {4704pub fn makeStaticString(bytes: []const u8) [16]u8 {
5046 var buf = [_]u8{0} ** 16;4705 var buf = [_]u8{0} ** 16;
5047 assert(bytes.len <= buf.len);4706 assert(bytes.len <= buf.len);
...@@ -5645,19 +5304,3 @@ pub fn logAtom(self: *MachO, atom: *const Atom) void {...@@ -5645,19 +5304,3 @@ pub fn logAtom(self: *MachO, atom: *const Atom) void {
5645 });5304 });
5646 }5305 }
5647}5306}
5648
5649/// Since `os.copy_file_range` cannot be used when copying overlapping ranges within the same file,
5650/// and since `File.copyRangeAll` uses `os.copy_file_range` under-the-hood, we use heap allocated
5651/// buffers on all hosts except Linux (if `copy_file_range` syscall is available).
5652pub fn copyRangeAllOverlappingAlloc(
5653 allocator: Allocator,
5654 file: std.fs.File,
5655 in_offset: u64,
5656 out_offset: u64,
5657 len: usize,
5658) !void {
5659 const buf = try allocator.alloc(u8, len);
5660 defer allocator.free(buf);
5661 const amt = try file.preadAll(buf, in_offset);
5662 try file.pwriteAll(buf[0..amt], out_offset);
5663}
src/link/MachO/Atom.zig+1-2
...@@ -312,9 +312,8 @@ pub fn parseRelocs(self: *Atom, relocs: []align(1) const macho.relocation_info,...@@ -312,9 +312,8 @@ pub fn parseRelocs(self: *Atom, relocs: []align(1) const macho.relocation_info,
312 const sect_id = @intCast(u16, rel.r_symbolnum - 1);312 const sect_id = @intCast(u16, rel.r_symbolnum - 1);
313 const sym_index = object.sections_as_symbols.get(sect_id) orelse blk: {313 const sym_index = object.sections_as_symbols.get(sect_id) orelse blk: {
314 const sect = object.getSourceSection(sect_id);314 const sect = object.getSourceSection(sect_id);
315 const gop = (try context.macho_file.getOutputSection(sect)) orelse315 const out_sect_id = (try context.macho_file.getOutputSection(sect)) orelse
316 unreachable;316 unreachable;
317 const out_sect_id = gop.sect_id;
318 const sym_index = @intCast(u32, object.symtab.items.len);317 const sym_index = @intCast(u32, object.symtab.items.len);
319 try object.symtab.append(gpa, .{318 try object.symtab.append(gpa, .{
320 .n_strx = 0,319 .n_strx = 0,
src/link/MachO/DebugSymbols.zig+15-2
...@@ -512,7 +512,7 @@ fn writeSymtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {...@@ -512,7 +512,7 @@ fn writeSymtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
512 const dwarf_seg = &self.segments.items[self.dwarf_segment_cmd_index.?];512 const dwarf_seg = &self.segments.items[self.dwarf_segment_cmd_index.?];
513 seg.filesize = aligned_size;513 seg.filesize = aligned_size;
514514
515 try MachO.copyRangeAllOverlappingAlloc(515 try copyRangeAllOverlappingAlloc(
516 self.base.base.allocator,516 self.base.base.allocator,
517 self.file,517 self.file,
518 dwarf_seg.fileoff,518 dwarf_seg.fileoff,
...@@ -571,7 +571,7 @@ fn writeStrtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {...@@ -571,7 +571,7 @@ fn writeStrtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
571 const dwarf_seg = &self.segments.items[self.dwarf_segment_cmd_index.?];571 const dwarf_seg = &self.segments.items[self.dwarf_segment_cmd_index.?];
572 seg.filesize = aligned_size;572 seg.filesize = aligned_size;
573573
574 try MachO.copyRangeAllOverlappingAlloc(574 try copyRangeAllOverlappingAlloc(
575 self.base.base.allocator,575 self.base.base.allocator,
576 self.file,576 self.file,
577 dwarf_seg.fileoff,577 dwarf_seg.fileoff,
...@@ -601,3 +601,16 @@ fn writeStrtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {...@@ -601,3 +601,16 @@ fn writeStrtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
601601
602 try self.file.pwriteAll(self.strtab.buffer.items, lc.stroff);602 try self.file.pwriteAll(self.strtab.buffer.items, lc.stroff);
603}603}
604
605fn copyRangeAllOverlappingAlloc(
606 allocator: Allocator,
607 file: std.fs.File,
608 in_offset: u64,
609 out_offset: u64,
610 len: usize,
611) !void {
612 const buf = try allocator.alloc(u8, len);
613 defer allocator.free(buf);
614 const amt = try file.preadAll(buf, in_offset);
615 try file.pwriteAll(buf[0..amt], out_offset);
616}
src/link/MachO/Object.zig+4-5
...@@ -220,15 +220,15 @@ fn filterRelocs(...@@ -220,15 +220,15 @@ fn filterRelocs(
220220
221pub fn scanInputSections(self: Object, macho_file: *MachO) !void {221pub fn scanInputSections(self: Object, macho_file: *MachO) !void {
222 for (self.sections.items) |sect| {222 for (self.sections.items) |sect| {
223 const gop = (try macho_file.getOutputSection(sect)) orelse {223 const sect_id = (try macho_file.getOutputSection(sect)) orelse {
224 log.debug(" unhandled section", .{});224 log.debug(" unhandled section", .{});
225 continue;225 continue;
226 };226 };
227 const output = macho_file.sections.items(.header)[gop.sect_id];227 const output = macho_file.sections.items(.header)[sect_id];
228 log.debug("mapping '{s},{s}' into output sect({d}, '{s},{s}')", .{228 log.debug("mapping '{s},{s}' into output sect({d}, '{s},{s}')", .{
229 sect.segName(),229 sect.segName(),
230 sect.sectName(),230 sect.sectName(),
231 gop.sect_id + 1,231 sect_id + 1,
232 output.segName(),232 output.segName(),
233 output.sectName(),233 output.sectName(),
234 });234 });
...@@ -335,11 +335,10 @@ pub fn splitIntoAtoms(self: *Object, macho_file: *MachO, object_id: u32) !void {...@@ -335,11 +335,10 @@ pub fn splitIntoAtoms(self: *Object, macho_file: *MachO, object_id: u32) !void {
335 log.debug("splitting section '{s},{s}' into atoms", .{ sect.segName(), sect.sectName() });335 log.debug("splitting section '{s},{s}' into atoms", .{ sect.segName(), sect.sectName() });
336336
337 // Get matching segment/section in the final artifact.337 // Get matching segment/section in the final artifact.
338 const gop = (try macho_file.getOutputSection(sect)) orelse {338 const out_sect_id = (try macho_file.getOutputSection(sect)) orelse {
339 log.debug(" unhandled section", .{});339 log.debug(" unhandled section", .{});
340 continue;340 continue;
341 };341 };
342 const out_sect_id = gop.sect_id;
343342
344 log.debug(" output sect({d}, '{s},{s}')", .{343 log.debug(" output sect({d}, '{s},{s}')", .{
345 out_sect_id + 1,344 out_sect_id + 1,
src/link/MachO/Relocation.zig-1
...@@ -181,7 +181,6 @@ fn resolveAarch64(...@@ -181,7 +181,6 @@ fn resolveAarch64(
181 const offset = @divExact(narrowed, 8);181 const offset = @divExact(narrowed, 8);
182 inst.load_store_register.offset = offset;182 inst.load_store_register.offset = offset;
183 mem.writeIntLittle(u32, &buffer, inst.toU32());183 mem.writeIntLittle(u32, &buffer, inst.toU32());
184 log.debug("HMM = {x}", .{std.fmt.fmtSliceHexLower(&buffer)});
185 },184 },
186 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {185 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {
187 const RegInfo = struct {186 const RegInfo = struct {
src/link/MachO/zld.zig+13-1
...@@ -876,11 +876,23 @@ fn allocateSegments(macho_file: *MachO) !void {...@@ -876,11 +876,23 @@ fn allocateSegments(macho_file: *MachO) !void {
876 }, 0);876 }, 0);
877}877}
878878
879fn getSegmentAllocBase(macho_file: *MachO, indices: []const ?u8) struct { vmaddr: u64, fileoff: u64 } {
880 for (indices) |maybe_prev_id| {
881 const prev_id = maybe_prev_id orelse continue;
882 const prev = macho_file.segments.items[prev_id];
883 return .{
884 .vmaddr = prev.vmaddr + prev.vmsize,
885 .fileoff = prev.fileoff + prev.filesize,
886 };
887 }
888 return .{ .vmaddr = 0, .fileoff = 0 };
889}
890
879fn allocateSegment(macho_file: *MachO, maybe_index: ?u8, indices: []const ?u8, init_size: u64) !void {891fn allocateSegment(macho_file: *MachO, maybe_index: ?u8, indices: []const ?u8, init_size: u64) !void {
880 const index = maybe_index orelse return;892 const index = maybe_index orelse return;
881 const seg = &macho_file.segments.items[index];893 const seg = &macho_file.segments.items[index];
882894
883 const base = macho_file.getSegmentAllocBase(indices);895 const base = getSegmentAllocBase(macho_file, indices);
884 seg.vmaddr = base.vmaddr;896 seg.vmaddr = base.vmaddr;
885 seg.fileoff = base.fileoff;897 seg.fileoff = base.fileoff;
886 seg.filesize = init_size;898 seg.filesize = init_size;