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 {
798798 return tt == S_ZEROFILL or tt == S_GB_ZEROFILL or tt == S_THREAD_LOCAL_ZEROFILL;
799799 }
800800
801 pub fn isSymbolStubs(sect: section_64) bool {
802 const tt = sect.@"type"();
803 return tt == S_SYMBOL_STUBS;
804 }
805
801806 pub fn isDebug(sect: section_64) bool {
802807 return sect.attrs() & S_ATTR_DEBUG != 0;
803808 }
src/link/Dwarf.zig+15-2
......@@ -948,7 +948,7 @@ pub fn commitDeclState(
948948 new_offset,
949949 });
950950
951 try File.MachO.copyRangeAllOverlappingAlloc(
951 try copyRangeAllOverlappingAlloc(
952952 gpa,
953953 d_sym.file,
954954 debug_line_sect.offset,
......@@ -1247,7 +1247,7 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co
12471247 new_offset,
12481248 });
12491249
1250 try File.MachO.copyRangeAllOverlappingAlloc(
1250 try copyRangeAllOverlappingAlloc(
12511251 gpa,
12521252 d_sym.file,
12531253 debug_info_sect.offset,
......@@ -2338,3 +2338,16 @@ fn addDbgInfoErrorSet(
23382338 // DW.AT.enumeration_type delimit children
23392339 try dbg_info_buffer.append(0);
23402340}
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) = .{},
115115sections: std.MultiArrayList(Section) = .{},
116116
117117pagezero_segment_cmd_index: ?u8 = null,
118header_segment_cmd_index: ?u8 = null,
118119text_segment_cmd_index: ?u8 = null,
119120data_const_segment_cmd_index: ?u8 = null,
120121data_segment_cmd_index: ?u8 = null,
......@@ -124,6 +125,7 @@ text_section_index: ?u8 = null,
124125stubs_section_index: ?u8 = null,
125126stub_helper_section_index: ?u8 = null,
126127got_section_index: ?u8 = null,
128data_const_section_index: ?u8 = null,
127129la_symbol_ptr_section_index: ?u8 = null,
128130data_section_index: ?u8 = null,
129131
......@@ -157,6 +159,8 @@ stubs_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
157159
158160error_flags: File.ErrorFlags = File.ErrorFlags{},
159161
162segment_table_dirty: bool = false,
163
160164/// A helper var to indicate if we are at the start of the incremental updates, or
161165/// already somewhere further along the update-and-run chain.
162166/// 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:
10661070 }
10671071}
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
11991073pub fn createEmptyAtom(gpa: Allocator, sym_index: u32, size: u64, alignment: u32) !*Atom {
12001074 const size_usize = math.cast(usize, size) orelse return error.Overflow;
12011075 const atom = try gpa.create(Atom);
......@@ -1263,7 +1137,11 @@ pub fn allocateSpecialSymbols(self: *MachO) !void {
12631137 const global = self.getGlobal(name) orelse continue;
12641138 if (global.file != null) continue;
12651139 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];
12671145 sym.n_sect = 1;
12681146 sym.n_value = seg.vmaddr;
12691147
......@@ -1284,7 +1162,7 @@ pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
12841162 atom.* = Atom.empty;
12851163 atom.sym_index = sym_index;
12861164 atom.size = @sizeOf(u64);
1287 atom.alignment = 3;
1165 atom.alignment = @alignOf(u64);
12881166 break :blk atom;
12891167 },
12901168 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3),
......@@ -1357,39 +1235,6 @@ pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
13571235 return atom;
13581236}
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
13931238pub fn createDyldPrivateAtom(self: *MachO) !void {
13941239 if (self.dyld_stub_binder_index == null) return;
13951240 if (self.dyld_private_atom != null) return;
......@@ -1403,7 +1248,7 @@ pub fn createDyldPrivateAtom(self: *MachO) !void {
14031248 atom.* = Atom.empty;
14041249 atom.sym_index = sym_index;
14051250 atom.size = @sizeOf(u64);
1406 atom.alignment = 3;
1251 atom.alignment = @alignOf(u64);
14071252 break :blk atom;
14081253 },
14091254 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3),
......@@ -1450,7 +1295,11 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
14501295 atom.* = Atom.empty;
14511296 atom.sym_index = sym_index;
14521297 atom.size = size;
1453 atom.alignment = alignment;
1298 atom.alignment = switch (arch) {
1299 .x86_64 => 1,
1300 .aarch64 => @alignOf(u32),
1301 else => unreachable,
1302 };
14541303 break :blk atom;
14551304 },
14561305 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, size, alignment),
......@@ -1621,7 +1470,7 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
16211470 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
16221471
16231472 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);
16251474 log.debug("allocated stub preamble atom at 0x{x}", .{sym.n_value});
16261475 try self.writeAtom(atom, code);
16271476 } else {
......@@ -1650,7 +1499,11 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
16501499 atom.* = Atom.empty;
16511500 atom.sym_index = sym_index;
16521501 atom.size = size;
1653 atom.alignment = alignment;
1502 atom.alignment = switch (arch) {
1503 .x86_64 => 1,
1504 .aarch64 => @alignOf(u32),
1505 else => unreachable,
1506 };
16541507 break :blk atom;
16551508 },
16561509 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, size, alignment),
......@@ -1738,7 +1591,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
17381591 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
17391592
17401593 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);
17421595 log.debug("allocated stub helper atom at 0x{x}", .{sym.n_value});
17431596 try self.writeAtom(atom, code);
17441597 } else {
......@@ -1758,7 +1611,7 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWi
17581611 atom.* = Atom.empty;
17591612 atom.sym_index = sym_index;
17601613 atom.size = @sizeOf(u64);
1761 atom.alignment = 3;
1614 atom.alignment = @alignOf(u64);
17621615 break :blk atom;
17631616 },
17641617 .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 {
18431696 atom.* = Atom.empty;
18441697 atom.sym_index = sym_index;
18451698 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 };
18471705 break :blk atom;
18481706 },
18491707 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, size, alignment),
......@@ -1945,7 +1803,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
19451803 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
19461804
19471805 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);
19491807 log.debug("allocated stub atom at 0x{x}", .{sym.n_value});
19501808 try self.writeAtom(atom, code);
19511809 } else {
......@@ -1956,7 +1814,41 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
19561814 return atom;
19571815}
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
19591850pub fn createTentativeDefAtoms(self: *MachO) !void {
1851 assert(self.mode == .one_shot);
19601852 const gpa = self.base.allocator;
19611853
19621854 for (self.globals.items) |global| {
......@@ -1971,20 +1863,15 @@ pub fn createTentativeDefAtoms(self: *MachO) !void {
19711863 // text blocks for each tentative definition.
19721864 const size = sym.n_value;
19731865 const alignment = (sym.n_desc >> 8) & 0x0f;
1974 const gop = (try self.getOutputSection(.{
1866 const sect_id = (try self.getOutputSection(.{
19751867 .segname = makeStaticString("__DATA"),
19761868 .sectname = makeStaticString("__bss"),
19771869 .flags = macho.S_ZEROFILL,
19781870 })).?;
1979 if (self.mode == .incremental and !gop.found_existing) {
1980 // TODO allocate section
1981 try self.allocateSection(gop.sect_id, size, alignment);
1982 }
1983
19841871 sym.* = .{
19851872 .n_strx = sym.n_strx,
19861873 .n_type = macho.N_SECT | macho.N_EXT,
1987 .n_sect = gop.sect_id,
1874 .n_sect = sect_id + 1,
19881875 .n_desc = 0,
19891876 .n_value = 0,
19901877 };
......@@ -1992,7 +1879,7 @@ pub fn createTentativeDefAtoms(self: *MachO) !void {
19921879 const atom = try MachO.createEmptyAtom(gpa, global.sym_index, size, alignment);
19931880 atom.file = global.file;
19941881
1995 try self.allocateAtomCommon(atom);
1882 try self.addAtomToSection(atom);
19961883
19971884 if (global.file) |file| {
19981885 const object = &self.objects.items[file];
......@@ -2350,7 +2237,12 @@ pub fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
23502237
23512238pub fn writeMainLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
23522239 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];
23542246 const global = try self.getEntryPoint();
23552247 const sym = self.getSymbol(global);
23562248 try lc_writer.writeStruct(macho.entry_point_command{
......@@ -2946,14 +2838,8 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
29462838
29472839 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
29482840 atom.size = code.len;
2949 atom.alignment = math.log2(required_alignment);
2950 const sect_id = try self.getOutputSectionAtom(
2951 atom,
2952 decl_name,
2953 typed_value.ty,
2954 typed_value.val,
2955 required_alignment,
2956 );
2841 atom.alignment = required_alignment;
2842 const sect_id = self.getDeclOutputSection(decl);
29572843 const symbol = atom.getSymbolPtr(self);
29582844 symbol.n_strx = name_str_index;
29592845 symbol.n_type = macho.N_SECT;
......@@ -3050,85 +2936,16 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
30502936 try self.updateDeclExports(module, decl_index, decl_exports);
30512937}
30522938
3053/// Checks if the value, or any of its embedded values stores a pointer, and thus requires
3054/// a rebase opcode for the dynamic linker.
3055fn needsPointerRebase(ty: Type, val: Value, mod: *Module) bool {
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);
2939fn getDeclOutputSection(self: *MachO, decl: *Module.Decl) u8 {
2940 const ty = decl.ty;
2941 const val = decl.val;
31172942 const zig_ty = ty.zigTypeTag();
31182943 const mode = self.base.options.optimize_mode;
3119
31202944 const sect_id: u8 = blk: {
31212945 // TODO finish and audit this function
31222946 if (val.isUndefDeep()) {
31232947 if (mode == .ReleaseFast or mode == .ReleaseSmall) {
3124 const gop = (try self.getOutputSection(.{
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;
2948 @panic("TODO __DATA,__bss");
31322949 } else {
31332950 break :blk self.data_section_index.?;
31342951 }
......@@ -3138,88 +2955,145 @@ fn getOutputSectionAtom(
31382955 break :blk self.data_section_index.?;
31392956 }
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
31522958 switch (zig_ty) {
3153 .Fn => {
3154 break :blk self.text_section_index.?;
3155 },
3156 .Array => {
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 }
2959 .Fn => break :blk self.text_section_index.?,
2960 else => {
2961 if (val.castTag(.variable)) |_| {
2962 break :blk self.data_section_index.?;
31752963 }
2964 break :blk self.data_const_section_index.?;
31762965 },
3177 else => {},
31782966 }
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;
31872967 };
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 });
31962968 return sect_id;
31972969}
31982970
3199fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8) !u64 {
3200 const gpa = self.base.allocator;
3201 const mod = self.base.options.module.?;
3202 const decl = mod.declPtr(decl_index);
3203
3204 const required_alignment = decl.getAlignment(self.base.options.target);
3205 assert(decl.link.macho.sym_index != 0); // Caller forgot to call allocateDeclIndexes()
2971pub fn getOutputSection(self: *MachO, sect: macho.section_64) !?u8 {
2972 const segname = sect.segName();
2973 const sectname = sect.sectName();
2974 const sect_id: ?u8 = blk: {
2975 if (mem.eql(u8, "__LLVM", segname)) {
2976 log.debug("TODO LLVM section: type 0x{x}, name '{s},{s}'", .{
2977 sect.flags, segname, sectname,
2978 });
2979 break :blk null;
2980 }
32062981
3207 const sym_name = try decl.getFullyQualifiedName(mod);
3208 defer self.base.allocator.free(sym_name);
2982 if (sect.isCode()) {
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;
3211 const decl_ptr = self.decls.getPtr(decl_index).?;
3212 if (decl_ptr.* == null) {
3213 decl_ptr.* = try self.getOutputSectionAtom(
3214 atom,
3215 sym_name,
3216 decl.ty,
3217 decl.val,
3218 required_alignment,
3219 );
3220 }
3221 const sect_id = decl_ptr.*.?;
3222 const code_len = code.len;
2993 if (sect.isDebug()) {
2994 // TODO debug attributes
2995 if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) {
2996 log.debug("TODO compact unwind section: type 0x{x}, name '{s},{s}'", .{
2997 sect.flags, segname, sectname,
2998 });
2999 }
3000 break :blk null;
3001 }
3002
3003 switch (sect.@"type"()) {
3004 macho.S_4BYTE_LITERALS,
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
32243098 if (atom.size != 0) {
32253099 const sym = atom.getSymbolPtr(self);
......@@ -3536,15 +3410,13 @@ pub fn populateMissingMetadata(self: *MachO) !void {
35363410 }
35373411 }
35383412
3539 if (self.text_segment_cmd_index == null) {
3540 self.text_segment_cmd_index = @intCast(u8, self.segments.items.len);
3541 const headerpad_size = @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size);
3542 const program_code_size_hint = self.base.options.program_code_size_hint;
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;
3413 if (self.header_segment_cmd_index == null) {
3414 // The first __TEXT segment is immovable and covers MachO header and load commands.
3415 self.header_segment_cmd_index = @intCast(u8, self.segments.items.len);
3416 const ideal_size = @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size);
35453417 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
35493421 try self.segments.append(gpa, .{
35503422 .segname = makeStaticString("__TEXT"),
......@@ -3555,150 +3427,101 @@ pub fn populateMissingMetadata(self: *MachO) !void {
35553427 .initprot = macho.PROT.READ | macho.PROT.EXEC,
35563428 .cmdsize = @sizeOf(macho.segment_command_64),
35573429 });
3430 self.segment_table_dirty = true;
35583431 }
35593432
35603433 if (self.text_section_index == null) {
3561 const alignment: u2 = switch (cpu_arch) {
3562 .x86_64 => 0,
3563 .aarch64 => 2,
3564 else => unreachable, // unhandled architecture type
3565 };
3566 const needed_size = self.base.options.program_code_size_hint;
3567 self.text_section_index = try self.initSection("__TEXT", "__text", .{
3434 self.text_section_index = try self.allocateSection("__TEXT1", "__text", .{
3435 .size = self.base.options.program_code_size_hint,
3436 .alignment = switch (cpu_arch) {
3437 .x86_64 => 1,
3438 .aarch64 => @sizeOf(u32),
3439 else => unreachable, // unhandled architecture type
3440 },
35683441 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
3442 .prot = macho.PROT.READ | macho.PROT.EXEC,
35693443 });
3570 try self.allocateSection(self.text_section_index.?, needed_size, alignment);
3444 self.segment_table_dirty = true;
35713445 }
35723446
35733447 if (self.stubs_section_index == null) {
3574 const alignment: u2 = 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) {
3448 const stub_size: u32 = switch (cpu_arch) {
35803449 .x86_64 => 6,
35813450 .aarch64 => 3 * @sizeOf(u32),
35823451 else => unreachable, // unhandled architecture type
35833452 };
3584 const needed_size = stub_size * self.base.options.symbol_count_hint;
3585 self.stubs_section_index = try self.initSection("__TEXT", "__stubs", .{
3453 self.stubs_section_index = try self.allocateSection("__TEXT2", "__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 },
35863460 .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
35873461 .reserved2 = stub_size,
3462 .prot = macho.PROT.READ | macho.PROT.EXEC,
35883463 });
3589 try self.allocateSection(self.stubs_section_index.?, needed_size, alignment);
3464 self.segment_table_dirty = true;
35903465 }
35913466
35923467 if (self.stub_helper_section_index == null) {
3593 const alignment: u2 = switch (cpu_arch) {
3594 .x86_64 => 0,
3595 .aarch64 => 2,
3596 else => unreachable, // unhandled architecture type
3597 };
3598 const preamble_size: u6 = switch (cpu_arch) {
3599 .x86_64 => 15,
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", .{
3468 self.stub_helper_section_index = try self.allocateSection("__TEXT3", "__stub_helper", .{
3469 .size = @sizeOf(u32),
3470 .alignment = switch (cpu_arch) {
3471 .x86_64 => 1,
3472 .aarch64 => @sizeOf(u32),
3473 else => unreachable, // unhandled architecture type
3474 },
36103475 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
3476 .prot = macho.PROT.READ | macho.PROT.EXEC,
36113477 });
3612 try self.allocateSection(self.stub_helper_section_index.?, needed_size, alignment);
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 });
3478 self.segment_table_dirty = true;
36383479 }
36393480
36403481 if (self.got_section_index == null) {
3641 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
3642 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
3643 self.got_section_index = try self.initSection("__DATA_CONST", "__got", .{
3482 self.got_section_index = try self.allocateSection("__DATA_CONST", "__got", .{
3483 .size = @sizeOf(u64) * self.base.options.symbol_count_hint,
3484 .alignment = @alignOf(u64),
36443485 .flags = macho.S_NON_LAZY_SYMBOL_POINTERS,
3486 .prot = macho.PROT.READ | macho.PROT.WRITE,
36453487 });
3646 try self.allocateSection(self.got_section_index.?, needed_size, alignment);
3488 self.segment_table_dirty = true;
36473489 }
36483490
3649 if (self.data_segment_cmd_index == null) {
3650 self.data_segment_cmd_index = @intCast(u8, self.segments.items.len);
3651 const base = self.getSegmentAllocBase(&.{self.data_const_segment_cmd_index.?});
3652 const vmaddr = base.vmaddr;
3653 const fileoff = base.fileoff;
3654 const ideal_size = 2 * @sizeOf(u64) * self.base.options.symbol_count_hint;
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),
3491 if (self.data_const_section_index == null) {
3492 self.data_const_section_index = try self.allocateSection("__DATA_CONST1", "__const", .{
3493 .size = @sizeOf(u64),
3494 .alignment = @alignOf(u64),
3495 .flags = macho.S_REGULAR,
3496 .prot = macho.PROT.READ | macho.PROT.WRITE,
36713497 });
3498 self.segment_table_dirty = true;
36723499 }
36733500
36743501 if (self.la_symbol_ptr_section_index == null) {
3675 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
3676 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
3677 self.la_symbol_ptr_section_index = try self.initSection("__DATA", "__la_symbol_ptr", .{
3502 self.la_symbol_ptr_section_index = try self.allocateSection("__DATA", "__la_symbol_ptr", .{
3503 .size = @sizeOf(u64),
3504 .alignment = @alignOf(u64),
36783505 .flags = macho.S_LAZY_SYMBOL_POINTERS,
3506 .prot = macho.PROT.READ | macho.PROT.WRITE,
36793507 });
3680 try self.allocateSection(self.la_symbol_ptr_section_index.?, needed_size, alignment);
3508 self.segment_table_dirty = true;
36813509 }
36823510
36833511 if (self.data_section_index == null) {
3684 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
3685 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
3686 self.data_section_index = try self.initSection("__DATA", "__data", .{});
3687 try self.allocateSection(self.data_section_index.?, needed_size, alignment);
3512 self.data_section_index = try self.allocateSection("__DATA1", "__data", .{
3513 .size = @sizeOf(u64),
3514 .alignment = @alignOf(u64),
3515 .flags = macho.S_REGULAR,
3516 .prot = macho.PROT.READ | macho.PROT.WRITE,
3517 });
3518 self.segment_table_dirty = true;
36883519 }
36893520
36903521 if (self.linkedit_segment_cmd_index == null) {
36913522 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
36983523 try self.segments.append(gpa, .{
36993524 .segname = makeStaticString("__LINKEDIT"),
3700 .vmaddr = vmaddr,
3701 .fileoff = fileoff,
37023525 .maxprot = macho.PROT.READ,
37033526 .initprot = macho.PROT.READ,
37043527 .cmdsize = @sizeOf(macho.segment_command_64),
......@@ -3825,338 +3648,65 @@ pub fn calcMinHeaderPad(self: *MachO) !u64 {
38253648 return offset;
38263649}
38273650
3828fn allocateSection(self: *MachO, sect_id: u8, size: u64, alignment: u32) !void {
3829 const segment_id = self.sections.items(.segment_index)[sect_id];
3830 const seg = &self.segments.items[segment_id];
3831 const header = &self.sections.items(.header)[sect_id];
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 {
3651fn allocateSection(self: *MachO, segname: []const u8, sectname: []const u8, opts: struct {
3652 size: u64 = 0,
3653 alignment: u32 = 0,
3654 prot: macho.vm_prot_t = macho.PROT.NONE,
38903655 flags: u32 = macho.S_REGULAR,
3891 reserved1: u32 = 0,
38923656 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(
3896 self: *MachO,
3897 segname: []const u8,
3898 sectname: []const u8,
3899 opts: InitSectionOpts,
3900) !u8 {
3901 const segment_id = self.getSegmentByName(segname).?;
3902 const seg = &self.segments.items[segment_id];
3903 const index = try self.insertSection(segment_id, .{
3680 const seg = try self.segments.addOne(gpa);
3681 seg.* = .{
3682 .segname = makeStaticString(segname),
3683 .vmaddr = vmaddr,
3684 .vmsize = vmsize,
3685 .fileoff = off,
3686 .filesize = opts.size,
3687 .maxprot = opts.prot,
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{
39043694 .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),
39063700 .flags = opts.flags,
3907 .reserved1 = opts.reserved1,
39083701 .reserved2 = opts.reserved2,
3909 });
3910 seg.cmdsize += @sizeOf(macho.section_64);
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}
3702 };
3703 assert(!section.isZerofill()); // TODO zerofill sections
39913704
3992fn growSegment(self: *MachO, segment_index: u8, new_size: u64) !void {
3993 const segment = &self.segments.items[segment_index];
3994 const new_segment_size = mem.alignForwardGeneric(u64, new_size, self.page_size);
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,
3705 try self.sections.append(gpa, .{
3706 .segment_index = segment_id,
3707 .header = section,
40103708 });
4011
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);
3709 return section_id;
41603710}
41613711
41623712fn 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) !
41643714 defer tracy.end();
41653715
41663716 const sect_id = atom.getSymbol(self).n_sect - 1;
3717 const segment = &self.segments.items[self.sections.items(.segment_index)[sect_id]];
41673718 const header = &self.sections.items(.header)[sect_id];
41683719 const free_list = &self.sections.items(.free_list)[sect_id];
41693720 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];
41703721 const requires_padding = blk: {
41713722 if (!header.isCode()) break :blk false;
4172 if (mem.eql(u8, "__stubs", header.sectName())) break :blk false;
3723 if (header.isSymbolStubs()) break :blk false;
41733724 if (mem.eql(u8, "__stub_helper", header.sectName())) break :blk false;
41743725 break :blk true;
41753726 };
......@@ -4229,24 +3780,58 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
42293780 atom_placement = last;
42303781 break :blk new_start_vaddr;
42313782 } else {
4232 break :blk mem.alignForwardGeneric(u64, header.addr, alignment);
3783 break :blk mem.alignForwardGeneric(u64, segment.vmaddr, alignment);
42333784 }
42343785 };
42353786
42363787 const expand_section = atom_placement == null or atom_placement.?.next == null;
42373788 if (expand_section) {
4238 const needed_size = @intCast(u32, (vaddr + new_atom_size) - header.addr);
4239 try self.growSection(sect_id, needed_size);
4240 self.markRelocsDirtyByAddress(header.addr + needed_size);
4241 maybe_last_atom.* = atom;
3789 const sect_capacity = self.allocatedSize(header.offset);
3790 const needed_size = (vaddr + new_atom_size) - segment.vmaddr;
3791 if (needed_size > sect_capacity) {
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
42423822 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;
42433829 }
3830
42443831 const align_pow = @intCast(u32, math.log2(alignment));
42453832 if (header.@"align" < align_pow) {
42463833 header.@"align" = align_pow;
42473834 }
4248 atom.size = new_atom_size;
4249 atom.alignment = align_pow;
42503835
42513836 if (atom.prev) |prev| {
42523837 prev.next = atom.next;
......@@ -4270,6 +3855,78 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
42703855 return vaddr;
42713856}
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
42733930pub fn addAtomToSection(self: *MachO, atom: *Atom) !void {
42743931 const sect_id = atom.getSymbol(self).n_sect - 1;
42753932 var section = self.sections.get(sect_id);
......@@ -4310,43 +3967,13 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {
43103967 return global_index;
43113968}
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
43253970fn writeSegmentHeaders(self: *MachO, ncmds: *u32, writer: anytype) !void {
43263971 for (self.segments.items) |seg, i| {
43273972 const indexes = self.getSectionIndexes(@intCast(u8, i));
4328 var out_seg = 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.
3973 try writer.writeStruct(seg);
43343974 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;
43473975 try writer.writeStruct(header);
43483976 }
4349
43503977 ncmds.* += 1;
43513978 }
43523979}
......@@ -4356,6 +3983,24 @@ fn writeLinkeditSegmentData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void
43563983 seg.filesize = 0;
43573984 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
43594004 try self.writeDyldInfoData(ncmds, lc_writer);
43604005 try self.writeSymtabs(ncmds, lc_writer);
43614006
......@@ -4471,7 +4116,7 @@ fn writeDyldInfoData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
44714116 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
44724117 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.?];
44754120 const base_address = text_segment.vmaddr;
44764121
44774122 if (self.base.options.output_mode == .Exe) {
......@@ -4593,7 +4238,8 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
45934238 var stub_atom = last_atom;
45944239 var laptr_atom = self.sections.items(.last_atom)[self.la_symbol_ptr_section_index.?].?;
45954240 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];
45974243 break :blk seg.vmaddr;
45984244 };
45994245
......@@ -4932,7 +4578,8 @@ fn writeCodeSignaturePadding(
49324578}
49334579
49344580fn 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
49374584 var buffer = std.ArrayList(u8).init(self.base.allocator);
49384585 defer buffer.deinit();
......@@ -5005,7 +4652,7 @@ pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
50054652
50064653fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
50074654 // 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;
50094656 if (start < header_size)
50104657 return header_size;
50114658
......@@ -5023,16 +4670,16 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
50234670 return null;
50244671}
50254672
5026// fn allocatedSize(self: *MachO, start: u64) u64 {
5027// if (start == 0)
5028// return 0;
5029// var min_pos: u64 = std.math.maxInt(u64);
5030// for (self.sections.items(.header)) |header| {
5031// if (header.offset <= start) continue;
5032// if (header.offset < min_pos) min_pos = header.offset;
5033// }
5034// return min_pos - start;
5035// }
4673fn allocatedSize(self: *MachO, start: u64) u64 {
4674 if (start == 0)
4675 return 0;
4676 var min_pos: u64 = std.math.maxInt(u64);
4677 for (self.sections.items(.header)) |header| {
4678 if (header.offset <= start) continue;
4679 if (header.offset < min_pos) min_pos = header.offset;
4680 }
4681 return min_pos - start;
4682}
50364683
50374684fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 {
50384685 var start: u64 = 0;
......@@ -5042,6 +4689,18 @@ fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 {
50424689 return start;
50434690}
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
50454704pub fn makeStaticString(bytes: []const u8) [16]u8 {
50464705 var buf = [_]u8{0} ** 16;
50474706 assert(bytes.len <= buf.len);
......@@ -5645,19 +5304,3 @@ pub fn logAtom(self: *MachO, atom: *const Atom) void {
56455304 });
56465305 }
56475306}
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,
312312 const sect_id = @intCast(u16, rel.r_symbolnum - 1);
313313 const sym_index = object.sections_as_symbols.get(sect_id) orelse blk: {
314314 const sect = object.getSourceSection(sect_id);
315 const gop = (try context.macho_file.getOutputSection(sect)) orelse
315 const out_sect_id = (try context.macho_file.getOutputSection(sect)) orelse
316316 unreachable;
317 const out_sect_id = gop.sect_id;
318317 const sym_index = @intCast(u32, object.symtab.items.len);
319318 try object.symtab.append(gpa, .{
320319 .n_strx = 0,
src/link/MachO/DebugSymbols.zig+15-2
......@@ -512,7 +512,7 @@ fn writeSymtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
512512 const dwarf_seg = &self.segments.items[self.dwarf_segment_cmd_index.?];
513513 seg.filesize = aligned_size;
514514
515 try MachO.copyRangeAllOverlappingAlloc(
515 try copyRangeAllOverlappingAlloc(
516516 self.base.base.allocator,
517517 self.file,
518518 dwarf_seg.fileoff,
......@@ -571,7 +571,7 @@ fn writeStrtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
571571 const dwarf_seg = &self.segments.items[self.dwarf_segment_cmd_index.?];
572572 seg.filesize = aligned_size;
573573
574 try MachO.copyRangeAllOverlappingAlloc(
574 try copyRangeAllOverlappingAlloc(
575575 self.base.base.allocator,
576576 self.file,
577577 dwarf_seg.fileoff,
......@@ -601,3 +601,16 @@ fn writeStrtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
601601
602602 try self.file.pwriteAll(self.strtab.buffer.items, lc.stroff);
603603}
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(
220220
221221pub fn scanInputSections(self: Object, macho_file: *MachO) !void {
222222 for (self.sections.items) |sect| {
223 const gop = (try macho_file.getOutputSection(sect)) orelse {
223 const sect_id = (try macho_file.getOutputSection(sect)) orelse {
224224 log.debug(" unhandled section", .{});
225225 continue;
226226 };
227 const output = macho_file.sections.items(.header)[gop.sect_id];
227 const output = macho_file.sections.items(.header)[sect_id];
228228 log.debug("mapping '{s},{s}' into output sect({d}, '{s},{s}')", .{
229229 sect.segName(),
230230 sect.sectName(),
231 gop.sect_id + 1,
231 sect_id + 1,
232232 output.segName(),
233233 output.sectName(),
234234 });
......@@ -335,11 +335,10 @@ pub fn splitIntoAtoms(self: *Object, macho_file: *MachO, object_id: u32) !void {
335335 log.debug("splitting section '{s},{s}' into atoms", .{ sect.segName(), sect.sectName() });
336336
337337 // 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 {
339339 log.debug(" unhandled section", .{});
340340 continue;
341341 };
342 const out_sect_id = gop.sect_id;
343342
344343 log.debug(" output sect({d}, '{s},{s}')", .{
345344 out_sect_id + 1,
src/link/MachO/Relocation.zig-1
......@@ -181,7 +181,6 @@ fn resolveAarch64(
181181 const offset = @divExact(narrowed, 8);
182182 inst.load_store_register.offset = offset;
183183 mem.writeIntLittle(u32, &buffer, inst.toU32());
184 log.debug("HMM = {x}", .{std.fmt.fmtSliceHexLower(&buffer)});
185184 },
186185 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {
187186 const RegInfo = struct {
src/link/MachO/zld.zig+13-1
......@@ -876,11 +876,23 @@ fn allocateSegments(macho_file: *MachO) !void {
876876 }, 0);
877877}
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
879891fn allocateSegment(macho_file: *MachO, maybe_index: ?u8, indices: []const ?u8, init_size: u64) !void {
880892 const index = maybe_index orelse return;
881893 const seg = &macho_file.segments.items[index];
882894
883 const base = macho_file.getSegmentAllocBase(indices);
895 const base = getSegmentAllocBase(macho_file, indices);
884896 seg.vmaddr = base.vmaddr;
885897 seg.fileoff = base.fileoff;
886898 seg.filesize = init_size;