authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-08-04 14:24:00-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-08-04 14:24:00-07:00
logba70eee8bb81da0d44982a84395aee660635e5ba
tree40f4ef036ab60572f9b19e94dc84415993ed3fa8
parenta3045b8abbba896da34a02266f2be89dd6c90ecc
parent616f65df750f53e6334cc5ed2c8f4b5668d573f2

Merge remote-tracking branch 'origin/master' into llvm15


88 files changed, 3870 insertions(+), 4371 deletions(-)

.github/CODEOWNERS created+3
......@@ -0,0 +1,3 @@
1# Autodoc
2/src/Autodoc.zig @kristoff-it
3/lib/docs/* @kristoff-it
\ No newline at end of file
.github/ISSUE_TEMPLATE/autodoc-issue.md created+12
......@@ -0,0 +1,12 @@
1---
2name: Autodoc Issue
3about: Issues with automatically generated docs, including stdlib docs.
4title: 'Autodoc: {your issue}'
5labels: autodoc
6assignees: kristoff-it
7
8---
9
10Autodoc is still work in progress and as such many bugs and missing features are already known.
11
12# Please report only <ins>regressions</ins>, i.e. things that worked in a previous build of new Autodoc (orange banner) that now don't work any more.
lib/docs/index.html+10
......@@ -284,6 +284,16 @@
284284 overflow-x: hidden;
285285 }
286286
287 .fieldHasDocs {
288 margin-bottom: 0;
289 }
290
291 .fieldDocs {
292 border: 1px solid #2A2A2A;
293 border-top: 0px;
294 padding: 1px 1em;
295 }
296
287297 /* help dialog */
288298 .help-modal {
289299 display: flex;
lib/docs/main.js+45-16
......@@ -618,15 +618,18 @@ var zigAnalysis;
618618 for (let i = 0; i < fields.length; i += 1) {
619619 let field = fields[i];
620620 let fieldNode = zigAnalysis.astNodes[field];
621 let docs = fieldNode.docs;
621622 if (fieldNode.docs == null) {
622623 continue;
623624 }
625 let docsNonEmpty = docs !== "";
624626 let divDom = domListParams.children[domIndex];
625627 domIndex += 1;
626628
627629
628630 let value = typeObj.params[i];
629 let html = '<pre>' + escapeHtml((fieldNode.name)) + ": ";
631 let preClass = docsNonEmpty ? ' class="fieldHasDocs"' : "";
632 let html = '<pre' + preClass + '>' + escapeHtml((fieldNode.name)) + ": ";
630633 if (isVarArgs && i === typeObj.params.length - 1) {
631634 html += '...';
632635 } else {
......@@ -636,9 +639,8 @@ var zigAnalysis;
636639
637640 html += ',</pre>';
638641
639 let docs = fieldNode.docs;
640 if (docs != null) {
641 html += markdown(docs);
642 if (docsNonEmpty) {
643 html += '<div class="fieldDocs">' + markdown(docs) + '</div>';
642644 }
643645 divDom.innerHTML = html;
644646 }
......@@ -824,7 +826,13 @@ var zigAnalysis;
824826
825827 function exprName(expr, opts) {
826828 switch (Object.keys(expr)[0]) {
827 default: throw "oh no";
829 default: throw "this expression is not implemented yet";
830 case "bool": {
831 if (expr.bool) {
832 return "true";
833 }
834 return "false";
835 }
828836 case "&": {
829837 return "&" + exprName(zigAnalysis.exprs[expr["&"]]);
830838 }
......@@ -929,8 +937,7 @@ var zigAnalysis;
929937 return exprName(switchIndex, opts);
930938 }
931939 case "refPath" : {
932 const declRef = expr.refPath[0].declRef;
933 let name = zigAnalysis.decls[declRef].name;
940 let name = exprName(expr.refPath[0]);
934941 for (let i = 1; i < expr.refPath.length; i++) {
935942 let component = undefined;
936943 if ("string" in expr.refPath[i]) {
......@@ -1402,6 +1409,11 @@ var zigAnalysis;
14021409 let payloadHtml = "@TypeOf(" + exprName(typeRefArg, {wantHtml: true, wantLink:true}) + ")";
14031410 return payloadHtml;
14041411 }
1412 case "typeInfo": {
1413 const typeRefArg = zigAnalysis.exprs[expr.typeInfo];
1414 let payloadHtml = "@typeInfo(" + exprName(typeRefArg, {wantHtml: true, wantLink:true}) + ")";
1415 return payloadHtml;
1416 }
14051417 case "null": {
14061418 return "null";
14071419 }
......@@ -1693,6 +1705,12 @@ var zigAnalysis;
16931705 let rhs = exprName(errUnionObj.rhs, opts);
16941706 return lhs + "!" + rhs;
16951707 }
1708 case typeKinds.InferredErrorUnion:
1709 {
1710 let errUnionObj = (typeObj);
1711 let payload = exprName(errUnionObj.payload, opts);
1712 return "!" + payload;
1713 }
16961714 case typeKinds.Fn:
16971715 {
16981716 let fnObj = (typeObj);
......@@ -2270,8 +2288,11 @@ var zigAnalysis;
22702288 let fieldNode = zigAnalysis.astNodes[containerNode.fields[i]];
22712289 let divDom = domListFields.children[i];
22722290 let fieldName = (fieldNode.name);
2291 let docs = fieldNode.docs;
2292 let docsNonEmpty = docs != null && docs !== "";
2293 let extraPreClass = docsNonEmpty ? " fieldHasDocs" : "";
22732294
2274 let html = '<div class="mobile-scroll-container"><pre class="scroll-item">' + escapeHtml(fieldName);
2295 let html = '<div class="mobile-scroll-container"><pre class="scroll-item' + extraPreClass + '">' + escapeHtml(fieldName);
22752296
22762297 if (container.kind === typeKinds.Enum) {
22772298 html += ' = <span class="tok-number">' + fieldName + '</span>';
......@@ -2289,9 +2310,8 @@ var zigAnalysis;
22892310
22902311 html += ',</pre></div>';
22912312
2292 let docs = fieldNode.docs;
2293 if (docs != null) {
2294 html += markdown(docs);
2313 if (docsNonEmpty) {
2314 html += '<div class="fieldDocs">' + markdown(docs) + '</div>';
22952315 }
22962316 divDom.innerHTML = html;
22972317 }
......@@ -2639,14 +2659,23 @@ var zigAnalysis;
26392659 });
26402660 }
26412661
2642
2662
26432663 function shortDescMarkdown(docs) {
2644 let parts = docs.trim().split("\n");
2645 let firstLine = parts[0];
2646 return markdown(firstLine);
2664 const trimmed_docs = docs.trim();
2665 let index = trimmed_docs.indexOf('.');
2666 if (index < 0) {
2667 index = trimmed_docs.indexOf('\n');
2668 if (index < 0) {
2669 index = trimmed_docs.length;
2670 }
2671 } else {
2672 index += 1; // include the period
2673 }
2674 const slice = trimmed_docs.slice(0, index);
2675 return markdown(slice);
26472676 }
26482677
2649
2678
26502679 function markdown(input) {
26512680 const raw_lines = input.split('\n'); // zig allows no '\r', so we don't need to split on CR
26522681
lib/init-exe/src/main.zig+19-6
......@@ -1,11 +1,24 @@
11const std = @import("std");
22
3pub fn main() anyerror!void {
4 // Note that info level log messages are by default printed only in Debug
5 // and ReleaseSafe build modes.
6 std.log.info("All your codebase are belong to us.", .{});
3pub fn main() !void {
4 // Prints to stderr (it's a shortcut based on `std.io.getStdErr()`)
5 std.debug.print("All your {s} are belong to us.\n", .{"codebase"});
6
7 // stdout is for the actual output of your application, for example if you
8 // are implementing gzip, then only the compressed bytes should be sent to
9 // stdout, not any debugging messages.
10 const stdout_file = std.io.getStdOut().writer();
11 var bw = std.io.bufferedWriter(stdout_file);
12 const stdout = bw.writer();
13
14 try stdout.print("Run `zig build test` to run the tests.\n", .{});
15
16 try bw.flush(); // don't forget to flush!
717}
818
9test "basic test" {
10 try std.testing.expectEqual(10, 3 + 7);
19test "simple test" {
20 var list = std.ArrayList(i32).init(std.testing.allocator);
21 defer list.deinit(); // try commenting this out and see if zig detects the memory leak!
22 try list.append(42);
23 try std.testing.expectEqual(@as(i32, 42), list.pop());
1124}
lib/std/build/CheckObjectStep.zig+47-42
......@@ -283,7 +283,14 @@ fn make(step: *Step) !void {
283283
284284 const gpa = self.builder.allocator;
285285 const src_path = self.source.getPath(self.builder);
286 const contents = try fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);
286 const contents = try fs.cwd().readFileAllocOptions(
287 gpa,
288 src_path,
289 self.max_bytes,
290 null,
291 @alignOf(u64),
292 null,
293 );
287294
288295 const output = switch (self.obj_format) {
289296 .macho => try MachODumper.parseAndDump(contents, .{
......@@ -370,9 +377,10 @@ const Opts = struct {
370377};
371378
372379const MachODumper = struct {
380 const LoadCommandIterator = macho.LoadCommandIterator;
373381 const symtab_label = "symtab";
374382
375 fn parseAndDump(bytes: []const u8, opts: Opts) ![]const u8 {
383 fn parseAndDump(bytes: []align(@alignOf(u64)) const u8, opts: Opts) ![]const u8 {
376384 const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator
377385 var stream = std.io.fixedBufferStream(bytes);
378386 const reader = stream.reader();
......@@ -385,55 +393,54 @@ const MachODumper = struct {
385393 var output = std.ArrayList(u8).init(gpa);
386394 const writer = output.writer();
387395
388 var load_commands = std.ArrayList(macho.LoadCommand).init(gpa);
389 try load_commands.ensureTotalCapacity(hdr.ncmds);
390
391 var sections = std.ArrayList(struct { seg: u16, sect: u16 }).init(gpa);
392 var imports = std.ArrayList(u16).init(gpa);
393
394 var symtab_cmd: ?u16 = null;
395 var i: u16 = 0;
396 while (i < hdr.ncmds) : (i += 1) {
397 var cmd = try macho.LoadCommand.read(gpa, reader);
398 load_commands.appendAssumeCapacity(cmd);
396 var symtab: []const macho.nlist_64 = undefined;
397 var strtab: []const u8 = undefined;
398 var sections = std.ArrayList(macho.section_64).init(gpa);
399 var imports = std.ArrayList([]const u8).init(gpa);
399400
401 var it = LoadCommandIterator{
402 .ncmds = hdr.ncmds,
403 .buffer = bytes[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds],
404 };
405 var i: usize = 0;
406 while (it.next()) |cmd| {
400407 switch (cmd.cmd()) {
401408 .SEGMENT_64 => {
402 const seg = cmd.segment;
403 for (seg.sections.items) |_, j| {
404 try sections.append(.{ .seg = i, .sect = @intCast(u16, j) });
409 const seg = cmd.cast(macho.segment_command_64).?;
410 try sections.ensureUnusedCapacity(seg.nsects);
411 for (cmd.getSections()) |sect| {
412 sections.appendAssumeCapacity(sect);
405413 }
406414 },
407 .SYMTAB => {
408 symtab_cmd = i;
415 .SYMTAB => if (opts.dump_symtab) {
416 const lc = cmd.cast(macho.symtab_command).?;
417 symtab = @ptrCast(
418 [*]const macho.nlist_64,
419 @alignCast(@alignOf(macho.nlist_64), &bytes[lc.symoff]),
420 )[0..lc.nsyms];
421 strtab = bytes[lc.stroff..][0..lc.strsize];
409422 },
410423 .LOAD_DYLIB,
411424 .LOAD_WEAK_DYLIB,
412425 .REEXPORT_DYLIB,
413426 => {
414 try imports.append(i);
427 try imports.append(cmd.getDylibPathName());
415428 },
416429 else => {},
417430 }
418431
419432 try dumpLoadCommand(cmd, i, writer);
420433 try writer.writeByte('\n');
434
435 i += 1;
421436 }
422437
423438 if (opts.dump_symtab) {
424 const cmd = load_commands.items[symtab_cmd.?].symtab;
425 try writer.writeAll(symtab_label ++ "\n");
426 const strtab = bytes[cmd.stroff..][0..cmd.strsize];
427 const raw_symtab = bytes[cmd.symoff..][0 .. cmd.nsyms * @sizeOf(macho.nlist_64)];
428 const symtab = mem.bytesAsSlice(macho.nlist_64, raw_symtab);
429
430439 for (symtab) |sym| {
431440 if (sym.stab()) continue;
432441 const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0);
433442 if (sym.sect()) {
434 const map = sections.items[sym.n_sect - 1];
435 const seg = load_commands.items[map.seg].segment;
436 const sect = seg.sections.items[map.sect];
443 const sect = sections.items[sym.n_sect - 1];
437444 try writer.print("{x} ({s},{s})", .{
438445 sym.n_value,
439446 sect.segName(),
......@@ -455,9 +462,7 @@ const MachODumper = struct {
455462 break :blk "flat lookup";
456463 unreachable;
457464 }
458 const import_id = imports.items[@bitCast(u16, ordinal) - 1];
459 const import = load_commands.items[import_id].dylib;
460 const full_path = mem.sliceTo(import.data, 0);
465 const full_path = imports.items[@bitCast(u16, ordinal) - 1];
461466 const basename = fs.path.basename(full_path);
462467 assert(basename.len > 0);
463468 const ext = mem.lastIndexOfScalar(u8, basename, '.') orelse basename.len;
......@@ -481,7 +486,7 @@ const MachODumper = struct {
481486 return output.toOwnedSlice();
482487 }
483488
484 fn dumpLoadCommand(lc: macho.LoadCommand, index: u16, writer: anytype) !void {
489 fn dumpLoadCommand(lc: macho.LoadCommandIterator.LoadCommand, index: usize, writer: anytype) !void {
485490 // print header first
486491 try writer.print(
487492 \\LC {d}
......@@ -491,8 +496,7 @@ const MachODumper = struct {
491496
492497 switch (lc.cmd()) {
493498 .SEGMENT_64 => {
494 // TODO dump section headers
495 const seg = lc.segment.inner;
499 const seg = lc.cast(macho.segment_command_64).?;
496500 try writer.writeByte('\n');
497501 try writer.print(
498502 \\segname {s}
......@@ -508,7 +512,7 @@ const MachODumper = struct {
508512 seg.filesize,
509513 });
510514
511 for (lc.segment.sections.items) |sect| {
515 for (lc.getSections()) |sect| {
512516 try writer.writeByte('\n');
513517 try writer.print(
514518 \\sectname {s}
......@@ -531,7 +535,7 @@ const MachODumper = struct {
531535 .LOAD_WEAK_DYLIB,
532536 .REEXPORT_DYLIB,
533537 => {
534 const dylib = lc.dylib.inner.dylib;
538 const dylib = lc.cast(macho.dylib_command).?;
535539 try writer.writeByte('\n');
536540 try writer.print(
537541 \\name {s}
......@@ -539,19 +543,20 @@ const MachODumper = struct {
539543 \\current version {x}
540544 \\compatibility version {x}
541545 , .{
542 mem.sliceTo(lc.dylib.data, 0),
543 dylib.timestamp,
544 dylib.current_version,
545 dylib.compatibility_version,
546 lc.getDylibPathName(),
547 dylib.dylib.timestamp,
548 dylib.dylib.current_version,
549 dylib.dylib.compatibility_version,
546550 });
547551 },
548552
549553 .MAIN => {
554 const main = lc.cast(macho.entry_point_command).?;
550555 try writer.writeByte('\n');
551556 try writer.print(
552557 \\entryoff {x}
553558 \\stacksize {x}
554 , .{ lc.main.entryoff, lc.main.stacksize });
559 , .{ main.entryoff, main.stacksize });
555560 },
556561
557562 .RPATH => {
......@@ -559,7 +564,7 @@ const MachODumper = struct {
559564 try writer.print(
560565 \\path {s}
561566 , .{
562 mem.sliceTo(lc.rpath.data, 0),
567 lc.getRpathPathName(),
563568 });
564569 },
565570
lib/std/builtin.zig+11
......@@ -846,6 +846,17 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn
846846 }
847847}
848848
849pub fn checkNonScalarSentinel(expected: anytype, actual: @TypeOf(expected)) void {
850 if (!std.meta.eql(expected, actual)) {
851 panicSentinelMismatch(expected, actual);
852 }
853}
854
855pub fn panicSentinelMismatch(expected: anytype, actual: @TypeOf(expected)) noreturn {
856 @setCold(true);
857 std.debug.panic("sentinel mismatch: expected {any}, found {any}", .{ expected, actual });
858}
859
849860pub fn panicUnwrapError(st: ?*StackTrace, err: anyerror) noreturn {
850861 @setCold(true);
851862 std.debug.panicExtra(st, "attempt to unwrap error: {s}", .{@errorName(err)});
lib/std/crypto/25519/ed25519.zig+18-9
......@@ -229,15 +229,14 @@ pub const Ed25519 = struct {
229229 blind_secret_key: BlindSecretKey,
230230 };
231231
232 /// Blind an existing key pair with a blinding seed.
233 pub fn blind(key_pair: Ed25519.KeyPair, blind_seed: [blind_seed_length]u8) !BlindKeyPair {
232 /// Blind an existing key pair with a blinding seed and a context.
233 pub fn blind(key_pair: Ed25519.KeyPair, blind_seed: [blind_seed_length]u8, ctx: []const u8) !BlindKeyPair {
234234 var h: [Sha512.digest_length]u8 = undefined;
235235 Sha512.hash(key_pair.secret_key[0..32], &h, .{});
236236 Curve.scalar.clamp(h[0..32]);
237237 const scalar = Curve.scalar.reduce(h[0..32].*);
238238
239 var blind_h: [Sha512.digest_length]u8 = undefined;
240 Sha512.hash(blind_seed[0..], &blind_h, .{});
239 const blind_h = blindCtx(blind_seed, ctx);
241240 const blind_factor = Curve.scalar.reduce(blind_h[0..32].*);
242241
243242 const blind_scalar = Curve.scalar.mul(scalar, blind_factor);
......@@ -259,9 +258,8 @@ pub const Ed25519 = struct {
259258 }
260259
261260 /// Recover a public key from a blind version of it.
262 pub fn unblindPublicKey(blind_public_key: [public_length]u8, blind_seed: [blind_seed_length]u8) ![public_length]u8 {
263 var blind_h: [Sha512.digest_length]u8 = undefined;
264 Sha512.hash(&blind_seed, &blind_h, .{});
261 pub fn unblindPublicKey(blind_public_key: [public_length]u8, blind_seed: [blind_seed_length]u8, ctx: []const u8) ![public_length]u8 {
262 const blind_h = blindCtx(blind_seed, ctx);
265263 const inv_blind_factor = Scalar.fromBytes(blind_h[0..32].*).invert().toBytes();
266264 const public_key = try (try Curve.fromBytes(blind_public_key)).mul(inv_blind_factor);
267265 return public_key.toBytes();
......@@ -297,6 +295,17 @@ pub const Ed25519 = struct {
297295 mem.copy(u8, sig[32..], s[0..]);
298296 return sig;
299297 }
298
299 /// Compute a blind context from a blinding seed and a context.
300 fn blindCtx(blind_seed: [blind_seed_length]u8, ctx: []const u8) [Sha512.digest_length]u8 {
301 var blind_h: [Sha512.digest_length]u8 = undefined;
302 var hx = Sha512.init(.{});
303 hx.update(&blind_seed);
304 hx.update(&[1]u8{0});
305 hx.update(ctx);
306 hx.final(&blind_h);
307 return blind_h;
308 }
300309 };
301310};
302311
......@@ -458,7 +467,7 @@ test "ed25519 with blind keys" {
458467 crypto.random.bytes(&blind);
459468
460469 // Blind the key pair
461 const blind_kp = try BlindKeySignatures.blind(kp, blind);
470 const blind_kp = try BlindKeySignatures.blind(kp, blind, "ctx");
462471
463472 // Sign a message and check that it can be verified with the blind public key
464473 const msg = "test";
......@@ -466,6 +475,6 @@ test "ed25519 with blind keys" {
466475 try Ed25519.verify(sig, msg, blind_kp.blind_public_key);
467476
468477 // Unblind the public key
469 const pk = try BlindKeySignatures.unblindPublicKey(blind_kp.blind_public_key, blind);
478 const pk = try BlindKeySignatures.unblindPublicKey(blind_kp.blind_public_key, blind, "ctx");
470479 try std.testing.expectEqualSlices(u8, &pk, &kp.public_key);
471480}
lib/std/fs.zig+29-2
......@@ -595,6 +595,19 @@ pub const IterableDir = struct {
595595 /// Memory such as file names referenced in this returned entry becomes invalid
596596 /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.
597597 pub fn next(self: *Self) Error!?Entry {
598 return self.nextLinux() catch |err| switch (err) {
599 // To be consistent across platforms, iteration ends if the directory being iterated is deleted during iteration.
600 // This matches the behavior of non-Linux UNIX platforms.
601 error.DirNotFound => null,
602 else => |e| return e,
603 };
604 }
605
606 pub const ErrorLinux = error{DirNotFound} || IteratorError;
607
608 /// Implementation of `next` that can return `error.DirNotFound` if the directory being
609 /// iterated was deleted during iteration (this error is Linux specific).
610 pub fn nextLinux(self: *Self) ErrorLinux!?Entry {
598611 start_over: while (true) {
599612 if (self.index >= self.end_index) {
600613 if (self.first_iter) {
......@@ -607,7 +620,7 @@ pub const IterableDir = struct {
607620 .BADF => unreachable, // Dir is invalid or was opened without iteration ability
608621 .FAULT => unreachable,
609622 .NOTDIR => unreachable,
610 .NOENT => return null, // The directory being iterated was deleted during iteration.
623 .NOENT => return error.DirNotFound, // The directory being iterated was deleted during iteration.
611624 .INVAL => return error.Unexpected, // Linux may in some cases return EINVAL when reading /proc/$PID/net.
612625 else => |err| return os.unexpectedErrno(err),
613626 }
......@@ -729,6 +742,20 @@ pub const IterableDir = struct {
729742 /// Memory such as file names referenced in this returned entry becomes invalid
730743 /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.
731744 pub fn next(self: *Self) Error!?Entry {
745 return self.nextWasi() catch |err| switch (err) {
746 // To be consistent across platforms, iteration ends if the directory being iterated is deleted during iteration.
747 // This matches the behavior of non-Linux UNIX platforms.
748 error.DirNotFound => null,
749 else => |e| return e,
750 };
751 }
752
753 pub const ErrorWasi = error{DirNotFound} || IteratorError;
754
755 /// Implementation of `next` that can return platform-dependent errors depending on the host platform.
756 /// When the host platform is Linux, `error.DirNotFound` can be returned if the directory being
757 /// iterated was deleted during iteration.
758 pub fn nextWasi(self: *Self) ErrorWasi!?Entry {
732759 // We intentinally use fd_readdir even when linked with libc,
733760 // since its implementation is exactly the same as below,
734761 // and we avoid the code complexity here.
......@@ -742,7 +769,7 @@ pub const IterableDir = struct {
742769 .FAULT => unreachable,
743770 .NOTDIR => unreachable,
744771 .INVAL => unreachable,
745 .NOENT => return null, // The directory being iterated was deleted during iteration.
772 .NOENT => return error.DirNotFound, // The directory being iterated was deleted during iteration.
746773 .NOTCAPABLE => return error.AccessDenied,
747774 else => |err| return os.unexpectedErrno(err),
748775 }
lib/std/fs/test.zig+5
......@@ -241,6 +241,11 @@ test "Dir.Iterator but dir is deleted during iteration" {
241241 // Now, when we try to iterate, the next call should return null immediately.
242242 const entry = try iterator.next();
243243 try std.testing.expect(entry == null);
244
245 // On Linux, we can opt-in to receiving a more specific error by calling `nextLinux`
246 if (builtin.os.tag == .linux) {
247 try std.testing.expectError(error.DirNotFound, iterator.nextLinux());
248 }
244249}
245250
246251fn entryEql(lhs: IterableDir.Entry, rhs: IterableDir.Entry) bool {
lib/std/macho.zig+55-409
......@@ -780,7 +780,7 @@ pub const section_64 = extern struct {
780780 return parseName(&sect.segname);
781781 }
782782
783 pub fn type_(sect: section_64) u8 {
783 pub fn @"type"(sect: section_64) u8 {
784784 return @truncate(u8, sect.flags & 0xff);
785785 }
786786
......@@ -793,6 +793,11 @@ pub const section_64 = extern struct {
793793 return attr & S_ATTR_PURE_INSTRUCTIONS != 0 or attr & S_ATTR_SOME_INSTRUCTIONS != 0;
794794 }
795795
796 pub fn isZerofill(sect: section_64) bool {
797 const tt = sect.@"type"();
798 return tt == S_ZEROFILL or tt == S_GB_ZEROFILL or tt == S_THREAD_LOCAL_ZEROFILL;
799 }
800
796801 pub fn isDebug(sect: section_64) bool {
797802 return sect.attrs() & S_ATTR_DEBUG != 0;
798803 }
......@@ -1835,429 +1840,70 @@ pub const data_in_code_entry = extern struct {
18351840 kind: u16,
18361841};
18371842
1838/// A Zig wrapper for all known MachO load commands.
1839/// Provides interface to read and write the load command data to a buffer.
1840pub const LoadCommand = union(enum) {
1841 segment: SegmentCommand,
1842 dyld_info_only: dyld_info_command,
1843 symtab: symtab_command,
1844 dysymtab: dysymtab_command,
1845 dylinker: GenericCommandWithData(dylinker_command),
1846 dylib: GenericCommandWithData(dylib_command),
1847 main: entry_point_command,
1848 version_min: version_min_command,
1849 source_version: source_version_command,
1850 build_version: GenericCommandWithData(build_version_command),
1851 uuid: uuid_command,
1852 linkedit_data: linkedit_data_command,
1853 rpath: GenericCommandWithData(rpath_command),
1854 unknown: GenericCommandWithData(load_command),
1855
1856 pub fn read(allocator: Allocator, reader: anytype) !LoadCommand {
1857 const header = try reader.readStruct(load_command);
1858 var buffer = try allocator.alloc(u8, header.cmdsize);
1859 defer allocator.free(buffer);
1860 mem.copy(u8, buffer, mem.asBytes(&header));
1861 try reader.readNoEof(buffer[@sizeOf(load_command)..]);
1862 var stream = io.fixedBufferStream(buffer);
1863
1864 return switch (header.cmd) {
1865 .SEGMENT_64 => LoadCommand{
1866 .segment = try SegmentCommand.read(allocator, stream.reader()),
1867 },
1868 .DYLD_INFO, .DYLD_INFO_ONLY => LoadCommand{
1869 .dyld_info_only = try stream.reader().readStruct(dyld_info_command),
1870 },
1871 .SYMTAB => LoadCommand{
1872 .symtab = try stream.reader().readStruct(symtab_command),
1873 },
1874 .DYSYMTAB => LoadCommand{
1875 .dysymtab = try stream.reader().readStruct(dysymtab_command),
1876 },
1877 .ID_DYLINKER, .LOAD_DYLINKER, .DYLD_ENVIRONMENT => LoadCommand{
1878 .dylinker = try GenericCommandWithData(dylinker_command).read(allocator, stream.reader()),
1879 },
1880 .ID_DYLIB, .LOAD_WEAK_DYLIB, .LOAD_DYLIB, .REEXPORT_DYLIB => LoadCommand{
1881 .dylib = try GenericCommandWithData(dylib_command).read(allocator, stream.reader()),
1882 },
1883 .MAIN => LoadCommand{
1884 .main = try stream.reader().readStruct(entry_point_command),
1885 },
1886 .VERSION_MIN_MACOSX, .VERSION_MIN_IPHONEOS, .VERSION_MIN_WATCHOS, .VERSION_MIN_TVOS => LoadCommand{
1887 .version_min = try stream.reader().readStruct(version_min_command),
1888 },
1889 .SOURCE_VERSION => LoadCommand{
1890 .source_version = try stream.reader().readStruct(source_version_command),
1891 },
1892 .BUILD_VERSION => LoadCommand{
1893 .build_version = try GenericCommandWithData(build_version_command).read(allocator, stream.reader()),
1894 },
1895 .UUID => LoadCommand{
1896 .uuid = try stream.reader().readStruct(uuid_command),
1897 },
1898 .FUNCTION_STARTS, .DATA_IN_CODE, .CODE_SIGNATURE => LoadCommand{
1899 .linkedit_data = try stream.reader().readStruct(linkedit_data_command),
1900 },
1901 .RPATH => LoadCommand{
1902 .rpath = try GenericCommandWithData(rpath_command).read(allocator, stream.reader()),
1903 },
1904 else => LoadCommand{
1905 .unknown = try GenericCommandWithData(load_command).read(allocator, stream.reader()),
1906 },
1907 };
1908 }
1909
1910 pub fn write(self: LoadCommand, writer: anytype) !void {
1911 return switch (self) {
1912 .dyld_info_only => |x| writeStruct(x, writer),
1913 .symtab => |x| writeStruct(x, writer),
1914 .dysymtab => |x| writeStruct(x, writer),
1915 .main => |x| writeStruct(x, writer),
1916 .version_min => |x| writeStruct(x, writer),
1917 .source_version => |x| writeStruct(x, writer),
1918 .uuid => |x| writeStruct(x, writer),
1919 .linkedit_data => |x| writeStruct(x, writer),
1920 .segment => |x| x.write(writer),
1921 .dylinker => |x| x.write(writer),
1922 .dylib => |x| x.write(writer),
1923 .rpath => |x| x.write(writer),
1924 .build_version => |x| x.write(writer),
1925 .unknown => |x| x.write(writer),
1926 };
1927 }
1928
1929 pub fn cmd(self: LoadCommand) LC {
1930 return switch (self) {
1931 .dyld_info_only => |x| x.cmd,
1932 .symtab => |x| x.cmd,
1933 .dysymtab => |x| x.cmd,
1934 .main => |x| x.cmd,
1935 .version_min => |x| x.cmd,
1936 .source_version => |x| x.cmd,
1937 .uuid => |x| x.cmd,
1938 .linkedit_data => |x| x.cmd,
1939 .segment => |x| x.inner.cmd,
1940 .dylinker => |x| x.inner.cmd,
1941 .dylib => |x| x.inner.cmd,
1942 .rpath => |x| x.inner.cmd,
1943 .build_version => |x| x.inner.cmd,
1944 .unknown => |x| x.inner.cmd,
1945 };
1946 }
1947
1948 pub fn cmdsize(self: LoadCommand) u32 {
1949 return switch (self) {
1950 .dyld_info_only => |x| x.cmdsize,
1951 .symtab => |x| x.cmdsize,
1952 .dysymtab => |x| x.cmdsize,
1953 .main => |x| x.cmdsize,
1954 .version_min => |x| x.cmdsize,
1955 .source_version => |x| x.cmdsize,
1956 .linkedit_data => |x| x.cmdsize,
1957 .uuid => |x| x.cmdsize,
1958 .segment => |x| x.inner.cmdsize,
1959 .dylinker => |x| x.inner.cmdsize,
1960 .dylib => |x| x.inner.cmdsize,
1961 .rpath => |x| x.inner.cmdsize,
1962 .build_version => |x| x.inner.cmdsize,
1963 .unknown => |x| x.inner.cmdsize,
1964 };
1965 }
1966
1967 pub fn deinit(self: *LoadCommand, allocator: Allocator) void {
1968 return switch (self.*) {
1969 .segment => |*x| x.deinit(allocator),
1970 .dylinker => |*x| x.deinit(allocator),
1971 .dylib => |*x| x.deinit(allocator),
1972 .rpath => |*x| x.deinit(allocator),
1973 .build_version => |*x| x.deinit(allocator),
1974 .unknown => |*x| x.deinit(allocator),
1975 else => {},
1976 };
1977 }
1978
1979 fn writeStruct(command: anytype, writer: anytype) !void {
1980 return writer.writeAll(mem.asBytes(&command));
1981 }
1982
1983 pub fn eql(self: LoadCommand, other: LoadCommand) bool {
1984 if (@as(meta.Tag(LoadCommand), self) != @as(meta.Tag(LoadCommand), other)) return false;
1985 return switch (self) {
1986 .dyld_info_only => |x| meta.eql(x, other.dyld_info_only),
1987 .symtab => |x| meta.eql(x, other.symtab),
1988 .dysymtab => |x| meta.eql(x, other.dysymtab),
1989 .main => |x| meta.eql(x, other.main),
1990 .version_min => |x| meta.eql(x, other.version_min),
1991 .source_version => |x| meta.eql(x, other.source_version),
1992 .build_version => |x| x.eql(other.build_version),
1993 .uuid => |x| meta.eql(x, other.uuid),
1994 .linkedit_data => |x| meta.eql(x, other.linkedit_data),
1995 .segment => |x| x.eql(other.segment),
1996 .dylinker => |x| x.eql(other.dylinker),
1997 .dylib => |x| x.eql(other.dylib),
1998 .rpath => |x| x.eql(other.rpath),
1999 .unknown => |x| x.eql(other.unknown),
2000 };
2001 }
2002};
2003
2004/// A Zig wrapper for segment_command_64.
2005/// Encloses the extern struct together with a list of sections for this segment.
2006pub const SegmentCommand = struct {
2007 inner: segment_command_64,
2008 sections: std.ArrayListUnmanaged(section_64) = .{},
1843pub const LoadCommandIterator = struct {
1844 ncmds: usize,
1845 buffer: []align(@alignOf(u64)) const u8,
1846 index: usize = 0,
20091847
2010 pub fn read(allocator: Allocator, reader: anytype) !SegmentCommand {
2011 const inner = try reader.readStruct(segment_command_64);
2012 var segment = SegmentCommand{
2013 .inner = inner,
2014 };
2015 try segment.sections.ensureTotalCapacityPrecise(allocator, inner.nsects);
1848 pub const LoadCommand = struct {
1849 hdr: load_command,
1850 data: []const u8,
20161851
2017 var i: usize = 0;
2018 while (i < inner.nsects) : (i += 1) {
2019 const sect = try reader.readStruct(section_64);
2020 segment.sections.appendAssumeCapacity(sect);
1852 pub fn cmd(lc: LoadCommand) LC {
1853 return lc.hdr.cmd;
20211854 }
20221855
2023 return segment;
2024 }
2025
2026 pub fn write(self: SegmentCommand, writer: anytype) !void {
2027 try writer.writeAll(mem.asBytes(&self.inner));
2028 for (self.sections.items) |sect| {
2029 try writer.writeAll(mem.asBytes(&sect));
2030 }
2031 }
2032
2033 pub fn deinit(self: *SegmentCommand, allocator: Allocator) void {
2034 self.sections.deinit(allocator);
2035 }
2036
2037 pub fn eql(self: SegmentCommand, other: SegmentCommand) bool {
2038 if (!meta.eql(self.inner, other.inner)) return false;
2039 const lhs = self.sections.items;
2040 const rhs = other.sections.items;
2041 var i: usize = 0;
2042 while (i < self.inner.nsects) : (i += 1) {
2043 if (!meta.eql(lhs[i], rhs[i])) return false;
1856 pub fn cmdsize(lc: LoadCommand) u32 {
1857 return lc.hdr.cmdsize;
20441858 }
2045 return true;
2046 }
2047};
2048
2049pub fn emptyGenericCommandWithData(cmd: anytype) GenericCommandWithData(@TypeOf(cmd)) {
2050 return .{ .inner = cmd };
2051}
20521859
2053/// A Zig wrapper for a generic load command with variable-length data.
2054pub fn GenericCommandWithData(comptime Cmd: type) type {
2055 return struct {
2056 inner: Cmd,
2057 /// This field remains undefined until `read` is called.
2058 data: []u8 = undefined,
2059
2060 const Self = @This();
2061
2062 pub fn read(allocator: Allocator, reader: anytype) !Self {
2063 const inner = try reader.readStruct(Cmd);
2064 var data = try allocator.alloc(u8, inner.cmdsize - @sizeOf(Cmd));
2065 errdefer allocator.free(data);
2066 try reader.readNoEof(data);
2067 return Self{
2068 .inner = inner,
2069 .data = data,
2070 };
1860 pub fn cast(lc: LoadCommand, comptime Cmd: type) ?Cmd {
1861 if (lc.data.len < @sizeOf(Cmd)) return null;
1862 return @ptrCast(*const Cmd, @alignCast(@alignOf(Cmd), &lc.data[0])).*;
20711863 }
20721864
2073 pub fn write(self: Self, writer: anytype) !void {
2074 try writer.writeAll(mem.asBytes(&self.inner));
2075 try writer.writeAll(self.data);
1865 /// Asserts LoadCommand is of type segment_command_64.
1866 pub fn getSections(lc: LoadCommand) []const section_64 {
1867 const segment_lc = lc.cast(segment_command_64).?;
1868 if (segment_lc.nsects == 0) return &[0]section_64{};
1869 const data = lc.data[@sizeOf(segment_command_64)..];
1870 const sections = @ptrCast(
1871 [*]const section_64,
1872 @alignCast(@alignOf(section_64), &data[0]),
1873 )[0..segment_lc.nsects];
1874 return sections;
20761875 }
20771876
2078 pub fn deinit(self: *Self, allocator: Allocator) void {
2079 allocator.free(self.data);
1877 /// Asserts LoadCommand is of type dylib_command.
1878 pub fn getDylibPathName(lc: LoadCommand) []const u8 {
1879 const dylib_lc = lc.cast(dylib_command).?;
1880 const data = lc.data[dylib_lc.dylib.name..];
1881 return mem.sliceTo(data, 0);
20801882 }
20811883
2082 pub fn eql(self: Self, other: Self) bool {
2083 if (!meta.eql(self.inner, other.inner)) return false;
2084 return mem.eql(u8, self.data, other.data);
1884 /// Asserts LoadCommand is of type rpath_command.
1885 pub fn getRpathPathName(lc: LoadCommand) []const u8 {
1886 const rpath_lc = lc.cast(rpath_command).?;
1887 const data = lc.data[rpath_lc.path..];
1888 return mem.sliceTo(data, 0);
20851889 }
20861890 };
2087}
2088
2089pub fn createLoadDylibCommand(
2090 allocator: Allocator,
2091 cmd_id: LC,
2092 name: []const u8,
2093 timestamp: u32,
2094 current_version: u32,
2095 compatibility_version: u32,
2096) !GenericCommandWithData(dylib_command) {
2097 assert(cmd_id == .LOAD_DYLIB or cmd_id == .LOAD_WEAK_DYLIB or cmd_id == .REEXPORT_DYLIB or cmd_id == .ID_DYLIB);
2098 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
2099 u64,
2100 @sizeOf(dylib_command) + name.len + 1, // +1 for nul
2101 @sizeOf(u64),
2102 ));
2103
2104 var dylib_cmd = emptyGenericCommandWithData(dylib_command{
2105 .cmd = cmd_id,
2106 .cmdsize = cmdsize,
2107 .dylib = .{
2108 .name = @sizeOf(dylib_command),
2109 .timestamp = timestamp,
2110 .current_version = current_version,
2111 .compatibility_version = compatibility_version,
2112 },
2113 });
2114 dylib_cmd.data = try allocator.alloc(u8, cmdsize - dylib_cmd.inner.dylib.name);
2115
2116 mem.set(u8, dylib_cmd.data, 0);
2117 mem.copy(u8, dylib_cmd.data, name);
2118
2119 return dylib_cmd;
2120}
2121
2122fn testRead(allocator: Allocator, buffer: []const u8, expected: anytype) !void {
2123 var stream = io.fixedBufferStream(buffer);
2124 var given = try LoadCommand.read(allocator, stream.reader());
2125 defer given.deinit(allocator);
2126 try testing.expect(expected.eql(given));
2127}
2128
2129fn testWrite(buffer: []u8, cmd: LoadCommand, expected: []const u8) !void {
2130 var stream = io.fixedBufferStream(buffer);
2131 try cmd.write(stream.writer());
2132 try testing.expect(mem.eql(u8, expected, buffer[0..expected.len]));
2133}
2134
2135fn makeStaticString(bytes: []const u8) [16]u8 {
2136 var buf = [_]u8{0} ** 16;
2137 assert(bytes.len <= buf.len);
2138 mem.copy(u8, &buf, bytes);
2139 return buf;
2140}
2141
2142test "read-write segment command" {
2143 // TODO compiling for macOS from big-endian arch
2144 if (builtin.target.cpu.arch.endian() != .Little) return error.SkipZigTest;
2145
2146 var gpa = testing.allocator;
2147 const in_buffer = &[_]u8{
2148 0x19, 0x00, 0x00, 0x00, // cmd
2149 0x98, 0x00, 0x00, 0x00, // cmdsize
2150 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // segname
2151 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // vmaddr
2152 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // vmsize
2153 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // fileoff
2154 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // filesize
2155 0x07, 0x00, 0x00, 0x00, // maxprot
2156 0x05, 0x00, 0x00, 0x00, // initprot
2157 0x01, 0x00, 0x00, 0x00, // nsects
2158 0x00, 0x00, 0x00, 0x00, // flags
2159 0x5f, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sectname
2160 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // segname
2161 0x00, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // address
2162 0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // size
2163 0x00, 0x40, 0x00, 0x00, // offset
2164 0x02, 0x00, 0x00, 0x00, // alignment
2165 0x00, 0x00, 0x00, 0x00, // reloff
2166 0x00, 0x00, 0x00, 0x00, // nreloc
2167 0x00, 0x04, 0x00, 0x80, // flags
2168 0x00, 0x00, 0x00, 0x00, // reserved1
2169 0x00, 0x00, 0x00, 0x00, // reserved2
2170 0x00, 0x00, 0x00, 0x00, // reserved3
2171 };
2172 var cmd = SegmentCommand{
2173 .inner = .{
2174 .cmdsize = 152,
2175 .segname = makeStaticString("__TEXT"),
2176 .vmaddr = 4294967296,
2177 .vmsize = 294912,
2178 .filesize = 294912,
2179 .maxprot = PROT.READ | PROT.WRITE | PROT.EXEC,
2180 .initprot = PROT.EXEC | PROT.READ,
2181 .nsects = 1,
2182 },
2183 };
2184 try cmd.sections.append(gpa, .{
2185 .sectname = makeStaticString("__text"),
2186 .segname = makeStaticString("__TEXT"),
2187 .addr = 4294983680,
2188 .size = 448,
2189 .offset = 16384,
2190 .@"align" = 2,
2191 .flags = S_REGULAR | S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS,
2192 });
2193 defer cmd.deinit(gpa);
2194 try testRead(gpa, in_buffer, LoadCommand{ .segment = cmd });
2195
2196 var out_buffer: [in_buffer.len]u8 = undefined;
2197 try testWrite(&out_buffer, LoadCommand{ .segment = cmd }, in_buffer);
2198}
21991891
2200test "read-write generic command with data" {
2201 // TODO compiling for macOS from big-endian arch
2202 if (builtin.target.cpu.arch.endian() != .Little) return error.SkipZigTest;
2203
2204 var gpa = testing.allocator;
2205 const in_buffer = &[_]u8{
2206 0x0c, 0x00, 0x00, 0x00, // cmd
2207 0x20, 0x00, 0x00, 0x00, // cmdsize
2208 0x18, 0x00, 0x00, 0x00, // name
2209 0x02, 0x00, 0x00, 0x00, // timestamp
2210 0x00, 0x00, 0x00, 0x00, // current_version
2211 0x00, 0x00, 0x00, 0x00, // compatibility_version
2212 0x2f, 0x75, 0x73, 0x72, 0x00, 0x00, 0x00, 0x00, // data
2213 };
2214 var cmd = GenericCommandWithData(dylib_command){
2215 .inner = .{
2216 .cmd = .LOAD_DYLIB,
2217 .cmdsize = 32,
2218 .dylib = .{
2219 .name = 24,
2220 .timestamp = 2,
2221 .current_version = 0,
2222 .compatibility_version = 0,
2223 },
2224 },
2225 };
2226 cmd.data = try gpa.alloc(u8, 8);
2227 defer gpa.free(cmd.data);
2228 cmd.data[0] = 0x2f;
2229 cmd.data[1] = 0x75;
2230 cmd.data[2] = 0x73;
2231 cmd.data[3] = 0x72;
2232 cmd.data[4] = 0x0;
2233 cmd.data[5] = 0x0;
2234 cmd.data[6] = 0x0;
2235 cmd.data[7] = 0x0;
2236 try testRead(gpa, in_buffer, LoadCommand{ .dylib = cmd });
2237
2238 var out_buffer: [in_buffer.len]u8 = undefined;
2239 try testWrite(&out_buffer, LoadCommand{ .dylib = cmd }, in_buffer);
2240}
1892 pub fn next(it: *LoadCommandIterator) ?LoadCommand {
1893 if (it.index >= it.ncmds) return null;
22411894
2242test "read-write C struct command" {
2243 // TODO compiling for macOS from big-endian arch
2244 if (builtin.target.cpu.arch.endian() != .Little) return error.SkipZigTest;
1895 const hdr = @ptrCast(
1896 *const load_command,
1897 @alignCast(@alignOf(load_command), &it.buffer[0]),
1898 ).*;
1899 const cmd = LoadCommand{
1900 .hdr = hdr,
1901 .data = it.buffer[0..hdr.cmdsize],
1902 };
22451903
2246 var gpa = testing.allocator;
2247 const in_buffer = &[_]u8{
2248 0x28, 0x00, 0x00, 0x80, // cmd
2249 0x18, 0x00, 0x00, 0x00, // cmdsize
2250 0x04, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // entryoff
2251 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // stacksize
2252 };
2253 const cmd = .{
2254 .cmd = .MAIN,
2255 .cmdsize = 24,
2256 .entryoff = 16644,
2257 .stacksize = 0,
2258 };
2259 try testRead(gpa, in_buffer, LoadCommand{ .main = cmd });
1904 it.buffer = @alignCast(@alignOf(u64), it.buffer[hdr.cmdsize..]);
1905 it.index += 1;
22601906
2261 var out_buffer: [in_buffer.len]u8 = undefined;
2262 try testWrite(&out_buffer, LoadCommand{ .main = cmd }, in_buffer);
2263}
1907 return cmd;
1908 }
1909};
src/AstGen.zig+113-24
......@@ -768,12 +768,12 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
768768 .if_simple => return ifExpr(gz, scope, rl.br(), node, tree.ifSimple(node)),
769769 .@"if" => return ifExpr(gz, scope, rl.br(), node, tree.ifFull(node)),
770770
771 .while_simple => return whileExpr(gz, scope, rl.br(), node, tree.whileSimple(node)),
772 .while_cont => return whileExpr(gz, scope, rl.br(), node, tree.whileCont(node)),
773 .@"while" => return whileExpr(gz, scope, rl.br(), node, tree.whileFull(node)),
771 .while_simple => return whileExpr(gz, scope, rl.br(), node, tree.whileSimple(node), false),
772 .while_cont => return whileExpr(gz, scope, rl.br(), node, tree.whileCont(node), false),
773 .@"while" => return whileExpr(gz, scope, rl.br(), node, tree.whileFull(node), false),
774774
775 .for_simple => return forExpr(gz, scope, rl.br(), node, tree.forSimple(node)),
776 .@"for" => return forExpr(gz, scope, rl.br(), node, tree.forFull(node)),
775 .for_simple => return forExpr(gz, scope, rl.br(), node, tree.forSimple(node), false),
776 .@"for" => return forExpr(gz, scope, rl.br(), node, tree.forFull(node), false),
777777
778778 .slice_open => {
779779 const lhs = try expr(gz, scope, .ref, node_datas[node].lhs);
......@@ -1899,6 +1899,17 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
18991899 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
19001900 .namespace => break,
19011901 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
1902 .defer_gen => {
1903 const defer_gen = scope.cast(Scope.DeferGen).?;
1904
1905 return astgen.failNodeNotes(node, "cannot break out of defer expression", .{}, &.{
1906 try astgen.errNoteNode(
1907 defer_gen.defer_node,
1908 "defer expression here",
1909 .{},
1910 ),
1911 });
1912 },
19021913 .top => unreachable,
19031914 }
19041915 }
......@@ -1958,6 +1969,17 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)
19581969 try unusedResultDeferExpr(parent_gz, defer_scope, defer_scope.parent, expr_node);
19591970 },
19601971 .defer_error => scope = scope.cast(Scope.Defer).?.parent,
1972 .defer_gen => {
1973 const defer_gen = scope.cast(Scope.DeferGen).?;
1974
1975 return astgen.failNodeNotes(node, "cannot continue out of defer expression", .{}, &.{
1976 try astgen.errNoteNode(
1977 defer_gen.defer_node,
1978 "defer expression here",
1979 .{},
1980 ),
1981 });
1982 },
19611983 .namespace => break,
19621984 .top => unreachable,
19631985 }
......@@ -2022,6 +2044,7 @@ fn checkLabelRedefinition(astgen: *AstGen, parent_scope: *Scope, label: Ast.Toke
20222044 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
20232045 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
20242046 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
2047 .defer_gen => scope = scope.cast(Scope.DeferGen).?.parent,
20252048 .namespace => break,
20262049 .top => unreachable,
20272050 }
......@@ -2129,6 +2152,7 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
21292152 const astgen = gz.astgen;
21302153 const tree = astgen.tree;
21312154 const node_tags = tree.nodes.items(.tag);
2155 const node_data = tree.nodes.items(.data);
21322156
21332157 if (statements.len == 0) return;
21342158
......@@ -2155,8 +2179,10 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
21552179 },
21562180 );
21572181 }
2158 switch (node_tags[statement]) {
2159 // zig fmt: off
2182 var inner_node = statement;
2183 while (true) {
2184 switch (node_tags[inner_node]) {
2185 // zig fmt: off
21602186 .global_var_decl => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.globalVarDecl(statement)),
21612187 .local_var_decl => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.localVarDecl(statement)),
21622188 .simple_var_decl => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.simpleVarDecl(statement)),
......@@ -2181,9 +2207,23 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
21812207 .assign_add_wrap => try assignOp(gz, scope, statement, .addwrap),
21822208 .assign_mul => try assignOp(gz, scope, statement, .mul),
21832209 .assign_mul_wrap => try assignOp(gz, scope, statement, .mulwrap),
2210
2211 .grouped_expression => {
2212 inner_node = node_data[statement].lhs;
2213 continue;
2214 },
2215
2216 .while_simple => _ = try whileExpr(gz, scope, .discard, inner_node, tree.whileSimple(inner_node), true),
2217 .while_cont => _ = try whileExpr(gz, scope, .discard, inner_node, tree.whileCont(inner_node), true),
2218 .@"while" => _ = try whileExpr(gz, scope, .discard, inner_node, tree.whileFull(inner_node), true),
2219
2220 .for_simple => _ = try forExpr(gz, scope, .discard, inner_node, tree.forSimple(inner_node), true),
2221 .@"for" => _ = try forExpr(gz, scope, .discard, inner_node, tree.forFull(inner_node), true),
21842222
2185 else => noreturn_src_node = try unusedResultExpr(gz, scope, statement),
2223 else => noreturn_src_node = try unusedResultExpr(gz, scope, inner_node),
21862224 // zig fmt: on
2225 }
2226 break;
21872227 }
21882228 }
21892229
......@@ -2206,7 +2246,13 @@ fn unusedResultDeferExpr(gz: *GenZir, defer_scope: *Scope.Defer, expr_scope: *Sc
22062246 astgen.source_offset = defer_scope.source_offset;
22072247 astgen.source_line = defer_scope.source_line;
22082248 astgen.source_column = defer_scope.source_column;
2209 _ = try unusedResultExpr(gz, expr_scope, expr_node);
2249
2250 var defer_gen: Scope.DeferGen = .{
2251 .parent = expr_scope,
2252 .defer_node = defer_scope.defer_node,
2253 };
2254
2255 _ = try unusedResultExpr(gz, &defer_gen.base, expr_node);
22102256}
22112257
22122258/// Returns AST source node of the thing that is noreturn if the statement is
......@@ -2216,6 +2262,10 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
22162262 // We need to emit an error if the result is not `noreturn` or `void`, but
22172263 // we want to avoid adding the ZIR instruction if possible for performance.
22182264 const maybe_unused_result = try expr(gz, scope, .none, statement);
2265 return addEnsureResult(gz, maybe_unused_result, statement);
2266}
2267
2268fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: Ast.Node.Index) InnerError!Ast.Node.Index {
22192269 var noreturn_src_node: Ast.Node.Index = 0;
22202270 const elide_check = if (refToIndex(maybe_unused_result)) |inst| b: {
22212271 // Note that this array becomes invalid after appending more items to it
......@@ -2553,6 +2603,7 @@ fn countDefers(astgen: *AstGen, outer_scope: *Scope, inner_scope: *Scope) struct
25532603 .gen_zir => scope = scope.cast(GenZir).?.parent,
25542604 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
25552605 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
2606 .defer_gen => scope = scope.cast(Scope.DeferGen).?.parent,
25562607 .defer_normal => {
25572608 const defer_scope = scope.cast(Scope.Defer).?;
25582609 scope = defer_scope.parent;
......@@ -2602,6 +2653,7 @@ fn genDefers(
26022653 .gen_zir => scope = scope.cast(GenZir).?.parent,
26032654 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
26042655 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
2656 .defer_gen => scope = scope.cast(Scope.DeferGen).?.parent,
26052657 .defer_normal => {
26062658 const defer_scope = scope.cast(Scope.Defer).?;
26072659 scope = defer_scope.parent;
......@@ -2644,6 +2696,7 @@ fn genDefers(
26442696 break :blk &local_val_scope.base;
26452697 };
26462698 try unusedResultDeferExpr(gz, defer_scope, sub_scope, expr_node);
2699 try checkUsed(gz, scope, sub_scope);
26472700 try gz.addDbgBlockEnd();
26482701 },
26492702 .normal_only => continue,
......@@ -2681,6 +2734,7 @@ fn checkUsed(
26812734 scope = s.parent;
26822735 },
26832736 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
2737 .defer_gen => scope = scope.cast(Scope.DeferGen).?.parent,
26842738 .namespace => unreachable,
26852739 .top => unreachable,
26862740 }
......@@ -4040,6 +4094,7 @@ fn testDecl(
40404094 .local_val, .local_ptr => unreachable, // a test cannot be in a local scope
40414095 .gen_zir => s = s.cast(GenZir).?.parent,
40424096 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
4097 .defer_gen => s = s.cast(Scope.DeferGen).?.parent,
40434098 .namespace => {
40444099 const ns = s.cast(Scope.Namespace).?;
40454100 if (ns.decls.get(name_str_index)) |i| {
......@@ -5330,7 +5385,7 @@ fn ifExpr(
53305385 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
53315386 break :c .{
53325387 .inst = err_union,
5333 .bool_bit = try block_scope.addUnNode(tag, err_union, node),
5388 .bool_bit = try block_scope.addUnNode(tag, err_union, if_full.ast.cond_expr),
53345389 };
53355390 } else if (if_full.payload_token) |_| {
53365391 const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none;
......@@ -5338,7 +5393,7 @@ fn ifExpr(
53385393 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null;
53395394 break :c .{
53405395 .inst = optional,
5341 .bool_bit = try block_scope.addUnNode(tag, optional, node),
5396 .bool_bit = try block_scope.addUnNode(tag, optional, if_full.ast.cond_expr),
53425397 };
53435398 } else {
53445399 const cond = try expr(&block_scope, &block_scope.base, bool_rl, if_full.ast.cond_expr);
......@@ -5369,7 +5424,7 @@ fn ifExpr(
53695424 .err_union_payload_unsafe_ptr
53705425 else
53715426 .err_union_payload_unsafe;
5372 const payload_inst = try then_scope.addUnNode(tag, cond.inst, node);
5427 const payload_inst = try then_scope.addUnNode(tag, cond.inst, if_full.ast.then_expr);
53735428 const token_name_index = payload_token + @boolToInt(payload_is_ref);
53745429 const ident_name = try astgen.identAsString(token_name_index);
53755430 const token_name_str = tree.tokenSlice(token_name_index);
......@@ -5398,7 +5453,7 @@ fn ifExpr(
53985453 const ident_bytes = tree.tokenSlice(ident_token);
53995454 if (mem.eql(u8, "_", ident_bytes))
54005455 break :s &then_scope.base;
5401 const payload_inst = try then_scope.addUnNode(tag, cond.inst, node);
5456 const payload_inst = try then_scope.addUnNode(tag, cond.inst, if_full.ast.then_expr);
54025457 const ident_name = try astgen.identAsString(ident_token);
54035458 try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes);
54045459 payload_val_scope = .{
......@@ -5441,7 +5496,7 @@ fn ifExpr(
54415496 .err_union_code_ptr
54425497 else
54435498 .err_union_code;
5444 const payload_inst = try else_scope.addUnNode(tag, cond.inst, node);
5499 const payload_inst = try else_scope.addUnNode(tag, cond.inst, if_full.ast.cond_expr);
54455500 const ident_name = try astgen.identAsString(error_token);
54465501 const error_token_str = tree.tokenSlice(error_token);
54475502 if (mem.eql(u8, "_", error_token_str))
......@@ -5615,6 +5670,7 @@ fn whileExpr(
56155670 rl: ResultLoc,
56165671 node: Ast.Node.Index,
56175672 while_full: Ast.full.While,
5673 is_statement: bool,
56185674) InnerError!Zir.Inst.Ref {
56195675 const astgen = parent_gz.astgen;
56205676 const tree = astgen.tree;
......@@ -5654,7 +5710,7 @@ fn whileExpr(
56545710 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
56555711 break :c .{
56565712 .inst = err_union,
5657 .bool_bit = try continue_scope.addUnNode(tag, err_union, node),
5713 .bool_bit = try continue_scope.addUnNode(tag, err_union, while_full.ast.then_expr),
56585714 };
56595715 } else if (while_full.payload_token) |_| {
56605716 const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none;
......@@ -5662,7 +5718,7 @@ fn whileExpr(
56625718 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null;
56635719 break :c .{
56645720 .inst = optional,
5665 .bool_bit = try continue_scope.addUnNode(tag, optional, node),
5721 .bool_bit = try continue_scope.addUnNode(tag, optional, while_full.ast.then_expr),
56665722 };
56675723 } else {
56685724 const cond = try expr(&continue_scope, &continue_scope.base, bool_rl, while_full.ast.cond_expr);
......@@ -5700,7 +5756,7 @@ fn whileExpr(
57005756 else
57015757 .err_union_payload_unsafe;
57025758 // will add this instruction to then_scope.instructions below
5703 payload_inst = try then_scope.makeUnNode(tag, cond.inst, node);
5759 payload_inst = try then_scope.makeUnNode(tag, cond.inst, while_full.ast.cond_expr);
57045760 const ident_token = if (payload_is_ref) payload_token + 1 else payload_token;
57055761 const ident_bytes = tree.tokenSlice(ident_token);
57065762 if (mem.eql(u8, "_", ident_bytes))
......@@ -5729,7 +5785,7 @@ fn whileExpr(
57295785 else
57305786 .optional_payload_unsafe;
57315787 // will add this instruction to then_scope.instructions below
5732 payload_inst = try then_scope.makeUnNode(tag, cond.inst, node);
5788 payload_inst = try then_scope.makeUnNode(tag, cond.inst, while_full.ast.cond_expr);
57335789 const ident_name = try astgen.identAsString(ident_token);
57345790 const ident_bytes = tree.tokenSlice(ident_token);
57355791 if (mem.eql(u8, "_", ident_bytes))
......@@ -5785,6 +5841,8 @@ fn whileExpr(
57855841 try then_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst);
57865842 }
57875843 const then_result = try expr(&then_scope, then_sub_scope, loop_scope.break_result_loc, while_full.ast.then_expr);
5844 _ = try addEnsureResult(&then_scope, then_result, while_full.ast.then_expr);
5845
57885846 try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
57895847 try then_scope.addDbgBlockEnd();
57905848
......@@ -5803,7 +5861,7 @@ fn whileExpr(
58035861 .err_union_code_ptr
58045862 else
58055863 .err_union_code;
5806 const else_payload_inst = try else_scope.addUnNode(tag, cond.inst, node);
5864 const else_payload_inst = try else_scope.addUnNode(tag, cond.inst, while_full.ast.cond_expr);
58075865 const ident_name = try astgen.identAsString(error_token);
58085866 const ident_bytes = tree.tokenSlice(error_token);
58095867 if (mem.eql(u8, ident_bytes, "_"))
......@@ -5827,7 +5885,11 @@ fn whileExpr(
58275885 // control flow apply to outer loops; not this one.
58285886 loop_scope.continue_block = 0;
58295887 loop_scope.break_block = 0;
5830 const e = try expr(&else_scope, sub_scope, loop_scope.break_result_loc, else_node);
5888 const else_result = try expr(&else_scope, sub_scope, loop_scope.break_result_loc, else_node);
5889 if (is_statement) {
5890 _ = try addEnsureResult(&else_scope, else_result, else_node);
5891 }
5892
58315893 if (!else_scope.endsWithNoReturn()) {
58325894 loop_scope.break_count += 1;
58335895 }
......@@ -5835,7 +5897,7 @@ fn whileExpr(
58355897 try else_scope.addDbgBlockEnd();
58365898 break :blk .{
58375899 .src = else_node,
5838 .result = e,
5900 .result = else_result,
58395901 };
58405902 } else .{
58415903 .src = while_full.ast.then_expr,
......@@ -5848,7 +5910,7 @@ fn whileExpr(
58485910 }
58495911 }
58505912 const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
5851 return finishThenElseBlock(
5913 const result = try finishThenElseBlock(
58525914 parent_gz,
58535915 rl,
58545916 node,
......@@ -5863,6 +5925,10 @@ fn whileExpr(
58635925 cond_block,
58645926 break_tag,
58655927 );
5928 if (is_statement) {
5929 _ = try parent_gz.addUnNode(.ensure_result_used, result, node);
5930 }
5931 return result;
58665932}
58675933
58685934fn forExpr(
......@@ -5871,6 +5937,7 @@ fn forExpr(
58715937 rl: ResultLoc,
58725938 node: Ast.Node.Index,
58735939 for_full: Ast.full.While,
5940 is_statement: bool,
58745941) InnerError!Zir.Inst.Ref {
58755942 const astgen = parent_gz.astgen;
58765943
......@@ -6014,6 +6081,8 @@ fn forExpr(
60146081 };
60156082
60166083 const then_result = try expr(&then_scope, then_sub_scope, loop_scope.break_result_loc, for_full.ast.then_expr);
6084 _ = try addEnsureResult(&then_scope, then_result, for_full.ast.then_expr);
6085
60176086 try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
60186087 try then_scope.addDbgBlockEnd();
60196088
......@@ -6031,6 +6100,10 @@ fn forExpr(
60316100 loop_scope.continue_block = 0;
60326101 loop_scope.break_block = 0;
60336102 const else_result = try expr(&else_scope, sub_scope, loop_scope.break_result_loc, else_node);
6103 if (is_statement) {
6104 _ = try addEnsureResult(&else_scope, else_result, else_node);
6105 }
6106
60346107 if (!else_scope.endsWithNoReturn()) {
60356108 loop_scope.break_count += 1;
60366109 }
......@@ -6049,7 +6122,7 @@ fn forExpr(
60496122 }
60506123 }
60516124 const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
6052 return finishThenElseBlock(
6125 const result = try finishThenElseBlock(
60536126 parent_gz,
60546127 rl,
60556128 node,
......@@ -6064,6 +6137,10 @@ fn forExpr(
60646137 cond_block,
60656138 break_tag,
60666139 );
6140 if (is_statement) {
6141 _ = try parent_gz.addUnNode(.ensure_result_used, result, node);
6142 }
6143 return result;
60676144}
60686145
60696146fn switchExpr(
......@@ -6730,6 +6807,7 @@ fn localVarRef(
67306807 },
67316808 .gen_zir => s = s.cast(GenZir).?.parent,
67326809 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
6810 .defer_gen => s = s.cast(Scope.DeferGen).?.parent,
67336811 .namespace => {
67346812 const ns = s.cast(Scope.Namespace).?;
67356813 if (ns.decls.get(name_str_index)) |i| {
......@@ -7351,6 +7429,7 @@ fn builtinCall(
73517429 },
73527430 .gen_zir => s = s.cast(GenZir).?.parent,
73537431 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
7432 .defer_gen => s = s.cast(Scope.DeferGen).?.parent,
73547433 .namespace => {
73557434 const ns = s.cast(Scope.Namespace).?;
73567435 if (ns.decls.get(decl_name)) |i| {
......@@ -7424,7 +7503,8 @@ fn builtinCall(
74247503 const token_starts = tree.tokens.items(.start);
74257504 const node_start = token_starts[tree.firstToken(node)];
74267505 astgen.advanceSourceCursor(node_start);
7427 const result = try gz.addExtendedPayload(.builtin_src, Zir.Inst.LineColumn{
7506 const result = try gz.addExtendedPayload(.builtin_src, Zir.Inst.Src{
7507 .node = gz.nodeIndexToRelative(node),
74287508 .line = astgen.source_line,
74297509 .column = astgen.source_column,
74307510 });
......@@ -9807,6 +9887,7 @@ const Scope = struct {
98079887 local_ptr,
98089888 defer_normal,
98099889 defer_error,
9890 defer_gen,
98109891 namespace,
98119892 top,
98129893 };
......@@ -9904,6 +9985,13 @@ const Scope = struct {
99049985 const base_tag: Scope.Tag = .top;
99059986 base: Scope = Scope{ .tag = base_tag },
99069987 };
9988
9989 const DeferGen = struct {
9990 const base_tag: Scope.Tag = .defer_gen;
9991 base: Scope = Scope{ .tag = base_tag },
9992 parent: *Scope,
9993 defer_node: Ast.Node.Index,
9994 };
99079995};
99089996
99099997/// This is a temporary structure; references to it are valid only
......@@ -11414,6 +11502,7 @@ fn detectLocalShadowing(
1141411502 },
1141511503 .gen_zir => s = s.cast(GenZir).?.parent,
1141611504 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
11505 .defer_gen => s = s.cast(Scope.DeferGen).?.parent,
1141711506 .top => break,
1141811507 };
1141911508}
src/Autodoc.zig+20-7
......@@ -468,7 +468,7 @@ const DocData = struct {
468468 child: Expr,
469469 },
470470 ErrorUnion: struct { lhs: Expr, rhs: Expr },
471 // ErrorUnion: struct { name: []const u8 },
471 InferredErrorUnion: struct { payload: Expr },
472472 ErrorSet: struct {
473473 name: []const u8,
474474 fields: ?[]const Field = null,
......@@ -580,8 +580,9 @@ const DocData = struct {
580580 enumLiteral: []const u8, // direct value
581581 alignOf: usize, // index in `exprs`
582582 typeOf: usize, // index in `exprs`
583 typeInfo: usize, // index in `exprs`
583584 typeOf_peer: []usize,
584 errorUnion: usize, // index in `exprs`
585 errorUnion: usize, // index in `types`
585586 as: As,
586587 sizeOf: usize, // index in `exprs`
587588 bitSizeOf: usize, // index in `exprs`
......@@ -1398,7 +1399,6 @@ fn walkInstruction(
13981399 const extra = file.zir.extraData(Zir.Inst.PtrType, ptr.payload_index);
13991400 var extra_index = extra.end;
14001401
1401 const type_slot_index = self.types.items.len;
14021402 const elem_type_ref = try self.walkRef(
14031403 file,
14041404 parent_scope,
......@@ -1445,6 +1445,7 @@ fn walkInstruction(
14451445 host_size = ref_result.expr;
14461446 }
14471447
1448 const type_slot_index = self.types.items.len;
14481449 try self.types.append(self.arena, .{
14491450 .Pointer = .{
14501451 .size = ptr.size,
......@@ -1788,7 +1789,7 @@ fn walkInstruction(
17881789
17891790 return DocData.WalkResult{
17901791 .typeRef = operand.typeRef,
1791 .expr = .{ .typeOf = operand_index },
1792 .expr = .{ .typeInfo = operand_index },
17921793 };
17931794 },
17941795 .as_node => {
......@@ -1928,7 +1929,7 @@ fn walkInstruction(
19281929 .comptimeExpr = self.comptime_exprs.items.len,
19291930 } };
19301931 try self.comptime_exprs.append(self.arena, .{
1931 .code = "if(banana) 1 else 0",
1932 .code = "if (...) { ... }",
19321933 });
19331934 return res;
19341935 },
......@@ -2118,6 +2119,7 @@ fn walkInstruction(
21182119 inst_index,
21192120 self_ast_node_index,
21202121 type_slot_index,
2122 tags[inst_index] == .func_inferred,
21212123 );
21222124
21232125 return result;
......@@ -2993,7 +2995,7 @@ fn tryResolveRefPath(
29932995 "TODO: handle `{s}`in tryResolveRefPath\nInfo: {}",
29942996 .{ @tagName(resolved_parent), resolved_parent },
29952997 );
2996 path[i + 1] = (try self.cteTodo("match failure")).expr;
2998 path[i + 1] = (try self.cteTodo("<match failure>")).expr;
29972999 continue :outer;
29983000 },
29993001 .comptimeExpr, .call, .typeOf => {
......@@ -3415,6 +3417,7 @@ fn analyzeFunction(
34153417 inst_index: usize,
34163418 self_ast_node_index: usize,
34173419 type_slot_index: usize,
3420 ret_is_inferred_error_set: bool,
34183421) AutodocErrors!DocData.WalkResult {
34193422 const tags = file.zir.instructions.items(.tag);
34203423 const data = file.zir.instructions.items(.data);
......@@ -3521,13 +3524,23 @@ fn analyzeFunction(
35213524 else => null,
35223525 };
35233526
3527 const ret_type: DocData.Expr = blk: {
3528 if (ret_is_inferred_error_set) {
3529 const ret_type_slot_index = self.types.items.len;
3530 try self.types.append(self.arena, .{
3531 .InferredErrorUnion = .{ .payload = ret_type_ref },
3532 });
3533 break :blk .{ .type = ret_type_slot_index };
3534 } else break :blk ret_type_ref;
3535 };
3536
35243537 self.ast_nodes.items[self_ast_node_index].fields = param_ast_indexes.items;
35253538 self.types.items[type_slot_index] = .{
35263539 .Fn = .{
35273540 .name = "todo_name func",
35283541 .src = self_ast_node_index,
35293542 .params = param_type_refs.items,
3530 .ret = ret_type_ref,
3543 .ret = ret_type,
35313544 .generic_ret = generic_ret,
35323545 },
35333546 };
src/Module.zig+94-13
......@@ -2161,6 +2161,10 @@ pub const SrcLoc = struct {
21612161 .local_var_decl => tree.localVarDecl(node),
21622162 .simple_var_decl => tree.simpleVarDecl(node),
21632163 .aligned_var_decl => tree.alignedVarDecl(node),
2164 .@"usingnamespace" => {
2165 const node_data = tree.nodes.items(.data);
2166 return nodeToSpan(tree, node_data[node].lhs);
2167 },
21642168 else => unreachable,
21652169 };
21662170 if (full.ast.type_node != 0) {
......@@ -2171,6 +2175,58 @@ pub const SrcLoc = struct {
21712175 const end = start + @intCast(u32, tree.tokenSlice(tok_index).len);
21722176 return Span{ .start = start, .end = end, .main = start };
21732177 },
2178 .node_offset_var_decl_align => |node_off| {
2179 const tree = try src_loc.file_scope.getTree(gpa);
2180 const node = src_loc.declRelativeToNodeIndex(node_off);
2181 const node_tags = tree.nodes.items(.tag);
2182 const full: Ast.full.VarDecl = switch (node_tags[node]) {
2183 .global_var_decl => tree.globalVarDecl(node),
2184 .local_var_decl => tree.localVarDecl(node),
2185 .simple_var_decl => tree.simpleVarDecl(node),
2186 .aligned_var_decl => tree.alignedVarDecl(node),
2187 else => unreachable,
2188 };
2189 return nodeToSpan(tree, full.ast.align_node);
2190 },
2191 .node_offset_var_decl_section => |node_off| {
2192 const tree = try src_loc.file_scope.getTree(gpa);
2193 const node = src_loc.declRelativeToNodeIndex(node_off);
2194 const node_tags = tree.nodes.items(.tag);
2195 const full: Ast.full.VarDecl = switch (node_tags[node]) {
2196 .global_var_decl => tree.globalVarDecl(node),
2197 .local_var_decl => tree.localVarDecl(node),
2198 .simple_var_decl => tree.simpleVarDecl(node),
2199 .aligned_var_decl => tree.alignedVarDecl(node),
2200 else => unreachable,
2201 };
2202 return nodeToSpan(tree, full.ast.section_node);
2203 },
2204 .node_offset_var_decl_addrspace => |node_off| {
2205 const tree = try src_loc.file_scope.getTree(gpa);
2206 const node = src_loc.declRelativeToNodeIndex(node_off);
2207 const node_tags = tree.nodes.items(.tag);
2208 const full: Ast.full.VarDecl = switch (node_tags[node]) {
2209 .global_var_decl => tree.globalVarDecl(node),
2210 .local_var_decl => tree.localVarDecl(node),
2211 .simple_var_decl => tree.simpleVarDecl(node),
2212 .aligned_var_decl => tree.alignedVarDecl(node),
2213 else => unreachable,
2214 };
2215 return nodeToSpan(tree, full.ast.addrspace_node);
2216 },
2217 .node_offset_var_decl_init => |node_off| {
2218 const tree = try src_loc.file_scope.getTree(gpa);
2219 const node = src_loc.declRelativeToNodeIndex(node_off);
2220 const node_tags = tree.nodes.items(.tag);
2221 const full: Ast.full.VarDecl = switch (node_tags[node]) {
2222 .global_var_decl => tree.globalVarDecl(node),
2223 .local_var_decl => tree.localVarDecl(node),
2224 .simple_var_decl => tree.simpleVarDecl(node),
2225 .aligned_var_decl => tree.alignedVarDecl(node),
2226 else => unreachable,
2227 };
2228 return nodeToSpan(tree, full.ast.init_node);
2229 },
21742230 .node_offset_builtin_call_arg0 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 0),
21752231 .node_offset_builtin_call_arg1 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 1),
21762232 .node_offset_builtin_call_arg2 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 2),
......@@ -2857,6 +2913,18 @@ pub const LazySrcLoc = union(enum) {
28572913 /// to the type expression.
28582914 /// The Decl is determined contextually.
28592915 node_offset_var_decl_ty: i32,
2916 /// The source location points to the alignment expression of a var decl.
2917 /// The Decl is determined contextually.
2918 node_offset_var_decl_align: i32,
2919 /// The source location points to the linksection expression of a var decl.
2920 /// The Decl is determined contextually.
2921 node_offset_var_decl_section: i32,
2922 /// The source location points to the addrspace expression of a var decl.
2923 /// The Decl is determined contextually.
2924 node_offset_var_decl_addrspace: i32,
2925 /// The source location points to the initializer of a var decl.
2926 /// The Decl is determined contextually.
2927 node_offset_var_decl_init: i32,
28602928 /// The source location points to a for loop condition expression,
28612929 /// found by taking this AST node index offset from the containing
28622930 /// Decl AST node, which points to a for loop AST node. Next, navigate
......@@ -3098,6 +3166,10 @@ pub const LazySrcLoc = union(enum) {
30983166 .node_offset,
30993167 .node_offset_initializer,
31003168 .node_offset_var_decl_ty,
3169 .node_offset_var_decl_align,
3170 .node_offset_var_decl_section,
3171 .node_offset_var_decl_addrspace,
3172 .node_offset_var_decl_init,
31013173 .node_offset_for_cond,
31023174 .node_offset_builtin_call_arg0,
31033175 .node_offset_builtin_call_arg1,
......@@ -4414,17 +4486,26 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
44144486 const body = zir.extra[extra.end..][0..extra.data.body_len];
44154487 const result_ref = (try sema.analyzeBodyBreak(&block_scope, body)).?.operand;
44164488 try wip_captures.finalize();
4417 const src = LazySrcLoc.nodeOffset(0);
4418 const decl_tv = try sema.resolveInstValue(&block_scope, .unneeded, result_ref, undefined);
4489 const align_src: LazySrcLoc = .{ .node_offset_var_decl_align = 0 };
4490 const section_src: LazySrcLoc = .{ .node_offset_var_decl_section = 0 };
4491 const address_space_src: LazySrcLoc = .{ .node_offset_var_decl_addrspace = 0 };
4492 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = 0 };
4493 const init_src: LazySrcLoc = .{ .node_offset_var_decl_init = 0 };
4494 const decl_tv = try sema.resolveInstValue(&block_scope, init_src, result_ref, undefined);
44194495 const decl_align: u32 = blk: {
44204496 const align_ref = decl.zirAlignRef();
44214497 if (align_ref == .none) break :blk 0;
4422 break :blk try sema.resolveAlign(&block_scope, src, align_ref);
4498 break :blk try sema.resolveAlign(&block_scope, align_src, align_ref);
44234499 };
44244500 const decl_linksection: ?[*:0]const u8 = blk: {
44254501 const linksection_ref = decl.zirLinksectionRef();
44264502 if (linksection_ref == .none) break :blk null;
4427 const bytes = try sema.resolveConstString(&block_scope, src, linksection_ref, "linksection must be comptime known");
4503 const bytes = try sema.resolveConstString(&block_scope, section_src, linksection_ref, "linksection must be comptime known");
4504 if (mem.indexOfScalar(u8, bytes, 0) != null) {
4505 return sema.fail(&block_scope, section_src, "linksection cannot contain null bytes", .{});
4506 } else if (bytes.len == 0) {
4507 return sema.fail(&block_scope, section_src, "linksection cannot be empty", .{});
4508 }
44284509 break :blk (try decl_arena_allocator.dupeZ(u8, bytes)).ptr;
44294510 };
44304511 const target = sema.mod.getTarget();
......@@ -4442,27 +4523,27 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
44424523 .constant => target_util.defaultAddressSpace(target, .global_constant),
44434524 else => unreachable,
44444525 },
4445 else => |addrspace_ref| try sema.analyzeAddrspace(&block_scope, src, addrspace_ref, addrspace_ctx),
4526 else => |addrspace_ref| try sema.analyzeAddrspace(&block_scope, address_space_src, addrspace_ref, addrspace_ctx),
44464527 };
44474528 };
44484529
44494530 // Note this resolves the type of the Decl, not the value; if this Decl
44504531 // is a struct, for example, this resolves `type` (which needs no resolution),
44514532 // not the struct itself.
4452 try sema.resolveTypeLayout(&block_scope, src, decl_tv.ty);
4533 try sema.resolveTypeLayout(&block_scope, ty_src, decl_tv.ty);
44534534
44544535 const decl_arena_state = try decl_arena_allocator.create(std.heap.ArenaAllocator.State);
44554536
44564537 if (decl.is_usingnamespace) {
44574538 if (!decl_tv.ty.eql(Type.type, mod)) {
4458 return sema.fail(&block_scope, src, "expected type, found {}", .{
4539 return sema.fail(&block_scope, ty_src, "expected type, found {}", .{
44594540 decl_tv.ty.fmt(mod),
44604541 });
44614542 }
44624543 var buffer: Value.ToTypeBuffer = undefined;
44634544 const ty = try decl_tv.val.toType(&buffer).copy(decl_arena_allocator);
44644545 if (ty.getNamespace() == null) {
4465 return sema.fail(&block_scope, src, "type {} has no namespace", .{ty.fmt(mod)});
4546 return sema.fail(&block_scope, ty_src, "type {} has no namespace", .{ty.fmt(mod)});
44664547 }
44674548
44684549 decl.ty = Type.type;
......@@ -4508,7 +4589,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
45084589 decl.analysis = .complete;
45094590 decl.generation = mod.generation;
45104591
4511 const has_runtime_bits = try sema.fnHasRuntimeBits(&block_scope, src, decl.ty);
4592 const has_runtime_bits = try sema.fnHasRuntimeBits(&block_scope, ty_src, decl.ty);
45124593
45134594 if (has_runtime_bits) {
45144595 // We don't fully codegen the decl until later, but we do need to reserve a global
......@@ -4525,7 +4606,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
45254606
45264607 const is_inline = decl.ty.fnCallingConvention() == .Inline;
45274608 if (decl.is_exported) {
4528 const export_src = src; // TODO make this point at `export` token
4609 const export_src: LazySrcLoc = .{ .token_offset = @boolToInt(decl.is_pub) };
45294610 if (is_inline) {
45304611 return sema.fail(&block_scope, export_src, "export of inline function", .{});
45314612 }
......@@ -4588,14 +4669,14 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
45884669 decl.generation = mod.generation;
45894670
45904671 const has_runtime_bits = is_extern or
4591 (queue_linker_work and try sema.typeHasRuntimeBits(&block_scope, src, decl.ty));
4672 (queue_linker_work and try sema.typeHasRuntimeBits(&block_scope, ty_src, decl.ty));
45924673
45934674 if (has_runtime_bits) {
45944675 log.debug("queue linker work for {*} ({s})", .{ decl, decl.name });
45954676
45964677 // Needed for codegen_decl which will call updateDecl and then the
45974678 // codegen backend wants full access to the Decl Type.
4598 try sema.resolveTypeFully(&block_scope, src, decl.ty);
4679 try sema.resolveTypeFully(&block_scope, ty_src, decl.ty);
45994680
46004681 try mod.comp.bin_file.allocateDeclIndexes(decl_index);
46014682 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl_index });
......@@ -4606,7 +4687,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
46064687 }
46074688
46084689 if (decl.is_exported) {
4609 const export_src = src; // TODO point to the export token
4690 const export_src: LazySrcLoc = .{ .token_offset = @boolToInt(decl.is_pub) };
46104691 // The scope needs to have the decl in it.
46114692 const options: std.builtin.ExportOptions = .{ .name = mem.sliceTo(decl.name, 0) };
46124693 try sema.analyzeExport(&block_scope, export_src, options, decl_index);
src/Sema.zig+561-185
......@@ -74,6 +74,8 @@ types_to_resolve: std.ArrayListUnmanaged(Air.Inst.Ref) = .{},
7474/// Sema must convert comptime control flow to runtime control flow, which means
7575/// breaking from a block.
7676post_hoc_blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, *LabeledBlock) = .{},
77/// Populated with the last compile error created.
78err: ?*Module.ErrorMsg = null,
7779
7880const std = @import("std");
7981const mem = std.mem;
......@@ -174,7 +176,6 @@ pub const Block = struct {
174176 pub const Inlining = struct {
175177 comptime_result: Air.Inst.Ref,
176178 merges: Merges,
177 err: ?*Module.ErrorMsg = null,
178179 };
179180
180181 pub const Merges = struct {
......@@ -1159,7 +1160,7 @@ fn analyzeBodyInner(
11591160 try sema.errNote(block, runtime_src, msg, "runtime control flow here", .{});
11601161 break :msg msg;
11611162 };
1162 return sema.failWithOwnedErrorMsg(block, msg);
1163 return sema.failWithOwnedErrorMsg(msg);
11631164 }
11641165 }
11651166 i += 1;
......@@ -1738,7 +1739,7 @@ fn failWithNeededComptime(sema: *Sema, block: *Block, src: LazySrcLoc, reason: [
17381739 try sema.errNote(block, src, msg, "{s}", .{reason});
17391740 break :msg msg;
17401741 };
1741 return sema.failWithOwnedErrorMsg(block, msg);
1742 return sema.failWithOwnedErrorMsg(msg);
17421743}
17431744
17441745fn failWithUseOfUndef(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError {
......@@ -1770,7 +1771,7 @@ fn failWithArrayInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty
17701771 }
17711772 break :msg msg;
17721773 };
1773 return sema.failWithOwnedErrorMsg(block, msg);
1774 return sema.failWithOwnedErrorMsg(msg);
17741775}
17751776
17761777fn failWithStructInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError {
......@@ -1801,7 +1802,7 @@ fn failWithIntegerOverflow(sema: *Sema, block: *Block, src: LazySrcLoc, int_ty:
18011802 try sema.errNote(block, src, msg, "when computing vector element at index '{d}'", .{vector_index});
18021803 break :msg msg;
18031804 };
1804 return sema.failWithOwnedErrorMsg(block, msg);
1805 return sema.failWithOwnedErrorMsg(msg);
18051806 }
18061807 return sema.fail(block, src, "overflow of integer type '{}' with value '{}'", .{
18071808 int_ty.fmt(sema.mod), val.fmtValue(int_ty, sema.mod),
......@@ -1823,7 +1824,7 @@ fn failWithInvalidComptimeFieldStore(sema: *Sema, block: *Block, init_src: LazyS
18231824 try sema.errNote(block, default_value_src, msg, "default value set here", .{});
18241825 break :msg msg;
18251826 };
1826 return sema.failWithOwnedErrorMsg(block, msg);
1827 return sema.failWithOwnedErrorMsg(msg);
18271828}
18281829
18291830/// We don't return a pointer to the new error note because the pointer
......@@ -1878,10 +1879,10 @@ pub fn fail(
18781879 args: anytype,
18791880) CompileError {
18801881 const err_msg = try sema.errMsg(block, src, format, args);
1881 return sema.failWithOwnedErrorMsg(block, err_msg);
1882 return sema.failWithOwnedErrorMsg(err_msg);
18821883}
18831884
1884fn failWithOwnedErrorMsg(sema: *Sema, block: *Block, err_msg: *Module.ErrorMsg) CompileError {
1885fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError {
18851886 @setCold(true);
18861887
18871888 if (crash_report.is_enabled and sema.mod.comp.debug_compile_errors) {
......@@ -1894,7 +1895,7 @@ fn failWithOwnedErrorMsg(sema: *Sema, block: *Block, err_msg: *Module.ErrorMsg)
18941895 }
18951896
18961897 const mod = sema.mod;
1897 if (block.inlining) |some| some.err = err_msg;
1898 sema.err = err_msg;
18981899
18991900 {
19001901 errdefer err_msg.destroy(mod.gpa);
......@@ -2591,7 +2592,7 @@ fn zirEnumDecl(
25912592 try sema.errNote(block, other_tag_src, msg, "other field here", .{});
25922593 break :msg msg;
25932594 };
2594 return sema.failWithOwnedErrorMsg(block, msg);
2595 return sema.failWithOwnedErrorMsg(msg);
25952596 }
25962597
25972598 if (has_tag_value) {
......@@ -2886,7 +2887,7 @@ fn ensureResultUsed(
28862887 try sema.errNote(block, src, msg, "consider using `try`, `catch`, or `if`", .{});
28872888 break :msg msg;
28882889 };
2889 return sema.failWithOwnedErrorMsg(block, msg);
2890 return sema.failWithOwnedErrorMsg(msg);
28902891 },
28912892 else => {
28922893 const msg = msg: {
......@@ -2896,7 +2897,7 @@ fn ensureResultUsed(
28962897 try sema.errNote(block, src, msg, "this error can be suppressed by assigning the value to '_'", .{});
28972898 break :msg msg;
28982899 };
2899 return sema.failWithOwnedErrorMsg(block, msg);
2900 return sema.failWithOwnedErrorMsg(msg);
29002901 },
29012902 }
29022903}
......@@ -2917,7 +2918,7 @@ fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
29172918 try sema.errNote(block, src, msg, "consider using `try`, `catch`, or `if`", .{});
29182919 break :msg msg;
29192920 };
2920 return sema.failWithOwnedErrorMsg(block, msg);
2921 return sema.failWithOwnedErrorMsg(msg);
29212922 },
29222923 else => return,
29232924 }
......@@ -2957,7 +2958,7 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
29572958 );
29582959 break :msg msg;
29592960 };
2960 return sema.failWithOwnedErrorMsg(block, msg);
2961 return sema.failWithOwnedErrorMsg(msg);
29612962 }
29622963
29632964 return sema.fieldVal(block, src, object, "len", src);
......@@ -2971,7 +2972,7 @@ fn zirAllocExtended(
29712972 const extra = sema.code.extraData(Zir.Inst.AllocExtended, extended.operand);
29722973 const src = LazySrcLoc.nodeOffset(extra.data.src_node);
29732974 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = extra.data.src_node };
2974 const align_src = src; // TODO better source location
2975 const align_src: LazySrcLoc = .{ .node_offset_var_decl_align = extra.data.src_node };
29752976 const small = @bitCast(Zir.Inst.AllocExtended.Small, extended.small);
29762977
29772978 var extra_index: usize = extra.end;
......@@ -3615,7 +3616,7 @@ fn validateUnionInit(
36153616 try sema.addDeclaredHereNote(msg, union_ty);
36163617 break :msg msg;
36173618 };
3618 return sema.failWithOwnedErrorMsg(block, msg);
3619 return sema.failWithOwnedErrorMsg(msg);
36193620 }
36203621
36213622 if ((is_comptime or block.is_comptime) and
......@@ -3747,7 +3748,7 @@ fn validateStructInit(
37473748 try sema.errNote(block, other_field_src, msg, "other field here", .{});
37483749 break :msg msg;
37493750 };
3750 return sema.failWithOwnedErrorMsg(block, msg);
3751 return sema.failWithOwnedErrorMsg(msg);
37513752 }
37523753 found_fields[field_index] = field_ptr;
37533754 }
......@@ -3808,7 +3809,7 @@ fn validateStructInit(
38083809 .{fqn},
38093810 );
38103811 }
3811 return sema.failWithOwnedErrorMsg(block, msg);
3812 return sema.failWithOwnedErrorMsg(msg);
38123813 }
38133814
38143815 return;
......@@ -3938,7 +3939,7 @@ fn validateStructInit(
39383939 .{fqn},
39393940 );
39403941 }
3941 return sema.failWithOwnedErrorMsg(block, msg);
3942 return sema.failWithOwnedErrorMsg(msg);
39423943 }
39433944
39443945 if (struct_is_comptime) {
......@@ -4000,7 +4001,7 @@ fn zirValidateArrayInit(
40004001 }
40014002
40024003 if (root_msg) |msg| {
4003 return sema.failWithOwnedErrorMsg(block, msg);
4004 return sema.failWithOwnedErrorMsg(msg);
40044005 }
40054006 }
40064007
......@@ -4180,7 +4181,7 @@ fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
41804181 try sema.explainWhyTypeIsComptime(block, src, msg, src.toSrcLoc(src_decl), elem_ty);
41814182 break :msg msg;
41824183 };
4183 return sema.failWithOwnedErrorMsg(block, msg);
4184 return sema.failWithOwnedErrorMsg(msg);
41844185 }
41854186}
41864187
......@@ -4206,7 +4207,7 @@ fn failWithBadMemberAccess(
42064207 try sema.addDeclaredHereNote(msg, agg_ty);
42074208 break :msg msg;
42084209 };
4209 return sema.failWithOwnedErrorMsg(block, msg);
4210 return sema.failWithOwnedErrorMsg(msg);
42104211}
42114212
42124213fn failWithBadStructFieldAccess(
......@@ -4232,7 +4233,7 @@ fn failWithBadStructFieldAccess(
42324233 try sema.mod.errNoteNonLazy(struct_obj.srcLoc(sema.mod), msg, "struct declared here", .{});
42334234 break :msg msg;
42344235 };
4235 return sema.failWithOwnedErrorMsg(block, msg);
4236 return sema.failWithOwnedErrorMsg(msg);
42364237}
42374238
42384239fn failWithBadUnionFieldAccess(
......@@ -4258,7 +4259,7 @@ fn failWithBadUnionFieldAccess(
42584259 try sema.mod.errNoteNonLazy(union_obj.srcLoc(sema.mod), msg, "union declared here", .{});
42594260 break :msg msg;
42604261 };
4261 return sema.failWithOwnedErrorMsg(block, msg);
4262 return sema.failWithOwnedErrorMsg(msg);
42624263}
42634264
42644265fn addDeclaredHereNote(sema: *Sema, parent: *Module.ErrorMsg, decl_ty: Type) !void {
......@@ -4747,7 +4748,7 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
47474748 @import("clang.zig").Stage2ErrorMsg.delete(c_import_res.errors.ptr, c_import_res.errors.len);
47484749 break :msg msg;
47494750 };
4750 return sema.failWithOwnedErrorMsg(parent_block, msg);
4751 return sema.failWithOwnedErrorMsg(msg);
47514752 }
47524753 const c_import_pkg = Package.create(
47534754 sema.gpa,
......@@ -4921,7 +4922,7 @@ fn analyzeBlockBody(
49214922
49224923 break :msg msg;
49234924 };
4924 return sema.failWithOwnedErrorMsg(child_block, msg);
4925 return sema.failWithOwnedErrorMsg(msg);
49254926 }
49264927 const ty_inst = try sema.addType(resolved_ty);
49274928 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
......@@ -5049,18 +5050,18 @@ pub fn analyzeExport(
50495050 try mod.ensureDeclAnalyzed(exported_decl_index);
50505051 const exported_decl = mod.declPtr(exported_decl_index);
50515052
5052 if (!(try sema.validateExternType(exported_decl.ty, .other))) {
5053 if (!sema.validateExternType(exported_decl.ty, .other)) {
50535054 const msg = msg: {
50545055 const msg = try sema.errMsg(block, src, "unable to export type '{}'", .{exported_decl.ty.fmt(sema.mod)});
50555056 errdefer msg.destroy(sema.gpa);
50565057
50575058 const src_decl = sema.mod.declPtr(block.src_decl);
5058 try sema.explainWhyTypeIsNotExtern(block, src, msg, src.toSrcLoc(src_decl), exported_decl.ty, .other);
5059 try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl), exported_decl.ty, .other);
50595060
50605061 try sema.addDeclaredHereNote(msg, exported_decl.ty);
50615062 break :msg msg;
50625063 };
5063 return sema.failWithOwnedErrorMsg(block, msg);
5064 return sema.failWithOwnedErrorMsg(msg);
50645065 }
50655066
50665067 const gpa = mod.gpa;
......@@ -5150,7 +5151,7 @@ fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst
51505151 try sema.errNote(block, gop.value_ptr.src, msg, "other instance here", .{});
51515152 break :msg msg;
51525153 };
5153 return sema.failWithOwnedErrorMsg(block, msg);
5154 return sema.failWithOwnedErrorMsg(msg);
51545155 }
51555156 gop.value_ptr.* = .{ .alignment = alignment, .src = src };
51565157}
......@@ -5311,7 +5312,14 @@ fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
53115312 const src = inst_data.src();
53125313 const decl_name = inst_data.get(sema.code);
53135314 const decl_index = try sema.lookupIdentifier(block, src, decl_name);
5314 return sema.analyzeDeclRef(decl_index);
5315 return sema.analyzeDeclRef(decl_index) catch |err| switch (err) {
5316 error.AnalysisFail => {
5317 const msg = sema.err orelse return err;
5318 try sema.errNote(block, src, msg, "referenced here", .{});
5319 return err;
5320 },
5321 else => return err,
5322 };
53155323}
53165324
53175325fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -5413,7 +5421,7 @@ fn lookupInNamespace(
54135421 }
54145422 break :msg msg;
54155423 };
5416 return sema.failWithOwnedErrorMsg(block, msg);
5424 return sema.failWithOwnedErrorMsg(msg);
54175425 },
54185426 }
54195427 } else if (namespace.decls.getKeyAdapted(ident_name, Module.DeclAdapter{ .mod = mod })) |decl_index| {
......@@ -5872,9 +5880,8 @@ fn analyzeCall(
58725880 sema.analyzeBody(&child_block, fn_info.body) catch |err| switch (err) {
58735881 error.ComptimeReturn => break :result inlining.comptime_result,
58745882 error.AnalysisFail => {
5875 const err_msg = inlining.err orelse return err;
5883 const err_msg = sema.err orelse return err;
58765884 try sema.errNote(block, call_src, err_msg, "called from here", .{});
5877 if (block.inlining) |some| some.err = err_msg;
58785885 return err;
58795886 },
58805887 else => |e| return e,
......@@ -6802,7 +6809,7 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
68026809 try sema.errNote(block, src, msg, "'||' merges error sets; 'or' performs boolean OR", .{});
68036810 break :msg msg;
68046811 };
6805 return sema.failWithOwnedErrorMsg(block, msg);
6812 return sema.failWithOwnedErrorMsg(msg);
68066813 }
68076814 const lhs_ty = try sema.analyzeAsType(block, lhs_src, lhs);
68086815 const rhs_ty = try sema.analyzeAsType(block, rhs_src, rhs);
......@@ -6927,7 +6934,7 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
69276934 try sema.addDeclaredHereNote(msg, dest_ty);
69286935 break :msg msg;
69296936 };
6930 return sema.failWithOwnedErrorMsg(block, msg);
6937 return sema.failWithOwnedErrorMsg(msg);
69316938 }
69326939 return sema.addConstant(dest_ty, int_val);
69336940 }
......@@ -7632,9 +7639,9 @@ fn funcCommon(
76327639 try sema.addDeclaredHereNote(msg, bare_return_type);
76337640 break :msg msg;
76347641 };
7635 return sema.failWithOwnedErrorMsg(block, msg);
7642 return sema.failWithOwnedErrorMsg(msg);
76367643 }
7637 if (!Type.fnCallingConventionAllowsZigTypes(cc_workaround) and !(try sema.validateExternType(return_type, .ret_ty))) {
7644 if (!Type.fnCallingConventionAllowsZigTypes(cc_workaround) and !sema.validateExternType(return_type, .ret_ty)) {
76387645 const msg = msg: {
76397646 const msg = try sema.errMsg(block, ret_ty_src, "return type '{}' not allowed in function with calling convention '{s}'", .{
76407647 return_type.fmt(sema.mod), @tagName(cc_workaround),
......@@ -7642,12 +7649,12 @@ fn funcCommon(
76427649 errdefer msg.destroy(sema.gpa);
76437650
76447651 const src_decl = sema.mod.declPtr(block.src_decl);
7645 try sema.explainWhyTypeIsNotExtern(block, ret_ty_src, msg, ret_ty_src.toSrcLoc(src_decl), return_type, .ret_ty);
7652 try sema.explainWhyTypeIsNotExtern(msg, ret_ty_src.toSrcLoc(src_decl), return_type, .ret_ty);
76467653
76477654 try sema.addDeclaredHereNote(msg, return_type);
76487655 break :msg msg;
76497656 };
7650 return sema.failWithOwnedErrorMsg(block, msg);
7657 return sema.failWithOwnedErrorMsg(msg);
76517658 }
76527659
76537660 const arch = sema.mod.getTarget().cpu.arch;
......@@ -7812,7 +7819,7 @@ fn analyzeParameter(
78127819 try sema.errNote(block, param_src, msg, "function is generic because of this parameter", .{});
78137820 break :msg msg;
78147821 };
7815 return sema.failWithOwnedErrorMsg(block, msg);
7822 return sema.failWithOwnedErrorMsg(msg);
78167823 }
78177824 if (this_generic and !Type.fnCallingConventionAllowsZigTypes(cc)) {
78187825 return sema.fail(block, param_src, "generic parameters not allowed in function with calling convention '{s}'", .{@tagName(cc)});
......@@ -7828,9 +7835,9 @@ fn analyzeParameter(
78287835 try sema.addDeclaredHereNote(msg, param.ty);
78297836 break :msg msg;
78307837 };
7831 return sema.failWithOwnedErrorMsg(block, msg);
7838 return sema.failWithOwnedErrorMsg(msg);
78327839 }
7833 if (!Type.fnCallingConventionAllowsZigTypes(cc) and !(try sema.validateExternType(param.ty, .param_ty))) {
7840 if (!Type.fnCallingConventionAllowsZigTypes(cc) and !sema.validateExternType(param.ty, .param_ty)) {
78347841 const msg = msg: {
78357842 const msg = try sema.errMsg(block, param_src, "parameter of type '{}' not allowed in function with calling convention '{s}'", .{
78367843 param.ty.fmt(sema.mod), @tagName(cc),
......@@ -7838,12 +7845,12 @@ fn analyzeParameter(
78387845 errdefer msg.destroy(sema.gpa);
78397846
78407847 const src_decl = sema.mod.declPtr(block.src_decl);
7841 try sema.explainWhyTypeIsNotExtern(block, param_src, msg, param_src.toSrcLoc(src_decl), param.ty, .param_ty);
7848 try sema.explainWhyTypeIsNotExtern(msg, param_src.toSrcLoc(src_decl), param.ty, .param_ty);
78427849
78437850 try sema.addDeclaredHereNote(msg, param.ty);
78447851 break :msg msg;
78457852 };
7846 return sema.failWithOwnedErrorMsg(block, msg);
7853 return sema.failWithOwnedErrorMsg(msg);
78477854 }
78487855 if (requires_comptime and !param.is_comptime) {
78497856 const msg = msg: {
......@@ -7855,7 +7862,7 @@ fn analyzeParameter(
78557862 try sema.addDeclaredHereNote(msg, param.ty);
78567863 break :msg msg;
78577864 };
7858 return sema.failWithOwnedErrorMsg(block, msg);
7865 return sema.failWithOwnedErrorMsg(msg);
78597866 }
78607867}
78617868
......@@ -8046,7 +8053,7 @@ fn zirPtrToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
80468053 if (try sema.resolveMaybeUndefValIntable(block, ptr_src, ptr)) |ptr_val| {
80478054 return sema.addConstant(Type.usize, ptr_val);
80488055 }
8049 try sema.requireRuntimeBlock(block, ptr_src, ptr_src);
8056 try sema.requireRuntimeBlock(block, inst_data.src(), ptr_src);
80508057 return block.addUnOp(.ptrtoint, ptr);
80518058}
80528059
......@@ -8288,6 +8295,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
82888295
82898296 const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
82908297 const operand = try sema.resolveInst(extra.rhs);
8298 const operand_ty = sema.typeOf(operand);
82918299 switch (dest_ty.zigTypeTag()) {
82928300 .AnyFrame,
82938301 .ComptimeFloat,
......@@ -8310,19 +8318,30 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
83108318 const msg = msg: {
83118319 const msg = try sema.errMsg(block, dest_ty_src, "cannot @bitCast to '{}'", .{dest_ty.fmt(sema.mod)});
83128320 errdefer msg.destroy(sema.gpa);
8313 switch (sema.typeOf(operand).zigTypeTag()) {
8314 .Int, .ComptimeInt => try sema.errNote(block, dest_ty_src, msg, "use @intToEnum for type coercion", .{}),
8321 switch (operand_ty.zigTypeTag()) {
8322 .Int, .ComptimeInt => try sema.errNote(block, dest_ty_src, msg, "use @intToEnum to cast from '{}'", .{operand_ty.fmt(sema.mod)}),
83158323 else => {},
83168324 }
83178325
83188326 break :msg msg;
83198327 };
8320 return sema.failWithOwnedErrorMsg(block, msg);
8328 return sema.failWithOwnedErrorMsg(msg);
83218329 },
83228330
8323 .Pointer => return sema.fail(block, dest_ty_src, "cannot @bitCast to '{}', use @ptrCast to cast to a pointer", .{
8324 dest_ty.fmt(sema.mod),
8325 }),
8331 .Pointer => {
8332 const msg = msg: {
8333 const msg = try sema.errMsg(block, dest_ty_src, "cannot @bitCast to '{}'", .{dest_ty.fmt(sema.mod)});
8334 errdefer msg.destroy(sema.gpa);
8335 switch (operand_ty.zigTypeTag()) {
8336 .Int, .ComptimeInt => try sema.errNote(block, dest_ty_src, msg, "use @intToPtr to cast from '{}'", .{operand_ty.fmt(sema.mod)}),
8337 .Pointer => try sema.errNote(block, dest_ty_src, msg, "use @ptrCast to cast from '{}'", .{operand_ty.fmt(sema.mod)}),
8338 else => {},
8339 }
8340
8341 break :msg msg;
8342 };
8343 return sema.failWithOwnedErrorMsg(msg);
8344 },
83268345 .Struct, .Union => if (dest_ty.containerLayout() == .Auto) {
83278346 const container = switch (dest_ty.zigTypeTag()) {
83288347 .Struct => "struct",
......@@ -8342,6 +8361,70 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
83428361 .Vector,
83438362 => {},
83448363 }
8364 switch (operand_ty.zigTypeTag()) {
8365 .AnyFrame,
8366 .ComptimeFloat,
8367 .ComptimeInt,
8368 .EnumLiteral,
8369 .ErrorSet,
8370 .ErrorUnion,
8371 .Fn,
8372 .Frame,
8373 .NoReturn,
8374 .Null,
8375 .Opaque,
8376 .Optional,
8377 .Type,
8378 .Undefined,
8379 .Void,
8380 => return sema.fail(block, operand_src, "cannot @bitCast from '{}'", .{operand_ty.fmt(sema.mod)}),
8381
8382 .Enum => {
8383 const msg = msg: {
8384 const msg = try sema.errMsg(block, operand_src, "cannot @bitCast from '{}'", .{operand_ty.fmt(sema.mod)});
8385 errdefer msg.destroy(sema.gpa);
8386 switch (dest_ty.zigTypeTag()) {
8387 .Int, .ComptimeInt => try sema.errNote(block, operand_src, msg, "use @enumToInt to cast to '{}'", .{dest_ty.fmt(sema.mod)}),
8388 else => {},
8389 }
8390
8391 break :msg msg;
8392 };
8393 return sema.failWithOwnedErrorMsg(msg);
8394 },
8395 .Pointer => {
8396 const msg = msg: {
8397 const msg = try sema.errMsg(block, operand_src, "cannot @bitCast from '{}'", .{operand_ty.fmt(sema.mod)});
8398 errdefer msg.destroy(sema.gpa);
8399 switch (dest_ty.zigTypeTag()) {
8400 .Int, .ComptimeInt => try sema.errNote(block, operand_src, msg, "use @ptrToInt to cast to '{}'", .{dest_ty.fmt(sema.mod)}),
8401 .Pointer => try sema.errNote(block, operand_src, msg, "use @ptrCast to cast to '{}'", .{dest_ty.fmt(sema.mod)}),
8402 else => {},
8403 }
8404
8405 break :msg msg;
8406 };
8407 return sema.failWithOwnedErrorMsg(msg);
8408 },
8409 .Struct, .Union => if (operand_ty.containerLayout() == .Auto) {
8410 const container = switch (operand_ty.zigTypeTag()) {
8411 .Struct => "struct",
8412 .Union => "union",
8413 else => unreachable,
8414 };
8415 return sema.fail(block, operand_src, "cannot @bitCast from '{}', {s} does not have a guaranteed in-memory layout", .{
8416 operand_ty.fmt(sema.mod), container,
8417 });
8418 },
8419 .BoundFn => @panic("TODO remove this type from the language and compiler"),
8420
8421 .Array,
8422 .Bool,
8423 .Float,
8424 .Int,
8425 .Vector,
8426 => {},
8427 }
83458428 return sema.bitCast(block, dest_ty, operand, operand_src);
83468429}
83478430
......@@ -8587,7 +8670,7 @@ fn zirSwitchCapture(
85878670 try sema.errNote(block, item_src, msg, "type '{}' here", .{field.ty.fmt(sema.mod)});
85888671 break :msg msg;
85898672 };
8590 return sema.failWithOwnedErrorMsg(block, msg);
8673 return sema.failWithOwnedErrorMsg(msg);
85918674 }
85928675 }
85938676
......@@ -8710,7 +8793,7 @@ fn zirSwitchCond(
87108793 }
87118794 break :msg msg;
87128795 };
8713 return sema.failWithOwnedErrorMsg(block, msg);
8796 return sema.failWithOwnedErrorMsg(msg);
87148797 };
87158798 return sema.unionToTag(block, enum_ty, operand, src);
87168799 },
......@@ -8799,7 +8882,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
87998882 );
88008883 break :msg msg;
88018884 };
8802 return sema.failWithOwnedErrorMsg(block, msg);
8885 return sema.failWithOwnedErrorMsg(msg);
88038886 }
88048887
88058888 const target = sema.mod.getTarget();
......@@ -8903,7 +8986,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
89038986 );
89048987 break :msg msg;
89058988 };
8906 return sema.failWithOwnedErrorMsg(block, msg);
8989 return sema.failWithOwnedErrorMsg(msg);
89078990 } else if (special_prong == .none and operand_ty.isNonexhaustiveEnum() and !union_originally) {
89088991 return sema.fail(
89098992 block,
......@@ -9003,7 +9086,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
90039086 if (maybe_msg) |msg| {
90049087 maybe_msg = null;
90059088 try sema.addDeclaredHereNote(msg, operand_ty);
9006 return sema.failWithOwnedErrorMsg(block, msg);
9089 return sema.failWithOwnedErrorMsg(msg);
90079090 }
90089091
90099092 if (special_prong == .@"else" and seen_errors.count() == operand_ty.errorSetNames().len) {
......@@ -9812,7 +9895,7 @@ fn validateSwitchDupe(
98129895 );
98139896 break :msg msg;
98149897 };
9815 return sema.failWithOwnedErrorMsg(block, msg);
9898 return sema.failWithOwnedErrorMsg(msg);
98169899}
98179900
98189901fn validateSwitchItemBool(
......@@ -9882,7 +9965,7 @@ fn validateSwitchNoRange(
98829965 );
98839966 break :msg msg;
98849967 };
9885 return sema.failWithOwnedErrorMsg(block, msg);
9968 return sema.failWithOwnedErrorMsg(msg);
98869969}
98879970
98889971fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -12806,7 +12889,7 @@ fn analyzeCmpUnionTag(
1280612889 try sema.mod.errNoteNonLazy(union_ty.declSrcLoc(sema.mod), msg, "union '{}' is not a tagged union", .{union_ty.fmt(sema.mod)});
1280712890 break :msg msg;
1280812891 };
12809 return sema.failWithOwnedErrorMsg(block, msg);
12892 return sema.failWithOwnedErrorMsg(msg);
1281012893 };
1281112894 // Coerce both the union and the tag to the union's tag type, and then execute the
1281212895 // enum comparison codepath.
......@@ -13067,6 +13150,36 @@ fn zirClosureGet(
1306713150 scope = scope.parent.?;
1306813151 } else unreachable;
1306913152
13153 if (tv.val.tag() == .generic_poison and !block.is_typeof and !block.is_comptime and sema.func != null) {
13154 const msg = msg: {
13155 const name = name: {
13156 const file = sema.owner_decl.getFileScope();
13157 const tree = file.getTree(sema.mod.gpa) catch |err| {
13158 // In this case we emit a warning + a less precise source location.
13159 log.warn("unable to load {s}: {s}", .{
13160 file.sub_file_path, @errorName(err),
13161 });
13162 break :name null;
13163 };
13164 const node = sema.owner_decl.relativeToNodeIndex(inst_data.src_node);
13165 const token = tree.nodes.items(.main_token)[node];
13166 break :name tree.tokenSlice(token);
13167 };
13168
13169 const msg = if (name) |some|
13170 try sema.errMsg(block, inst_data.src(), "'{s}' not accessible from inner function", .{some})
13171 else
13172 try sema.errMsg(block, inst_data.src(), "variable not accessible from inner function", .{});
13173 errdefer msg.destroy(sema.gpa);
13174
13175 try sema.errNote(block, LazySrcLoc.nodeOffset(0), msg, "crossed function definition here", .{});
13176
13177 // TODO add "declared here" note
13178 break :msg msg;
13179 };
13180 return sema.failWithOwnedErrorMsg(msg);
13181 }
13182
1307013183 return sema.addConstant(tv.ty, tv.val);
1307113184}
1307213185
......@@ -13098,8 +13211,8 @@ fn zirBuiltinSrc(
1309813211 const tracy = trace(@src());
1309913212 defer tracy.end();
1310013213
13101 const src = sema.src; // TODO better source location
13102 const extra = sema.code.extraData(Zir.Inst.LineColumn, extended.operand).data;
13214 const extra = sema.code.extraData(Zir.Inst.Src, extended.operand).data;
13215 const src = LazySrcLoc.nodeOffset(extra.node);
1310313216 const func = sema.func orelse return sema.fail(block, src, "@src outside function", .{});
1310413217 const fn_owner_decl = sema.mod.declPtr(func.owner_decl);
1310513218
......@@ -14790,18 +14903,18 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
1479014903 } else if (inst_data.size == .Many and elem_ty.zigTypeTag() == .Opaque) {
1479114904 return sema.fail(block, elem_ty_src, "unknown-length pointer to opaque not allowed", .{});
1479214905 } else if (inst_data.size == .C) {
14793 if (!(try sema.validateExternType(elem_ty, .other))) {
14906 if (!sema.validateExternType(elem_ty, .other)) {
1479414907 const msg = msg: {
1479514908 const msg = try sema.errMsg(block, elem_ty_src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(sema.mod)});
1479614909 errdefer msg.destroy(sema.gpa);
1479714910
1479814911 const src_decl = sema.mod.declPtr(block.src_decl);
14799 try sema.explainWhyTypeIsNotExtern(block, elem_ty_src, msg, elem_ty_src.toSrcLoc(src_decl), elem_ty, .other);
14912 try sema.explainWhyTypeIsNotExtern(msg, elem_ty_src.toSrcLoc(src_decl), elem_ty, .other);
1480014913
1480114914 try sema.addDeclaredHereNote(msg, elem_ty);
1480214915 break :msg msg;
1480314916 };
14804 return sema.failWithOwnedErrorMsg(block, msg);
14917 return sema.failWithOwnedErrorMsg(msg);
1480514918 }
1480614919 if (elem_ty.zigTypeTag() == .Opaque) {
1480714920 return sema.fail(block, elem_ty_src, "C pointers cannot point to opaque types", .{});
......@@ -14972,7 +15085,7 @@ fn zirStructInit(
1497215085 try sema.errNote(block, other_field_src, msg, "other field here", .{});
1497315086 break :msg msg;
1497415087 };
14975 return sema.failWithOwnedErrorMsg(block, msg);
15088 return sema.failWithOwnedErrorMsg(msg);
1497615089 }
1497715090 found_fields[field_index] = item.data.field_type;
1497815091 field_inits[field_index] = try sema.resolveInst(item.data.init);
......@@ -15113,7 +15226,7 @@ fn finishStructInit(
1511315226 .{fqn},
1511415227 );
1511515228 }
15116 return sema.failWithOwnedErrorMsg(block, msg);
15229 return sema.failWithOwnedErrorMsg(msg);
1511715230 }
1511815231
1511915232 const is_comptime = for (field_inits) |field_init| {
......@@ -15188,7 +15301,7 @@ fn zirStructInitAnon(
1518815301 try sema.errNote(block, prev_source, msg, "other field here", .{});
1518915302 break :msg msg;
1519015303 };
15191 return sema.failWithOwnedErrorMsg(block, msg);
15304 return sema.failWithOwnedErrorMsg(msg);
1519215305 }
1519315306 gop.value_ptr.* = @intCast(u32, i);
1519415307
......@@ -15204,7 +15317,7 @@ fn zirStructInitAnon(
1520415317 try sema.addDeclaredHereNote(msg, types[i]);
1520515318 break :msg msg;
1520615319 };
15207 return sema.failWithOwnedErrorMsg(block, msg);
15320 return sema.failWithOwnedErrorMsg(msg);
1520815321 }
1520915322 const init_src = src; // TODO better source location
1521015323 if (try sema.resolveMaybeUndefVal(block, init_src, init)) |init_val| {
......@@ -15402,7 +15515,7 @@ fn zirArrayInitAnon(
1540215515 try sema.addDeclaredHereNote(msg, types[i]);
1540315516 break :msg msg;
1540415517 };
15405 return sema.failWithOwnedErrorMsg(block, msg);
15518 return sema.failWithOwnedErrorMsg(msg);
1540615519 }
1540715520 if (try sema.resolveMaybeUndefVal(block, operand_src, elem)) |val| {
1540815521 values[i] = val;
......@@ -15716,7 +15829,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
1571615829 try sema.addDeclaredHereNote(msg, operand_ty);
1571715830 break :msg msg;
1571815831 };
15719 return sema.failWithOwnedErrorMsg(block, msg);
15832 return sema.failWithOwnedErrorMsg(msg);
1572015833 },
1572115834 else => return sema.fail(block, operand_src, "expected enum or union; found '{}'", .{
1572215835 operand_ty.fmt(mod),
......@@ -15735,7 +15848,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
1573515848 try mod.errNoteNonLazy(enum_decl.srcLoc(), msg, "declared here", .{});
1573615849 break :msg msg;
1573715850 };
15738 return sema.failWithOwnedErrorMsg(block, msg);
15851 return sema.failWithOwnedErrorMsg(msg);
1573915852 };
1574015853 const field_name = enum_ty.enumFieldName(field_index);
1574115854 return sema.addStrLit(block, field_name);
......@@ -15874,18 +15987,18 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
1587415987 } else if (ptr_size == .Many and elem_ty.zigTypeTag() == .Opaque) {
1587515988 return sema.fail(block, src, "unknown-length pointer to opaque not allowed", .{});
1587615989 } else if (ptr_size == .C) {
15877 if (!(try sema.validateExternType(elem_ty, .other))) {
15990 if (!sema.validateExternType(elem_ty, .other)) {
1587815991 const msg = msg: {
1587915992 const msg = try sema.errMsg(block, src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(sema.mod)});
1588015993 errdefer msg.destroy(sema.gpa);
1588115994
1588215995 const src_decl = sema.mod.declPtr(block.src_decl);
15883 try sema.explainWhyTypeIsNotExtern(block, src, msg, src.toSrcLoc(src_decl), elem_ty, .other);
15996 try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl), elem_ty, .other);
1588415997
1588515998 try sema.addDeclaredHereNote(msg, elem_ty);
1588615999 break :msg msg;
1588716000 };
15888 return sema.failWithOwnedErrorMsg(block, msg);
16001 return sema.failWithOwnedErrorMsg(msg);
1588916002 }
1589016003 if (elem_ty.zigTypeTag() == .Opaque) {
1589116004 return sema.fail(block, src, "C pointers cannot point to opaque types", .{});
......@@ -16288,7 +16401,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
1628816401 try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
1628916402 break :msg msg;
1629016403 };
16291 return sema.failWithOwnedErrorMsg(block, msg);
16404 return sema.failWithOwnedErrorMsg(msg);
1629216405 }
1629316406 }
1629416407
......@@ -16322,7 +16435,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
1632216435 try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
1632316436 break :msg msg;
1632416437 };
16325 return sema.failWithOwnedErrorMsg(block, msg);
16438 return sema.failWithOwnedErrorMsg(msg);
1632616439 }
1632716440 }
1632816441
......@@ -16353,10 +16466,24 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
1635316466 return sema.fail(block, src, "varargs functions must have C calling convention", .{});
1635416467 }
1635516468
16356 const alignment = @intCast(u29, alignment_val.toUnsignedInt(target)); // TODO: Validate this value.
16469 const alignment = alignment: {
16470 if (!try sema.intFitsInType(block, src, alignment_val, Type.u32, null)) {
16471 return sema.fail(block, src, "alignment must fit in 'u32'", .{});
16472 }
16473 const alignment = @intCast(u29, alignment_val.toUnsignedInt(target));
16474 if (alignment == target_util.defaultFunctionAlignment(target)) {
16475 break :alignment 0;
16476 } else {
16477 break :alignment alignment;
16478 }
16479 };
1635716480 var buf: Value.ToTypeBuffer = undefined;
1635816481
16359 const args: []Value = if (args_val.castTag(.aggregate)) |some| some.data else &.{};
16482 const args_slice_val = args_val.castTag(.slice).?.data;
16483 const args_decl_index = args_slice_val.ptr.pointerDecl().?;
16484 try sema.ensureDeclAnalyzed(args_decl_index);
16485 const args_decl = mod.declPtr(args_decl_index);
16486 const args: []Value = if (args_decl.val.castTag(.aggregate)) |some| some.data else &.{};
1636016487 var param_types = try sema.arena.alloc(Type, args.len);
1636116488 var comptime_params = try sema.arena.alloc(bool, args.len);
1636216489 var noalias_bits: u32 = 0;
......@@ -16778,7 +16905,7 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat
1677816905 try sema.addDeclaredHereNote(msg, dest_ty);
1677916906 break :msg msg;
1678016907 };
16781 return sema.failWithOwnedErrorMsg(block, msg);
16908 return sema.failWithOwnedErrorMsg(msg);
1678216909 }
1678316910
1678416911 if (maybe_operand_val) |val| {
......@@ -16796,7 +16923,7 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat
1679616923 try sema.addDeclaredHereNote(msg, dest_ty);
1679716924 break :msg msg;
1679816925 };
16799 return sema.failWithOwnedErrorMsg(block, msg);
16926 return sema.failWithOwnedErrorMsg(msg);
1680016927 }
1680116928 }
1680216929
......@@ -16934,7 +17061,7 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1693417061 });
1693517062 break :msg msg;
1693617063 };
16937 return sema.failWithOwnedErrorMsg(block, msg);
17064 return sema.failWithOwnedErrorMsg(msg);
1693817065 }
1693917066 }
1694017067
......@@ -17225,7 +17352,7 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6
1722517352 try sema.addDeclaredHereNote(msg, ty);
1722617353 break :msg msg;
1722717354 };
17228 return sema.failWithOwnedErrorMsg(block, msg);
17355 return sema.failWithOwnedErrorMsg(msg);
1722917356 },
1723017357 }
1723117358
......@@ -17329,7 +17456,7 @@ fn checkPtrOperand(
1732917456
1733017457 break :msg msg;
1733117458 };
17332 return sema.failWithOwnedErrorMsg(block, msg);
17459 return sema.failWithOwnedErrorMsg(msg);
1733317460 },
1733417461 .Optional => if (ty.isPtrLikeOptional()) return,
1733517462 else => {},
......@@ -17359,7 +17486,7 @@ fn checkPtrType(
1735917486
1736017487 break :msg msg;
1736117488 };
17362 return sema.failWithOwnedErrorMsg(block, msg);
17489 return sema.failWithOwnedErrorMsg(msg);
1736317490 },
1736417491 .Optional => if (ty.isPtrLikeOptional()) return,
1736517492 else => {},
......@@ -17495,7 +17622,7 @@ fn checkComptimeVarStore(
1749517622 try sema.errNote(block, cond_src, msg, "runtime condition here", .{});
1749617623 break :msg msg;
1749717624 };
17498 return sema.failWithOwnedErrorMsg(block, msg);
17625 return sema.failWithOwnedErrorMsg(msg);
1749917626 }
1750017627 if (block.runtime_loop) |loop_src| {
1750117628 const msg = msg: {
......@@ -17504,7 +17631,7 @@ fn checkComptimeVarStore(
1750417631 try sema.errNote(block, loop_src, msg, "non-inline loop here", .{});
1750517632 break :msg msg;
1750617633 };
17507 return sema.failWithOwnedErrorMsg(block, msg);
17634 return sema.failWithOwnedErrorMsg(msg);
1750817635 }
1750917636 unreachable;
1751017637 }
......@@ -17641,7 +17768,7 @@ fn checkVectorizableBinaryOperands(
1764117768 try sema.errNote(block, rhs_src, msg, "length {d} here", .{rhs_len});
1764217769 break :msg msg;
1764317770 };
17644 return sema.failWithOwnedErrorMsg(block, msg);
17771 return sema.failWithOwnedErrorMsg(msg);
1764517772 }
1764617773 } else {
1764717774 const msg = msg: {
......@@ -17658,7 +17785,7 @@ fn checkVectorizableBinaryOperands(
1765817785 }
1765917786 break :msg msg;
1766017787 };
17661 return sema.failWithOwnedErrorMsg(block, msg);
17788 return sema.failWithOwnedErrorMsg(msg);
1766217789 }
1766317790}
1766417791
......@@ -18058,7 +18185,7 @@ fn analyzeShuffle(
1805818185
1805918186 break :msg msg;
1806018187 };
18061 return sema.failWithOwnedErrorMsg(block, msg);
18188 return sema.failWithOwnedErrorMsg(msg);
1806218189 }
1806318190 }
1806418191
......@@ -18654,7 +18781,7 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
1865418781 try sema.addDeclaredHereNote(msg, struct_ty);
1865518782 break :msg msg;
1865618783 };
18657 return sema.failWithOwnedErrorMsg(block, msg);
18784 return sema.failWithOwnedErrorMsg(msg);
1865818785 }
1865918786 return sema.addConstant(result_ptr, payload.data.container_ptr);
1866018787 }
......@@ -18883,10 +19010,8 @@ fn zirVarExtended(
1888319010 extended: Zir.Inst.Extended.InstData,
1888419011) CompileError!Air.Inst.Ref {
1888519012 const extra = sema.code.extraData(Zir.Inst.ExtendedVar, extended.operand);
18886 const src = sema.src;
18887 const ty_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at type
18888 const name_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at the name token
18889 const init_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at init expr
19013 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = 0 };
19014 const init_src: LazySrcLoc = .{ .node_offset_var_decl_init = 0 };
1889019015 const small = @bitCast(Zir.Inst.ExtendedVar.Small, extended.small);
1889119016
1889219017 var extra_index: usize = extra.end;
......@@ -18900,12 +19025,6 @@ fn zirVarExtended(
1890019025 // ZIR supports encoding this information but it is not used; the information
1890119026 // is encoded via the Decl entry.
1890219027 assert(!small.has_align);
18903 //const align_val: Value = if (small.has_align) blk: {
18904 // const align_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
18905 // extra_index += 1;
18906 // const align_tv = try sema.resolveInstConst(block, align_src, align_ref);
18907 // break :blk align_tv.val;
18908 //} else Value.@"null";
1890919028
1891019029 const uncasted_init: Air.Inst.Ref = if (small.has_init) blk: {
1891119030 const init_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
......@@ -18929,7 +19048,7 @@ fn zirVarExtended(
1892919048 return sema.failWithNeededComptime(block, init_src, "container level variable initializers must be comptime known");
1893019049 } else Value.initTag(.unreachable_value);
1893119050
18932 try sema.validateVarType(block, name_src, var_ty, small.is_extern);
19051 try sema.validateVarType(block, ty_src, var_ty, small.is_extern);
1893319052
1893419053 const new_var = try sema.gpa.create(Module.Var);
1893519054 errdefer sema.gpa.destroy(new_var);
......@@ -19462,7 +19581,7 @@ fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src:
1946219581 }
1946319582 break :msg msg;
1946419583 };
19465 return sema.failWithOwnedErrorMsg(block, msg);
19584 return sema.failWithOwnedErrorMsg(msg);
1946619585 }
1946719586 try sema.requireFunctionBlock(block, src);
1946819587}
......@@ -19491,7 +19610,7 @@ fn validateVarType(
1949119610
1949219611 break :msg msg;
1949319612 };
19494 return sema.failWithOwnedErrorMsg(block, msg);
19613 return sema.failWithOwnedErrorMsg(msg);
1949519614}
1949619615
1949719616fn validateRunTimeType(
......@@ -19668,7 +19787,7 @@ const ExternPosition = enum {
1966819787
1966919788/// Returns true if `ty` is allowed in extern types.
1967019789/// Does *NOT* require `ty` to be resolved in any way.
19671fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) CompileError!bool {
19790fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) bool {
1967219791 switch (ty.zigTypeTag()) {
1967319792 .Type,
1967419793 .ComptimeFloat,
......@@ -19713,8 +19832,6 @@ fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) CompileEr
1971319832
1971419833fn explainWhyTypeIsNotExtern(
1971519834 sema: *Sema,
19716 block: *Block,
19717 src: LazySrcLoc,
1971819835 msg: *Module.ErrorMsg,
1971919836 src_loc: Module.SrcLoc,
1972019837 ty: Type,
......@@ -19758,7 +19875,7 @@ fn explainWhyTypeIsNotExtern(
1975819875 var buf: Type.Payload.Bits = undefined;
1975919876 const tag_ty = ty.intTagType(&buf);
1976019877 try mod.errNoteNonLazy(src_loc, msg, "enum tag type '{}' is not extern compatible", .{tag_ty.fmt(sema.mod)});
19761 try sema.explainWhyTypeIsNotExtern(block, src, msg, src_loc, tag_ty, position);
19878 try sema.explainWhyTypeIsNotExtern(msg, src_loc, tag_ty, position);
1976219879 },
1976319880 .Struct => try mod.errNoteNonLazy(src_loc, msg, "only structs with packed or extern layout are extern compatible", .{}),
1976419881 .Union => try mod.errNoteNonLazy(src_loc, msg, "only unions with packed or extern layout are extern compatible", .{}),
......@@ -19768,13 +19885,87 @@ fn explainWhyTypeIsNotExtern(
1976819885 } else if (position == .param_ty) {
1976919886 return mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a parameter type", .{});
1977019887 }
19771 try sema.explainWhyTypeIsNotExtern(block, src, msg, src_loc, ty.elemType2(), position);
19888 try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(), position);
1977219889 },
19773 .Vector => try sema.explainWhyTypeIsNotExtern(block, src, msg, src_loc, ty.elemType2(), position),
19890 .Vector => try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(), position),
1977419891 .Optional => try mod.errNoteNonLazy(src_loc, msg, "only pointer like optionals are extern compatible", .{}),
1977519892 }
1977619893}
1977719894
19895/// Returns true if `ty` is allowed in packed types.
19896/// Does *NOT* require `ty` to be resolved in any way.
19897fn validatePackedType(ty: Type) bool {
19898 switch (ty.zigTypeTag()) {
19899 .Type,
19900 .ComptimeFloat,
19901 .ComptimeInt,
19902 .EnumLiteral,
19903 .Undefined,
19904 .Null,
19905 .ErrorUnion,
19906 .ErrorSet,
19907 .BoundFn,
19908 .Frame,
19909 .NoReturn,
19910 .Opaque,
19911 .AnyFrame,
19912 .Fn,
19913 .Array,
19914 .Optional,
19915 => return false,
19916 .Void,
19917 .Bool,
19918 .Float,
19919 .Pointer,
19920 .Int,
19921 .Vector,
19922 .Enum,
19923 => return true,
19924 .Struct, .Union => return ty.containerLayout() == .Packed,
19925 }
19926}
19927
19928fn explainWhyTypeIsNotPacked(
19929 sema: *Sema,
19930 msg: *Module.ErrorMsg,
19931 src_loc: Module.SrcLoc,
19932 ty: Type,
19933) CompileError!void {
19934 const mod = sema.mod;
19935 switch (ty.zigTypeTag()) {
19936 .Void,
19937 .Bool,
19938 .Float,
19939 .Pointer,
19940 .Int,
19941 .Vector,
19942 .Enum,
19943 => return,
19944 .Type,
19945 .ComptimeFloat,
19946 .ComptimeInt,
19947 .EnumLiteral,
19948 .Undefined,
19949 .Null,
19950 .BoundFn,
19951 .Frame,
19952 .NoReturn,
19953 .Opaque,
19954 .ErrorUnion,
19955 .ErrorSet,
19956 .AnyFrame,
19957 .Optional,
19958 .Array,
19959 => try mod.errNoteNonLazy(src_loc, msg, "type has no guaranteed in-memory representation", .{}),
19960 .Fn => {
19961 try mod.errNoteNonLazy(src_loc, msg, "type has no guaranteed in-memory representation", .{});
19962 try mod.errNoteNonLazy(src_loc, msg, "use '*const ' to make a function pointer type", .{});
19963 },
19964 .Struct => try mod.errNoteNonLazy(src_loc, msg, "only packed structs layout are allowed in packed types", .{}),
19965 .Union => try mod.errNoteNonLazy(src_loc, msg, "only packed unions layout are allowed in packed types", .{}),
19966 }
19967}
19968
1977819969pub const PanicId = enum {
1977919970 unreach,
1978019971 unwrap_null,
......@@ -19994,6 +20185,77 @@ fn panicIndexOutOfBounds(
1999420185 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
1999520186}
1999620187
20188fn panicSentinelMismatch(
20189 sema: *Sema,
20190 parent_block: *Block,
20191 src: LazySrcLoc,
20192 maybe_sentinel: ?Value,
20193 sentinel_ty: Type,
20194 ptr: Air.Inst.Ref,
20195 sentinel_index: Air.Inst.Ref,
20196) !void {
20197 const expected_sentinel_val = maybe_sentinel orelse return;
20198 const expected_sentinel = try sema.addConstant(sentinel_ty, expected_sentinel_val);
20199
20200 const ptr_ty = sema.typeOf(ptr);
20201 const actual_sentinel = if (ptr_ty.isSlice())
20202 try parent_block.addBinOp(.slice_elem_val, ptr, sentinel_index)
20203 else blk: {
20204 const elem_ptr_ty = try sema.elemPtrType(ptr_ty, null);
20205 const sentinel_ptr = try parent_block.addPtrElemPtr(ptr, sentinel_index, elem_ptr_ty);
20206 break :blk try parent_block.addTyOp(.load, sentinel_ty, sentinel_ptr);
20207 };
20208
20209 const ok = if (sentinel_ty.zigTypeTag() == .Vector) ok: {
20210 const eql =
20211 try parent_block.addCmpVector(expected_sentinel, actual_sentinel, .eq, try sema.addType(sentinel_ty));
20212 break :ok try parent_block.addInst(.{
20213 .tag = .reduce,
20214 .data = .{ .reduce = .{
20215 .operand = eql,
20216 .operation = .And,
20217 } },
20218 });
20219 } else if (sentinel_ty.isSelfComparable(true))
20220 try parent_block.addBinOp(.cmp_eq, expected_sentinel, actual_sentinel)
20221 else {
20222 const panic_fn = try sema.getBuiltin(parent_block, src, "checkNonScalarSentinel");
20223 const args: [2]Air.Inst.Ref = .{ expected_sentinel, actual_sentinel };
20224 _ = try sema.analyzeCall(parent_block, panic_fn, src, src, .auto, false, &args, null);
20225 return;
20226 };
20227 const gpa = sema.gpa;
20228
20229 var fail_block: Block = .{
20230 .parent = parent_block,
20231 .sema = sema,
20232 .src_decl = parent_block.src_decl,
20233 .namespace = parent_block.namespace,
20234 .wip_capture_scope = parent_block.wip_capture_scope,
20235 .instructions = .{},
20236 .inlining = parent_block.inlining,
20237 .is_comptime = parent_block.is_comptime,
20238 };
20239
20240 defer fail_block.instructions.deinit(gpa);
20241
20242 {
20243 const this_feature_is_implemented_in_the_backend =
20244 sema.mod.comp.bin_file.options.use_llvm;
20245
20246 if (!this_feature_is_implemented_in_the_backend) {
20247 // TODO implement this feature in all the backends and then delete this branch
20248 _ = try fail_block.addNoOp(.breakpoint);
20249 _ = try fail_block.addNoOp(.unreach);
20250 } else {
20251 const panic_fn = try sema.getBuiltin(&fail_block, src, "panicSentinelMismatch");
20252 const args: [2]Air.Inst.Ref = .{ expected_sentinel, actual_sentinel };
20253 _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args, null);
20254 }
20255 }
20256 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
20257}
20258
1999720259fn safetyPanic(
1999820260 sema: *Sema,
1999920261 block: *Block,
......@@ -20050,7 +20312,7 @@ fn emitBackwardBranch(sema: *Sema, block: *Block, src: LazySrcLoc) !void {
2005020312 "use @setEvalBranchQuota() to raise the branch limit from {d}",
2005120313 .{sema.branch_quota},
2005220314 );
20053 return sema.failWithOwnedErrorMsg(block, msg);
20315 return sema.failWithOwnedErrorMsg(msg);
2005420316 }
2005520317}
2005620318
......@@ -20158,7 +20420,7 @@ fn fieldVal(
2015820420 try sema.addDeclaredHereNote(msg, child_type);
2015920421 break :msg msg;
2016020422 };
20161 return sema.failWithOwnedErrorMsg(block, msg);
20423 return sema.failWithOwnedErrorMsg(msg);
2016220424 } else (try sema.mod.getErrorValue(field_name)).key;
2016320425
2016420426 return sema.addConstant(
......@@ -20213,7 +20475,7 @@ fn fieldVal(
2021320475 if (child_type.zigTypeTag() == .Array) try sema.errNote(block, src, msg, "array values have 'len' member", .{});
2021420476 break :msg msg;
2021520477 };
20216 return sema.failWithOwnedErrorMsg(block, msg);
20478 return sema.failWithOwnedErrorMsg(msg);
2021720479 },
2021820480 }
2021920481 },
......@@ -20551,7 +20813,7 @@ fn fieldCallBind(
2055120813 try sema.addDeclaredHereNote(msg, concrete_ty);
2055220814 break :msg msg;
2055320815 };
20554 return sema.failWithOwnedErrorMsg(block, msg);
20816 return sema.failWithOwnedErrorMsg(msg);
2055520817}
2055620818
2055720819fn finishFieldCallBind(
......@@ -20606,7 +20868,7 @@ fn namespaceLookup(
2060620868 try sema.mod.errNoteNonLazy(decl.srcLoc(), msg, "declared here", .{});
2060720869 break :msg msg;
2060820870 };
20609 return sema.failWithOwnedErrorMsg(block, msg);
20871 return sema.failWithOwnedErrorMsg(msg);
2061020872 }
2061120873 return decl_index;
2061220874 }
......@@ -20621,7 +20883,14 @@ fn namespaceLookupRef(
2062120883 decl_name: []const u8,
2062220884) CompileError!?Air.Inst.Ref {
2062320885 const decl = (try sema.namespaceLookup(block, src, namespace, decl_name)) orelse return null;
20624 return try sema.analyzeDeclRef(decl);
20886 return sema.analyzeDeclRef(decl) catch |err| switch (err) {
20887 error.AnalysisFail => {
20888 const msg = sema.err orelse return err;
20889 try sema.errNote(block, src, msg, "referenced here", .{});
20890 return err;
20891 },
20892 else => return err,
20893 };
2062520894}
2062620895
2062720896fn namespaceLookupVal(
......@@ -20939,7 +21208,7 @@ fn unionFieldPtr(
2093921208 try sema.addDeclaredHereNote(msg, union_ty);
2094021209 break :msg msg;
2094121210 };
20942 return sema.failWithOwnedErrorMsg(block, msg);
21211 return sema.failWithOwnedErrorMsg(msg);
2094321212 }
2094421213 },
2094521214 .Packed, .Extern => {},
......@@ -21009,7 +21278,7 @@ fn unionFieldVal(
2100921278 try sema.addDeclaredHereNote(msg, union_ty);
2101021279 break :msg msg;
2101121280 };
21012 return sema.failWithOwnedErrorMsg(block, msg);
21281 return sema.failWithOwnedErrorMsg(msg);
2101321282 }
2101421283 },
2101521284 .Packed, .Extern => {
......@@ -21183,7 +21452,7 @@ fn validateRuntimeElemAccess(
2118321452
2118421453 break :msg msg;
2118521454 };
21186 return sema.failWithOwnedErrorMsg(block, msg);
21455 return sema.failWithOwnedErrorMsg(msg);
2118721456 }
2118821457}
2118921458
......@@ -21959,7 +22228,7 @@ fn coerceExtra(
2195922228 try sema.addDeclaredHereNote(msg, dest_ty);
2196022229 break :msg msg;
2196122230 };
21962 return sema.failWithOwnedErrorMsg(block, msg);
22231 return sema.failWithOwnedErrorMsg(msg);
2196322232 };
2196422233 return sema.addConstant(
2196522234 dest_ty,
......@@ -22087,7 +22356,7 @@ fn coerceExtra(
2208722356 try sema.mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl), msg, "'noreturn' declared here", .{});
2208822357 break :msg msg;
2208922358 };
22090 return sema.failWithOwnedErrorMsg(block, msg);
22359 return sema.failWithOwnedErrorMsg(msg);
2209122360 }
2209222361
2209322362 const msg = msg: {
......@@ -22128,7 +22397,7 @@ fn coerceExtra(
2212822397
2212922398 break :msg msg;
2213022399 };
22131 return sema.failWithOwnedErrorMsg(block, msg);
22400 return sema.failWithOwnedErrorMsg(msg);
2213222401}
2213322402
2213422403const InMemoryCoercionResult = union(enum) {
......@@ -24193,7 +24462,7 @@ fn coerceEnumToUnion(
2419324462 try sema.addDeclaredHereNote(msg, union_ty);
2419424463 break :msg msg;
2419524464 };
24196 return sema.failWithOwnedErrorMsg(block, msg);
24465 return sema.failWithOwnedErrorMsg(msg);
2419724466 };
2419824467
2419924468 const enum_tag = try sema.coerce(block, tag_ty, inst, inst_src);
......@@ -24208,7 +24477,7 @@ fn coerceEnumToUnion(
2420824477 try sema.addDeclaredHereNote(msg, union_ty);
2420924478 break :msg msg;
2421024479 };
24211 return sema.failWithOwnedErrorMsg(block, msg);
24480 return sema.failWithOwnedErrorMsg(msg);
2421224481 };
2421324482 const field = union_obj.fields.values()[field_index];
2421424483 const field_ty = try sema.resolveTypeFields(block, inst_src, field.ty);
......@@ -24224,7 +24493,7 @@ fn coerceEnumToUnion(
2422424493 try sema.addDeclaredHereNote(msg, union_ty);
2422524494 break :msg msg;
2422624495 };
24227 return sema.failWithOwnedErrorMsg(block, msg);
24496 return sema.failWithOwnedErrorMsg(msg);
2422824497 };
2422924498
2423024499 return sema.addConstant(union_ty, try Value.Tag.@"union".create(sema.arena, .{
......@@ -24244,7 +24513,7 @@ fn coerceEnumToUnion(
2424424513 try sema.addDeclaredHereNote(msg, tag_ty);
2424524514 break :msg msg;
2424624515 };
24247 return sema.failWithOwnedErrorMsg(block, msg);
24516 return sema.failWithOwnedErrorMsg(msg);
2424824517 }
2424924518
2425024519 // If the union has all fields 0 bits, the union value is just the enum value.
......@@ -24273,7 +24542,7 @@ fn coerceEnumToUnion(
2427324542 try sema.addDeclaredHereNote(msg, union_ty);
2427424543 break :msg msg;
2427524544 };
24276 return sema.failWithOwnedErrorMsg(block, msg);
24545 return sema.failWithOwnedErrorMsg(msg);
2427724546}
2427824547
2427924548fn coerceAnonStructToUnion(
......@@ -24302,7 +24571,7 @@ fn coerceAnonStructToUnion(
2430224571 try sema.addDeclaredHereNote(msg, union_ty);
2430324572 break :msg msg;
2430424573 };
24305 return sema.failWithOwnedErrorMsg(block, msg);
24574 return sema.failWithOwnedErrorMsg(msg);
2430624575 }
2430724576
2430824577 const field_name = anon_struct.names[0];
......@@ -24362,7 +24631,7 @@ fn coerceArrayLike(
2436224631 try sema.errNote(block, inst_src, msg, "source has length {d}", .{inst_len});
2436324632 break :msg msg;
2436424633 };
24365 return sema.failWithOwnedErrorMsg(block, msg);
24634 return sema.failWithOwnedErrorMsg(msg);
2436624635 }
2436724636
2436824637 const dest_elem_ty = dest_ty.childType();
......@@ -24434,7 +24703,7 @@ fn coerceTupleToArray(
2443424703 try sema.errNote(block, inst_src, msg, "source has length {d}", .{inst_len});
2443524704 break :msg msg;
2443624705 };
24437 return sema.failWithOwnedErrorMsg(block, msg);
24706 return sema.failWithOwnedErrorMsg(msg);
2443824707 }
2443924708
2444024709 const dest_elems = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLenIncludingSentinel());
......@@ -24597,7 +24866,7 @@ fn coerceTupleToStruct(
2459724866
2459824867 if (root_msg) |msg| {
2459924868 try sema.addDeclaredHereNote(msg, struct_ty);
24600 return sema.failWithOwnedErrorMsg(block, msg);
24869 return sema.failWithOwnedErrorMsg(msg);
2460124870 }
2460224871
2460324872 if (runtime_src) |rs| {
......@@ -24701,7 +24970,7 @@ fn coerceTupleToTuple(
2470124970
2470224971 if (root_msg) |msg| {
2470324972 try sema.addDeclaredHereNote(msg, tuple_ty);
24704 return sema.failWithOwnedErrorMsg(block, msg);
24973 return sema.failWithOwnedErrorMsg(msg);
2470524974 }
2470624975
2470724976 if (runtime_src) |rs| {
......@@ -24724,7 +24993,14 @@ fn analyzeDeclVal(
2472424993 if (sema.decl_val_table.get(decl_index)) |result| {
2472524994 return result;
2472624995 }
24727 const decl_ref = try sema.analyzeDeclRef(decl_index);
24996 const decl_ref = sema.analyzeDeclRef(decl_index) catch |err| switch (err) {
24997 error.AnalysisFail => {
24998 const msg = sema.err orelse return err;
24999 try sema.errNote(block, src, msg, "referenced here", .{});
25000 return err;
25001 },
25002 else => return err,
25003 };
2472825004 const result = try sema.analyzeLoad(block, src, decl_ref, src);
2472925005 if (Air.refToIndex(result)) |index| {
2473025006 if (sema.air_instructions.items(.tag)[index] == .constant and !block.is_typeof) {
......@@ -24735,6 +25011,12 @@ fn analyzeDeclVal(
2473525011}
2473625012
2473725013fn ensureDeclAnalyzed(sema: *Sema, decl_index: Decl.Index) CompileError!void {
25014 const decl = sema.mod.declPtr(decl_index);
25015 if (decl.analysis == .in_progress) {
25016 const msg = try Module.ErrorMsg.create(sema.gpa, decl.srcLoc(), "dependency loop detected", .{});
25017 return sema.failWithOwnedErrorMsg(msg);
25018 }
25019
2473825020 sema.mod.ensureDeclAnalyzed(decl_index) catch |err| {
2473925021 if (sema.owner_func) |owner_func| {
2474025022 owner_func.state = .dependency_failure;
......@@ -25214,6 +25496,7 @@ fn analyzeSlice(
2521425496 }
2521525497 break :s null;
2521625498 };
25499 const slice_sentinel = if (sentinel_opt != .none) sentinel else null;
2521725500
2521825501 // requirement: start <= end
2521925502 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
......@@ -25265,7 +25548,7 @@ fn analyzeSlice(
2526525548
2526625549 break :msg msg;
2526725550 };
25268 return sema.failWithOwnedErrorMsg(block, msg);
25551 return sema.failWithOwnedErrorMsg(msg);
2526925552 }
2527025553 }
2527125554 }
......@@ -25293,7 +25576,12 @@ fn analyzeSlice(
2529325576
2529425577 const opt_new_ptr_val = try sema.resolveMaybeUndefVal(block, ptr_src, new_ptr);
2529525578 const new_ptr_val = opt_new_ptr_val orelse {
25296 return block.addBitCast(return_ty, new_ptr);
25579 const result = try block.addBitCast(return_ty, new_ptr);
25580 if (block.wantSafety()) {
25581 // requirement: result[new_len] == slice_sentinel
25582 try sema.panicSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len);
25583 }
25584 return result;
2529725585 };
2529825586
2529925587 if (!new_ptr_val.isUndef()) {
......@@ -25357,7 +25645,7 @@ fn analyzeSlice(
2535725645 // requirement: start <= end
2535825646 try sema.panicIndexOutOfBounds(block, src, start, end, .cmp_lte);
2535925647 }
25360 return block.addInst(.{
25648 const result = try block.addInst(.{
2536125649 .tag = .slice,
2536225650 .data = .{ .ty_pl = .{
2536325651 .ty = try sema.addType(return_ty),
......@@ -25367,6 +25655,11 @@ fn analyzeSlice(
2536725655 }),
2536825656 } },
2536925657 });
25658 if (block.wantSafety()) {
25659 // requirement: result[new_len] == slice_sentinel
25660 try sema.panicSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len);
25661 }
25662 return result;
2537025663}
2537125664
2537225665/// Asserts that lhs and rhs types are both numeric.
......@@ -26204,7 +26497,7 @@ fn resolvePeerTypes(
2620426497
2620526498 break :msg msg;
2620626499 };
26207 return sema.failWithOwnedErrorMsg(block, msg);
26500 return sema.failWithOwnedErrorMsg(msg);
2620826501 }
2620926502
2621026503 const chosen_ty = sema.typeOf(chosen);
......@@ -26363,21 +26656,41 @@ fn resolveStructLayout(
2636326656 switch (struct_obj.status) {
2636426657 .none, .have_field_types => {},
2636526658 .field_types_wip, .layout_wip => {
26366 return sema.fail(block, src, "struct '{}' depends on itself", .{ty.fmt(sema.mod)});
26659 const msg = try Module.ErrorMsg.create(
26660 sema.gpa,
26661 struct_obj.srcLoc(sema.mod),
26662 "struct '{}' depends on itself",
26663 .{ty.fmt(sema.mod)},
26664 );
26665 return sema.failWithOwnedErrorMsg(msg);
2636726666 },
2636826667 .have_layout, .fully_resolved_wip, .fully_resolved => return,
2636926668 }
2637026669 struct_obj.status = .layout_wip;
26371 for (struct_obj.fields.values()) |field| {
26372 try sema.resolveTypeLayout(block, src, field.ty);
26670 for (struct_obj.fields.values()) |field, i| {
26671 sema.resolveTypeLayout(block, src, field.ty) catch |err| switch (err) {
26672 error.AnalysisFail => {
26673 const msg = sema.err orelse return err;
26674 try sema.addFieldErrNote(block, ty, i, msg, "while checking this field", .{});
26675 return err;
26676 },
26677 else => return err,
26678 };
2637326679 }
2637426680 struct_obj.status = .have_layout;
2637526681
2637626682 // In case of querying the ABI alignment of this struct, we will ask
2637726683 // for hasRuntimeBits() of each field, so we need "requires comptime"
2637826684 // to be known already before this function returns.
26379 for (struct_obj.fields.values()) |field| {
26380 _ = try sema.typeRequiresComptime(block, src, field.ty);
26685 for (struct_obj.fields.values()) |field, i| {
26686 _ = sema.typeRequiresComptime(block, src, field.ty) catch |err| switch (err) {
26687 error.AnalysisFail => {
26688 const msg = sema.err orelse return err;
26689 try sema.addFieldErrNote(block, ty, i, msg, "while checking this field", .{});
26690 return err;
26691 },
26692 else => return err,
26693 };
2638126694 }
2638226695 }
2638326696 // otherwise it's a tuple; no need to resolve anything
......@@ -26394,13 +26707,26 @@ fn resolveUnionLayout(
2639426707 switch (union_obj.status) {
2639526708 .none, .have_field_types => {},
2639626709 .field_types_wip, .layout_wip => {
26397 return sema.fail(block, src, "union '{}' depends on itself", .{ty.fmt(sema.mod)});
26710 const msg = try Module.ErrorMsg.create(
26711 sema.gpa,
26712 union_obj.srcLoc(sema.mod),
26713 "union '{}' depends on itself",
26714 .{ty.fmt(sema.mod)},
26715 );
26716 return sema.failWithOwnedErrorMsg(msg);
2639826717 },
2639926718 .have_layout, .fully_resolved_wip, .fully_resolved => return,
2640026719 }
2640126720 union_obj.status = .layout_wip;
26402 for (union_obj.fields.values()) |field| {
26403 try sema.resolveTypeLayout(block, src, field.ty);
26721 for (union_obj.fields.values()) |field, i| {
26722 sema.resolveTypeLayout(block, src, field.ty) catch |err| switch (err) {
26723 error.AnalysisFail => {
26724 const msg = sema.err orelse return err;
26725 try sema.addFieldErrNote(block, ty, i, msg, "while checking this field", .{});
26726 return err;
26727 },
26728 else => return err,
26729 };
2640426730 }
2640526731 union_obj.status = .have_layout;
2640626732}
......@@ -26528,12 +26854,12 @@ pub fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type)
2652826854 switch (ty.tag()) {
2652926855 .@"struct" => {
2653026856 const struct_obj = ty.castTag(.@"struct").?.data;
26531 try sema.resolveTypeFieldsStruct(block, src, ty, struct_obj);
26857 try sema.resolveTypeFieldsStruct(ty, struct_obj);
2653226858 return ty;
2653326859 },
2653426860 .@"union", .union_safety_tagged, .union_tagged => {
2653526861 const union_obj = ty.cast(Type.Payload.Union).?.data;
26536 try sema.resolveTypeFieldsUnion(block, src, ty, union_obj);
26862 try sema.resolveTypeFieldsUnion(ty, union_obj);
2653726863 return ty;
2653826864 },
2653926865 .type_info => return sema.resolveBuiltinTypeFields(block, src, "Type"),
......@@ -26554,15 +26880,19 @@ pub fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type)
2655426880
2655526881fn resolveTypeFieldsStruct(
2655626882 sema: *Sema,
26557 block: *Block,
26558 src: LazySrcLoc,
2655926883 ty: Type,
2656026884 struct_obj: *Module.Struct,
2656126885) CompileError!void {
2656226886 switch (struct_obj.status) {
2656326887 .none => {},
2656426888 .field_types_wip => {
26565 return sema.fail(block, src, "struct '{}' depends on itself", .{ty.fmt(sema.mod)});
26889 const msg = try Module.ErrorMsg.create(
26890 sema.gpa,
26891 struct_obj.srcLoc(sema.mod),
26892 "struct '{}' depends on itself",
26893 .{ty.fmt(sema.mod)},
26894 );
26895 return sema.failWithOwnedErrorMsg(msg);
2656626896 },
2656726897 .have_field_types,
2656826898 .have_layout,
......@@ -26576,17 +26906,17 @@ fn resolveTypeFieldsStruct(
2657626906 try semaStructFields(sema.mod, struct_obj);
2657726907}
2657826908
26579fn resolveTypeFieldsUnion(
26580 sema: *Sema,
26581 block: *Block,
26582 src: LazySrcLoc,
26583 ty: Type,
26584 union_obj: *Module.Union,
26585) CompileError!void {
26909fn resolveTypeFieldsUnion(sema: *Sema, ty: Type, union_obj: *Module.Union) CompileError!void {
2658626910 switch (union_obj.status) {
2658726911 .none => {},
2658826912 .field_types_wip => {
26589 return sema.fail(block, src, "union '{}' depends on itself", .{ty.fmt(sema.mod)});
26913 const msg = try Module.ErrorMsg.create(
26914 sema.gpa,
26915 union_obj.srcLoc(sema.mod),
26916 "union '{}' depends on itself",
26917 .{ty.fmt(sema.mod)},
26918 );
26919 return sema.failWithOwnedErrorMsg(msg);
2659026920 },
2659126921 .have_field_types,
2659226922 .have_layout,
......@@ -26802,7 +27132,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
2680227132 try sema.errNote(&block_scope, src, msg, "struct declared here", .{});
2680327133 break :msg msg;
2680427134 };
26805 return sema.failWithOwnedErrorMsg(&block_scope, msg);
27135 return sema.failWithOwnedErrorMsg(msg);
2680627136 }
2680727137 gop.value_ptr.* = .{
2680827138 .ty = Type.initTag(.noreturn),
......@@ -26851,31 +27181,44 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
2685127181 const field = &struct_obj.fields.values()[i];
2685227182 field.ty = try field_ty.copy(decl_arena_allocator);
2685327183
26854 if (struct_obj.layout == .Extern and !(try sema.validateExternType(field.ty, .other))) {
27184 if (field_ty.zigTypeTag() == .Opaque) {
27185 const msg = msg: {
27186 const tree = try sema.getAstTree(&block_scope);
27187 const field_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, i);
27188 const msg = try sema.errMsg(&block_scope, field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
27189 errdefer msg.destroy(sema.gpa);
27190
27191 try sema.addDeclaredHereNote(msg, field_ty);
27192 break :msg msg;
27193 };
27194 return sema.failWithOwnedErrorMsg(msg);
27195 }
27196 if (struct_obj.layout == .Extern and !sema.validateExternType(field.ty, .other)) {
2685527197 const msg = msg: {
2685627198 const tree = try sema.getAstTree(&block_scope);
2685727199 const fields_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, i);
2685827200 const msg = try sema.errMsg(&block_scope, fields_src, "extern structs cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)});
2685927201 errdefer msg.destroy(sema.gpa);
2686027202
26861 try sema.explainWhyTypeIsNotExtern(&block_scope, fields_src, msg, fields_src.toSrcLoc(decl), field.ty, .other);
27203 try sema.explainWhyTypeIsNotExtern(msg, fields_src.toSrcLoc(decl), field.ty, .other);
2686227204
2686327205 try sema.addDeclaredHereNote(msg, field.ty);
2686427206 break :msg msg;
2686527207 };
26866 return sema.failWithOwnedErrorMsg(&block_scope, msg);
26867 }
26868 if (field_ty.zigTypeTag() == .Opaque) {
27208 return sema.failWithOwnedErrorMsg(msg);
27209 } else if (struct_obj.layout == .Packed and !(validatePackedType(field.ty))) {
2686927210 const msg = msg: {
2687027211 const tree = try sema.getAstTree(&block_scope);
26871 const field_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, i);
26872 const msg = try sema.errMsg(&block_scope, field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
27212 const fields_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, i);
27213 const msg = try sema.errMsg(&block_scope, fields_src, "packed structs cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)});
2687327214 errdefer msg.destroy(sema.gpa);
2687427215
26875 try sema.addDeclaredHereNote(msg, field_ty);
27216 try sema.explainWhyTypeIsNotPacked(msg, fields_src.toSrcLoc(decl), field.ty);
27217
27218 try sema.addDeclaredHereNote(msg, field.ty);
2687627219 break :msg msg;
2687727220 };
26878 return sema.failWithOwnedErrorMsg(&block_scope, msg);
27221 return sema.failWithOwnedErrorMsg(msg);
2687927222 }
2688027223
2688127224 if (zir_field.align_body_len > 0) {
......@@ -27157,7 +27500,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
2715727500 try sema.errNote(&block_scope, src, msg, "union declared here", .{});
2715827501 break :msg msg;
2715927502 };
27160 return sema.failWithOwnedErrorMsg(&block_scope, msg);
27503 return sema.failWithOwnedErrorMsg(msg);
2716127504 }
2716227505
2716327506 if (tag_ty_field_names) |*names| {
......@@ -27171,35 +27514,48 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
2717127514 try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
2717227515 break :msg msg;
2717327516 };
27174 return sema.failWithOwnedErrorMsg(&block_scope, msg);
27517 return sema.failWithOwnedErrorMsg(msg);
2717527518 }
2717627519 }
2717727520
27178 if (union_obj.layout == .Extern and !(try sema.validateExternType(field_ty, .union_field))) {
27521 if (field_ty.zigTypeTag() == .Opaque) {
2717927522 const msg = msg: {
2718027523 const tree = try sema.getAstTree(&block_scope);
2718127524 const field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i);
27182 const msg = try sema.errMsg(&block_scope, field_src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)});
27525 const msg = try sema.errMsg(&block_scope, field_src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{});
2718327526 errdefer msg.destroy(sema.gpa);
2718427527
27185 try sema.explainWhyTypeIsNotExtern(&block_scope, field_src, msg, field_src.toSrcLoc(decl), field_ty, .union_field);
27186
2718727528 try sema.addDeclaredHereNote(msg, field_ty);
2718827529 break :msg msg;
2718927530 };
27190 return sema.failWithOwnedErrorMsg(&block_scope, msg);
27531 return sema.failWithOwnedErrorMsg(msg);
2719127532 }
27192 if (field_ty.zigTypeTag() == .Opaque) {
27533 if (union_obj.layout == .Extern and !sema.validateExternType(field_ty, .union_field)) {
2719327534 const msg = msg: {
2719427535 const tree = try sema.getAstTree(&block_scope);
2719527536 const field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i);
27196 const msg = try sema.errMsg(&block_scope, field_src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{});
27537 const msg = try sema.errMsg(&block_scope, field_src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)});
2719727538 errdefer msg.destroy(sema.gpa);
2719827539
27540 try sema.explainWhyTypeIsNotExtern(msg, field_src.toSrcLoc(decl), field_ty, .union_field);
27541
2719927542 try sema.addDeclaredHereNote(msg, field_ty);
2720027543 break :msg msg;
2720127544 };
27202 return sema.failWithOwnedErrorMsg(&block_scope, msg);
27545 return sema.failWithOwnedErrorMsg(msg);
27546 } else if (union_obj.layout == .Packed and !(validatePackedType(field_ty))) {
27547 const msg = msg: {
27548 const tree = try sema.getAstTree(&block_scope);
27549 const fields_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i);
27550 const msg = try sema.errMsg(&block_scope, fields_src, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)});
27551 errdefer msg.destroy(sema.gpa);
27552
27553 try sema.explainWhyTypeIsNotPacked(msg, fields_src.toSrcLoc(decl), field_ty);
27554
27555 try sema.addDeclaredHereNote(msg, field_ty);
27556 break :msg msg;
27557 };
27558 return sema.failWithOwnedErrorMsg(msg);
2720327559 }
2720427560
2720527561 gop.value_ptr.* = .{
......@@ -27231,7 +27587,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
2723127587 try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
2723227588 break :msg msg;
2723327589 };
27234 return sema.failWithOwnedErrorMsg(&block_scope, msg);
27590 return sema.failWithOwnedErrorMsg(msg);
2723527591 }
2723627592 }
2723727593}
......@@ -27494,9 +27850,19 @@ pub fn typeHasOnePossibleValue(
2749427850 .@"struct" => {
2749527851 const resolved_ty = try sema.resolveTypeFields(block, src, ty);
2749627852 const s = resolved_ty.castTag(.@"struct").?.data;
27497 for (s.fields.values()) |value| {
27498 if (value.is_comptime) continue;
27499 if ((try sema.typeHasOnePossibleValue(block, src, value.ty)) == null) {
27853 for (s.fields.values()) |field, i| {
27854 if (field.is_comptime) continue;
27855 if (field.ty.eql(resolved_ty, sema.mod)) {
27856 const msg = try Module.ErrorMsg.create(
27857 sema.gpa,
27858 s.srcLoc(sema.mod),
27859 "struct '{}' depends on itself",
27860 .{ty.fmt(sema.mod)},
27861 );
27862 try sema.addFieldErrNote(block, resolved_ty, i, msg, "while checking this field", .{});
27863 return sema.failWithOwnedErrorMsg(msg);
27864 }
27865 if ((try sema.typeHasOnePossibleValue(block, src, field.ty)) == null) {
2750027866 return null;
2750127867 }
2750227868 }
......@@ -27562,6 +27928,16 @@ pub fn typeHasOnePossibleValue(
2756227928 const tag_val = (try sema.typeHasOnePossibleValue(block, src, union_obj.tag_ty)) orelse
2756327929 return null;
2756427930 const only_field = union_obj.fields.values()[0];
27931 if (only_field.ty.eql(resolved_ty, sema.mod)) {
27932 const msg = try Module.ErrorMsg.create(
27933 sema.gpa,
27934 union_obj.srcLoc(sema.mod),
27935 "union '{}' depends on itself",
27936 .{ty.fmt(sema.mod)},
27937 );
27938 try sema.addFieldErrNote(block, resolved_ty, 0, msg, "while checking this field", .{});
27939 return sema.failWithOwnedErrorMsg(msg);
27940 }
2756527941 const val_val = (try sema.typeHasOnePossibleValue(block, src, only_field.ty)) orelse
2756627942 return null;
2756727943 // TODO make this not allocate. The function in `Type.onePossibleValue`
......@@ -28201,7 +28577,7 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ
2820128577 if (struct_obj.status == .field_types_wip)
2820228578 return false;
2820328579
28204 try sema.resolveTypeFieldsStruct(block, src, ty, struct_obj);
28580 try sema.resolveTypeFieldsStruct(ty, struct_obj);
2820528581
2820628582 struct_obj.requires_comptime = .wip;
2820728583 for (struct_obj.fields.values()) |field| {
......@@ -28226,7 +28602,7 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ
2822628602 if (union_obj.status == .field_types_wip)
2822728603 return false;
2822828604
28229 try sema.resolveTypeFieldsUnion(block, src, ty, union_obj);
28605 try sema.resolveTypeFieldsUnion(ty, union_obj);
2823028606
2823128607 union_obj.requires_comptime = .wip;
2823228608 for (union_obj.fields.values()) |field| {
src/Zir.zig+6
......@@ -3548,6 +3548,12 @@ pub const Inst = struct {
35483548 ty: Ref,
35493549 init_count: u32,
35503550 };
3551
3552 pub const Src = struct {
3553 node: i32,
3554 line: u32,
3555 column: u32,
3556 };
35513557};
35523558
35533559pub const SpecialProng = enum { none, @"else", under };
src/link/Dwarf.zig+18-26
......@@ -853,8 +853,7 @@ pub fn commitDeclState(
853853 .macho => {
854854 const macho_file = file.cast(File.MachO).?;
855855 const d_sym = &macho_file.d_sym.?;
856 const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
857 const debug_line_sect = &dwarf_segment.sections.items[d_sym.debug_line_section_index.?];
856 const debug_line_sect = &d_sym.sections.items[d_sym.debug_line_section_index.?];
858857 const file_pos = debug_line_sect.offset + src_fn.off;
859858 try pwriteDbgLineNops(d_sym.file, file_pos, 0, &[0]u8{}, src_fn.len);
860859 },
......@@ -933,8 +932,8 @@ pub fn commitDeclState(
933932 .macho => {
934933 const macho_file = file.cast(File.MachO).?;
935934 const d_sym = &macho_file.d_sym.?;
936 const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
937 const debug_line_sect = &dwarf_segment.sections.items[d_sym.debug_line_section_index.?];
935 const dwarf_segment = d_sym.segments.items[d_sym.dwarf_segment_cmd_index.?];
936 const debug_line_sect = &d_sym.sections.items[d_sym.debug_line_section_index.?];
938937 if (needed_size != debug_line_sect.size) {
939938 if (needed_size > d_sym.allocatedSize(debug_line_sect.offset)) {
940939 const new_offset = d_sym.findFreeSpace(needed_size, 1);
......@@ -955,10 +954,9 @@ pub fn commitDeclState(
955954 );
956955
957956 debug_line_sect.offset = @intCast(u32, new_offset);
958 debug_line_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;
957 debug_line_sect.addr = dwarf_segment.vmaddr + new_offset - dwarf_segment.fileoff;
959958 }
960959 debug_line_sect.size = needed_size;
961 d_sym.load_commands_dirty = true; // TODO look into making only the one section dirty
962960 d_sym.debug_line_header_dirty = true;
963961 }
964962 const file_pos = debug_line_sect.offset + src_fn.off;
......@@ -1137,8 +1135,7 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u3
11371135 .macho => {
11381136 const macho_file = file.cast(File.MachO).?;
11391137 const d_sym = &macho_file.d_sym.?;
1140 const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
1141 const debug_info_sect = &dwarf_segment.sections.items[d_sym.debug_info_section_index.?];
1138 const debug_info_sect = &d_sym.sections.items[d_sym.debug_info_section_index.?];
11421139 const file_pos = debug_info_sect.offset + atom.off;
11431140 try pwriteDbgInfoNops(d_sym.file, file_pos, 0, &[0]u8{}, atom.len, false);
11441141 },
......@@ -1235,8 +1232,8 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co
12351232 .macho => {
12361233 const macho_file = file.cast(File.MachO).?;
12371234 const d_sym = &macho_file.d_sym.?;
1238 const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
1239 const debug_info_sect = &dwarf_segment.sections.items[d_sym.debug_info_section_index.?];
1235 const dwarf_segment = d_sym.segments.items[d_sym.dwarf_segment_cmd_index.?];
1236 const debug_info_sect = &d_sym.sections.items[d_sym.debug_info_section_index.?];
12401237 if (needed_size != debug_info_sect.size) {
12411238 if (needed_size > d_sym.allocatedSize(debug_info_sect.offset)) {
12421239 const new_offset = d_sym.findFreeSpace(needed_size, 1);
......@@ -1257,10 +1254,9 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co
12571254 );
12581255
12591256 debug_info_sect.offset = @intCast(u32, new_offset);
1260 debug_info_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;
1257 debug_info_sect.addr = dwarf_segment.vmaddr + new_offset - dwarf_segment.fileoff;
12611258 }
12621259 debug_info_sect.size = needed_size;
1263 d_sym.load_commands_dirty = true; // TODO look into making only the one section dirty
12641260 d_sym.debug_line_header_dirty = true;
12651261 }
12661262 const file_pos = debug_info_sect.offset + atom.off;
......@@ -1330,8 +1326,7 @@ pub fn updateDeclLineNumber(self: *Dwarf, file: *File, decl: *const Module.Decl)
13301326 .macho => {
13311327 const macho_file = file.cast(File.MachO).?;
13321328 const d_sym = macho_file.d_sym.?;
1333 const dwarf_seg = d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
1334 const sect = dwarf_seg.sections.items[d_sym.debug_line_section_index.?];
1329 const sect = d_sym.sections.items[d_sym.debug_line_section_index.?];
13351330 const file_pos = sect.offset + decl.fn_link.macho.off + self.getRelocDbgLineOff();
13361331 try d_sym.file.pwriteAll(&data, file_pos);
13371332 },
......@@ -1557,14 +1552,14 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void {
15571552 .macho => {
15581553 const macho_file = file.cast(File.MachO).?;
15591554 const d_sym = &macho_file.d_sym.?;
1560 const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
1561 const debug_abbrev_sect = &dwarf_segment.sections.items[d_sym.debug_abbrev_section_index.?];
1555 const dwarf_segment = d_sym.segments.items[d_sym.dwarf_segment_cmd_index.?];
1556 const debug_abbrev_sect = &d_sym.sections.items[d_sym.debug_abbrev_section_index.?];
15621557 const allocated_size = d_sym.allocatedSize(debug_abbrev_sect.offset);
15631558 if (needed_size > allocated_size) {
15641559 debug_abbrev_sect.size = 0; // free the space
15651560 const offset = d_sym.findFreeSpace(needed_size, 1);
15661561 debug_abbrev_sect.offset = @intCast(u32, offset);
1567 debug_abbrev_sect.addr = dwarf_segment.inner.vmaddr + offset - dwarf_segment.inner.fileoff;
1562 debug_abbrev_sect.addr = dwarf_segment.vmaddr + offset - dwarf_segment.fileoff;
15681563 }
15691564 debug_abbrev_sect.size = needed_size;
15701565 log.debug("__debug_abbrev start=0x{x} end=0x{x}", .{
......@@ -1681,8 +1676,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, file: *File, module: *Module, low_pc: u6
16811676 .macho => {
16821677 const macho_file = file.cast(File.MachO).?;
16831678 const d_sym = &macho_file.d_sym.?;
1684 const dwarf_seg = d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
1685 const debug_info_sect = dwarf_seg.sections.items[d_sym.debug_info_section_index.?];
1679 const debug_info_sect = d_sym.sections.items[d_sym.debug_info_section_index.?];
16861680 const file_pos = debug_info_sect.offset;
16871681 try pwriteDbgInfoNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt, false);
16881682 },
......@@ -1998,13 +1992,13 @@ pub fn writeDbgAranges(self: *Dwarf, file: *File, addr: u64, size: u64) !void {
19981992 .macho => {
19991993 const macho_file = file.cast(File.MachO).?;
20001994 const d_sym = &macho_file.d_sym.?;
2001 const dwarf_seg = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
2002 const debug_aranges_sect = &dwarf_seg.sections.items[d_sym.debug_aranges_section_index.?];
1995 const dwarf_seg = d_sym.segments.items[d_sym.dwarf_segment_cmd_index.?];
1996 const debug_aranges_sect = &d_sym.sections.items[d_sym.debug_aranges_section_index.?];
20031997 const allocated_size = d_sym.allocatedSize(debug_aranges_sect.offset);
20041998 if (needed_size > allocated_size) {
20051999 debug_aranges_sect.size = 0; // free the space
20062000 const new_offset = d_sym.findFreeSpace(needed_size, 16);
2007 debug_aranges_sect.addr = dwarf_seg.inner.vmaddr + new_offset - dwarf_seg.inner.fileoff;
2001 debug_aranges_sect.addr = dwarf_seg.vmaddr + new_offset - dwarf_seg.fileoff;
20082002 debug_aranges_sect.offset = @intCast(u32, new_offset);
20092003 }
20102004 debug_aranges_sect.size = needed_size;
......@@ -2134,8 +2128,7 @@ pub fn writeDbgLineHeader(self: *Dwarf, file: *File, module: *Module) !void {
21342128 .macho => {
21352129 const macho_file = file.cast(File.MachO).?;
21362130 const d_sym = &macho_file.d_sym.?;
2137 const dwarf_seg = d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
2138 const debug_line_sect = dwarf_seg.sections.items[d_sym.debug_line_section_index.?];
2131 const debug_line_sect = d_sym.sections.items[d_sym.debug_line_section_index.?];
21392132 const file_pos = debug_line_sect.offset;
21402133 try pwriteDbgLineNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt);
21412134 },
......@@ -2264,8 +2257,7 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void {
22642257 .macho => {
22652258 const macho_file = file.cast(File.MachO).?;
22662259 const d_sym = &macho_file.d_sym.?;
2267 const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
2268 const debug_info_sect = &dwarf_segment.sections.items[d_sym.debug_info_section_index.?];
2260 const debug_info_sect = &d_sym.sections.items[d_sym.debug_info_section_index.?];
22692261 break :blk debug_info_sect.offset;
22702262 },
22712263 // for wasm, the offset is always 0 as we write to memory first
src/link/MachO.zig+1753-2486
......@@ -17,6 +17,7 @@ const aarch64 = @import("../arch/aarch64/bits.zig");
1717const bind = @import("MachO/bind.zig");
1818const codegen = @import("../codegen.zig");
1919const dead_strip = @import("MachO/dead_strip.zig");
20const fat = @import("MachO/fat.zig");
2021const link = @import("../link.zig");
2122const llvm_backend = @import("../codegen/llvm.zig");
2223const target_util = @import("../target.zig");
......@@ -60,6 +61,32 @@ const SystemLib = struct {
6061 weak: bool = false,
6162};
6263
64const Section = struct {
65 header: macho.section_64,
66 segment_index: u8,
67
68 // TODO is null here necessary, or can we do away with tracking via section
69 // size in incremental context?
70 last_atom: ?*Atom = null,
71
72 /// A list of atoms that have surplus capacity. This list can have false
73 /// positives, as functions grow and shrink over time, only sometimes being added
74 /// or removed from the freelist.
75 ///
76 /// An atom has surplus capacity when its overcapacity value is greater than
77 /// padToIdeal(minimum_atom_size). That is, when it has so
78 /// much extra capacity, that we could fit a small new symbol in it, itself with
79 /// ideal_capacity or more.
80 ///
81 /// Ideal capacity is defined by size + (size / ideal_factor).
82 ///
83 /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
84 /// overcapacity can be negative. A simple way to have negative overcapacity is to
85 /// allocate a fresh atom, which will have ideal capacity, and then grow it
86 /// by 1 byte. It will then have -1 overcapacity.
87 free_list: std.ArrayListUnmanaged(*Atom) = .{},
88};
89
6390base: File,
6491
6592/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
......@@ -77,81 +104,32 @@ page_size: u16,
77104/// fashion (default for LLVM backend).
78105mode: enum { incremental, one_shot },
79106
80/// The absolute address of the entry point.
81entry_addr: ?u64 = null,
82
83/// Code signature (if any)
84code_signature: ?CodeSignature = null,
107uuid: macho.uuid_command = .{
108 .cmdsize = @sizeOf(macho.uuid_command),
109 .uuid = undefined,
110},
85111
86112objects: std.ArrayListUnmanaged(Object) = .{},
87113archives: std.ArrayListUnmanaged(Archive) = .{},
88
89114dylibs: std.ArrayListUnmanaged(Dylib) = .{},
90115dylibs_map: std.StringHashMapUnmanaged(u16) = .{},
91116referenced_dylibs: std.AutoArrayHashMapUnmanaged(u16, void) = .{},
92117
93load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},
94
95pagezero_segment_cmd_index: ?u16 = null,
96text_segment_cmd_index: ?u16 = null,
97data_const_segment_cmd_index: ?u16 = null,
98data_segment_cmd_index: ?u16 = null,
99linkedit_segment_cmd_index: ?u16 = null,
100dyld_info_cmd_index: ?u16 = null,
101symtab_cmd_index: ?u16 = null,
102dysymtab_cmd_index: ?u16 = null,
103dylinker_cmd_index: ?u16 = null,
104data_in_code_cmd_index: ?u16 = null,
105function_starts_cmd_index: ?u16 = null,
106main_cmd_index: ?u16 = null,
107dylib_id_cmd_index: ?u16 = null,
108source_version_cmd_index: ?u16 = null,
109build_version_cmd_index: ?u16 = null,
110uuid_cmd_index: ?u16 = null,
111code_signature_cmd_index: ?u16 = null,
112
113// __TEXT segment sections
114text_section_index: ?u16 = null,
115stubs_section_index: ?u16 = null,
116stub_helper_section_index: ?u16 = null,
117text_const_section_index: ?u16 = null,
118cstring_section_index: ?u16 = null,
119ustring_section_index: ?u16 = null,
120gcc_except_tab_section_index: ?u16 = null,
121unwind_info_section_index: ?u16 = null,
122eh_frame_section_index: ?u16 = null,
123
124objc_methlist_section_index: ?u16 = null,
125objc_methname_section_index: ?u16 = null,
126objc_methtype_section_index: ?u16 = null,
127objc_classname_section_index: ?u16 = null,
128
129// __DATA_CONST segment sections
130got_section_index: ?u16 = null,
131mod_init_func_section_index: ?u16 = null,
132mod_term_func_section_index: ?u16 = null,
133data_const_section_index: ?u16 = null,
134
135objc_cfstring_section_index: ?u16 = null,
136objc_classlist_section_index: ?u16 = null,
137objc_imageinfo_section_index: ?u16 = null,
138
139// __DATA segment sections
140tlv_section_index: ?u16 = null,
141tlv_data_section_index: ?u16 = null,
142tlv_bss_section_index: ?u16 = null,
143tlv_ptrs_section_index: ?u16 = null,
144la_symbol_ptr_section_index: ?u16 = null,
145data_section_index: ?u16 = null,
146bss_section_index: ?u16 = null,
147
148objc_const_section_index: ?u16 = null,
149objc_selrefs_section_index: ?u16 = null,
150objc_classrefs_section_index: ?u16 = null,
151objc_data_section_index: ?u16 = null,
152
153rustc_section_index: ?u16 = null,
154rustc_section_size: u64 = 0,
118segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
119sections: std.MultiArrayList(Section) = .{},
120
121pagezero_segment_cmd_index: ?u8 = null,
122text_segment_cmd_index: ?u8 = null,
123data_const_segment_cmd_index: ?u8 = null,
124data_segment_cmd_index: ?u8 = null,
125linkedit_segment_cmd_index: ?u8 = null,
126
127text_section_index: ?u8 = null,
128stubs_section_index: ?u8 = null,
129stub_helper_section_index: ?u8 = null,
130got_section_index: ?u8 = null,
131la_symbol_ptr_section_index: ?u8 = null,
132data_section_index: ?u8 = null,
155133
156134locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
157135globals: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{},
......@@ -188,37 +166,12 @@ stubs_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
188166
189167error_flags: File.ErrorFlags = File.ErrorFlags{},
190168
191load_commands_dirty: bool = false,
192sections_order_dirty: bool = false,
193
194169/// A helper var to indicate if we are at the start of the incremental updates, or
195170/// already somewhere further along the update-and-run chain.
196171/// TODO once we add opening a prelinked output binary from file, this will become
197172/// obsolete as we will carry on where we left off.
198173cold_start: bool = true,
199174
200section_ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{},
201
202/// A list of atoms that have surplus capacity. This list can have false
203/// positives, as functions grow and shrink over time, only sometimes being added
204/// or removed from the freelist.
205///
206/// An atom has surplus capacity when its overcapacity value is greater than
207/// padToIdeal(minimum_atom_size). That is, when it has so
208/// much extra capacity, that we could fit a small new symbol in it, itself with
209/// ideal_capacity or more.
210///
211/// Ideal capacity is defined by size + (size / ideal_factor).
212///
213/// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
214/// overcapacity can be negative. A simple way to have negative overcapacity is to
215/// allocate a fresh atom, which will have ideal capacity, and then grow it
216/// by 1 byte. It will then have -1 overcapacity.
217atom_free_lists: std.AutoHashMapUnmanaged(MatchingSection, std.ArrayListUnmanaged(*Atom)) = .{},
218
219/// Pointer to the last allocated atom
220atoms: std.AutoHashMapUnmanaged(MatchingSection, *Atom) = .{},
221
222175/// List of atoms that are either synthetic or map directly to the Zig source program.
223176managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
224177
......@@ -250,7 +203,7 @@ unnamed_const_atoms: UnnamedConstTable = .{},
250203/// We store them here so that we can properly dispose of any allocated
251204/// memory within the atom in the incremental linker.
252205/// TODO consolidate this.
253decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, ?MatchingSection) = .{},
206decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, ?u8) = .{},
254207
255208const Entry = struct {
256209 target: SymbolWithLoc,
......@@ -408,12 +361,7 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
408361
409362pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
410363 const cpu_arch = options.target.cpu.arch;
411 const os_tag = options.target.os.tag;
412 const abi = options.target.abi;
413364 const page_size: u16 = if (cpu_arch == .aarch64) 0x4000 else 0x1000;
414 // Adhoc code signature is required when targeting aarch64-macos either directly or indirectly via the simulator
415 // ABI such as aarch64-ios-simulator, etc.
416 const requires_adhoc_codesig = cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator);
417365 const use_llvm = build_options.have_llvm and options.use_llvm;
418366 const use_stage1 = build_options.is_stage1 and options.use_stage1;
419367
......@@ -428,10 +376,6 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
428376 .file = null,
429377 },
430378 .page_size = page_size,
431 .code_signature = if (requires_adhoc_codesig)
432 CodeSignature.init(page_size)
433 else
434 null,
435379 .mode = if (use_stage1 or use_llvm or options.module == null or options.cache_mode == .whole)
436380 .one_shot
437381 else
......@@ -562,8 +506,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
562506 var dependent_libs = std.fifo.LinearFifo(struct {
563507 id: Dylib.Id,
564508 parent: u16,
565 }, .Dynamic).init(self.base.allocator);
566 defer dependent_libs.deinit();
509 }, .Dynamic).init(arena);
510
567511 try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs);
568512 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);
569513 }
......@@ -573,7 +517,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
573517 try self.createDyldPrivateAtom();
574518 try self.createStubHelperPreambleAtom();
575519 try self.resolveSymbolsInDylibs();
576 try self.addCodeSignatureLC();
577520
578521 if (self.unresolved.count() > 0) {
579522 return error.UndefinedSymbolReference;
......@@ -583,67 +526,92 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
583526
584527 if (build_options.enable_logging) {
585528 self.logSymtab();
586 self.logSectionOrdinals();
529 self.logSections();
587530 self.logAtoms();
588531 }
589532
590533 try self.writeAtomsIncremental();
591534
592 try self.setEntryPoint();
593 try self.updateSectionOrdinals();
594 try self.writeLinkeditSegment();
535 var lc_buffer = std.ArrayList(u8).init(arena);
536 const lc_writer = lc_buffer.writer();
537 var ncmds: u32 = 0;
595538
596 if (self.d_sym) |*d_sym| {
597 // Flush debug symbols bundle.
598 try d_sym.flushModule(self.base.allocator, self.base.options);
539 try self.writeLinkeditSegmentData(&ncmds, lc_writer);
540 try writeDylinkerLC(&ncmds, lc_writer);
541
542 self.writeMainLC(&ncmds, lc_writer) catch |err| switch (err) {
543 error.MissingMainEntrypoint => {
544 self.error_flags.no_entry_point_found = true;
545 },
546 else => |e| return e,
547 };
548
549 try self.writeDylibIdLC(&ncmds, lc_writer);
550 try self.writeRpathLCs(&ncmds, lc_writer);
551
552 {
553 try lc_writer.writeStruct(macho.source_version_command{
554 .cmdsize = @sizeOf(macho.source_version_command),
555 .version = 0x0,
556 });
557 ncmds += 1;
599558 }
600559
601 // code signature and entitlements
602 if (self.base.options.entitlements) |path| {
603 if (self.code_signature) |*csig| {
604 try csig.addEntitlements(self.base.allocator, path);
605 csig.code_directory.ident = self.base.options.emit.?.sub_path;
606 } else {
607 var csig = CodeSignature.init(self.page_size);
608 try csig.addEntitlements(self.base.allocator, path);
609 csig.code_directory.ident = self.base.options.emit.?.sub_path;
610 self.code_signature = csig;
611 }
560 try self.writeBuildVersionLC(&ncmds, lc_writer);
561
562 {
563 std.crypto.random.bytes(&self.uuid.uuid);
564 try lc_writer.writeStruct(self.uuid);
565 ncmds += 1;
612566 }
613567
614 if (self.code_signature) |*csig| {
615 csig.clear(self.base.allocator);
616 csig.code_directory.ident = self.base.options.emit.?.sub_path;
568 try self.writeLoadDylibLCs(&ncmds, lc_writer);
569
570 const target = self.base.options.target;
571 const requires_codesig = blk: {
572 if (self.base.options.entitlements) |_| break :blk true;
573 if (target.cpu.arch == .aarch64 and (target.os.tag == .macos or target.abi == .simulator))
574 break :blk true;
575 break :blk false;
576 };
577 var codesig_offset: ?u32 = null;
578 var codesig: ?CodeSignature = if (requires_codesig) blk: {
617579 // Preallocate space for the code signature.
618580 // We need to do this at this stage so that we have the load commands with proper values
619581 // written out to the file.
620582 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
621583 // where the code signature goes into.
622 try self.writeCodeSignaturePadding(csig);
623 }
584 var codesig = CodeSignature.init(self.page_size);
585 codesig.code_directory.ident = self.base.options.emit.?.sub_path;
586 if (self.base.options.entitlements) |path| {
587 try codesig.addEntitlements(arena, path);
588 }
589 codesig_offset = try self.writeCodeSignaturePadding(&codesig, &ncmds, lc_writer);
590 break :blk codesig;
591 } else null;
624592
625 try self.writeLoadCommands();
626 try self.writeHeader();
593 var headers_buf = std.ArrayList(u8).init(arena);
594 try self.writeSegmentHeaders(&ncmds, headers_buf.writer());
627595
628 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
629 log.debug("flushing. no_entry_point_found = true", .{});
630 self.error_flags.no_entry_point_found = true;
631 } else {
632 log.debug("flushing. no_entry_point_found = false", .{});
633 self.error_flags.no_entry_point_found = false;
634 }
596 try self.base.file.?.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));
597 try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);
635598
636 assert(!self.load_commands_dirty);
599 try self.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len));
637600
638 if (self.code_signature) |*csig| {
639 try self.writeCodeSignature(csig); // code signing always comes last
601 if (codesig) |*csig| {
602 try self.writeCodeSignature(csig, codesig_offset.?); // code signing always comes last
640603 }
641604
642 if (build_options.enable_link_snapshots) {
643 if (self.base.options.enable_link_snapshots)
644 try self.snapshotState();
605 if (self.d_sym) |*d_sym| {
606 // Flush debug symbols bundle.
607 try d_sym.flushModule(self.base.allocator, self.base.options);
645608 }
646609
610 // if (build_options.enable_link_snapshots) {
611 // if (self.base.options.enable_link_snapshots)
612 // try self.snapshotState();
613 // }
614
647615 if (cache_miss) {
648616 // Update the file with the digest. If it fails we can continue; it only
649617 // means that the next invocation will have an unnecessary cache miss.
......@@ -708,6 +676,9 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
708676 sub_prog_node.context.refresh();
709677 defer sub_prog_node.end();
710678
679 const cpu_arch = self.base.options.target.cpu.arch;
680 const os_tag = self.base.options.target.os.tag;
681 const abi = self.base.options.target.abi;
711682 const is_lib = self.base.options.output_mode == .Lib;
712683 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
713684 const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
......@@ -990,40 +961,6 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
990961 }
991962 }
992963
993 // rpaths
994 var rpath_table = std.StringArrayHashMap(void).init(arena);
995 for (self.base.options.rpath_list) |rpath| {
996 if (rpath_table.contains(rpath)) continue;
997 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
998 u64,
999 @sizeOf(macho.rpath_command) + rpath.len + 1,
1000 @sizeOf(u64),
1001 ));
1002 var rpath_cmd = macho.emptyGenericCommandWithData(macho.rpath_command{
1003 .cmdsize = cmdsize,
1004 .path = @sizeOf(macho.rpath_command),
1005 });
1006 rpath_cmd.data = try gpa.alloc(u8, cmdsize - rpath_cmd.inner.path);
1007 mem.set(u8, rpath_cmd.data, 0);
1008 mem.copy(u8, rpath_cmd.data, rpath);
1009 try self.load_commands.append(gpa, .{ .rpath = rpath_cmd });
1010 try rpath_table.putNoClobber(rpath, {});
1011 self.load_commands_dirty = true;
1012 }
1013
1014 // code signature and entitlements
1015 if (self.base.options.entitlements) |path| {
1016 if (self.code_signature) |*csig| {
1017 try csig.addEntitlements(gpa, path);
1018 csig.code_directory.ident = self.base.options.emit.?.sub_path;
1019 } else {
1020 var csig = CodeSignature.init(self.page_size);
1021 try csig.addEntitlements(gpa, path);
1022 csig.code_directory.ident = self.base.options.emit.?.sub_path;
1023 self.code_signature = csig;
1024 }
1025 }
1026
1027964 if (self.base.options.verbose_link) {
1028965 var argv = std.ArrayList([]const u8).init(arena);
1029966
......@@ -1048,7 +985,7 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
1048985 try argv.append(syslibroot);
1049986 }
1050987
1051 for (rpath_table.keys()) |rpath| {
988 for (self.base.options.rpath_list) |rpath| {
1052989 try argv.append("-rpath");
1053990 try argv.append(rpath);
1054991 }
......@@ -1157,25 +1094,22 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
11571094 var dependent_libs = std.fifo.LinearFifo(struct {
11581095 id: Dylib.Id,
11591096 parent: u16,
1160 }, .Dynamic).init(gpa);
1161 defer dependent_libs.deinit();
1097 }, .Dynamic).init(arena);
1098
11621099 try self.parseInputFiles(positionals.items, self.base.options.sysroot, &dependent_libs);
11631100 try self.parseAndForceLoadStaticArchives(must_link_archives.keys());
11641101 try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs);
11651102 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);
11661103
1167 for (self.objects.items) |*object, object_id| {
1168 try self.resolveSymbolsInObject(object, @intCast(u16, object_id));
1104 for (self.objects.items) |_, object_id| {
1105 try self.resolveSymbolsInObject(@intCast(u16, object_id));
11691106 }
11701107
11711108 try self.resolveSymbolsInArchives();
11721109 try self.resolveDyldStubBinder();
1173 try self.createDyldPrivateAtom();
1174 try self.createStubHelperPreambleAtom();
11751110 try self.resolveSymbolsInDylibs();
11761111 try self.createMhExecuteHeaderSymbol();
11771112 try self.createDsoHandleSymbol();
1178 try self.addCodeSignatureLC();
11791113 try self.resolveSymbolsAtLoading();
11801114
11811115 if (self.unresolved.count() > 0) {
......@@ -1188,7 +1122,13 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
11881122 return error.FrameworkNotFound;
11891123 }
11901124
1125 for (self.objects.items) |*object| {
1126 try object.scanInputSections(self);
1127 }
1128
1129 try self.createDyldPrivateAtom();
11911130 try self.createTentativeDefAtoms();
1131 try self.createStubHelperPreambleAtom();
11921132
11931133 for (self.objects.items) |*object, object_id| {
11941134 try object.splitIntoAtomsOneShot(self, @intCast(u32, object_id));
......@@ -1198,49 +1138,82 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
11981138 try dead_strip.gcAtoms(self);
11991139 }
12001140
1201 try self.pruneAndSortSections();
12021141 try self.allocateSegments();
12031142 try self.allocateSymbols();
12041143
12051144 try self.allocateSpecialSymbols();
12061145
1207 if (build_options.enable_logging) {
1146 if (build_options.enable_logging or true) {
12081147 self.logSymtab();
1209 self.logSectionOrdinals();
1148 self.logSections();
12101149 self.logAtoms();
12111150 }
12121151
12131152 try self.writeAtomsOneShot();
12141153
1215 if (self.rustc_section_index) |id| {
1216 const sect = self.getSectionPtr(.{
1217 .seg = self.data_segment_cmd_index.?,
1218 .sect = id,
1154 var lc_buffer = std.ArrayList(u8).init(arena);
1155 const lc_writer = lc_buffer.writer();
1156 var ncmds: u32 = 0;
1157
1158 try self.writeLinkeditSegmentData(&ncmds, lc_writer);
1159 try writeDylinkerLC(&ncmds, lc_writer);
1160 try self.writeMainLC(&ncmds, lc_writer);
1161 try self.writeDylibIdLC(&ncmds, lc_writer);
1162 try self.writeRpathLCs(&ncmds, lc_writer);
1163
1164 {
1165 try lc_writer.writeStruct(macho.source_version_command{
1166 .cmdsize = @sizeOf(macho.source_version_command),
1167 .version = 0x0,
12191168 });
1220 sect.size = self.rustc_section_size;
1169 ncmds += 1;
12211170 }
12221171
1223 try self.setEntryPoint();
1224 try self.writeLinkeditSegment();
1172 try self.writeBuildVersionLC(&ncmds, lc_writer);
12251173
1226 if (self.code_signature) |*csig| {
1227 csig.clear(gpa);
1228 csig.code_directory.ident = self.base.options.emit.?.sub_path;
1174 {
1175 var uuid_lc = macho.uuid_command{
1176 .cmdsize = @sizeOf(macho.uuid_command),
1177 .uuid = undefined,
1178 };
1179 std.crypto.random.bytes(&uuid_lc.uuid);
1180 try lc_writer.writeStruct(uuid_lc);
1181 ncmds += 1;
1182 }
1183
1184 try self.writeLoadDylibLCs(&ncmds, lc_writer);
1185
1186 const requires_codesig = blk: {
1187 if (self.base.options.entitlements) |_| break :blk true;
1188 if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) break :blk true;
1189 break :blk false;
1190 };
1191 var codesig_offset: ?u32 = null;
1192 var codesig: ?CodeSignature = if (requires_codesig) blk: {
12291193 // Preallocate space for the code signature.
12301194 // We need to do this at this stage so that we have the load commands with proper values
12311195 // written out to the file.
12321196 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
12331197 // where the code signature goes into.
1234 try self.writeCodeSignaturePadding(csig);
1235 }
1198 var codesig = CodeSignature.init(self.page_size);
1199 codesig.code_directory.ident = self.base.options.emit.?.sub_path;
1200 if (self.base.options.entitlements) |path| {
1201 try codesig.addEntitlements(arena, path);
1202 }
1203 codesig_offset = try self.writeCodeSignaturePadding(&codesig, &ncmds, lc_writer);
1204 break :blk codesig;
1205 } else null;
1206
1207 var headers_buf = std.ArrayList(u8).init(arena);
1208 try self.writeSegmentHeaders(&ncmds, headers_buf.writer());
12361209
1237 try self.writeLoadCommands();
1238 try self.writeHeader();
1210 try self.base.file.?.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));
1211 try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);
12391212
1240 assert(!self.load_commands_dirty);
1213 try self.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len));
12411214
1242 if (self.code_signature) |*csig| {
1243 try self.writeCodeSignature(csig); // code signing always comes last
1215 if (codesig) |*csig| {
1216 try self.writeCodeSignature(csig, codesig_offset.?); // code signing always comes last
12441217 }
12451218 }
12461219
......@@ -1395,66 +1368,77 @@ fn resolveFramework(
13951368}
13961369
13971370fn parseObject(self: *MachO, path: []const u8) !bool {
1371 const gpa = self.base.allocator;
13981372 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
13991373 error.FileNotFound => return false,
14001374 else => |e| return e,
14011375 };
1402 errdefer file.close();
1403
1404 const name = try self.base.allocator.dupe(u8, path);
1405 errdefer self.base.allocator.free(name);
1376 defer file.close();
14061377
1378 const name = try gpa.dupe(u8, path);
1379 errdefer gpa.free(name);
1380 const cpu_arch = self.base.options.target.cpu.arch;
14071381 const mtime: u64 = mtime: {
14081382 const stat = file.stat() catch break :mtime 0;
14091383 break :mtime @intCast(u64, @divFloor(stat.mtime, 1_000_000_000));
14101384 };
1385 const file_stat = try file.stat();
1386 const file_size = math.cast(usize, file_stat.size) orelse return error.Overflow;
1387 const contents = try file.readToEndAllocOptions(gpa, file_size, file_size, @alignOf(u64), null);
14111388
14121389 var object = Object{
14131390 .name = name,
1414 .file = file,
14151391 .mtime = mtime,
1392 .contents = contents,
14161393 };
14171394
1418 object.parse(self.base.allocator, self.base.options.target.cpu.arch) catch |err| switch (err) {
1395 object.parse(gpa, cpu_arch) catch |err| switch (err) {
14191396 error.EndOfStream, error.NotObject => {
1420 object.deinit(self.base.allocator);
1397 object.deinit(gpa);
14211398 return false;
14221399 },
14231400 else => |e| return e,
14241401 };
14251402
1426 try self.objects.append(self.base.allocator, object);
1403 try self.objects.append(gpa, object);
14271404
14281405 return true;
14291406}
14301407
14311408fn parseArchive(self: *MachO, path: []const u8, force_load: bool) !bool {
1409 const gpa = self.base.allocator;
14321410 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
14331411 error.FileNotFound => return false,
14341412 else => |e| return e,
14351413 };
14361414 errdefer file.close();
14371415
1438 const name = try self.base.allocator.dupe(u8, path);
1439 errdefer self.base.allocator.free(name);
1416 const name = try gpa.dupe(u8, path);
1417 errdefer gpa.free(name);
1418 const cpu_arch = self.base.options.target.cpu.arch;
1419 const reader = file.reader();
1420 const fat_offset = try fat.getLibraryOffset(reader, cpu_arch);
1421 try reader.context.seekTo(fat_offset);
14401422
14411423 var archive = Archive{
14421424 .name = name,
1425 .fat_offset = fat_offset,
14431426 .file = file,
14441427 };
14451428
1446 archive.parse(self.base.allocator, self.base.options.target.cpu.arch) catch |err| switch (err) {
1429 archive.parse(gpa, reader) catch |err| switch (err) {
14471430 error.EndOfStream, error.NotArchive => {
1448 archive.deinit(self.base.allocator);
1431 archive.deinit(gpa);
14491432 return false;
14501433 },
14511434 else => |e| return e,
14521435 };
14531436
14541437 if (force_load) {
1455 defer archive.deinit(self.base.allocator);
1438 defer archive.deinit(gpa);
1439 defer file.close();
14561440 // Get all offsets from the ToC
1457 var offsets = std.AutoArrayHashMap(u32, void).init(self.base.allocator);
1441 var offsets = std.AutoArrayHashMap(u32, void).init(gpa);
14581442 defer offsets.deinit();
14591443 for (archive.toc.values()) |offs| {
14601444 for (offs.items) |off| {
......@@ -1462,15 +1446,11 @@ fn parseArchive(self: *MachO, path: []const u8, force_load: bool) !bool {
14621446 }
14631447 }
14641448 for (offsets.keys()) |off| {
1465 const object = try self.objects.addOne(self.base.allocator);
1466 object.* = try archive.parseObject(
1467 self.base.allocator,
1468 self.base.options.target.cpu.arch,
1469 off,
1470 );
1449 const object = try archive.parseObject(gpa, cpu_arch, off);
1450 try self.objects.append(gpa, object);
14711451 }
14721452 } else {
1473 try self.archives.append(self.base.allocator, archive);
1453 try self.archives.append(gpa, archive);
14741454 }
14751455
14761456 return true;
......@@ -1481,6 +1461,7 @@ const ParseDylibError = error{
14811461 EmptyStubFile,
14821462 MismatchedCpuArchitecture,
14831463 UnsupportedCpuArchitecture,
1464 EndOfStream,
14841465} || fs.File.OpenError || std.os.PReadError || Dylib.Id.ParseError;
14851466
14861467const DylibCreateOpts = struct {
......@@ -1497,43 +1478,53 @@ pub fn parseDylib(
14971478 dependent_libs: anytype,
14981479 opts: DylibCreateOpts,
14991480) ParseDylibError!bool {
1481 const gpa = self.base.allocator;
15001482 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
15011483 error.FileNotFound => return false,
15021484 else => |e| return e,
15031485 };
1504 errdefer file.close();
1486 defer file.close();
1487
1488 const cpu_arch = self.base.options.target.cpu.arch;
1489 const file_stat = try file.stat();
1490 var file_size = math.cast(usize, file_stat.size) orelse return error.Overflow;
15051491
1506 const name = try self.base.allocator.dupe(u8, path);
1507 errdefer self.base.allocator.free(name);
1492 const reader = file.reader();
1493 const fat_offset = math.cast(usize, try fat.getLibraryOffset(reader, cpu_arch)) orelse
1494 return error.Overflow;
1495 try file.seekTo(fat_offset);
1496 file_size -= fat_offset;
1497
1498 const contents = try file.readToEndAllocOptions(gpa, file_size, file_size, @alignOf(u64), null);
1499 defer gpa.free(contents);
15081500
15091501 const dylib_id = @intCast(u16, self.dylibs.items.len);
1510 var dylib = Dylib{
1511 .name = name,
1512 .file = file,
1513 .weak = opts.weak,
1514 };
1502 var dylib = Dylib{ .weak = opts.weak };
15151503
1516 dylib.parse(
1517 self.base.allocator,
1518 self.base.options.target.cpu.arch,
1504 dylib.parseFromBinary(
1505 gpa,
1506 cpu_arch,
15191507 dylib_id,
15201508 dependent_libs,
1509 path,
1510 contents,
15211511 ) catch |err| switch (err) {
15221512 error.EndOfStream, error.NotDylib => {
15231513 try file.seekTo(0);
15241514
1525 var lib_stub = LibStub.loadFromFile(self.base.allocator, file) catch {
1526 dylib.deinit(self.base.allocator);
1515 var lib_stub = LibStub.loadFromFile(gpa, file) catch {
1516 dylib.deinit(gpa);
15271517 return false;
15281518 };
15291519 defer lib_stub.deinit();
15301520
15311521 try dylib.parseFromStub(
1532 self.base.allocator,
1522 gpa,
15331523 self.base.options.target,
15341524 lib_stub,
15351525 dylib_id,
15361526 dependent_libs,
1527 path,
15371528 );
15381529 },
15391530 else => |e| return e,
......@@ -1547,13 +1538,13 @@ pub fn parseDylib(
15471538 log.warn(" dylib version: {}", .{dylib.id.?.current_version});
15481539
15491540 // TODO maybe this should be an error and facilitate auto-cleanup?
1550 dylib.deinit(self.base.allocator);
1541 dylib.deinit(gpa);
15511542 return false;
15521543 }
15531544 }
15541545
1555 try self.dylibs.append(self.base.allocator, dylib);
1556 try self.dylibs_map.putNoClobber(self.base.allocator, dylib.id.?.name, dylib_id);
1546 try self.dylibs.append(gpa, dylib);
1547 try self.dylibs_map.putNoClobber(gpa, dylib.id.?.name, dylib_id);
15571548
15581549 const should_link_dylib_even_if_unreachable = blk: {
15591550 if (self.base.options.dead_strip_dylibs and !opts.needed) break :blk false;
......@@ -1561,8 +1552,7 @@ pub fn parseDylib(
15611552 };
15621553
15631554 if (should_link_dylib_even_if_unreachable) {
1564 try self.addLoadDylibLC(dylib_id);
1565 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});
1555 try self.referenced_dylibs.putNoClobber(gpa, dylib_id, {});
15661556 }
15671557
15681558 return true;
......@@ -1572,10 +1562,8 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const
15721562 for (files) |file_name| {
15731563 const full_path = full_path: {
15741564 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
1575 const path = try fs.realpath(file_name, &buffer);
1576 break :full_path try self.base.allocator.dupe(u8, path);
1565 break :full_path try fs.realpath(file_name, &buffer);
15771566 };
1578 defer self.base.allocator.free(full_path);
15791567 log.debug("parsing input file path '{s}'", .{full_path});
15801568
15811569 if (try self.parseObject(full_path)) continue;
......@@ -1592,10 +1580,8 @@ fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !voi
15921580 for (files) |file_name| {
15931581 const full_path = full_path: {
15941582 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
1595 const path = try fs.realpath(file_name, &buffer);
1596 break :full_path try self.base.allocator.dupe(u8, path);
1583 break :full_path try fs.realpath(file_name, &buffer);
15971584 };
1598 defer self.base.allocator.free(full_path);
15991585 log.debug("parsing and force loading static archive '{s}'", .{full_path});
16001586
16011587 if (try self.parseArchive(full_path, true)) continue;
......@@ -1669,639 +1655,232 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any
16691655 }
16701656}
16711657
1672pub const MatchingSection = struct {
1673 seg: u16,
1674 sect: u16,
1675
1676 pub fn eql(this: MatchingSection, other: struct {
1677 seg: ?u16,
1678 sect: ?u16,
1679 }) bool {
1680 const seg = other.seg orelse return false;
1681 const sect = other.sect orelse return false;
1682 return this.seg == seg and this.sect == sect;
1683 }
1684};
1685
1686pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSection {
1658pub fn getOutputSection(self: *MachO, sect: macho.section_64) !?u8 {
16871659 const segname = sect.segName();
16881660 const sectname = sect.sectName();
1689 const res: ?MatchingSection = blk: {
1690 switch (sect.type_()) {
1691 macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => {
1692 if (self.text_const_section_index == null) {
1693 self.text_const_section_index = try self.initSection(
1694 self.text_segment_cmd_index.?,
1695 "__const",
1661 const res: ?u8 = blk: {
1662 if (mem.eql(u8, "__LLVM", segname)) {
1663 log.debug("TODO LLVM section: type 0x{x}, name '{s},{s}'", .{
1664 sect.flags, segname, sectname,
1665 });
1666 break :blk null;
1667 }
1668
1669 if (sect.isCode()) {
1670 if (self.text_section_index == null) {
1671 self.text_section_index = try self.initSection(
1672 "__TEXT",
1673 "__text",
1674 sect.size,
1675 sect.@"align",
1676 .{
1677 .flags = macho.S_REGULAR |
1678 macho.S_ATTR_PURE_INSTRUCTIONS |
1679 macho.S_ATTR_SOME_INSTRUCTIONS,
1680 },
1681 );
1682 }
1683 break :blk self.text_section_index.?;
1684 }
1685
1686 if (sect.isDebug()) {
1687 // TODO debug attributes
1688 if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) {
1689 log.debug("TODO compact unwind section: type 0x{x}, name '{s},{s}'", .{
1690 sect.flags, segname, sectname,
1691 });
1692 }
1693 break :blk null;
1694 }
1695
1696 switch (sect.@"type"()) {
1697 macho.S_4BYTE_LITERALS,
1698 macho.S_8BYTE_LITERALS,
1699 macho.S_16BYTE_LITERALS,
1700 => {
1701 break :blk self.getSectionByName("__TEXT", "__const") orelse try self.initSection(
1702 "__TEXT",
1703 "__const",
1704 sect.size,
1705 sect.@"align",
1706 .{},
1707 );
1708 },
1709 macho.S_CSTRING_LITERALS => {
1710 if (mem.startsWith(u8, sectname, "__objc")) {
1711 break :blk self.getSectionByName(segname, sectname) orelse try self.initSection(
1712 segname,
1713 sectname,
16961714 sect.size,
16971715 sect.@"align",
16981716 .{},
16991717 );
17001718 }
1701
1702 break :blk .{
1703 .seg = self.text_segment_cmd_index.?,
1704 .sect = self.text_const_section_index.?,
1705 };
1719 break :blk self.getSectionByName("__TEXT", "__cstring") orelse try self.initSection(
1720 "__TEXT",
1721 "__cstring",
1722 sect.size,
1723 sect.@"align",
1724 .{ .flags = macho.S_CSTRING_LITERALS },
1725 );
17061726 },
1707 macho.S_CSTRING_LITERALS => {
1708 if (mem.eql(u8, sectname, "__objc_methname")) {
1709 // TODO it seems the common values within the sections in objects are deduplicated/merged
1710 // on merging the sections' contents.
1711 if (self.objc_methname_section_index == null) {
1712 self.objc_methname_section_index = try self.initSection(
1713 self.text_segment_cmd_index.?,
1714 "__objc_methname",
1727 macho.S_MOD_INIT_FUNC_POINTERS,
1728 macho.S_MOD_TERM_FUNC_POINTERS,
1729 => {
1730 break :blk self.getSectionByName("__DATA_CONST", sectname) orelse try self.initSection(
1731 "__DATA_CONST",
1732 sectname,
1733 sect.size,
1734 sect.@"align",
1735 .{ .flags = sect.flags },
1736 );
1737 },
1738 macho.S_LITERAL_POINTERS,
1739 macho.S_ZEROFILL,
1740 macho.S_THREAD_LOCAL_VARIABLES,
1741 macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
1742 macho.S_THREAD_LOCAL_REGULAR,
1743 macho.S_THREAD_LOCAL_ZEROFILL,
1744 => {
1745 break :blk self.getSectionByName(segname, sectname) orelse try self.initSection(
1746 segname,
1747 sectname,
1748 sect.size,
1749 sect.@"align",
1750 .{ .flags = sect.flags },
1751 );
1752 },
1753 macho.S_COALESCED => {
1754 break :blk self.getSectionByName(segname, sectname) orelse try self.initSection(
1755 segname,
1756 sectname,
1757 sect.size,
1758 sect.@"align",
1759 .{},
1760 );
1761 },
1762 macho.S_REGULAR => {
1763 if (mem.eql(u8, segname, "__TEXT")) {
1764 if (mem.eql(u8, sectname, "__rodata") or
1765 mem.eql(u8, sectname, "__typelink") or
1766 mem.eql(u8, sectname, "__itablink") or
1767 mem.eql(u8, sectname, "__gosymtab") or
1768 mem.eql(u8, sectname, "__gopclntab"))
1769 {
1770 break :blk self.getSectionByName("__DATA_CONST", "__const") orelse try self.initSection(
1771 "__DATA_CONST",
1772 "__const",
17151773 sect.size,
17161774 sect.@"align",
17171775 .{},
17181776 );
17191777 }
1720
1721 break :blk .{
1722 .seg = self.text_segment_cmd_index.?,
1723 .sect = self.objc_methname_section_index.?,
1724 };
1725 } else if (mem.eql(u8, sectname, "__objc_methtype")) {
1726 if (self.objc_methtype_section_index == null) {
1727 self.objc_methtype_section_index = try self.initSection(
1728 self.text_segment_cmd_index.?,
1729 "__objc_methtype",
1778 }
1779 if (mem.eql(u8, segname, "__DATA")) {
1780 if (mem.eql(u8, sectname, "__const") or
1781 mem.eql(u8, sectname, "__cfstring") or
1782 mem.eql(u8, sectname, "__objc_classlist") or
1783 mem.eql(u8, sectname, "__objc_imageinfo"))
1784 {
1785 break :blk self.getSectionByName("__DATA_CONST", sectname) orelse
1786 try self.initSection(
1787 "__DATA_CONST",
1788 sectname,
17301789 sect.size,
17311790 sect.@"align",
17321791 .{},
17331792 );
1793 } else if (mem.eql(u8, sectname, "__data")) {
1794 if (self.data_section_index == null) {
1795 self.data_section_index = try self.initSection(
1796 segname,
1797 sectname,
1798 sect.size,
1799 sect.@"align",
1800 .{},
1801 );
1802 }
1803 break :blk self.data_section_index.?;
17341804 }
1805 }
1806 break :blk self.getSectionByName(segname, sectname) orelse try self.initSection(
1807 segname,
1808 sectname,
1809 sect.size,
1810 sect.@"align",
1811 .{},
1812 );
1813 },
1814 else => break :blk null,
1815 }
1816 };
1817 return res;
1818}
17351819
1736 break :blk .{
1737 .seg = self.text_segment_cmd_index.?,
1738 .sect = self.objc_methtype_section_index.?,
1739 };
1740 } else if (mem.eql(u8, sectname, "__objc_classname")) {
1741 if (self.objc_classname_section_index == null) {
1742 self.objc_classname_section_index = try self.initSection(
1743 self.text_segment_cmd_index.?,
1744 "__objc_classname",
1745 sect.size,
1746 sect.@"align",
1747 .{},
1748 );
1749 }
1820pub fn createEmptyAtom(gpa: Allocator, sym_index: u32, size: u64, alignment: u32) !*Atom {
1821 const size_usize = math.cast(usize, size) orelse return error.Overflow;
1822 const atom = try gpa.create(Atom);
1823 errdefer gpa.destroy(atom);
1824 atom.* = Atom.empty;
1825 atom.sym_index = sym_index;
1826 atom.size = size;
1827 atom.alignment = alignment;
17501828
1751 break :blk .{
1752 .seg = self.text_segment_cmd_index.?,
1753 .sect = self.objc_classname_section_index.?,
1754 };
1755 }
1829 try atom.code.resize(gpa, size_usize);
1830 mem.set(u8, atom.code.items, 0);
17561831
1757 if (self.cstring_section_index == null) {
1758 self.cstring_section_index = try self.initSection(
1759 self.text_segment_cmd_index.?,
1760 "__cstring",
1761 sect.size,
1762 sect.@"align",
1763 .{
1764 .flags = macho.S_CSTRING_LITERALS,
1765 },
1766 );
1767 }
1832 return atom;
1833}
17681834
1769 break :blk .{
1770 .seg = self.text_segment_cmd_index.?,
1771 .sect = self.cstring_section_index.?,
1772 };
1773 },
1774 macho.S_LITERAL_POINTERS => {
1775 if (mem.eql(u8, segname, "__DATA") and mem.eql(u8, sectname, "__objc_selrefs")) {
1776 if (self.objc_selrefs_section_index == null) {
1777 self.objc_selrefs_section_index = try self.initSection(
1778 self.data_segment_cmd_index.?,
1779 "__objc_selrefs",
1780 sect.size,
1781 sect.@"align",
1782 .{
1783 .flags = macho.S_LITERAL_POINTERS,
1784 },
1785 );
1786 }
1835pub fn writeAtom(self: *MachO, atom: *Atom, sect_id: u8) !void {
1836 const section = self.sections.get(sect_id);
1837 const sym = atom.getSymbol(self);
1838 const file_offset = section.header.offset + sym.n_value - section.header.addr;
1839 try atom.resolveRelocs(self);
1840 log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset });
1841 try self.base.file.?.pwriteAll(atom.code.items, file_offset);
1842}
17871843
1788 break :blk .{
1789 .seg = self.data_segment_cmd_index.?,
1790 .sect = self.objc_selrefs_section_index.?,
1791 };
1792 } else {
1793 // TODO investigate
1794 break :blk null;
1795 }
1796 },
1797 macho.S_MOD_INIT_FUNC_POINTERS => {
1798 if (self.mod_init_func_section_index == null) {
1799 self.mod_init_func_section_index = try self.initSection(
1800 self.data_const_segment_cmd_index.?,
1801 "__mod_init_func",
1802 sect.size,
1803 sect.@"align",
1804 .{
1805 .flags = macho.S_MOD_INIT_FUNC_POINTERS,
1806 },
1807 );
1808 }
1844fn allocateSymbols(self: *MachO) !void {
1845 const slice = self.sections.slice();
1846 for (slice.items(.last_atom)) |last_atom, sect_id| {
1847 const header = slice.items(.header)[sect_id];
1848 var atom = last_atom orelse continue;
18091849
1810 break :blk .{
1811 .seg = self.data_const_segment_cmd_index.?,
1812 .sect = self.mod_init_func_section_index.?,
1813 };
1814 },
1815 macho.S_MOD_TERM_FUNC_POINTERS => {
1816 if (self.mod_term_func_section_index == null) {
1817 self.mod_term_func_section_index = try self.initSection(
1818 self.data_const_segment_cmd_index.?,
1819 "__mod_term_func",
1820 sect.size,
1821 sect.@"align",
1822 .{
1823 .flags = macho.S_MOD_TERM_FUNC_POINTERS,
1824 },
1825 );
1826 }
1850 while (atom.prev) |prev| {
1851 atom = prev;
1852 }
18271853
1828 break :blk .{
1829 .seg = self.data_const_segment_cmd_index.?,
1830 .sect = self.mod_term_func_section_index.?,
1831 };
1832 },
1833 macho.S_ZEROFILL => {
1834 if (self.bss_section_index == null) {
1835 self.bss_section_index = try self.initSection(
1836 self.data_segment_cmd_index.?,
1837 "__bss",
1838 sect.size,
1839 sect.@"align",
1840 .{
1841 .flags = macho.S_ZEROFILL,
1842 },
1843 );
1844 }
1854 const n_sect = @intCast(u8, sect_id + 1);
1855 var base_vaddr = header.addr;
18451856
1846 break :blk .{
1847 .seg = self.data_segment_cmd_index.?,
1848 .sect = self.bss_section_index.?,
1849 };
1850 },
1851 macho.S_THREAD_LOCAL_VARIABLES => {
1852 if (self.tlv_section_index == null) {
1853 self.tlv_section_index = try self.initSection(
1854 self.data_segment_cmd_index.?,
1855 "__thread_vars",
1856 sect.size,
1857 sect.@"align",
1858 .{
1859 .flags = macho.S_THREAD_LOCAL_VARIABLES,
1860 },
1861 );
1862 }
1857 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{
1858 n_sect,
1859 header.segName(),
1860 header.sectName(),
1861 });
18631862
1864 break :blk .{
1865 .seg = self.data_segment_cmd_index.?,
1866 .sect = self.tlv_section_index.?,
1867 };
1868 },
1869 macho.S_THREAD_LOCAL_VARIABLE_POINTERS => {
1870 if (self.tlv_ptrs_section_index == null) {
1871 self.tlv_ptrs_section_index = try self.initSection(
1872 self.data_segment_cmd_index.?,
1873 "__thread_ptrs",
1874 sect.size,
1875 sect.@"align",
1876 .{
1877 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
1878 },
1879 );
1880 }
1863 while (true) {
1864 const alignment = try math.powi(u32, 2, atom.alignment);
1865 base_vaddr = mem.alignForwardGeneric(u64, base_vaddr, alignment);
18811866
1882 break :blk .{
1883 .seg = self.data_segment_cmd_index.?,
1884 .sect = self.tlv_ptrs_section_index.?,
1885 };
1886 },
1887 macho.S_THREAD_LOCAL_REGULAR => {
1888 if (self.tlv_data_section_index == null) {
1889 self.tlv_data_section_index = try self.initSection(
1890 self.data_segment_cmd_index.?,
1891 "__thread_data",
1892 sect.size,
1893 sect.@"align",
1894 .{
1895 .flags = macho.S_THREAD_LOCAL_REGULAR,
1896 },
1897 );
1898 }
1867 const sym = atom.getSymbolPtr(self);
1868 sym.n_value = base_vaddr;
1869 sym.n_sect = n_sect;
18991870
1900 break :blk .{
1901 .seg = self.data_segment_cmd_index.?,
1902 .sect = self.tlv_data_section_index.?,
1903 };
1904 },
1905 macho.S_THREAD_LOCAL_ZEROFILL => {
1906 if (self.tlv_bss_section_index == null) {
1907 self.tlv_bss_section_index = try self.initSection(
1908 self.data_segment_cmd_index.?,
1909 "__thread_bss",
1910 sect.size,
1911 sect.@"align",
1912 .{
1913 .flags = macho.S_THREAD_LOCAL_ZEROFILL,
1914 },
1915 );
1916 }
1871 log.debug(" ATOM(%{d}, '{s}') @{x}", .{ atom.sym_index, atom.getName(self), base_vaddr });
19171872
1918 break :blk .{
1919 .seg = self.data_segment_cmd_index.?,
1920 .sect = self.tlv_bss_section_index.?,
1921 };
1922 },
1923 macho.S_COALESCED => {
1924 if (mem.eql(u8, "__TEXT", segname) and mem.eql(u8, "__eh_frame", sectname)) {
1925 // TODO I believe __eh_frame is currently part of __unwind_info section
1926 // in the latest ld64 output.
1927 if (self.eh_frame_section_index == null) {
1928 self.eh_frame_section_index = try self.initSection(
1929 self.text_segment_cmd_index.?,
1930 "__eh_frame",
1931 sect.size,
1932 sect.@"align",
1933 .{},
1934 );
1935 }
1873 // Update each symbol contained within the atom
1874 for (atom.contained.items) |sym_at_off| {
1875 const contained_sym = self.getSymbolPtr(.{
1876 .sym_index = sym_at_off.sym_index,
1877 .file = atom.file,
1878 });
1879 contained_sym.n_value = base_vaddr + sym_at_off.offset;
1880 contained_sym.n_sect = n_sect;
1881 }
19361882
1937 break :blk .{
1938 .seg = self.text_segment_cmd_index.?,
1939 .sect = self.eh_frame_section_index.?,
1940 };
1941 }
1942
1943 // TODO audit this: is this the right mapping?
1944 if (self.data_const_section_index == null) {
1945 self.data_const_section_index = try self.initSection(
1946 self.data_const_segment_cmd_index.?,
1947 "__const",
1948 sect.size,
1949 sect.@"align",
1950 .{},
1951 );
1952 }
1953
1954 break :blk .{
1955 .seg = self.data_const_segment_cmd_index.?,
1956 .sect = self.data_const_section_index.?,
1957 };
1958 },
1959 macho.S_REGULAR => {
1960 if (sect.isCode()) {
1961 if (self.text_section_index == null) {
1962 self.text_section_index = try self.initSection(
1963 self.text_segment_cmd_index.?,
1964 "__text",
1965 sect.size,
1966 sect.@"align",
1967 .{
1968 .flags = macho.S_REGULAR |
1969 macho.S_ATTR_PURE_INSTRUCTIONS |
1970 macho.S_ATTR_SOME_INSTRUCTIONS,
1971 },
1972 );
1973 }
1974
1975 break :blk .{
1976 .seg = self.text_segment_cmd_index.?,
1977 .sect = self.text_section_index.?,
1978 };
1979 }
1980 if (sect.isDebug()) {
1981 // TODO debug attributes
1982 if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) {
1983 log.debug("TODO compact unwind section: type 0x{x}, name '{s},{s}'", .{
1984 sect.flags, segname, sectname,
1985 });
1986 }
1987 break :blk null;
1988 }
1989
1990 if (mem.eql(u8, segname, "__TEXT")) {
1991 if (mem.eql(u8, sectname, "__ustring")) {
1992 if (self.ustring_section_index == null) {
1993 self.ustring_section_index = try self.initSection(
1994 self.text_segment_cmd_index.?,
1995 "__ustring",
1996 sect.size,
1997 sect.@"align",
1998 .{},
1999 );
2000 }
2001
2002 break :blk .{
2003 .seg = self.text_segment_cmd_index.?,
2004 .sect = self.ustring_section_index.?,
2005 };
2006 } else if (mem.eql(u8, sectname, "__gcc_except_tab")) {
2007 if (self.gcc_except_tab_section_index == null) {
2008 self.gcc_except_tab_section_index = try self.initSection(
2009 self.text_segment_cmd_index.?,
2010 "__gcc_except_tab",
2011 sect.size,
2012 sect.@"align",
2013 .{},
2014 );
2015 }
2016
2017 break :blk .{
2018 .seg = self.text_segment_cmd_index.?,
2019 .sect = self.gcc_except_tab_section_index.?,
2020 };
2021 } else if (mem.eql(u8, sectname, "__objc_methlist")) {
2022 if (self.objc_methlist_section_index == null) {
2023 self.objc_methlist_section_index = try self.initSection(
2024 self.text_segment_cmd_index.?,
2025 "__objc_methlist",
2026 sect.size,
2027 sect.@"align",
2028 .{},
2029 );
2030 }
2031
2032 break :blk .{
2033 .seg = self.text_segment_cmd_index.?,
2034 .sect = self.objc_methlist_section_index.?,
2035 };
2036 } else if (mem.eql(u8, sectname, "__rodata") or
2037 mem.eql(u8, sectname, "__typelink") or
2038 mem.eql(u8, sectname, "__itablink") or
2039 mem.eql(u8, sectname, "__gosymtab") or
2040 mem.eql(u8, sectname, "__gopclntab"))
2041 {
2042 if (self.data_const_section_index == null) {
2043 self.data_const_section_index = try self.initSection(
2044 self.data_const_segment_cmd_index.?,
2045 "__const",
2046 sect.size,
2047 sect.@"align",
2048 .{},
2049 );
2050 }
2051
2052 break :blk .{
2053 .seg = self.data_const_segment_cmd_index.?,
2054 .sect = self.data_const_section_index.?,
2055 };
2056 } else {
2057 if (self.text_const_section_index == null) {
2058 self.text_const_section_index = try self.initSection(
2059 self.text_segment_cmd_index.?,
2060 "__const",
2061 sect.size,
2062 sect.@"align",
2063 .{},
2064 );
2065 }
2066
2067 break :blk .{
2068 .seg = self.text_segment_cmd_index.?,
2069 .sect = self.text_const_section_index.?,
2070 };
2071 }
2072 }
2073
2074 if (mem.eql(u8, segname, "__DATA_CONST")) {
2075 if (self.data_const_section_index == null) {
2076 self.data_const_section_index = try self.initSection(
2077 self.data_const_segment_cmd_index.?,
2078 "__const",
2079 sect.size,
2080 sect.@"align",
2081 .{},
2082 );
2083 }
2084
2085 break :blk .{
2086 .seg = self.data_const_segment_cmd_index.?,
2087 .sect = self.data_const_section_index.?,
2088 };
2089 }
2090
2091 if (mem.eql(u8, segname, "__DATA")) {
2092 if (mem.eql(u8, sectname, "__const")) {
2093 if (self.data_const_section_index == null) {
2094 self.data_const_section_index = try self.initSection(
2095 self.data_const_segment_cmd_index.?,
2096 "__const",
2097 sect.size,
2098 sect.@"align",
2099 .{},
2100 );
2101 }
2102
2103 break :blk .{
2104 .seg = self.data_const_segment_cmd_index.?,
2105 .sect = self.data_const_section_index.?,
2106 };
2107 } else if (mem.eql(u8, sectname, "__cfstring")) {
2108 if (self.objc_cfstring_section_index == null) {
2109 self.objc_cfstring_section_index = try self.initSection(
2110 self.data_const_segment_cmd_index.?,
2111 "__cfstring",
2112 sect.size,
2113 sect.@"align",
2114 .{},
2115 );
2116 }
2117
2118 break :blk .{
2119 .seg = self.data_const_segment_cmd_index.?,
2120 .sect = self.objc_cfstring_section_index.?,
2121 };
2122 } else if (mem.eql(u8, sectname, "__objc_classlist")) {
2123 if (self.objc_classlist_section_index == null) {
2124 self.objc_classlist_section_index = try self.initSection(
2125 self.data_const_segment_cmd_index.?,
2126 "__objc_classlist",
2127 sect.size,
2128 sect.@"align",
2129 .{},
2130 );
2131 }
2132
2133 break :blk .{
2134 .seg = self.data_const_segment_cmd_index.?,
2135 .sect = self.objc_classlist_section_index.?,
2136 };
2137 } else if (mem.eql(u8, sectname, "__objc_imageinfo")) {
2138 if (self.objc_imageinfo_section_index == null) {
2139 self.objc_imageinfo_section_index = try self.initSection(
2140 self.data_const_segment_cmd_index.?,
2141 "__objc_imageinfo",
2142 sect.size,
2143 sect.@"align",
2144 .{},
2145 );
2146 }
2147
2148 break :blk .{
2149 .seg = self.data_const_segment_cmd_index.?,
2150 .sect = self.objc_imageinfo_section_index.?,
2151 };
2152 } else if (mem.eql(u8, sectname, "__objc_const")) {
2153 if (self.objc_const_section_index == null) {
2154 self.objc_const_section_index = try self.initSection(
2155 self.data_segment_cmd_index.?,
2156 "__objc_const",
2157 sect.size,
2158 sect.@"align",
2159 .{},
2160 );
2161 }
2162
2163 break :blk .{
2164 .seg = self.data_segment_cmd_index.?,
2165 .sect = self.objc_const_section_index.?,
2166 };
2167 } else if (mem.eql(u8, sectname, "__objc_classrefs")) {
2168 if (self.objc_classrefs_section_index == null) {
2169 self.objc_classrefs_section_index = try self.initSection(
2170 self.data_segment_cmd_index.?,
2171 "__objc_classrefs",
2172 sect.size,
2173 sect.@"align",
2174 .{},
2175 );
2176 }
2177
2178 break :blk .{
2179 .seg = self.data_segment_cmd_index.?,
2180 .sect = self.objc_classrefs_section_index.?,
2181 };
2182 } else if (mem.eql(u8, sectname, "__objc_data")) {
2183 if (self.objc_data_section_index == null) {
2184 self.objc_data_section_index = try self.initSection(
2185 self.data_segment_cmd_index.?,
2186 "__objc_data",
2187 sect.size,
2188 sect.@"align",
2189 .{},
2190 );
2191 }
2192
2193 break :blk .{
2194 .seg = self.data_segment_cmd_index.?,
2195 .sect = self.objc_data_section_index.?,
2196 };
2197 } else if (mem.eql(u8, sectname, ".rustc")) {
2198 if (self.rustc_section_index == null) {
2199 self.rustc_section_index = try self.initSection(
2200 self.data_segment_cmd_index.?,
2201 ".rustc",
2202 sect.size,
2203 sect.@"align",
2204 .{},
2205 );
2206 // We need to preserve the section size for rustc to properly
2207 // decompress the metadata.
2208 self.rustc_section_size = sect.size;
2209 }
2210
2211 break :blk .{
2212 .seg = self.data_segment_cmd_index.?,
2213 .sect = self.rustc_section_index.?,
2214 };
2215 } else {
2216 if (self.data_section_index == null) {
2217 self.data_section_index = try self.initSection(
2218 self.data_segment_cmd_index.?,
2219 "__data",
2220 sect.size,
2221 sect.@"align",
2222 .{},
2223 );
2224 }
2225
2226 break :blk .{
2227 .seg = self.data_segment_cmd_index.?,
2228 .sect = self.data_section_index.?,
2229 };
2230 }
2231 }
2232
2233 if (mem.eql(u8, "__LLVM", segname) and mem.eql(u8, "__asm", sectname)) {
2234 log.debug("TODO LLVM asm section: type 0x{x}, name '{s},{s}'", .{
2235 sect.flags, segname, sectname,
2236 });
2237 }
2238
2239 break :blk null;
2240 },
2241 else => break :blk null,
2242 }
2243 };
2244 return res;
2245}
2246
2247pub fn createEmptyAtom(gpa: Allocator, sym_index: u32, size: u64, alignment: u32) !*Atom {
2248 const size_usize = math.cast(usize, size) orelse return error.Overflow;
2249 const atom = try gpa.create(Atom);
2250 errdefer gpa.destroy(atom);
2251 atom.* = Atom.empty;
2252 atom.sym_index = sym_index;
2253 atom.size = size;
2254 atom.alignment = alignment;
2255
2256 try atom.code.resize(gpa, size_usize);
2257 mem.set(u8, atom.code.items, 0);
2258
2259 return atom;
2260}
2261
2262pub fn writeAtom(self: *MachO, atom: *Atom, match: MatchingSection) !void {
2263 const sect = self.getSection(match);
2264 const sym = atom.getSymbol(self);
2265 const file_offset = sect.offset + sym.n_value - sect.addr;
2266 try atom.resolveRelocs(self);
2267 log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset });
2268 try self.base.file.?.pwriteAll(atom.code.items, file_offset);
2269}
2270
2271fn allocateSymbols(self: *MachO) !void {
2272 var it = self.atoms.iterator();
2273 while (it.next()) |entry| {
2274 const match = entry.key_ptr.*;
2275 var atom = entry.value_ptr.*;
2276
2277 while (atom.prev) |prev| {
2278 atom = prev;
2279 }
2280
2281 const n_sect = self.getSectionOrdinal(match);
2282 const sect = self.getSection(match);
2283 var base_vaddr = sect.addr;
2284
2285 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{ n_sect, sect.segName(), sect.sectName() });
2286
2287 while (true) {
2288 const alignment = try math.powi(u32, 2, atom.alignment);
2289 base_vaddr = mem.alignForwardGeneric(u64, base_vaddr, alignment);
2290
2291 const sym = atom.getSymbolPtr(self);
2292 sym.n_value = base_vaddr;
2293 sym.n_sect = n_sect;
2294
2295 log.debug(" ATOM(%{d}, '{s}') @{x}", .{ atom.sym_index, atom.getName(self), base_vaddr });
2296
2297 // Update each symbol contained within the atom
2298 for (atom.contained.items) |sym_at_off| {
2299 const contained_sym = self.getSymbolPtr(.{ .sym_index = sym_at_off.sym_index, .file = atom.file });
2300 contained_sym.n_value = base_vaddr + sym_at_off.offset;
2301 contained_sym.n_sect = n_sect;
2302 }
2303
2304 base_vaddr += atom.size;
1883 base_vaddr += atom.size;
23051884
23061885 if (atom.next) |next| {
23071886 atom = next;
......@@ -2310,24 +1889,6 @@ fn allocateSymbols(self: *MachO) !void {
23101889 }
23111890}
23121891
2313fn shiftLocalsByOffset(self: *MachO, match: MatchingSection, offset: i64) !void {
2314 var atom = self.atoms.get(match) orelse return;
2315
2316 while (true) {
2317 const atom_sym = atom.getSymbolPtr(self);
2318 atom_sym.n_value = @intCast(u64, @intCast(i64, atom_sym.n_value) + offset);
2319
2320 for (atom.contained.items) |sym_at_off| {
2321 const contained_sym = self.getSymbolPtr(.{ .sym_index = sym_at_off.sym_index, .file = atom.file });
2322 contained_sym.n_value = @intCast(u64, @intCast(i64, contained_sym.n_value) + offset);
2323 }
2324
2325 if (atom.prev) |prev| {
2326 atom = prev;
2327 } else break;
2328 }
2329}
2330
23311892fn allocateSpecialSymbols(self: *MachO) !void {
23321893 for (&[_][]const u8{
23331894 "___dso_handle",
......@@ -2336,16 +1897,13 @@ fn allocateSpecialSymbols(self: *MachO) !void {
23361897 const global = self.globals.get(name) orelse continue;
23371898 if (global.file != null) continue;
23381899 const sym = self.getSymbolPtr(global);
2339 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
2340 sym.n_sect = self.getSectionOrdinal(.{
2341 .seg = self.text_segment_cmd_index.?,
2342 .sect = 0,
2343 });
2344 sym.n_value = seg.inner.vmaddr;
1900 const seg = self.segments.items[self.text_segment_cmd_index.?];
1901 sym.n_sect = 1;
1902 sym.n_value = seg.vmaddr;
23451903
23461904 log.debug("allocating {s} at the start of {s}", .{
23471905 name,
2348 seg.inner.segName(),
1906 seg.segName(),
23491907 });
23501908 }
23511909}
......@@ -2353,18 +1911,21 @@ fn allocateSpecialSymbols(self: *MachO) !void {
23531911fn writeAtomsOneShot(self: *MachO) !void {
23541912 assert(self.mode == .one_shot);
23551913
2356 var it = self.atoms.iterator();
2357 while (it.next()) |entry| {
2358 const sect = self.getSection(entry.key_ptr.*);
2359 var atom: *Atom = entry.value_ptr.*;
1914 const gpa = self.base.allocator;
1915 const slice = self.sections.slice();
1916
1917 for (slice.items(.last_atom)) |last_atom, sect_id| {
1918 const header = slice.items(.header)[sect_id];
1919 if (header.size == 0) continue;
1920 var atom = last_atom.?;
23601921
2361 if (sect.flags == macho.S_ZEROFILL or sect.flags == macho.S_THREAD_LOCAL_ZEROFILL) continue;
1922 if (header.isZerofill()) continue;
23621923
2363 var buffer = std.ArrayList(u8).init(self.base.allocator);
1924 var buffer = std.ArrayList(u8).init(gpa);
23641925 defer buffer.deinit();
2365 try buffer.ensureTotalCapacity(math.cast(usize, sect.size) orelse return error.Overflow);
1926 try buffer.ensureTotalCapacity(math.cast(usize, header.size) orelse return error.Overflow);
23661927
2367 log.debug("writing atoms in {s},{s}", .{ sect.segName(), sect.sectName() });
1928 log.debug("writing atoms in {s},{s}", .{ header.segName(), header.sectName() });
23681929
23691930 while (atom.prev) |prev| {
23701931 atom = prev;
......@@ -2399,18 +1960,18 @@ fn writeAtomsOneShot(self: *MachO) !void {
23991960 if (atom.next) |next| {
24001961 atom = next;
24011962 } else {
2402 assert(buffer.items.len == sect.size);
2403 log.debug(" (writing at file offset 0x{x})", .{sect.offset});
2404 try self.base.file.?.pwriteAll(buffer.items, sect.offset);
1963 assert(buffer.items.len == header.size);
1964 log.debug(" (writing at file offset 0x{x})", .{header.offset});
1965 try self.base.file.?.pwriteAll(buffer.items, header.offset);
24051966 break;
24061967 }
24071968 }
24081969 }
24091970}
24101971
2411fn writePadding(self: *MachO, match: MatchingSection, size: usize, writer: anytype) !void {
2412 const is_code = match.seg == self.text_segment_cmd_index.? and match.sect == self.text_section_index.?;
2413 const min_alignment: u3 = if (!is_code)
1972fn writePadding(self: *MachO, sect_id: u8, size: usize, writer: anytype) !void {
1973 const header = self.sections.items(.header)[sect_id];
1974 const min_alignment: u3 = if (!header.isCode())
24141975 1
24151976 else switch (self.base.options.target.cpu.arch) {
24161977 .aarch64 => @sizeOf(u32),
......@@ -2421,7 +1982,7 @@ fn writePadding(self: *MachO, match: MatchingSection, size: usize, writer: anyty
24211982 const len = @divExact(size, min_alignment);
24221983 var i: usize = 0;
24231984 while (i < len) : (i += 1) {
2424 if (!is_code) {
1985 if (!header.isCode()) {
24251986 try writer.writeByte(0);
24261987 } else switch (self.base.options.target.cpu.arch) {
24271988 .aarch64 => {
......@@ -2439,20 +2000,19 @@ fn writePadding(self: *MachO, match: MatchingSection, size: usize, writer: anyty
24392000fn writeAtomsIncremental(self: *MachO) !void {
24402001 assert(self.mode == .incremental);
24412002
2442 var it = self.atoms.iterator();
2443 while (it.next()) |entry| {
2444 const match = entry.key_ptr.*;
2445 const sect = self.getSection(match);
2446 var atom: *Atom = entry.value_ptr.*;
2003 const slice = self.sections.slice();
2004 for (slice.items(.last_atom)) |last, i| {
2005 var atom: *Atom = last orelse continue;
2006 const sect_i = @intCast(u8, i);
2007 const header = slice.items(.header)[sect_i];
24472008
2448 // TODO handle zerofill in stage2
2449 // if (sect.flags == macho.S_ZEROFILL or sect.flags == macho.S_THREAD_LOCAL_ZEROFILL) continue;
2009 if (header.isZerofill()) continue;
24502010
2451 log.debug("writing atoms in {s},{s}", .{ sect.segName(), sect.sectName() });
2011 log.debug("writing atoms in {s},{s}", .{ header.segName(), header.sectName() });
24522012
24532013 while (true) {
24542014 if (atom.dirty) {
2455 try self.writeAtom(atom, match);
2015 try self.writeAtom(atom, sect_i);
24562016 atom.dirty = false;
24572017 }
24582018
......@@ -2503,10 +2063,7 @@ pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
25032063 try self.managed_atoms.append(gpa, atom);
25042064 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
25052065
2506 try self.allocateAtomCommon(atom, .{
2507 .seg = self.data_const_segment_cmd_index.?,
2508 .sect = self.got_section_index.?,
2509 });
2066 try self.allocateAtomCommon(atom, self.got_section_index.?);
25102067
25112068 return atom;
25122069}
......@@ -2535,7 +2092,7 @@ pub fn createTlvPtrAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
25352092 try self.managed_atoms.append(gpa, atom);
25362093 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
25372094
2538 const match = (try self.getMatchingSection(.{
2095 const match = (try self.getOutputSection(.{
25392096 .segname = makeStaticString("__DATA"),
25402097 .sectname = makeStaticString("__thread_ptrs"),
25412098 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
......@@ -2561,10 +2118,7 @@ fn createDyldPrivateAtom(self: *MachO) !void {
25612118 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);
25622119 self.dyld_private_atom = atom;
25632120
2564 try self.allocateAtomCommon(atom, .{
2565 .seg = self.data_segment_cmd_index.?,
2566 .sect = self.data_section_index.?,
2567 });
2121 try self.allocateAtomCommon(atom, self.data_section_index.?);
25682122
25692123 try self.managed_atoms.append(gpa, atom);
25702124 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
......@@ -2692,10 +2246,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
26922246 }
26932247 self.stub_helper_preamble_atom = atom;
26942248
2695 try self.allocateAtomCommon(atom, .{
2696 .seg = self.text_segment_cmd_index.?,
2697 .sect = self.stub_helper_section_index.?,
2698 });
2249 try self.allocateAtomCommon(atom, self.stub_helper_section_index.?);
26992250
27002251 try self.managed_atoms.append(gpa, atom);
27012252 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
......@@ -2771,10 +2322,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
27712322 try self.managed_atoms.append(gpa, atom);
27722323 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
27732324
2774 try self.allocateAtomCommon(atom, .{
2775 .seg = self.text_segment_cmd_index.?,
2776 .sect = self.stub_helper_section_index.?,
2777 });
2325 try self.allocateAtomCommon(atom, self.stub_helper_section_index.?);
27782326
27792327 return atom;
27802328}
......@@ -2814,10 +2362,7 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWi
28142362 try self.managed_atoms.append(gpa, atom);
28152363 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
28162364
2817 try self.allocateAtomCommon(atom, .{
2818 .seg = self.data_segment_cmd_index.?,
2819 .sect = self.la_symbol_ptr_section_index.?,
2820 });
2365 try self.allocateAtomCommon(atom, self.la_symbol_ptr_section_index.?);
28212366
28222367 return atom;
28232368}
......@@ -2896,10 +2441,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
28962441 try self.managed_atoms.append(gpa, atom);
28972442 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
28982443
2899 try self.allocateAtomCommon(atom, .{
2900 .seg = self.text_segment_cmd_index.?,
2901 .sect = self.stubs_section_index.?,
2902 });
2444 try self.allocateAtomCommon(atom, self.stubs_section_index.?);
29032445
29042446 return atom;
29052447}
......@@ -2917,19 +2459,18 @@ fn createTentativeDefAtoms(self: *MachO) !void {
29172459
29182460 // Convert any tentative definition into a regular symbol and allocate
29192461 // text blocks for each tentative definition.
2920 const match = MatchingSection{
2921 .seg = self.data_segment_cmd_index.?,
2922 .sect = self.bss_section_index.?,
2923 };
2924 _ = try self.section_ordinals.getOrPut(gpa, match);
2925
29262462 const size = sym.n_value;
29272463 const alignment = (sym.n_desc >> 8) & 0x0f;
2464 const n_sect = (try self.getOutputSection(.{
2465 .segname = makeStaticString("__DATA"),
2466 .sectname = makeStaticString("__bss"),
2467 .flags = macho.S_ZEROFILL,
2468 })).?;
29282469
29292470 sym.* = .{
29302471 .n_strx = sym.n_strx,
29312472 .n_type = macho.N_SECT | macho.N_EXT,
2932 .n_sect = 0,
2473 .n_sect = n_sect,
29332474 .n_desc = 0,
29342475 .n_value = 0,
29352476 };
......@@ -2937,7 +2478,7 @@ fn createTentativeDefAtoms(self: *MachO) !void {
29372478 const atom = try MachO.createEmptyAtom(gpa, global.sym_index, size, alignment);
29382479 atom.file = global.file;
29392480
2940 try self.allocateAtomCommon(atom, match);
2481 try self.allocateAtomCommon(atom, n_sect);
29412482
29422483 if (global.file) |file| {
29432484 const object = &self.objects.items[file];
......@@ -3060,7 +2601,8 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
30602601 gop.value_ptr.* = current;
30612602}
30622603
3063fn resolveSymbolsInObject(self: *MachO, object: *Object, object_id: u16) !void {
2604fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
2605 const object = &self.objects.items[object_id];
30642606 log.debug("resolving symbols in '{s}'", .{object.name});
30652607
30662608 for (object.symtab.items) |sym, index| {
......@@ -3115,6 +2657,8 @@ fn resolveSymbolsInObject(self: *MachO, object: *Object, object_id: u16) !void {
31152657fn resolveSymbolsInArchives(self: *MachO) !void {
31162658 if (self.archives.items.len == 0) return;
31172659
2660 const gpa = self.base.allocator;
2661 const cpu_arch = self.base.options.target.cpu.arch;
31182662 var next_sym: usize = 0;
31192663 loop: while (next_sym < self.unresolved.count()) {
31202664 const global = self.globals.values()[self.unresolved.keys()[next_sym]];
......@@ -3129,13 +2673,9 @@ fn resolveSymbolsInArchives(self: *MachO) !void {
31292673 assert(offsets.items.len > 0);
31302674
31312675 const object_id = @intCast(u16, self.objects.items.len);
3132 const object = try self.objects.addOne(self.base.allocator);
3133 object.* = try archive.parseObject(
3134 self.base.allocator,
3135 self.base.options.target.cpu.arch,
3136 offsets.items[0],
3137 );
3138 try self.resolveSymbolsInObject(object, object_id);
2676 const object = try archive.parseObject(gpa, cpu_arch, offsets.items[0]);
2677 try self.objects.append(gpa, object);
2678 try self.resolveSymbolsInObject(object_id);
31392679
31402680 continue :loop;
31412681 }
......@@ -3159,7 +2699,6 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
31592699
31602700 const dylib_id = @intCast(u16, id);
31612701 if (!self.referenced_dylibs.contains(dylib_id)) {
3162 try self.addLoadDylibLC(dylib_id);
31632702 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});
31642703 }
31652704
......@@ -3257,7 +2796,6 @@ fn resolveDyldStubBinder(self: *MachO) !void {
32572796
32582797 const dylib_id = @intCast(u16, id);
32592798 if (!self.referenced_dylibs.contains(dylib_id)) {
3260 try self.addLoadDylibLC(dylib_id);
32612799 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});
32622800 }
32632801
......@@ -3280,47 +2818,192 @@ fn resolveDyldStubBinder(self: *MachO) !void {
32802818 self.got_entries.items[got_index].sym_index = got_atom.sym_index;
32812819}
32822820
3283fn addLoadDylibLC(self: *MachO, id: u16) !void {
3284 const dylib = self.dylibs.items[id];
3285 const dylib_id = dylib.id orelse unreachable;
3286 var dylib_cmd = try macho.createLoadDylibCommand(
3287 self.base.allocator,
3288 if (dylib.weak) .LOAD_WEAK_DYLIB else .LOAD_DYLIB,
3289 dylib_id.name,
3290 dylib_id.timestamp,
3291 dylib_id.current_version,
3292 dylib_id.compatibility_version,
3293 );
3294 errdefer dylib_cmd.deinit(self.base.allocator);
3295 try self.load_commands.append(self.base.allocator, .{ .dylib = dylib_cmd });
3296 self.load_commands_dirty = true;
2821fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
2822 const name_len = mem.sliceTo(default_dyld_path, 0).len;
2823 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
2824 u64,
2825 @sizeOf(macho.dylinker_command) + name_len,
2826 @sizeOf(u64),
2827 ));
2828 try lc_writer.writeStruct(macho.dylinker_command{
2829 .cmd = .LOAD_DYLINKER,
2830 .cmdsize = cmdsize,
2831 .name = @sizeOf(macho.dylinker_command),
2832 });
2833 try lc_writer.writeAll(mem.sliceTo(default_dyld_path, 0));
2834 const padding = cmdsize - @sizeOf(macho.dylinker_command) - name_len;
2835 if (padding > 0) {
2836 try lc_writer.writeByteNTimes(0, padding);
2837 }
2838 ncmds.* += 1;
32972839}
32982840
3299fn addCodeSignatureLC(self: *MachO) !void {
3300 if (self.code_signature_cmd_index != null or self.code_signature == null) return;
3301 self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);
3302 try self.load_commands.append(self.base.allocator, .{
3303 .linkedit_data = .{
3304 .cmd = .CODE_SIGNATURE,
3305 .cmdsize = @sizeOf(macho.linkedit_data_command),
3306 .dataoff = 0,
3307 .datasize = 0,
2841fn writeMainLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
2842 if (self.base.options.output_mode != .Exe) return;
2843 const seg = self.segments.items[self.text_segment_cmd_index.?];
2844 const global = try self.getEntryPoint();
2845 const sym = self.getSymbol(global);
2846 try lc_writer.writeStruct(macho.entry_point_command{
2847 .cmd = .MAIN,
2848 .cmdsize = @sizeOf(macho.entry_point_command),
2849 .entryoff = @intCast(u32, sym.n_value - seg.vmaddr),
2850 .stacksize = self.base.options.stack_size_override orelse 0,
2851 });
2852 ncmds.* += 1;
2853}
2854
2855const WriteDylibLCCtx = struct {
2856 cmd: macho.LC,
2857 name: []const u8,
2858 timestamp: u32 = 2,
2859 current_version: u32 = 0x10000,
2860 compatibility_version: u32 = 0x10000,
2861};
2862
2863fn writeDylibLC(ctx: WriteDylibLCCtx, ncmds: *u32, lc_writer: anytype) !void {
2864 const name_len = ctx.name.len + 1;
2865 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
2866 u64,
2867 @sizeOf(macho.dylib_command) + name_len,
2868 @sizeOf(u64),
2869 ));
2870 try lc_writer.writeStruct(macho.dylib_command{
2871 .cmd = ctx.cmd,
2872 .cmdsize = cmdsize,
2873 .dylib = .{
2874 .name = @sizeOf(macho.dylib_command),
2875 .timestamp = ctx.timestamp,
2876 .current_version = ctx.current_version,
2877 .compatibility_version = ctx.compatibility_version,
33082878 },
33092879 });
3310 self.load_commands_dirty = true;
2880 try lc_writer.writeAll(ctx.name);
2881 try lc_writer.writeByte(0);
2882 const padding = cmdsize - @sizeOf(macho.dylib_command) - name_len;
2883 if (padding > 0) {
2884 try lc_writer.writeByteNTimes(0, padding);
2885 }
2886 ncmds.* += 1;
33112887}
33122888
3313fn setEntryPoint(self: *MachO) !void {
3314 if (self.base.options.output_mode != .Exe) return;
2889fn writeDylibIdLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
2890 if (self.base.options.output_mode != .Lib) return;
2891 const install_name = self.base.options.install_name orelse self.base.options.emit.?.sub_path;
2892 const curr = self.base.options.version orelse std.builtin.Version{
2893 .major = 1,
2894 .minor = 0,
2895 .patch = 0,
2896 };
2897 const compat = self.base.options.compatibility_version orelse std.builtin.Version{
2898 .major = 1,
2899 .minor = 0,
2900 .patch = 0,
2901 };
2902 try writeDylibLC(.{
2903 .cmd = .ID_DYLIB,
2904 .name = install_name,
2905 .current_version = curr.major << 16 | curr.minor << 8 | curr.patch,
2906 .compatibility_version = compat.major << 16 | compat.minor << 8 | compat.patch,
2907 }, ncmds, lc_writer);
2908}
33152909
3316 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
3317 const global = try self.getEntryPoint();
3318 const sym = self.getSymbol(global);
3319 const ec = &self.load_commands.items[self.main_cmd_index.?].main;
3320 ec.entryoff = @intCast(u32, sym.n_value - seg.inner.vmaddr);
3321 ec.stacksize = self.base.options.stack_size_override orelse 0;
3322 self.entry_addr = sym.n_value;
3323 self.load_commands_dirty = true;
2910const RpathIterator = struct {
2911 buffer: []const []const u8,
2912 table: std.StringHashMap(void),
2913 count: usize = 0,
2914
2915 fn init(gpa: Allocator, rpaths: []const []const u8) RpathIterator {
2916 return .{ .buffer = rpaths, .table = std.StringHashMap(void).init(gpa) };
2917 }
2918
2919 fn deinit(it: *RpathIterator) void {
2920 it.table.deinit();
2921 }
2922
2923 fn next(it: *RpathIterator) !?[]const u8 {
2924 while (true) {
2925 if (it.count >= it.buffer.len) return null;
2926 const rpath = it.buffer[it.count];
2927 it.count += 1;
2928 const gop = try it.table.getOrPut(rpath);
2929 if (gop.found_existing) continue;
2930 return rpath;
2931 }
2932 }
2933};
2934
2935fn writeRpathLCs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
2936 const gpa = self.base.allocator;
2937
2938 var it = RpathIterator.init(gpa, self.base.options.rpath_list);
2939 defer it.deinit();
2940
2941 while (try it.next()) |rpath| {
2942 const rpath_len = rpath.len + 1;
2943 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
2944 u64,
2945 @sizeOf(macho.rpath_command) + rpath_len,
2946 @sizeOf(u64),
2947 ));
2948 try lc_writer.writeStruct(macho.rpath_command{
2949 .cmdsize = cmdsize,
2950 .path = @sizeOf(macho.rpath_command),
2951 });
2952 try lc_writer.writeAll(rpath);
2953 try lc_writer.writeByte(0);
2954 const padding = cmdsize - @sizeOf(macho.rpath_command) - rpath_len;
2955 if (padding > 0) {
2956 try lc_writer.writeByteNTimes(0, padding);
2957 }
2958 ncmds.* += 1;
2959 }
2960}
2961
2962fn writeBuildVersionLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
2963 const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
2964 const platform_version = blk: {
2965 const ver = self.base.options.target.os.version_range.semver.min;
2966 const platform_version = ver.major << 16 | ver.minor << 8;
2967 break :blk platform_version;
2968 };
2969 const sdk_version = if (self.base.options.native_darwin_sdk) |sdk| blk: {
2970 const ver = sdk.version;
2971 const sdk_version = ver.major << 16 | ver.minor << 8;
2972 break :blk sdk_version;
2973 } else platform_version;
2974 const is_simulator_abi = self.base.options.target.abi == .simulator;
2975 try lc_writer.writeStruct(macho.build_version_command{
2976 .cmdsize = cmdsize,
2977 .platform = switch (self.base.options.target.os.tag) {
2978 .macos => .MACOS,
2979 .ios => if (is_simulator_abi) macho.PLATFORM.IOSSIMULATOR else macho.PLATFORM.IOS,
2980 .watchos => if (is_simulator_abi) macho.PLATFORM.WATCHOSSIMULATOR else macho.PLATFORM.WATCHOS,
2981 .tvos => if (is_simulator_abi) macho.PLATFORM.TVOSSIMULATOR else macho.PLATFORM.TVOS,
2982 else => unreachable,
2983 },
2984 .minos = platform_version,
2985 .sdk = sdk_version,
2986 .ntools = 1,
2987 });
2988 try lc_writer.writeAll(mem.asBytes(&macho.build_tool_version{
2989 .tool = .LD,
2990 .version = 0x0,
2991 }));
2992 ncmds.* += 1;
2993}
2994
2995fn writeLoadDylibLCs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
2996 for (self.referenced_dylibs.keys()) |id| {
2997 const dylib = self.dylibs.items[id];
2998 const dylib_id = dylib.id orelse unreachable;
2999 try writeDylibLC(.{
3000 .cmd = if (dylib.weak) .LOAD_WEAK_DYLIB else .LOAD_DYLIB,
3001 .name = dylib_id.name,
3002 .timestamp = dylib_id.timestamp,
3003 .current_version = dylib_id.current_version,
3004 .compatibility_version = dylib_id.compatibility_version,
3005 }, ncmds, lc_writer);
3006 }
33243007}
33253008
33263009pub fn deinit(self: *MachO) void {
......@@ -3334,7 +3017,6 @@ pub fn deinit(self: *MachO) void {
33343017 d_sym.deinit(gpa);
33353018 }
33363019
3337 self.section_ordinals.deinit(gpa);
33383020 self.tlv_ptr_entries.deinit(gpa);
33393021 self.tlv_ptr_entries_free_list.deinit(gpa);
33403022 self.tlv_ptr_entries_table.deinit(gpa);
......@@ -3371,24 +3053,19 @@ pub fn deinit(self: *MachO) void {
33713053 self.dylibs_map.deinit(gpa);
33723054 self.referenced_dylibs.deinit(gpa);
33733055
3374 for (self.load_commands.items) |*lc| {
3375 lc.deinit(gpa);
3056 self.segments.deinit(gpa);
3057
3058 for (self.sections.items(.free_list)) |*list| {
3059 list.deinit(gpa);
33763060 }
3377 self.load_commands.deinit(gpa);
3061 self.sections.deinit(gpa);
33783062
33793063 for (self.managed_atoms.items) |atom| {
33803064 atom.deinit(gpa);
33813065 gpa.destroy(atom);
33823066 }
33833067 self.managed_atoms.deinit(gpa);
3384 self.atoms.deinit(gpa);
3385 {
3386 var it = self.atom_free_lists.valueIterator();
3387 while (it.next()) |free_list| {
3388 free_list.deinit(gpa);
3389 }
3390 self.atom_free_lists.deinit(gpa);
3391 }
3068
33923069 if (self.base.options.module) |mod| {
33933070 for (self.decls.keys()) |decl_index| {
33943071 const decl = mod.declPtr(decl_index);
......@@ -3408,34 +3085,24 @@ pub fn deinit(self: *MachO) void {
34083085 }
34093086
34103087 self.atom_by_index_table.deinit(gpa);
3411
3412 if (self.code_signature) |*csig| {
3413 csig.deinit(gpa);
3414 }
34153088}
34163089
34173090pub fn closeFiles(self: MachO) void {
3418 for (self.objects.items) |object| {
3419 object.file.close();
3420 }
34213091 for (self.archives.items) |archive| {
34223092 archive.file.close();
34233093 }
3424 for (self.dylibs.items) |dylib| {
3425 dylib.file.close();
3426 }
34273094 if (self.d_sym) |ds| {
34283095 ds.file.close();
34293096 }
34303097}
34313098
3432fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool) void {
3099fn freeAtom(self: *MachO, atom: *Atom, sect_id: u8, owns_atom: bool) void {
34333100 log.debug("freeAtom {*}", .{atom});
34343101 if (!owns_atom) {
34353102 atom.deinit(self.base.allocator);
34363103 }
34373104
3438 const free_list = self.atom_free_lists.getPtr(match).?;
3105 const free_list = &self.sections.items(.free_list)[sect_id];
34393106 var already_have_free_list_node = false;
34403107 {
34413108 var i: usize = 0;
......@@ -3452,13 +3119,14 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool)
34523119 }
34533120 }
34543121
3455 if (self.atoms.getPtr(match)) |last_atom| {
3456 if (last_atom.* == atom) {
3122 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];
3123 if (maybe_last_atom.*) |last_atom| {
3124 if (last_atom == atom) {
34573125 if (atom.prev) |prev| {
34583126 // TODO shrink the section size here
3459 last_atom.* = prev;
3127 maybe_last_atom.* = prev;
34603128 } else {
3461 _ = self.atoms.fetchRemove(match);
3129 maybe_last_atom.* = null;
34623130 }
34633131 }
34643132 }
......@@ -3486,21 +3154,21 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool)
34863154 }
34873155}
34883156
3489fn shrinkAtom(self: *MachO, atom: *Atom, new_block_size: u64, match: MatchingSection) void {
3157fn shrinkAtom(self: *MachO, atom: *Atom, new_block_size: u64, sect_id: u8) void {
34903158 _ = self;
34913159 _ = atom;
34923160 _ = new_block_size;
3493 _ = match;
3161 _ = sect_id;
34943162 // TODO check the new capacity, and if it crosses the size threshold into a big enough
34953163 // capacity, insert a free list node for it.
34963164}
34973165
3498fn growAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, match: MatchingSection) !u64 {
3166fn growAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, sect_id: u8) !u64 {
34993167 const sym = atom.getSymbol(self);
35003168 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;
35013169 const need_realloc = !align_ok or new_atom_size > atom.capacity(self);
35023170 if (!need_realloc) return sym.n_value;
3503 return self.allocateAtom(atom, new_atom_size, alignment, match);
3171 return self.allocateAtom(atom, new_atom_size, alignment, sect_id);
35043172}
35053173
35063174fn allocateSymbol(self: *MachO) !u32 {
......@@ -3671,10 +3339,11 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
36713339}
36723340
36733341pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Module.Decl.Index) !u32 {
3674 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
3342 const gpa = self.base.allocator;
3343
3344 var code_buffer = std.ArrayList(u8).init(gpa);
36753345 defer code_buffer.deinit();
36763346
3677 const gpa = self.base.allocator;
36783347 const module = self.base.options.module.?;
36793348 const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index);
36803349 if (!gop.found_existing) {
......@@ -3725,25 +3394,25 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
37253394 atom.code.clearRetainingCapacity();
37263395 try atom.code.appendSlice(gpa, code);
37273396
3728 const match = try self.getMatchingSectionAtom(
3397 const sect_id = try self.getOutputSectionAtom(
37293398 atom,
37303399 decl_name,
37313400 typed_value.ty,
37323401 typed_value.val,
37333402 required_alignment,
37343403 );
3735 const addr = try self.allocateAtom(atom, code.len, required_alignment, match);
3404 const addr = try self.allocateAtom(atom, code.len, required_alignment, sect_id);
37363405
37373406 log.debug("allocated atom for {?s} at 0x{x}", .{ name, addr });
37383407 log.debug(" (required alignment 0x{x})", .{required_alignment});
37393408
3740 errdefer self.freeAtom(atom, match, true);
3409 errdefer self.freeAtom(atom, sect_id, true);
37413410
37423411 const symbol = atom.getSymbolPtr(self);
37433412 symbol.* = .{
37443413 .n_strx = name_str_index,
37453414 .n_type = macho.N_SECT,
3746 .n_sect = self.getSectionOrdinal(match),
3415 .n_sect = sect_id + 1,
37473416 .n_desc = 0,
37483417 .n_value = addr,
37493418 };
......@@ -3894,44 +3563,40 @@ fn needsPointerRebase(ty: Type, val: Value, mod: *Module) bool {
38943563 }
38953564}
38963565
3897fn getMatchingSectionAtom(
3566fn getOutputSectionAtom(
38983567 self: *MachO,
38993568 atom: *Atom,
39003569 name: []const u8,
39013570 ty: Type,
39023571 val: Value,
39033572 alignment: u32,
3904) !MatchingSection {
3573) !u8 {
39053574 const code = atom.code.items;
39063575 const mod = self.base.options.module.?;
39073576 const align_log_2 = math.log2(alignment);
39083577 const zig_ty = ty.zigTypeTag();
39093578 const mode = self.base.options.optimize_mode;
3910 const match: MatchingSection = blk: {
3579 const sect_id: u8 = blk: {
39113580 // TODO finish and audit this function
39123581 if (val.isUndefDeep()) {
39133582 if (mode == .ReleaseFast or mode == .ReleaseSmall) {
3914 break :blk MatchingSection{
3915 .seg = self.data_segment_cmd_index.?,
3916 .sect = self.bss_section_index.?,
3917 };
3583 break :blk (try self.getOutputSection(.{
3584 .segname = makeStaticString("__DATA"),
3585 .sectname = makeStaticString("__bss"),
3586 .size = code.len,
3587 .@"align" = align_log_2,
3588 })).?;
39183589 } else {
3919 break :blk MatchingSection{
3920 .seg = self.data_segment_cmd_index.?,
3921 .sect = self.data_section_index.?,
3922 };
3590 break :blk self.data_section_index.?;
39233591 }
39243592 }
39253593
39263594 if (val.castTag(.variable)) |_| {
3927 break :blk MatchingSection{
3928 .seg = self.data_segment_cmd_index.?,
3929 .sect = self.data_section_index.?,
3930 };
3595 break :blk self.data_section_index.?;
39313596 }
39323597
39333598 if (needsPointerRebase(ty, val, mod)) {
3934 break :blk (try self.getMatchingSection(.{
3599 break :blk (try self.getOutputSection(.{
39353600 .segname = makeStaticString("__DATA_CONST"),
39363601 .sectname = makeStaticString("__const"),
39373602 .size = code.len,
......@@ -3941,10 +3606,7 @@ fn getMatchingSectionAtom(
39413606
39423607 switch (zig_ty) {
39433608 .Fn => {
3944 break :blk MatchingSection{
3945 .seg = self.text_segment_cmd_index.?,
3946 .sect = self.text_section_index.?,
3947 };
3609 break :blk self.text_section_index.?;
39483610 },
39493611 .Array => {
39503612 if (val.tag() == .bytes) {
......@@ -3953,7 +3615,7 @@ fn getMatchingSectionAtom(
39533615 .const_slice_u8_sentinel_0,
39543616 .manyptr_const_u8_sentinel_0,
39553617 => {
3956 break :blk (try self.getMatchingSection(.{
3618 break :blk (try self.getOutputSection(.{
39573619 .segname = makeStaticString("__TEXT"),
39583620 .sectname = makeStaticString("__cstring"),
39593621 .flags = macho.S_CSTRING_LITERALS,
......@@ -3967,22 +3629,21 @@ fn getMatchingSectionAtom(
39673629 },
39683630 else => {},
39693631 }
3970 break :blk (try self.getMatchingSection(.{
3632 break :blk (try self.getOutputSection(.{
39713633 .segname = makeStaticString("__TEXT"),
39723634 .sectname = makeStaticString("__const"),
39733635 .size = code.len,
39743636 .@"align" = align_log_2,
39753637 })).?;
39763638 };
3977 const sect = self.getSection(match);
3978 log.debug(" allocating atom '{s}' in '{s},{s}' ({d},{d})", .{
3639 const header = self.sections.items(.header)[sect_id];
3640 log.debug(" allocating atom '{s}' in '{s},{s}', ord({d})", .{
39793641 name,
3980 sect.segName(),
3981 sect.sectName(),
3982 match.seg,
3983 match.sect,
3642 header.segName(),
3643 header.sectName(),
3644 sect_id,
39843645 });
3985 return match;
3646 return sect_id;
39863647}
39873648
39883649fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !u64 {
......@@ -3996,7 +3657,7 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !u64
39963657
39973658 const decl_ptr = self.decls.getPtr(decl_index).?;
39983659 if (decl_ptr.* == null) {
3999 decl_ptr.* = try self.getMatchingSectionAtom(
3660 decl_ptr.* = try self.getOutputSectionAtom(
40003661 &decl.link.macho,
40013662 sym_name,
40023663 decl.ty,
......@@ -4045,7 +3706,7 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !u64
40453706 symbol.* = .{
40463707 .n_strx = name_str_index,
40473708 .n_type = macho.N_SECT,
4048 .n_sect = self.getSectionOrdinal(match),
3709 .n_sect = match + 1,
40493710 .n_desc = 0,
40503711 .n_value = addr,
40513712 };
......@@ -4134,10 +3795,7 @@ pub fn updateDeclExports(
41343795 sym.* = .{
41353796 .n_strx = try self.strtab.insert(gpa, exp_name),
41363797 .n_type = macho.N_SECT | macho.N_EXT,
4137 .n_sect = self.getSectionOrdinal(.{
4138 .seg = self.text_segment_cmd_index.?,
4139 .sect = self.text_section_index.?, // TODO what if we export a variable?
4140 }),
3798 .n_sect = self.text_section_index.? + 1, // TODO what if we export a variable?
41413799 .n_desc = 0,
41423800 .n_value = decl_sym.n_value,
41433801 };
......@@ -4208,10 +3866,10 @@ pub fn deleteExport(self: *MachO, exp: Export) void {
42083866fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {
42093867 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;
42103868 for (unnamed_consts.items) |atom| {
4211 self.freeAtom(atom, .{
4212 .seg = self.text_segment_cmd_index.?,
4213 .sect = self.text_const_section_index.?,
4214 }, true);
3869 // TODO
3870 // const sect_id = atom.getSymbol(self).n_sect;
3871 const sect_id = self.getSectionByName("__TEXT", "__const").?;
3872 self.freeAtom(atom, sect_id, true);
42153873 self.locals_free_list.append(self.base.allocator, atom.sym_index) catch {};
42163874 self.locals.items[atom.sym_index].n_type = 0;
42173875 _ = self.atom_by_index_table.remove(atom.sym_index);
......@@ -4294,6 +3952,7 @@ pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: Fil
42943952}
42953953
42963954fn populateMissingMetadata(self: *MachO) !void {
3955 const gpa = self.base.allocator;
42973956 const cpu_arch = self.base.options.target.cpu.arch;
42983957 const pagezero_vmsize = self.base.options.pagezero_size orelse default_pagezero_vmsize;
42993958 const aligned_pagezero_vmsize = mem.alignBackwardGeneric(u64, pagezero_vmsize, self.page_size);
......@@ -4305,21 +3964,16 @@ fn populateMissingMetadata(self: *MachO) !void {
43053964 log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_vmsize});
43063965 log.warn(" rounding down to 0x{x}", .{aligned_pagezero_vmsize});
43073966 }
4308 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
4309 try self.load_commands.append(self.base.allocator, .{
4310 .segment = .{
4311 .inner = .{
4312 .segname = makeStaticString("__PAGEZERO"),
4313 .vmsize = aligned_pagezero_vmsize,
4314 .cmdsize = @sizeOf(macho.segment_command_64),
4315 },
4316 },
3967 self.pagezero_segment_cmd_index = @intCast(u8, self.segments.items.len);
3968 try self.segments.append(gpa, .{
3969 .segname = makeStaticString("__PAGEZERO"),
3970 .vmsize = aligned_pagezero_vmsize,
3971 .cmdsize = @sizeOf(macho.segment_command_64),
43173972 });
4318 self.load_commands_dirty = true;
43193973 }
43203974
43213975 if (self.text_segment_cmd_index == null) {
4322 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
3976 self.text_segment_cmd_index = @intCast(u8, self.segments.items.len);
43233977 const needed_size = if (self.mode == .incremental) blk: {
43243978 const headerpad_size = @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size);
43253979 const program_code_size_hint = self.base.options.program_code_size_hint;
......@@ -4329,20 +3983,15 @@ fn populateMissingMetadata(self: *MachO) !void {
43293983 log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size });
43303984 break :blk needed_size;
43313985 } else 0;
4332 try self.load_commands.append(self.base.allocator, .{
4333 .segment = .{
4334 .inner = .{
4335 .segname = makeStaticString("__TEXT"),
4336 .vmaddr = aligned_pagezero_vmsize,
4337 .vmsize = needed_size,
4338 .filesize = needed_size,
4339 .maxprot = macho.PROT.READ | macho.PROT.EXEC,
4340 .initprot = macho.PROT.READ | macho.PROT.EXEC,
4341 .cmdsize = @sizeOf(macho.segment_command_64),
4342 },
4343 },
3986 try self.segments.append(gpa, .{
3987 .segname = makeStaticString("__TEXT"),
3988 .vmaddr = aligned_pagezero_vmsize,
3989 .vmsize = needed_size,
3990 .filesize = needed_size,
3991 .maxprot = macho.PROT.READ | macho.PROT.EXEC,
3992 .initprot = macho.PROT.READ | macho.PROT.EXEC,
3993 .cmdsize = @sizeOf(macho.segment_command_64),
43443994 });
4345 self.load_commands_dirty = true;
43463995 }
43473996
43483997 if (self.text_section_index == null) {
......@@ -4353,7 +4002,7 @@ fn populateMissingMetadata(self: *MachO) !void {
43534002 };
43544003 const needed_size = if (self.mode == .incremental) self.base.options.program_code_size_hint else 0;
43554004 self.text_section_index = try self.initSection(
4356 self.text_segment_cmd_index.?,
4005 "__TEXT",
43574006 "__text",
43584007 needed_size,
43594008 alignment,
......@@ -4376,7 +4025,7 @@ fn populateMissingMetadata(self: *MachO) !void {
43764025 };
43774026 const needed_size = if (self.mode == .incremental) stub_size * self.base.options.symbol_count_hint else 0;
43784027 self.stubs_section_index = try self.initSection(
4379 self.text_segment_cmd_index.?,
4028 "__TEXT",
43804029 "__stubs",
43814030 needed_size,
43824031 alignment,
......@@ -4408,7 +4057,7 @@ fn populateMissingMetadata(self: *MachO) !void {
44084057 else
44094058 0;
44104059 self.stub_helper_section_index = try self.initSection(
4411 self.text_segment_cmd_index.?,
4060 "__TEXT",
44124061 "__stub_helper",
44134062 needed_size,
44144063 alignment,
......@@ -4419,7 +4068,7 @@ fn populateMissingMetadata(self: *MachO) !void {
44194068 }
44204069
44214070 if (self.data_const_segment_cmd_index == null) {
4422 self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
4071 self.data_const_segment_cmd_index = @intCast(u8, self.segments.items.len);
44234072 var vmaddr: u64 = 0;
44244073 var fileoff: u64 = 0;
44254074 var needed_size: u64 = 0;
......@@ -4434,21 +4083,16 @@ fn populateMissingMetadata(self: *MachO) !void {
44344083 fileoff + needed_size,
44354084 });
44364085 }
4437 try self.load_commands.append(self.base.allocator, .{
4438 .segment = .{
4439 .inner = .{
4440 .segname = makeStaticString("__DATA_CONST"),
4441 .vmaddr = vmaddr,
4442 .vmsize = needed_size,
4443 .fileoff = fileoff,
4444 .filesize = needed_size,
4445 .maxprot = macho.PROT.READ | macho.PROT.WRITE,
4446 .initprot = macho.PROT.READ | macho.PROT.WRITE,
4447 .cmdsize = @sizeOf(macho.segment_command_64),
4448 },
4449 },
4086 try self.segments.append(gpa, .{
4087 .segname = makeStaticString("__DATA_CONST"),
4088 .vmaddr = vmaddr,
4089 .vmsize = needed_size,
4090 .fileoff = fileoff,
4091 .filesize = needed_size,
4092 .maxprot = macho.PROT.READ | macho.PROT.WRITE,
4093 .initprot = macho.PROT.READ | macho.PROT.WRITE,
4094 .cmdsize = @sizeOf(macho.segment_command_64),
44504095 });
4451 self.load_commands_dirty = true;
44524096 }
44534097
44544098 if (self.got_section_index == null) {
......@@ -4458,7 +4102,7 @@ fn populateMissingMetadata(self: *MachO) !void {
44584102 0;
44594103 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
44604104 self.got_section_index = try self.initSection(
4461 self.data_const_segment_cmd_index.?,
4105 "__DATA_CONST",
44624106 "__got",
44634107 needed_size,
44644108 alignment,
......@@ -4469,7 +4113,7 @@ fn populateMissingMetadata(self: *MachO) !void {
44694113 }
44704114
44714115 if (self.data_segment_cmd_index == null) {
4472 self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
4116 self.data_segment_cmd_index = @intCast(u8, self.segments.items.len);
44734117 var vmaddr: u64 = 0;
44744118 var fileoff: u64 = 0;
44754119 var needed_size: u64 = 0;
......@@ -4484,21 +4128,16 @@ fn populateMissingMetadata(self: *MachO) !void {
44844128 fileoff + needed_size,
44854129 });
44864130 }
4487 try self.load_commands.append(self.base.allocator, .{
4488 .segment = .{
4489 .inner = .{
4490 .segname = makeStaticString("__DATA"),
4491 .vmaddr = vmaddr,
4492 .vmsize = needed_size,
4493 .fileoff = fileoff,
4494 .filesize = needed_size,
4495 .maxprot = macho.PROT.READ | macho.PROT.WRITE,
4496 .initprot = macho.PROT.READ | macho.PROT.WRITE,
4497 .cmdsize = @sizeOf(macho.segment_command_64),
4498 },
4499 },
4131 try self.segments.append(gpa, .{
4132 .segname = makeStaticString("__DATA"),
4133 .vmaddr = vmaddr,
4134 .vmsize = needed_size,
4135 .fileoff = fileoff,
4136 .filesize = needed_size,
4137 .maxprot = macho.PROT.READ | macho.PROT.WRITE,
4138 .initprot = macho.PROT.READ | macho.PROT.WRITE,
4139 .cmdsize = @sizeOf(macho.segment_command_64),
45004140 });
4501 self.load_commands_dirty = true;
45024141 }
45034142
45044143 if (self.la_symbol_ptr_section_index == null) {
......@@ -4508,7 +4147,7 @@ fn populateMissingMetadata(self: *MachO) !void {
45084147 0;
45094148 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
45104149 self.la_symbol_ptr_section_index = try self.initSection(
4511 self.data_segment_cmd_index.?,
4150 "__DATA",
45124151 "__la_symbol_ptr",
45134152 needed_size,
45144153 alignment,
......@@ -4525,7 +4164,7 @@ fn populateMissingMetadata(self: *MachO) !void {
45254164 0;
45264165 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
45274166 self.data_section_index = try self.initSection(
4528 self.data_segment_cmd_index.?,
4167 "__DATA",
45294168 "__data",
45304169 needed_size,
45314170 alignment,
......@@ -4533,76 +4172,8 @@ fn populateMissingMetadata(self: *MachO) !void {
45334172 );
45344173 }
45354174
4536 if (self.tlv_section_index == null) {
4537 const needed_size = if (self.mode == .incremental)
4538 @sizeOf(u64) * self.base.options.symbol_count_hint
4539 else
4540 0;
4541 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4542 self.tlv_section_index = try self.initSection(
4543 self.data_segment_cmd_index.?,
4544 "__thread_vars",
4545 needed_size,
4546 alignment,
4547 .{
4548 .flags = macho.S_THREAD_LOCAL_VARIABLES,
4549 },
4550 );
4551 }
4552
4553 if (self.tlv_data_section_index == null) {
4554 const needed_size = if (self.mode == .incremental)
4555 @sizeOf(u64) * self.base.options.symbol_count_hint
4556 else
4557 0;
4558 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4559 self.tlv_data_section_index = try self.initSection(
4560 self.data_segment_cmd_index.?,
4561 "__thread_data",
4562 needed_size,
4563 alignment,
4564 .{
4565 .flags = macho.S_THREAD_LOCAL_REGULAR,
4566 },
4567 );
4568 }
4569
4570 if (self.tlv_bss_section_index == null) {
4571 const needed_size = if (self.mode == .incremental)
4572 @sizeOf(u64) * self.base.options.symbol_count_hint
4573 else
4574 0;
4575 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4576 self.tlv_bss_section_index = try self.initSection(
4577 self.data_segment_cmd_index.?,
4578 "__thread_bss",
4579 needed_size,
4580 alignment,
4581 .{
4582 .flags = macho.S_THREAD_LOCAL_ZEROFILL,
4583 },
4584 );
4585 }
4586
4587 if (self.bss_section_index == null) {
4588 const needed_size = if (self.mode == .incremental)
4589 @sizeOf(u64) * self.base.options.symbol_count_hint
4590 else
4591 0;
4592 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4593 self.bss_section_index = try self.initSection(
4594 self.data_segment_cmd_index.?,
4595 "__bss",
4596 needed_size,
4597 alignment,
4598 .{
4599 .flags = macho.S_ZEROFILL,
4600 },
4601 );
4602 }
4603
46044175 if (self.linkedit_segment_cmd_index == null) {
4605 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
4176 self.linkedit_segment_cmd_index = @intCast(u8, self.segments.items.len);
46064177 var vmaddr: u64 = 0;
46074178 var fileoff: u64 = 0;
46084179 if (self.mode == .incremental) {
......@@ -4611,249 +4182,113 @@ fn populateMissingMetadata(self: *MachO) !void {
46114182 fileoff = base.fileoff;
46124183 log.debug("found __LINKEDIT segment free space at 0x{x}", .{fileoff});
46134184 }
4614 try self.load_commands.append(self.base.allocator, .{
4615 .segment = .{
4616 .inner = .{
4617 .segname = makeStaticString("__LINKEDIT"),
4618 .vmaddr = vmaddr,
4619 .fileoff = fileoff,
4620 .maxprot = macho.PROT.READ,
4621 .initprot = macho.PROT.READ,
4622 .cmdsize = @sizeOf(macho.segment_command_64),
4623 },
4624 },
4625 });
4626 self.load_commands_dirty = true;
4627 }
4628
4629 if (self.dyld_info_cmd_index == null) {
4630 self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len);
4631 try self.load_commands.append(self.base.allocator, .{
4632 .dyld_info_only = .{
4633 .cmd = .DYLD_INFO_ONLY,
4634 .cmdsize = @sizeOf(macho.dyld_info_command),
4635 .rebase_off = 0,
4636 .rebase_size = 0,
4637 .bind_off = 0,
4638 .bind_size = 0,
4639 .weak_bind_off = 0,
4640 .weak_bind_size = 0,
4641 .lazy_bind_off = 0,
4642 .lazy_bind_size = 0,
4643 .export_off = 0,
4644 .export_size = 0,
4645 },
4185 try self.segments.append(gpa, .{
4186 .segname = makeStaticString("__LINKEDIT"),
4187 .vmaddr = vmaddr,
4188 .fileoff = fileoff,
4189 .maxprot = macho.PROT.READ,
4190 .initprot = macho.PROT.READ,
4191 .cmdsize = @sizeOf(macho.segment_command_64),
46464192 });
4647 self.load_commands_dirty = true;
4648 }
4649
4650 if (self.symtab_cmd_index == null) {
4651 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
4652 try self.load_commands.append(self.base.allocator, .{
4653 .symtab = .{
4654 .cmdsize = @sizeOf(macho.symtab_command),
4655 .symoff = 0,
4656 .nsyms = 0,
4657 .stroff = 0,
4658 .strsize = 0,
4659 },
4660 });
4661 self.load_commands_dirty = true;
4662 }
4663
4664 if (self.dysymtab_cmd_index == null) {
4665 self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len);
4666 try self.load_commands.append(self.base.allocator, .{
4667 .dysymtab = .{
4668 .cmdsize = @sizeOf(macho.dysymtab_command),
4669 .ilocalsym = 0,
4670 .nlocalsym = 0,
4671 .iextdefsym = 0,
4672 .nextdefsym = 0,
4673 .iundefsym = 0,
4674 .nundefsym = 0,
4675 .tocoff = 0,
4676 .ntoc = 0,
4677 .modtaboff = 0,
4678 .nmodtab = 0,
4679 .extrefsymoff = 0,
4680 .nextrefsyms = 0,
4681 .indirectsymoff = 0,
4682 .nindirectsyms = 0,
4683 .extreloff = 0,
4684 .nextrel = 0,
4685 .locreloff = 0,
4686 .nlocrel = 0,
4687 },
4688 });
4689 self.load_commands_dirty = true;
46904193 }
4194}
46914195
4692 if (self.dylinker_cmd_index == null) {
4693 self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len);
4694 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
4695 u64,
4696 @sizeOf(macho.dylinker_command) + mem.sliceTo(default_dyld_path, 0).len,
4697 @sizeOf(u64),
4698 ));
4699 var dylinker_cmd = macho.emptyGenericCommandWithData(macho.dylinker_command{
4700 .cmd = .LOAD_DYLINKER,
4701 .cmdsize = cmdsize,
4702 .name = @sizeOf(macho.dylinker_command),
4703 });
4704 dylinker_cmd.data = try self.base.allocator.alloc(u8, cmdsize - dylinker_cmd.inner.name);
4705 mem.set(u8, dylinker_cmd.data, 0);
4706 mem.copy(u8, dylinker_cmd.data, mem.sliceTo(default_dyld_path, 0));
4707 try self.load_commands.append(self.base.allocator, .{ .dylinker = dylinker_cmd });
4708 self.load_commands_dirty = true;
4709 }
4710
4711 if (self.main_cmd_index == null and self.base.options.output_mode == .Exe) {
4712 self.main_cmd_index = @intCast(u16, self.load_commands.items.len);
4713 try self.load_commands.append(self.base.allocator, .{
4714 .main = .{
4715 .cmdsize = @sizeOf(macho.entry_point_command),
4716 .entryoff = 0x0,
4717 .stacksize = 0,
4718 },
4719 });
4720 self.load_commands_dirty = true;
4721 }
4196inline fn calcInstallNameLen(cmd_size: u64, name: []const u8, assume_max_path_len: bool) u64 {
4197 const name_len = if (assume_max_path_len) std.os.PATH_MAX else std.mem.len(name) + 1;
4198 return mem.alignForwardGeneric(u64, cmd_size + name_len, @alignOf(u64));
4199}
47224200
4723 if (self.dylib_id_cmd_index == null and self.base.options.output_mode == .Lib) {
4724 self.dylib_id_cmd_index = @intCast(u16, self.load_commands.items.len);
4725 const install_name = self.base.options.install_name orelse self.base.options.emit.?.sub_path;
4726 const current_version = self.base.options.version orelse
4727 std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 };
4728 const compat_version = self.base.options.compatibility_version orelse
4729 std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 };
4730 var dylib_cmd = try macho.createLoadDylibCommand(
4731 self.base.allocator,
4732 .ID_DYLIB,
4733 install_name,
4734 2,
4735 current_version.major << 16 | current_version.minor << 8 | current_version.patch,
4736 compat_version.major << 16 | compat_version.minor << 8 | compat_version.patch,
4737 );
4738 errdefer dylib_cmd.deinit(self.base.allocator);
4739 try self.load_commands.append(self.base.allocator, .{ .dylib = dylib_cmd });
4740 self.load_commands_dirty = true;
4201fn calcLCsSize(self: *MachO, assume_max_path_len: bool) !u32 {
4202 const gpa = self.base.allocator;
4203 var sizeofcmds: u64 = 0;
4204 for (self.segments.items) |seg| {
4205 sizeofcmds += seg.nsects * @sizeOf(macho.section_64) + @sizeOf(macho.segment_command_64);
4206 }
4207
4208 // LC_DYLD_INFO_ONLY
4209 sizeofcmds += @sizeOf(macho.dyld_info_command);
4210 // LC_FUNCTION_STARTS
4211 if (self.text_section_index != null) {
4212 sizeofcmds += @sizeOf(macho.linkedit_data_command);
4213 }
4214 // LC_DATA_IN_CODE
4215 sizeofcmds += @sizeOf(macho.linkedit_data_command);
4216 // LC_SYMTAB
4217 sizeofcmds += @sizeOf(macho.symtab_command);
4218 // LC_DYSYMTAB
4219 sizeofcmds += @sizeOf(macho.dysymtab_command);
4220 // LC_LOAD_DYLINKER
4221 sizeofcmds += calcInstallNameLen(
4222 @sizeOf(macho.dylinker_command),
4223 mem.sliceTo(default_dyld_path, 0),
4224 false,
4225 );
4226 // LC_MAIN
4227 if (self.base.options.output_mode == .Exe) {
4228 sizeofcmds += @sizeOf(macho.entry_point_command);
4229 }
4230 // LC_ID_DYLIB
4231 if (self.base.options.output_mode == .Lib) {
4232 sizeofcmds += blk: {
4233 const install_name = self.base.options.install_name orelse self.base.options.emit.?.sub_path;
4234 break :blk calcInstallNameLen(
4235 @sizeOf(macho.dylib_command),
4236 install_name,
4237 assume_max_path_len,
4238 );
4239 };
47414240 }
4742
4743 if (self.source_version_cmd_index == null) {
4744 self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len);
4745 try self.load_commands.append(self.base.allocator, .{
4746 .source_version = .{
4747 .cmdsize = @sizeOf(macho.source_version_command),
4748 .version = 0x0,
4749 },
4750 });
4751 self.load_commands_dirty = true;
4241 // LC_RPATH
4242 {
4243 var it = RpathIterator.init(gpa, self.base.options.rpath_list);
4244 defer it.deinit();
4245 while (try it.next()) |rpath| {
4246 sizeofcmds += calcInstallNameLen(
4247 @sizeOf(macho.rpath_command),
4248 rpath,
4249 assume_max_path_len,
4250 );
4251 }
47524252 }
4753
4754 if (self.build_version_cmd_index == null) {
4755 self.build_version_cmd_index = @intCast(u16, self.load_commands.items.len);
4756 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
4757 u64,
4758 @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version),
4759 @sizeOf(u64),
4760 ));
4761 const platform_version = blk: {
4762 const ver = self.base.options.target.os.version_range.semver.min;
4763 const platform_version = ver.major << 16 | ver.minor << 8;
4764 break :blk platform_version;
4765 };
4766 const sdk_version = if (self.base.options.native_darwin_sdk) |sdk| blk: {
4767 const ver = sdk.version;
4768 const sdk_version = ver.major << 16 | ver.minor << 8;
4769 break :blk sdk_version;
4770 } else platform_version;
4771 const is_simulator_abi = self.base.options.target.abi == .simulator;
4772 var cmd = macho.emptyGenericCommandWithData(macho.build_version_command{
4773 .cmdsize = cmdsize,
4774 .platform = switch (self.base.options.target.os.tag) {
4775 .macos => .MACOS,
4776 .ios => if (is_simulator_abi) macho.PLATFORM.IOSSIMULATOR else macho.PLATFORM.IOS,
4777 .watchos => if (is_simulator_abi) macho.PLATFORM.WATCHOSSIMULATOR else macho.PLATFORM.WATCHOS,
4778 .tvos => if (is_simulator_abi) macho.PLATFORM.TVOSSIMULATOR else macho.PLATFORM.TVOS,
4779 else => unreachable,
4780 },
4781 .minos = platform_version,
4782 .sdk = sdk_version,
4783 .ntools = 1,
4784 });
4785 const ld_ver = macho.build_tool_version{
4786 .tool = .LD,
4787 .version = 0x0,
4788 };
4789 cmd.data = try self.base.allocator.alloc(u8, cmdsize - @sizeOf(macho.build_version_command));
4790 mem.set(u8, cmd.data, 0);
4791 mem.copy(u8, cmd.data, mem.asBytes(&ld_ver));
4792 try self.load_commands.append(self.base.allocator, .{ .build_version = cmd });
4793 self.load_commands_dirty = true;
4794 }
4795
4796 if (self.uuid_cmd_index == null) {
4797 self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len);
4798 var uuid_cmd: macho.uuid_command = .{
4799 .cmdsize = @sizeOf(macho.uuid_command),
4800 .uuid = undefined,
4253 // LC_SOURCE_VERSION
4254 sizeofcmds += @sizeOf(macho.source_version_command);
4255 // LC_BUILD_VERSION
4256 sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
4257 // LC_UUID
4258 sizeofcmds += @sizeOf(macho.uuid_command);
4259 // LC_LOAD_DYLIB
4260 for (self.referenced_dylibs.keys()) |id| {
4261 const dylib = self.dylibs.items[id];
4262 const dylib_id = dylib.id orelse unreachable;
4263 sizeofcmds += calcInstallNameLen(
4264 @sizeOf(macho.dylib_command),
4265 dylib_id.name,
4266 assume_max_path_len,
4267 );
4268 }
4269 // LC_CODE_SIGNATURE
4270 {
4271 const target = self.base.options.target;
4272 const requires_codesig = blk: {
4273 if (self.base.options.entitlements) |_| break :blk true;
4274 if (target.cpu.arch == .aarch64 and (target.os.tag == .macos or target.abi == .simulator))
4275 break :blk true;
4276 break :blk false;
48014277 };
4802 std.crypto.random.bytes(&uuid_cmd.uuid);
4803 try self.load_commands.append(self.base.allocator, .{ .uuid = uuid_cmd });
4804 self.load_commands_dirty = true;
4805 }
4806
4807 if (self.function_starts_cmd_index == null) {
4808 self.function_starts_cmd_index = @intCast(u16, self.load_commands.items.len);
4809 try self.load_commands.append(self.base.allocator, .{
4810 .linkedit_data = .{
4811 .cmd = .FUNCTION_STARTS,
4812 .cmdsize = @sizeOf(macho.linkedit_data_command),
4813 .dataoff = 0,
4814 .datasize = 0,
4815 },
4816 });
4817 self.load_commands_dirty = true;
4278 if (requires_codesig) {
4279 sizeofcmds += @sizeOf(macho.linkedit_data_command);
4280 }
48184281 }
48194282
4820 if (self.data_in_code_cmd_index == null) {
4821 self.data_in_code_cmd_index = @intCast(u16, self.load_commands.items.len);
4822 try self.load_commands.append(self.base.allocator, .{
4823 .linkedit_data = .{
4824 .cmd = .DATA_IN_CODE,
4825 .cmdsize = @sizeOf(macho.linkedit_data_command),
4826 .dataoff = 0,
4827 .datasize = 0,
4828 },
4829 });
4830 self.load_commands_dirty = true;
4831 }
4283 return @intCast(u32, sizeofcmds);
48324284}
48334285
4834fn calcMinHeaderpad(self: *MachO) u64 {
4835 var sizeofcmds: u32 = 0;
4836 for (self.load_commands.items) |lc| {
4837 if (lc.cmd() == .NONE) continue;
4838 sizeofcmds += lc.cmdsize();
4839 }
4840
4841 var padding: u32 = sizeofcmds + (self.base.options.headerpad_size orelse 0);
4286fn calcMinHeaderPad(self: *MachO) !u64 {
4287 var padding: u32 = (try self.calcLCsSize(false)) + (self.base.options.headerpad_size orelse 0);
48424288 log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)});
48434289
48444290 if (self.base.options.headerpad_max_install_names) {
4845 var min_headerpad_size: u32 = 0;
4846 for (self.load_commands.items) |lc| switch (lc.cmd()) {
4847 .ID_DYLIB,
4848 .LOAD_WEAK_DYLIB,
4849 .LOAD_DYLIB,
4850 .REEXPORT_DYLIB,
4851 => {
4852 min_headerpad_size += @sizeOf(macho.dylib_command) + std.os.PATH_MAX + 1;
4853 },
4854
4855 else => {},
4856 };
4291 var min_headerpad_size: u32 = try self.calcLCsSize(true);
48574292 log.debug("headerpad_max_install_names minimum headerpad size 0x{x}", .{
48584293 min_headerpad_size + @sizeOf(macho.mach_header_64),
48594294 });
......@@ -4868,32 +4303,32 @@ fn calcMinHeaderpad(self: *MachO) u64 {
48684303fn allocateSegments(self: *MachO) !void {
48694304 try self.allocateSegment(self.text_segment_cmd_index, &.{
48704305 self.pagezero_segment_cmd_index,
4871 }, self.calcMinHeaderpad());
4306 }, try self.calcMinHeaderPad());
48724307
48734308 if (self.text_segment_cmd_index) |index| blk: {
4874 const seg = &self.load_commands.items[index].segment;
4875 if (seg.sections.items.len == 0) break :blk;
4309 const indexes = self.getSectionIndexes(index);
4310 if (indexes.start == indexes.end) break :blk;
4311 const seg = self.segments.items[index];
48764312
48774313 // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments.
48784314 var min_alignment: u32 = 0;
4879 for (seg.sections.items) |sect| {
4880 const alignment = try math.powi(u32, 2, sect.@"align");
4315 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
4316 const alignment = try math.powi(u32, 2, header.@"align");
48814317 min_alignment = math.max(min_alignment, alignment);
48824318 }
48834319
48844320 assert(min_alignment > 0);
4885 const last_sect_idx = seg.sections.items.len - 1;
4886 const last_sect = seg.sections.items[last_sect_idx];
4321 const last_header = self.sections.items(.header)[indexes.end - 1];
48874322 const shift: u32 = shift: {
4888 const diff = seg.inner.filesize - last_sect.offset - last_sect.size;
4323 const diff = seg.filesize - last_header.offset - last_header.size;
48894324 const factor = @divTrunc(diff, min_alignment);
48904325 break :shift @intCast(u32, factor * min_alignment);
48914326 };
48924327
48934328 if (shift > 0) {
4894 for (seg.sections.items) |*sect| {
4895 sect.offset += shift;
4896 sect.addr += shift;
4329 for (self.sections.items(.header)[indexes.start..indexes.end]) |*header| {
4330 header.offset += shift;
4331 header.addr += shift;
48974332 }
48984333 }
48994334 }
......@@ -4917,42 +4352,40 @@ fn allocateSegments(self: *MachO) !void {
49174352 }, 0);
49184353}
49194354
4920fn allocateSegment(self: *MachO, maybe_index: ?u16, indices: []const ?u16, init_size: u64) !void {
4355fn allocateSegment(self: *MachO, maybe_index: ?u8, indices: []const ?u8, init_size: u64) !void {
49214356 const index = maybe_index orelse return;
4922 const seg = &self.load_commands.items[index].segment;
4357 const seg = &self.segments.items[index];
49234358
49244359 const base = self.getSegmentAllocBase(indices);
4925 seg.inner.vmaddr = base.vmaddr;
4926 seg.inner.fileoff = base.fileoff;
4927 seg.inner.filesize = init_size;
4928 seg.inner.vmsize = init_size;
4360 seg.vmaddr = base.vmaddr;
4361 seg.fileoff = base.fileoff;
4362 seg.filesize = init_size;
4363 seg.vmsize = init_size;
49294364
49304365 // Allocate the sections according to their alignment at the beginning of the segment.
4366 const indexes = self.getSectionIndexes(index);
49314367 var start = init_size;
4932 for (seg.sections.items) |*sect| {
4933 const is_zerofill = sect.flags == macho.S_ZEROFILL or sect.flags == macho.S_THREAD_LOCAL_ZEROFILL;
4934 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;
4935 const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1;
4936 const alignment = try math.powi(u32, 2, sect.@"align");
4368 const slice = self.sections.slice();
4369 for (slice.items(.header)[indexes.start..indexes.end]) |*header| {
4370 const alignment = try math.powi(u32, 2, header.@"align");
49374371 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);
49384372
4939 // TODO handle zerofill sections in stage2
4940 sect.offset = if (is_zerofill and (use_stage1 or use_llvm))
4373 header.offset = if (header.isZerofill())
49414374 0
49424375 else
4943 @intCast(u32, seg.inner.fileoff + start_aligned);
4944 sect.addr = seg.inner.vmaddr + start_aligned;
4376 @intCast(u32, seg.fileoff + start_aligned);
4377 header.addr = seg.vmaddr + start_aligned;
49454378
4946 start = start_aligned + sect.size;
4379 start = start_aligned + header.size;
49474380
4948 if (!(is_zerofill and (use_stage1 or use_llvm))) {
4949 seg.inner.filesize = start;
4381 if (!header.isZerofill()) {
4382 seg.filesize = start;
49504383 }
4951 seg.inner.vmsize = start;
4384 seg.vmsize = start;
49524385 }
49534386
4954 seg.inner.filesize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size);
4955 seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.vmsize, self.page_size);
4387 seg.filesize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
4388 seg.vmsize = mem.alignForwardGeneric(u64, seg.vmsize, self.page_size);
49564389}
49574390
49584391const InitSectionOpts = struct {
......@@ -4963,184 +4396,270 @@ const InitSectionOpts = struct {
49634396
49644397fn initSection(
49654398 self: *MachO,
4966 segment_id: u16,
4399 segname: []const u8,
49674400 sectname: []const u8,
49684401 size: u64,
49694402 alignment: u32,
49704403 opts: InitSectionOpts,
4971) !u16 {
4972 const seg = &self.load_commands.items[segment_id].segment;
4973 var sect = macho.section_64{
4404) !u8 {
4405 const segment_id = self.getSegmentByName(segname).?;
4406 const seg = &self.segments.items[segment_id];
4407 const index = try self.insertSection(segment_id, .{
49744408 .sectname = makeStaticString(sectname),
4975 .segname = seg.inner.segname,
4976 .size = if (self.mode == .incremental) @intCast(u32, size) else 0,
4977 .@"align" = alignment,
4409 .segname = seg.segname,
49784410 .flags = opts.flags,
49794411 .reserved1 = opts.reserved1,
49804412 .reserved2 = opts.reserved2,
4981 };
4413 });
4414 seg.cmdsize += @sizeOf(macho.section_64);
4415 seg.nsects += 1;
49824416
49834417 if (self.mode == .incremental) {
4418 const header = &self.sections.items(.header)[index];
4419 header.size = size;
4420 header.@"align" = alignment;
4421
4422 const prev_end_off = if (index > 0) blk: {
4423 const prev_section = self.sections.get(index - 1);
4424 if (prev_section.segment_index == segment_id) {
4425 const prev_header = prev_section.header;
4426 break :blk prev_header.offset + padToIdeal(prev_header.size);
4427 } else break :blk seg.fileoff;
4428 } else 0;
49844429 const alignment_pow_2 = try math.powi(u32, 2, alignment);
4985 const padding: ?u32 = if (segment_id == self.text_segment_cmd_index.?)
4986 @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size)
4987 else
4988 null;
4989 const off = self.findFreeSpace(segment_id, alignment_pow_2, padding);
4990 log.debug("allocating {s},{s} section from 0x{x} to 0x{x}", .{
4991 sect.segName(),
4992 sect.sectName(),
4993 off,
4994 off + size,
4995 });
4430 // TODO better prealloc for __text section
4431 // const padding: u64 = if (index == 0) try self.calcMinHeaderPad() else 0;
4432 const padding: u64 = if (index == 0) 0x1000 else 0;
4433 const off = mem.alignForwardGeneric(u64, padding + prev_end_off, alignment_pow_2);
49964434
4997 sect.addr = seg.inner.vmaddr + off - seg.inner.fileoff;
4435 if (!header.isZerofill()) {
4436 header.offset = @intCast(u32, off);
4437 }
4438 header.addr = seg.vmaddr + off - seg.fileoff;
49984439
4999 const is_zerofill = opts.flags == macho.S_ZEROFILL or opts.flags == macho.S_THREAD_LOCAL_ZEROFILL;
5000 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;
5001 const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1;
4440 // TODO Will this break if we are inserting section that is not the last section
4441 // in a segment?
4442 const max_size = self.allocatedSize(segment_id, off);
50024443
5003 // TODO handle zerofill in stage2
5004 if (!(is_zerofill and (use_stage1 or use_llvm))) {
5005 sect.offset = @intCast(u32, off);
4444 if (size > max_size) {
4445 try self.growSection(index, @intCast(u32, size));
50064446 }
4447
4448 log.debug("allocating {s},{s} section at 0x{x}", .{ header.segName(), header.sectName(), off });
4449
4450 self.updateSectionOrdinals(index + 1);
4451 }
4452
4453 return index;
4454}
4455
4456fn getSectionPrecedence(header: macho.section_64) u4 {
4457 if (header.isCode()) {
4458 if (mem.eql(u8, "__text", header.sectName())) return 0x0;
4459 if (header.@"type"() == macho.S_SYMBOL_STUBS) return 0x1;
4460 return 0x2;
4461 }
4462 switch (header.@"type"()) {
4463 macho.S_NON_LAZY_SYMBOL_POINTERS,
4464 macho.S_LAZY_SYMBOL_POINTERS,
4465 => return 0x0,
4466 macho.S_MOD_INIT_FUNC_POINTERS => return 0x1,
4467 macho.S_MOD_TERM_FUNC_POINTERS => return 0x2,
4468 macho.S_ZEROFILL => return 0xf,
4469 macho.S_THREAD_LOCAL_REGULAR => return 0xd,
4470 macho.S_THREAD_LOCAL_ZEROFILL => return 0xe,
4471 else => if (mem.eql(u8, "__eh_frame", header.sectName()))
4472 return 0xf
4473 else
4474 return 0x3,
50074475 }
4476}
50084477
5009 const index = @intCast(u16, seg.sections.items.len);
5010 try seg.sections.append(self.base.allocator, sect);
5011 seg.inner.cmdsize += @sizeOf(macho.section_64);
5012 seg.inner.nsects += 1;
4478fn insertSection(self: *MachO, segment_index: u8, header: macho.section_64) !u8 {
4479 const precedence = getSectionPrecedence(header);
4480 const indexes = self.getSectionIndexes(segment_index);
4481 const insertion_index = for (self.sections.items(.header)[indexes.start..indexes.end]) |hdr, i| {
4482 if (getSectionPrecedence(hdr) > precedence) break @intCast(u8, i + indexes.start);
4483 } else indexes.end;
4484 log.debug("inserting section '{s},{s}' at index {d}", .{
4485 header.segName(),
4486 header.sectName(),
4487 insertion_index,
4488 });
4489 for (&[_]*?u8{
4490 &self.text_section_index,
4491 &self.stubs_section_index,
4492 &self.stub_helper_section_index,
4493 &self.got_section_index,
4494 &self.la_symbol_ptr_section_index,
4495 &self.data_section_index,
4496 }) |maybe_index| {
4497 const index = maybe_index.* orelse continue;
4498 if (insertion_index <= index) maybe_index.* = index + 1;
4499 }
4500 try self.sections.insert(self.base.allocator, insertion_index, .{
4501 .segment_index = segment_index,
4502 .header = header,
4503 });
4504 return insertion_index;
4505}
50134506
5014 const match = MatchingSection{
5015 .seg = segment_id,
5016 .sect = index,
5017 };
5018 _ = try self.section_ordinals.getOrPut(self.base.allocator, match);
5019 try self.atom_free_lists.putNoClobber(self.base.allocator, match, .{});
4507fn updateSectionOrdinals(self: *MachO, start: u8) void {
4508 const tracy = trace(@src());
4509 defer tracy.end();
50204510
5021 self.load_commands_dirty = true;
5022 self.sections_order_dirty = true;
4511 const slice = self.sections.slice();
4512 for (slice.items(.last_atom)[start..]) |last_atom| {
4513 var atom = last_atom orelse continue;
50234514
5024 return index;
4515 while (true) {
4516 const sym = atom.getSymbolPtr(self);
4517 sym.n_sect = start + 1;
4518
4519 for (atom.contained.items) |sym_at_off| {
4520 const contained_sym = self.getSymbolPtr(.{
4521 .sym_index = sym_at_off.sym_index,
4522 .file = atom.file,
4523 });
4524 contained_sym.n_sect = start + 1;
4525 }
4526
4527 if (atom.prev) |prev| {
4528 atom = prev;
4529 } else break;
4530 }
4531 }
50254532}
50264533
5027fn findFreeSpace(self: MachO, segment_id: u16, alignment: u64, start: ?u32) u64 {
5028 const seg = self.load_commands.items[segment_id].segment;
5029 if (seg.sections.items.len == 0) {
5030 return if (start) |v| v else seg.inner.fileoff;
4534fn shiftLocalsByOffset(self: *MachO, sect_id: u8, offset: i64) !void {
4535 var atom = self.sections.items(.last_atom)[sect_id] orelse return;
4536
4537 while (true) {
4538 const atom_sym = atom.getSymbolPtr(self);
4539 atom_sym.n_value = @intCast(u64, @intCast(i64, atom_sym.n_value) + offset);
4540
4541 for (atom.contained.items) |sym_at_off| {
4542 const contained_sym = self.getSymbolPtr(.{
4543 .sym_index = sym_at_off.sym_index,
4544 .file = atom.file,
4545 });
4546 contained_sym.n_value = @intCast(u64, @intCast(i64, contained_sym.n_value) + offset);
4547 }
4548
4549 if (atom.prev) |prev| {
4550 atom = prev;
4551 } else break;
50314552 }
5032 const last_sect = seg.sections.items[seg.sections.items.len - 1];
5033 const final_off = last_sect.offset + padToIdeal(last_sect.size);
5034 return mem.alignForwardGeneric(u64, final_off, alignment);
50354553}
50364554
5037fn growSegment(self: *MachO, seg_id: u16, new_size: u64) !void {
5038 const seg = &self.load_commands.items[seg_id].segment;
5039 const new_seg_size = mem.alignForwardGeneric(u64, new_size, self.page_size);
5040 assert(new_seg_size > seg.inner.filesize);
5041 const offset_amt = new_seg_size - seg.inner.filesize;
4555fn growSegment(self: *MachO, segment_index: u8, new_size: u64) !void {
4556 const segment = &self.segments.items[segment_index];
4557 const new_segment_size = mem.alignForwardGeneric(u64, new_size, self.page_size);
4558 assert(new_segment_size > segment.filesize);
4559 const offset_amt = new_segment_size - segment.filesize;
50424560 log.debug("growing segment {s} from 0x{x} to 0x{x}", .{
5043 seg.inner.segname,
5044 seg.inner.filesize,
5045 new_seg_size,
4561 segment.segname,
4562 segment.filesize,
4563 new_segment_size,
50464564 });
5047 seg.inner.filesize = new_seg_size;
5048 seg.inner.vmsize = new_seg_size;
4565 segment.filesize = new_segment_size;
4566 segment.vmsize = new_segment_size;
50494567
50504568 log.debug(" (new segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
5051 seg.inner.fileoff,
5052 seg.inner.fileoff + seg.inner.filesize,
5053 seg.inner.vmaddr,
5054 seg.inner.vmaddr + seg.inner.vmsize,
4569 segment.fileoff,
4570 segment.fileoff + segment.filesize,
4571 segment.vmaddr,
4572 segment.vmaddr + segment.vmsize,
50554573 });
50564574
5057 var next: usize = seg_id + 1;
4575 var next: u8 = segment_index + 1;
50584576 while (next < self.linkedit_segment_cmd_index.? + 1) : (next += 1) {
5059 const next_seg = &self.load_commands.items[next].segment;
4577 const next_segment = &self.segments.items[next];
50604578
50614579 try MachO.copyRangeAllOverlappingAlloc(
50624580 self.base.allocator,
50634581 self.base.file.?,
5064 next_seg.inner.fileoff,
5065 next_seg.inner.fileoff + offset_amt,
5066 math.cast(usize, next_seg.inner.filesize) orelse return error.Overflow,
4582 next_segment.fileoff,
4583 next_segment.fileoff + offset_amt,
4584 math.cast(usize, next_segment.filesize) orelse return error.Overflow,
50674585 );
50684586
5069 next_seg.inner.fileoff += offset_amt;
5070 next_seg.inner.vmaddr += offset_amt;
4587 next_segment.fileoff += offset_amt;
4588 next_segment.vmaddr += offset_amt;
50714589
50724590 log.debug(" (new {s} segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
5073 next_seg.inner.segname,
5074 next_seg.inner.fileoff,
5075 next_seg.inner.fileoff + next_seg.inner.filesize,
5076 next_seg.inner.vmaddr,
5077 next_seg.inner.vmaddr + next_seg.inner.vmsize,
4591 next_segment.segname,
4592 next_segment.fileoff,
4593 next_segment.fileoff + next_segment.filesize,
4594 next_segment.vmaddr,
4595 next_segment.vmaddr + next_segment.vmsize,
50784596 });
50794597
5080 for (next_seg.sections.items) |*moved_sect, moved_sect_id| {
5081 moved_sect.offset += @intCast(u32, offset_amt);
5082 moved_sect.addr += offset_amt;
5083
5084 log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
5085 moved_sect.segName(),
5086 moved_sect.sectName(),
5087 moved_sect.offset,
5088 moved_sect.offset + moved_sect.size,
5089 moved_sect.addr,
5090 moved_sect.addr + moved_sect.size,
4598 const indexes = self.getSectionIndexes(next);
4599 for (self.sections.items(.header)[indexes.start..indexes.end]) |*header, i| {
4600 header.offset += @intCast(u32, offset_amt);
4601 header.addr += offset_amt;
4602
4603 log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
4604 header.segName(),
4605 header.sectName(),
4606 header.offset,
4607 header.offset + header.size,
4608 header.addr,
4609 header.addr + header.size,
50914610 });
50924611
5093 try self.shiftLocalsByOffset(.{
5094 .seg = @intCast(u16, next),
5095 .sect = @intCast(u16, moved_sect_id),
5096 }, @intCast(i64, offset_amt));
4612 try self.shiftLocalsByOffset(@intCast(u8, i + indexes.start), @intCast(i64, offset_amt));
50974613 }
50984614 }
50994615}
51004616
5101fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {
4617fn growSection(self: *MachO, sect_id: u8, new_size: u32) !void {
51024618 const tracy = trace(@src());
51034619 defer tracy.end();
51044620
5105 const seg = &self.load_commands.items[match.seg].segment;
5106 const sect = &seg.sections.items[match.sect];
4621 const section = self.sections.get(sect_id);
4622 const segment_index = section.segment_index;
4623 const header = section.header;
4624 const segment = self.segments.items[segment_index];
51074625
5108 const alignment = try math.powi(u32, 2, sect.@"align");
5109 const max_size = self.allocatedSize(match.seg, sect.offset);
4626 const alignment = try math.powi(u32, 2, header.@"align");
4627 const max_size = self.allocatedSize(segment_index, header.offset);
51104628 const ideal_size = padToIdeal(new_size);
51114629 const needed_size = mem.alignForwardGeneric(u32, ideal_size, alignment);
51124630
51134631 if (needed_size > max_size) blk: {
51144632 log.debug(" (need to grow! needed 0x{x}, max 0x{x})", .{ needed_size, max_size });
51154633
5116 if (match.sect == seg.sections.items.len - 1) {
4634 const indexes = self.getSectionIndexes(segment_index);
4635 if (sect_id == indexes.end - 1) {
51174636 // Last section, just grow segments
5118 try self.growSegment(match.seg, seg.inner.filesize + needed_size - max_size);
4637 try self.growSegment(segment_index, segment.filesize + needed_size - max_size);
51194638 break :blk;
51204639 }
51214640
51224641 // Need to move all sections below in file and address spaces.
51234642 const offset_amt = offset: {
5124 const max_alignment = try self.getSectionMaxAlignment(match.seg, match.sect + 1);
4643 const max_alignment = try self.getSectionMaxAlignment(sect_id + 1, indexes.end);
51254644 break :offset mem.alignForwardGeneric(u64, needed_size - max_size, max_alignment);
51264645 };
51274646
51284647 // Before we commit to this, check if the segment needs to grow too.
51294648 // We assume that each section header is growing linearly with the increasing
51304649 // file offset / virtual memory address space.
5131 const last_sect = seg.sections.items[seg.sections.items.len - 1];
5132 const last_sect_off = last_sect.offset + last_sect.size;
5133 const seg_off = seg.inner.fileoff + seg.inner.filesize;
4650 const last_sect_header = self.sections.items(.header)[indexes.end - 1];
4651 const last_sect_off = last_sect_header.offset + last_sect_header.size;
4652 const seg_off = segment.fileoff + segment.filesize;
51344653
51354654 if (last_sect_off + offset_amt > seg_off) {
51364655 // Need to grow segment first.
51374656 const spill_size = (last_sect_off + offset_amt) - seg_off;
5138 try self.growSegment(match.seg, seg.inner.filesize + spill_size);
4657 try self.growSegment(segment_index, segment.filesize + spill_size);
51394658 }
51404659
51414660 // We have enough space to expand within the segment, so move all sections by
51424661 // the required amount and update their header offsets.
5143 const next_sect = seg.sections.items[match.sect + 1];
4662 const next_sect = self.sections.items(.header)[sect_id + 1];
51444663 const total_size = last_sect_off - next_sect.offset;
51454664
51464665 try MachO.copyRangeAllOverlappingAlloc(
......@@ -5151,9 +4670,7 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {
51514670 math.cast(usize, total_size) orelse return error.Overflow,
51524671 );
51534672
5154 var next = match.sect + 1;
5155 while (next < seg.sections.items.len) : (next += 1) {
5156 const moved_sect = &seg.sections.items[next];
4673 for (self.sections.items(.header)[sect_id + 1 .. indexes.end]) |*moved_sect, i| {
51574674 moved_sect.offset += @intCast(u32, offset_amt);
51584675 moved_sect.addr += offset_amt;
51594676
......@@ -5166,49 +4683,45 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {
51664683 moved_sect.addr + moved_sect.size,
51674684 });
51684685
5169 try self.shiftLocalsByOffset(.{
5170 .seg = match.seg,
5171 .sect = next,
5172 }, @intCast(i64, offset_amt));
4686 try self.shiftLocalsByOffset(@intCast(u8, sect_id + 1 + i), @intCast(i64, offset_amt));
51734687 }
51744688 }
51754689}
51764690
5177fn allocatedSize(self: MachO, segment_id: u16, start: u64) u64 {
5178 const seg = self.load_commands.items[segment_id].segment;
5179 assert(start >= seg.inner.fileoff);
5180 var min_pos: u64 = seg.inner.fileoff + seg.inner.filesize;
4691fn allocatedSize(self: MachO, segment_id: u8, start: u64) u64 {
4692 const segment = self.segments.items[segment_id];
4693 const indexes = self.getSectionIndexes(segment_id);
4694 assert(start >= segment.fileoff);
4695 var min_pos: u64 = segment.fileoff + segment.filesize;
51814696 if (start > min_pos) return 0;
5182 for (seg.sections.items) |section| {
5183 if (section.offset <= start) continue;
5184 if (section.offset < min_pos) min_pos = section.offset;
4697 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
4698 if (header.offset <= start) continue;
4699 if (header.offset < min_pos) min_pos = header.offset;
51854700 }
51864701 return min_pos - start;
51874702}
51884703
5189fn getSectionMaxAlignment(self: *MachO, segment_id: u16, start_sect_id: u16) !u32 {
5190 const seg = self.load_commands.items[segment_id].segment;
4704fn getSectionMaxAlignment(self: *MachO, start: u8, end: u8) !u32 {
51914705 var max_alignment: u32 = 1;
5192 var next = start_sect_id;
5193 while (next < seg.sections.items.len) : (next += 1) {
5194 const sect = seg.sections.items[next];
5195 const alignment = try math.powi(u32, 2, sect.@"align");
4706 const slice = self.sections.slice();
4707 for (slice.items(.header)[start..end]) |header| {
4708 const alignment = try math.powi(u32, 2, header.@"align");
51964709 max_alignment = math.max(max_alignment, alignment);
51974710 }
51984711 return max_alignment;
51994712}
52004713
5201fn allocateAtomCommon(self: *MachO, atom: *Atom, match: MatchingSection) !void {
4714fn allocateAtomCommon(self: *MachO, atom: *Atom, sect_id: u8) !void {
52024715 const sym = atom.getSymbolPtr(self);
52034716 if (self.mode == .incremental) {
52044717 const size = atom.size;
52054718 const alignment = try math.powi(u32, 2, atom.alignment);
5206 const vaddr = try self.allocateAtom(atom, size, alignment, match);
4719 const vaddr = try self.allocateAtom(atom, size, alignment, sect_id);
52074720 const sym_name = atom.getName(self);
52084721 log.debug("allocated {s} atom at 0x{x}", .{ sym_name, vaddr });
52094722 sym.n_value = vaddr;
5210 } else try self.addAtomToSection(atom, match);
5211 sym.n_sect = self.getSectionOrdinal(match);
4723 } else try self.addAtomToSection(atom, sect_id);
4724 sym.n_sect = sect_id + 1;
52124725}
52134726
52144727fn allocateAtom(
......@@ -5216,15 +4729,15 @@ fn allocateAtom(
52164729 atom: *Atom,
52174730 new_atom_size: u64,
52184731 alignment: u64,
5219 match: MatchingSection,
4732 sect_id: u8,
52204733) !u64 {
52214734 const tracy = trace(@src());
52224735 defer tracy.end();
52234736
5224 const sect = self.getSectionPtr(match);
5225 var free_list = self.atom_free_lists.get(match).?;
5226 const needs_padding = match.seg == self.text_segment_cmd_index.? and match.sect == self.text_section_index.?;
5227 const new_atom_ideal_capacity = if (needs_padding) padToIdeal(new_atom_size) else new_atom_size;
4737 const header = &self.sections.items(.header)[sect_id];
4738 const free_list = &self.sections.items(.free_list)[sect_id];
4739 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];
4740 const new_atom_ideal_capacity = if (header.isCode()) padToIdeal(new_atom_size) else new_atom_size;
52284741
52294742 // We use these to indicate our intention to update metadata, placing the new atom,
52304743 // and possibly removing a free list node.
......@@ -5244,7 +4757,7 @@ fn allocateAtom(
52444757 // Is it enough that we could fit this new atom?
52454758 const sym = big_atom.getSymbol(self);
52464759 const capacity = big_atom.capacity(self);
5247 const ideal_capacity = if (needs_padding) padToIdeal(capacity) else capacity;
4760 const ideal_capacity = if (header.isCode()) padToIdeal(capacity) else capacity;
52484761 const ideal_capacity_end_vaddr = math.add(u64, sym.n_value, ideal_capacity) catch ideal_capacity;
52494762 const capacity_end_vaddr = sym.n_value + capacity;
52504763 const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity;
......@@ -5272,30 +4785,28 @@ fn allocateAtom(
52724785 free_list_removal = i;
52734786 }
52744787 break :blk new_start_vaddr;
5275 } else if (self.atoms.get(match)) |last| {
4788 } else if (maybe_last_atom.*) |last| {
52764789 const last_symbol = last.getSymbol(self);
5277 const ideal_capacity = if (needs_padding) padToIdeal(last.size) else last.size;
4790 const ideal_capacity = if (header.isCode()) padToIdeal(last.size) else last.size;
52784791 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;
52794792 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
52804793 atom_placement = last;
52814794 break :blk new_start_vaddr;
52824795 } else {
5283 break :blk mem.alignForwardGeneric(u64, sect.addr, alignment);
4796 break :blk mem.alignForwardGeneric(u64, header.addr, alignment);
52844797 }
52854798 };
52864799
52874800 const expand_section = atom_placement == null or atom_placement.?.next == null;
52884801 if (expand_section) {
5289 const needed_size = @intCast(u32, (vaddr + new_atom_size) - sect.addr);
5290 try self.growSection(match, needed_size);
5291 _ = try self.atoms.put(self.base.allocator, match, atom);
5292 sect.size = needed_size;
5293 self.load_commands_dirty = true;
4802 const needed_size = @intCast(u32, (vaddr + new_atom_size) - header.addr);
4803 try self.growSection(sect_id, needed_size);
4804 maybe_last_atom.* = atom;
4805 header.size = needed_size;
52944806 }
52954807 const align_pow = @intCast(u32, math.log2(alignment));
5296 if (sect.@"align" < align_pow) {
5297 sect.@"align" = align_pow;
5298 self.load_commands_dirty = true;
4808 if (header.@"align" < align_pow) {
4809 header.@"align" = align_pow;
52994810 }
53004811 atom.size = new_atom_size;
53014812 atom.alignment = align_pow;
......@@ -5322,20 +4833,19 @@ fn allocateAtom(
53224833 return vaddr;
53234834}
53244835
5325pub fn addAtomToSection(self: *MachO, atom: *Atom, match: MatchingSection) !void {
5326 if (self.atoms.getPtr(match)) |last| {
5327 last.*.next = atom;
5328 atom.prev = last.*;
5329 last.* = atom;
5330 } else {
5331 try self.atoms.putNoClobber(self.base.allocator, match, atom);
4836pub fn addAtomToSection(self: *MachO, atom: *Atom, sect_id: u8) !void {
4837 var section = self.sections.get(sect_id);
4838 if (section.header.size > 0) {
4839 section.last_atom.?.next = atom;
4840 atom.prev = section.last_atom.?;
53324841 }
5333 const sect = self.getSectionPtr(match);
4842 section.last_atom = atom;
53344843 const atom_alignment = try math.powi(u32, 2, atom.alignment);
5335 const aligned_end_addr = mem.alignForwardGeneric(u64, sect.size, atom_alignment);
5336 const padding = aligned_end_addr - sect.size;
5337 sect.size += padding + atom.size;
5338 sect.@"align" = @maximum(sect.@"align", atom.alignment);
4844 const aligned_end_addr = mem.alignForwardGeneric(u64, section.header.size, atom_alignment);
4845 const padding = aligned_end_addr - section.header.size;
4846 section.header.size += padding + atom.size;
4847 section.header.@"align" = @maximum(section.header.@"align", atom.alignment);
4848 self.sections.set(sect_id, section);
53394849}
53404850
53414851pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {
......@@ -5368,208 +4878,61 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {
53684878 return sym_index;
53694879}
53704880
5371fn getSegmentAllocBase(self: MachO, indices: []const ?u16) struct { vmaddr: u64, fileoff: u64 } {
4881fn getSegmentAllocBase(self: MachO, indices: []const ?u8) struct { vmaddr: u64, fileoff: u64 } {
53724882 for (indices) |maybe_prev_id| {
53734883 const prev_id = maybe_prev_id orelse continue;
5374 const prev = self.load_commands.items[prev_id].segment;
4884 const prev = self.segments.items[prev_id];
53754885 return .{
5376 .vmaddr = prev.inner.vmaddr + prev.inner.vmsize,
5377 .fileoff = prev.inner.fileoff + prev.inner.filesize,
4886 .vmaddr = prev.vmaddr + prev.vmsize,
4887 .fileoff = prev.fileoff + prev.filesize,
53784888 };
53794889 }
53804890 return .{ .vmaddr = 0, .fileoff = 0 };
53814891}
53824892
5383fn pruneAndSortSectionsInSegment(self: *MachO, maybe_seg_id: *?u16, indices: []*?u16) !void {
5384 const seg_id = maybe_seg_id.* orelse return;
5385
5386 var mapping = std.AutoArrayHashMap(u16, ?u16).init(self.base.allocator);
5387 defer mapping.deinit();
5388
5389 const seg = &self.load_commands.items[seg_id].segment;
5390 var sections = seg.sections.toOwnedSlice(self.base.allocator);
5391 defer self.base.allocator.free(sections);
5392 try seg.sections.ensureTotalCapacity(self.base.allocator, sections.len);
5393
5394 for (indices) |maybe_index| {
5395 const old_idx = maybe_index.* orelse continue;
5396 const sect = &sections[old_idx];
5397 if (sect.size == 0) {
5398 log.debug("pruning section {s},{s}", .{ sect.segName(), sect.sectName() });
5399 maybe_index.* = null;
5400 seg.inner.cmdsize -= @sizeOf(macho.section_64);
5401 seg.inner.nsects -= 1;
5402 } else {
5403 maybe_index.* = @intCast(u16, seg.sections.items.len);
5404 seg.sections.appendAssumeCapacity(sect.*);
4893fn writeSegmentHeaders(self: *MachO, ncmds: *u32, writer: anytype) !void {
4894 for (self.segments.items) |seg, i| {
4895 const indexes = self.getSectionIndexes(@intCast(u8, i));
4896 var out_seg = seg;
4897 out_seg.cmdsize = @sizeOf(macho.segment_command_64);
4898 out_seg.nsects = 0;
4899
4900 // Update section headers count; any section with size of 0 is excluded
4901 // since it doesn't have any data in the final binary file.
4902 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
4903 if (header.size == 0) continue;
4904 out_seg.cmdsize += @sizeOf(macho.section_64);
4905 out_seg.nsects += 1;
54054906 }
5406 try mapping.putNoClobber(old_idx, maybe_index.*);
5407 }
5408
5409 var atoms = std.ArrayList(struct { match: MatchingSection, atom: *Atom }).init(self.base.allocator);
5410 defer atoms.deinit();
5411 try atoms.ensureTotalCapacity(mapping.count());
5412
5413 for (mapping.keys()) |old_sect| {
5414 const new_sect = mapping.get(old_sect).? orelse {
5415 _ = self.atoms.remove(.{ .seg = seg_id, .sect = old_sect });
5416 continue;
5417 };
5418 const kv = self.atoms.fetchRemove(.{ .seg = seg_id, .sect = old_sect }).?;
5419 atoms.appendAssumeCapacity(.{
5420 .match = .{ .seg = seg_id, .sect = new_sect },
5421 .atom = kv.value,
5422 });
5423 }
5424
5425 while (atoms.popOrNull()) |next| {
5426 try self.atoms.putNoClobber(self.base.allocator, next.match, next.atom);
5427 }
5428
5429 if (seg.inner.nsects == 0 and !mem.eql(u8, "__TEXT", seg.inner.segName())) {
5430 // Segment has now become empty, so mark it as such
5431 log.debug("marking segment {s} as dead", .{seg.inner.segName()});
5432 seg.inner.cmd = @intToEnum(macho.LC, 0);
5433 maybe_seg_id.* = null;
5434 }
5435}
54364907
5437fn pruneAndSortSections(self: *MachO) !void {
5438 try self.pruneAndSortSectionsInSegment(&self.text_segment_cmd_index, &.{
5439 &self.text_section_index,
5440 &self.stubs_section_index,
5441 &self.stub_helper_section_index,
5442 &self.gcc_except_tab_section_index,
5443 &self.cstring_section_index,
5444 &self.ustring_section_index,
5445 &self.text_const_section_index,
5446 &self.objc_methlist_section_index,
5447 &self.objc_methname_section_index,
5448 &self.objc_methtype_section_index,
5449 &self.objc_classname_section_index,
5450 &self.eh_frame_section_index,
5451 });
5452
5453 try self.pruneAndSortSectionsInSegment(&self.data_const_segment_cmd_index, &.{
5454 &self.got_section_index,
5455 &self.mod_init_func_section_index,
5456 &self.mod_term_func_section_index,
5457 &self.data_const_section_index,
5458 &self.objc_cfstring_section_index,
5459 &self.objc_classlist_section_index,
5460 &self.objc_imageinfo_section_index,
5461 });
5462
5463 try self.pruneAndSortSectionsInSegment(&self.data_segment_cmd_index, &.{
5464 &self.rustc_section_index,
5465 &self.la_symbol_ptr_section_index,
5466 &self.objc_const_section_index,
5467 &self.objc_selrefs_section_index,
5468 &self.objc_classrefs_section_index,
5469 &self.objc_data_section_index,
5470 &self.data_section_index,
5471 &self.tlv_section_index,
5472 &self.tlv_ptrs_section_index,
5473 &self.tlv_data_section_index,
5474 &self.tlv_bss_section_index,
5475 &self.bss_section_index,
5476 });
4908 if (out_seg.nsects == 0 and
4909 (mem.eql(u8, out_seg.segName(), "__DATA_CONST") or
4910 mem.eql(u8, out_seg.segName(), "__DATA"))) continue;
54774911
5478 // Create new section ordinals.
5479 self.section_ordinals.clearRetainingCapacity();
5480 if (self.text_segment_cmd_index) |seg_id| {
5481 const seg = self.load_commands.items[seg_id].segment;
5482 for (seg.sections.items) |_, sect_id| {
5483 const res = self.section_ordinals.getOrPutAssumeCapacity(.{
5484 .seg = seg_id,
5485 .sect = @intCast(u16, sect_id),
5486 });
5487 assert(!res.found_existing);
5488 }
5489 }
5490 if (self.data_const_segment_cmd_index) |seg_id| {
5491 const seg = self.load_commands.items[seg_id].segment;
5492 for (seg.sections.items) |_, sect_id| {
5493 const res = self.section_ordinals.getOrPutAssumeCapacity(.{
5494 .seg = seg_id,
5495 .sect = @intCast(u16, sect_id),
5496 });
5497 assert(!res.found_existing);
5498 }
5499 }
5500 if (self.data_segment_cmd_index) |seg_id| {
5501 const seg = self.load_commands.items[seg_id].segment;
5502 for (seg.sections.items) |_, sect_id| {
5503 const res = self.section_ordinals.getOrPutAssumeCapacity(.{
5504 .seg = seg_id,
5505 .sect = @intCast(u16, sect_id),
5506 });
5507 assert(!res.found_existing);
4912 try writer.writeStruct(out_seg);
4913 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
4914 if (header.size == 0) continue;
4915 try writer.writeStruct(header);
55084916 }
4917
4918 ncmds.* += 1;
55094919 }
5510 self.sections_order_dirty = false;
55114920}
55124921
5513fn updateSectionOrdinals(self: *MachO) !void {
5514 if (!self.sections_order_dirty) return;
5515
5516 const tracy = trace(@src());
5517 defer tracy.end();
5518
5519 log.debug("updating section ordinals", .{});
5520
5521 const gpa = self.base.allocator;
5522
5523 var ordinal_remap = std.AutoHashMap(u8, u8).init(gpa);
5524 defer ordinal_remap.deinit();
5525 var ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{};
5526
5527 var new_ordinal: u8 = 0;
5528 for (&[_]?u16{
5529 self.text_segment_cmd_index,
5530 self.data_const_segment_cmd_index,
5531 self.data_segment_cmd_index,
5532 }) |maybe_index| {
5533 const index = maybe_index orelse continue;
5534 const seg = self.load_commands.items[index].segment;
5535 for (seg.sections.items) |sect, sect_id| {
5536 const match = MatchingSection{
5537 .seg = @intCast(u16, index),
5538 .sect = @intCast(u16, sect_id),
5539 };
5540 const old_ordinal = self.getSectionOrdinal(match);
5541 new_ordinal += 1;
5542 log.debug("'{s},{s}': sect({d}, '_,_') => sect({d}, '_,_')", .{
5543 sect.segName(),
5544 sect.sectName(),
5545 old_ordinal,
5546 new_ordinal,
5547 });
5548 try ordinal_remap.putNoClobber(old_ordinal, new_ordinal);
5549 try ordinals.putNoClobber(gpa, match, {});
5550 }
5551 }
4922fn writeLinkeditSegmentData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
4923 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
4924 seg.filesize = 0;
4925 seg.vmsize = 0;
55524926
5553 // FIXME Jakub
5554 // TODO no need for duping work here; simply walk the atom graph
5555 for (self.locals.items) |*sym| {
5556 if (sym.undf()) continue;
5557 if (sym.n_sect == 0) continue;
5558 sym.n_sect = ordinal_remap.get(sym.n_sect).?;
5559 }
5560 for (self.objects.items) |*object| {
5561 for (object.symtab.items) |*sym| {
5562 if (sym.undf()) continue;
5563 if (sym.n_sect == 0) continue;
5564 sym.n_sect = ordinal_remap.get(sym.n_sect).?;
5565 }
5566 }
4927 try self.writeDyldInfoData(ncmds, lc_writer);
4928 try self.writeFunctionStarts(ncmds, lc_writer);
4929 try self.writeDataInCode(ncmds, lc_writer);
4930 try self.writeSymtabs(ncmds, lc_writer);
55674931
5568 self.section_ordinals.deinit(gpa);
5569 self.section_ordinals = ordinals;
4932 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
55704933}
55714934
5572fn writeDyldInfoData(self: *MachO) !void {
4935fn writeDyldInfoData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
55734936 const tracy = trace(@src());
55744937 defer tracy.end();
55754938
......@@ -5582,89 +4945,86 @@ fn writeDyldInfoData(self: *MachO) !void {
55824945 var lazy_bind_pointers = std.ArrayList(bind.Pointer).init(gpa);
55834946 defer lazy_bind_pointers.deinit();
55844947
5585 {
5586 var it = self.atoms.iterator();
5587 while (it.next()) |entry| {
5588 const match = entry.key_ptr.*;
5589 var atom: *Atom = entry.value_ptr.*;
4948 const slice = self.sections.slice();
4949 for (slice.items(.last_atom)) |last_atom, sect_id| {
4950 var atom = last_atom orelse continue;
4951 const segment_index = slice.items(.segment_index)[sect_id];
4952 const header = slice.items(.header)[sect_id];
55904953
5591 if (self.text_segment_cmd_index) |seg| {
5592 if (match.seg == seg) continue; // __TEXT is non-writable
5593 }
4954 if (mem.eql(u8, header.segName(), "__TEXT")) continue; // __TEXT is non-writable
55944955
5595 const seg = self.getSegment(match);
5596 const sect = self.getSection(match);
5597 log.debug("dyld info for {s},{s}", .{ sect.segName(), sect.sectName() });
5598
5599 while (true) {
5600 log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, atom.getName(self) });
5601 const sym = atom.getSymbol(self);
5602 const base_offset = sym.n_value - seg.inner.vmaddr;
5603
5604 for (atom.rebases.items) |offset| {
5605 log.debug(" | rebase at {x}", .{base_offset + offset});
5606 try rebase_pointers.append(.{
5607 .offset = base_offset + offset,
5608 .segment_id = match.seg,
5609 });
5610 }
4956 log.debug("dyld info for {s},{s}", .{ header.segName(), header.sectName() });
56114957
5612 for (atom.bindings.items) |binding| {
5613 const bind_sym = self.getSymbol(binding.target);
5614 const bind_sym_name = self.getSymbolName(binding.target);
5615 const dylib_ordinal = @divTrunc(
5616 @bitCast(i16, bind_sym.n_desc),
5617 macho.N_SYMBOL_RESOLVER,
5618 );
5619 var flags: u4 = 0;
5620 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
5621 binding.offset + base_offset,
5622 bind_sym_name,
5623 dylib_ordinal,
5624 });
5625 if (bind_sym.weakRef()) {
5626 log.debug(" | marking as weak ref ", .{});
5627 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
5628 }
5629 try bind_pointers.append(.{
5630 .offset = binding.offset + base_offset,
5631 .segment_id = match.seg,
5632 .dylib_ordinal = dylib_ordinal,
5633 .name = bind_sym_name,
5634 .bind_flags = flags,
5635 });
5636 }
4958 const seg = self.segments.items[segment_index];
56374959
5638 for (atom.lazy_bindings.items) |binding| {
5639 const bind_sym = self.getSymbol(binding.target);
5640 const bind_sym_name = self.getSymbolName(binding.target);
5641 const dylib_ordinal = @divTrunc(
5642 @bitCast(i16, bind_sym.n_desc),
5643 macho.N_SYMBOL_RESOLVER,
5644 );
5645 var flags: u4 = 0;
5646 log.debug(" | lazy bind at {x} import('{s}') ord({d})", .{
5647 binding.offset + base_offset,
5648 bind_sym_name,
5649 dylib_ordinal,
5650 });
5651 if (bind_sym.weakRef()) {
5652 log.debug(" | marking as weak ref ", .{});
5653 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
5654 }
5655 try lazy_bind_pointers.append(.{
5656 .offset = binding.offset + base_offset,
5657 .segment_id = match.seg,
5658 .dylib_ordinal = dylib_ordinal,
5659 .name = bind_sym_name,
5660 .bind_flags = flags,
5661 });
4960 while (true) {
4961 log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, atom.getName(self) });
4962 const sym = atom.getSymbol(self);
4963 const base_offset = sym.n_value - seg.vmaddr;
4964
4965 for (atom.rebases.items) |offset| {
4966 log.debug(" | rebase at {x}", .{base_offset + offset});
4967 try rebase_pointers.append(.{
4968 .offset = base_offset + offset,
4969 .segment_id = segment_index,
4970 });
4971 }
4972
4973 for (atom.bindings.items) |binding| {
4974 const bind_sym = self.getSymbol(binding.target);
4975 const bind_sym_name = self.getSymbolName(binding.target);
4976 const dylib_ordinal = @divTrunc(
4977 @bitCast(i16, bind_sym.n_desc),
4978 macho.N_SYMBOL_RESOLVER,
4979 );
4980 var flags: u4 = 0;
4981 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
4982 binding.offset + base_offset,
4983 bind_sym_name,
4984 dylib_ordinal,
4985 });
4986 if (bind_sym.weakRef()) {
4987 log.debug(" | marking as weak ref ", .{});
4988 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
56624989 }
4990 try bind_pointers.append(.{
4991 .offset = binding.offset + base_offset,
4992 .segment_id = segment_index,
4993 .dylib_ordinal = dylib_ordinal,
4994 .name = bind_sym_name,
4995 .bind_flags = flags,
4996 });
4997 }
56634998
5664 if (atom.prev) |prev| {
5665 atom = prev;
5666 } else break;
4999 for (atom.lazy_bindings.items) |binding| {
5000 const bind_sym = self.getSymbol(binding.target);
5001 const bind_sym_name = self.getSymbolName(binding.target);
5002 const dylib_ordinal = @divTrunc(
5003 @bitCast(i16, bind_sym.n_desc),
5004 macho.N_SYMBOL_RESOLVER,
5005 );
5006 var flags: u4 = 0;
5007 log.debug(" | lazy bind at {x} import('{s}') ord({d})", .{
5008 binding.offset + base_offset,
5009 bind_sym_name,
5010 dylib_ordinal,
5011 });
5012 if (bind_sym.weakRef()) {
5013 log.debug(" | marking as weak ref ", .{});
5014 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
5015 }
5016 try lazy_bind_pointers.append(.{
5017 .offset = binding.offset + base_offset,
5018 .segment_id = segment_index,
5019 .dylib_ordinal = dylib_ordinal,
5020 .name = bind_sym_name,
5021 .bind_flags = flags,
5022 });
56675023 }
5024
5025 if (atom.prev) |prev| {
5026 atom = prev;
5027 } else break;
56685028 }
56695029 }
56705030
......@@ -5675,8 +5035,8 @@ fn writeDyldInfoData(self: *MachO) !void {
56755035 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
56765036 log.debug("generating export trie", .{});
56775037
5678 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;
5679 const base_address = text_segment.inner.vmaddr;
5038 const text_segment = self.segments.items[self.text_segment_cmd_index.?];
5039 const base_address = text_segment.vmaddr;
56805040
56815041 if (self.base.options.output_mode == .Exe) {
56825042 for (&[_]SymbolWithLoc{
......@@ -5714,103 +5074,91 @@ fn writeDyldInfoData(self: *MachO) !void {
57145074 try trie.finalize(gpa);
57155075 }
57165076
5717 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
5718 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].dyld_info_only;
5719
5720 const rebase_off = mem.alignForwardGeneric(u64, seg.inner.fileoff, @alignOf(u64));
5077 const link_seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
5078 const rebase_off = mem.alignForwardGeneric(u64, link_seg.fileoff, @alignOf(u64));
5079 assert(rebase_off == link_seg.fileoff);
57215080 const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items);
5722 dyld_info.rebase_off = @intCast(u32, rebase_off);
5723 dyld_info.rebase_size = @intCast(u32, rebase_size);
5724 log.debug("writing rebase info from 0x{x} to 0x{x}", .{
5725 dyld_info.rebase_off,
5726 dyld_info.rebase_off + dyld_info.rebase_size,
5727 });
5081 log.debug("writing rebase info from 0x{x} to 0x{x}", .{ rebase_off, rebase_off + rebase_size });
57285082
5729 const bind_off = mem.alignForwardGeneric(u64, dyld_info.rebase_off + dyld_info.rebase_size, @alignOf(u64));
5083 const bind_off = mem.alignForwardGeneric(u64, rebase_off + rebase_size, @alignOf(u64));
57305084 const bind_size = try bind.bindInfoSize(bind_pointers.items);
5731 dyld_info.bind_off = @intCast(u32, bind_off);
5732 dyld_info.bind_size = @intCast(u32, bind_size);
5733 log.debug("writing bind info from 0x{x} to 0x{x}", .{
5734 dyld_info.bind_off,
5735 dyld_info.bind_off + dyld_info.bind_size,
5736 });
5085 log.debug("writing bind info from 0x{x} to 0x{x}", .{ bind_off, bind_off + bind_size });
57375086
5738 const lazy_bind_off = mem.alignForwardGeneric(u64, dyld_info.bind_off + dyld_info.bind_size, @alignOf(u64));
5087 const lazy_bind_off = mem.alignForwardGeneric(u64, bind_off + bind_size, @alignOf(u64));
57395088 const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items);
5740 dyld_info.lazy_bind_off = @intCast(u32, lazy_bind_off);
5741 dyld_info.lazy_bind_size = @intCast(u32, lazy_bind_size);
5742 log.debug("writing lazy bind info from 0x{x} to 0x{x}", .{
5743 dyld_info.lazy_bind_off,
5744 dyld_info.lazy_bind_off + dyld_info.lazy_bind_size,
5745 });
5089 log.debug("writing lazy bind info from 0x{x} to 0x{x}", .{ lazy_bind_off, lazy_bind_off + lazy_bind_size });
57465090
5747 const export_off = mem.alignForwardGeneric(u64, dyld_info.lazy_bind_off + dyld_info.lazy_bind_size, @alignOf(u64));
5091 const export_off = mem.alignForwardGeneric(u64, lazy_bind_off + lazy_bind_size, @alignOf(u64));
57485092 const export_size = trie.size;
5749 dyld_info.export_off = @intCast(u32, export_off);
5750 dyld_info.export_size = @intCast(u32, export_size);
5751 log.debug("writing export trie from 0x{x} to 0x{x}", .{
5752 dyld_info.export_off,
5753 dyld_info.export_off + dyld_info.export_size,
5754 });
5093 log.debug("writing export trie from 0x{x} to 0x{x}", .{ export_off, export_off + export_size });
57555094
5756 seg.inner.filesize = dyld_info.export_off + dyld_info.export_size - seg.inner.fileoff;
5095 const needed_size = export_off + export_size - rebase_off;
5096 link_seg.filesize = needed_size;
57575097
5758 const needed_size = dyld_info.export_off + dyld_info.export_size - dyld_info.rebase_off;
5759 var buffer = try gpa.alloc(u8, needed_size);
5098 var buffer = try gpa.alloc(u8, math.cast(usize, needed_size) orelse return error.Overflow);
57605099 defer gpa.free(buffer);
57615100 mem.set(u8, buffer, 0);
57625101
57635102 var stream = std.io.fixedBufferStream(buffer);
57645103 const writer = stream.writer();
57655104
5766 const base_off = dyld_info.rebase_off;
57675105 try bind.writeRebaseInfo(rebase_pointers.items, writer);
5768 try stream.seekTo(dyld_info.bind_off - base_off);
5106 try stream.seekTo(bind_off - rebase_off);
57695107
57705108 try bind.writeBindInfo(bind_pointers.items, writer);
5771 try stream.seekTo(dyld_info.lazy_bind_off - base_off);
5109 try stream.seekTo(lazy_bind_off - rebase_off);
57725110
57735111 try bind.writeLazyBindInfo(lazy_bind_pointers.items, writer);
5774 try stream.seekTo(dyld_info.export_off - base_off);
5112 try stream.seekTo(export_off - rebase_off);
57755113
57765114 _ = try trie.write(writer);
57775115
57785116 log.debug("writing dyld info from 0x{x} to 0x{x}", .{
5779 dyld_info.rebase_off,
5780 dyld_info.rebase_off + needed_size,
5117 rebase_off,
5118 rebase_off + needed_size,
57815119 });
57825120
5783 try self.base.file.?.pwriteAll(buffer, dyld_info.rebase_off);
5784 try self.populateLazyBindOffsetsInStubHelper(
5785 buffer[dyld_info.lazy_bind_off - base_off ..][0..dyld_info.lazy_bind_size],
5786 );
5787
5788 self.load_commands_dirty = true;
5121 try self.base.file.?.pwriteAll(buffer, rebase_off);
5122 const start = math.cast(usize, lazy_bind_off - rebase_off) orelse return error.Overflow;
5123 const end = start + (math.cast(usize, lazy_bind_size) orelse return error.Overflow);
5124 try self.populateLazyBindOffsetsInStubHelper(buffer[start..end]);
5125
5126 try lc_writer.writeStruct(macho.dyld_info_command{
5127 .cmd = .DYLD_INFO_ONLY,
5128 .cmdsize = @sizeOf(macho.dyld_info_command),
5129 .rebase_off = @intCast(u32, rebase_off),
5130 .rebase_size = @intCast(u32, rebase_size),
5131 .bind_off = @intCast(u32, bind_off),
5132 .bind_size = @intCast(u32, bind_size),
5133 .weak_bind_off = 0,
5134 .weak_bind_size = 0,
5135 .lazy_bind_off = @intCast(u32, lazy_bind_off),
5136 .lazy_bind_size = @intCast(u32, lazy_bind_size),
5137 .export_off = @intCast(u32, export_off),
5138 .export_size = @intCast(u32, export_size),
5139 });
5140 ncmds.* += 1;
57895141}
57905142
57915143fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
57925144 const gpa = self.base.allocator;
5793 const text_segment_cmd_index = self.text_segment_cmd_index orelse return;
5145
57945146 const stub_helper_section_index = self.stub_helper_section_index orelse return;
5795 const last_atom = self.atoms.get(.{
5796 .seg = text_segment_cmd_index,
5797 .sect = stub_helper_section_index,
5798 }) orelse return;
57995147 if (self.stub_helper_preamble_atom == null) return;
5800 if (last_atom == self.stub_helper_preamble_atom.?) return;
5148
5149 const section = self.sections.get(stub_helper_section_index);
5150 const last_atom = section.last_atom orelse return;
5151 if (last_atom == self.stub_helper_preamble_atom.?) return; // TODO is this a redundant check?
58015152
58025153 var table = std.AutoHashMap(i64, *Atom).init(gpa);
58035154 defer table.deinit();
58045155
58055156 {
58065157 var stub_atom = last_atom;
5807 var laptr_atom = self.atoms.get(.{
5808 .seg = self.data_segment_cmd_index.?,
5809 .sect = self.la_symbol_ptr_section_index.?,
5810 }).?;
5158 var laptr_atom = self.sections.items(.last_atom)[self.la_symbol_ptr_section_index.?].?;
58115159 const base_addr = blk: {
5812 const seg = self.load_commands.items[self.data_segment_cmd_index.?].segment;
5813 break :blk seg.inner.vmaddr;
5160 const seg = self.segments.items[self.data_segment_cmd_index.?];
5161 break :blk seg.vmaddr;
58145162 };
58155163
58165164 while (true) {
......@@ -5871,10 +5219,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
58715219 }
58725220 }
58735221
5874 const sect = self.getSection(.{
5875 .seg = text_segment_cmd_index,
5876 .sect = stub_helper_section_index,
5877 });
5222 const header = self.sections.items(.header)[stub_helper_section_index];
58785223 const stub_offset: u4 = switch (self.base.options.target.cpu.arch) {
58795224 .x86_64 => 1,
58805225 .aarch64 => 2 * @sizeOf(u32),
......@@ -5886,7 +5231,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
58865231 while (offsets.popOrNull()) |bind_offset| {
58875232 const atom = table.get(bind_offset.sym_offset).?;
58885233 const sym = atom.getSymbol(self);
5889 const file_offset = sect.offset + sym.n_value - sect.addr + stub_offset;
5234 const file_offset = header.offset + sym.n_value - header.addr + stub_offset;
58905235 mem.writeIntLittle(u32, &buf, bind_offset.offset);
58915236 log.debug("writing lazy bind offset in stub helper of 0x{x} for symbol {s} at offset 0x{x}", .{
58925237 bind_offset.offset,
......@@ -5899,14 +5244,14 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
58995244
59005245const asc_u64 = std.sort.asc(u64);
59015246
5902fn writeFunctionStarts(self: *MachO) !void {
5903 const text_seg_index = self.text_segment_cmd_index orelse return;
5904 const text_sect_index = self.text_section_index orelse return;
5905 const text_seg = self.load_commands.items[text_seg_index].segment;
5906
5247fn writeFunctionStarts(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
59075248 const tracy = trace(@src());
59085249 defer tracy.end();
59095250
5251 const text_seg_index = self.text_segment_cmd_index orelse return;
5252 const text_sect_index = self.text_section_index orelse return;
5253 const text_seg = self.segments.items[text_seg_index];
5254
59105255 const gpa = self.base.allocator;
59115256
59125257 // We need to sort by address first
......@@ -5918,8 +5263,8 @@ fn writeFunctionStarts(self: *MachO) !void {
59185263 const sym = self.getSymbol(global);
59195264 if (sym.undf()) continue;
59205265 if (sym.n_desc == N_DESC_GCED) continue;
5921 const match = self.getMatchingSectionFromOrdinal(sym.n_sect);
5922 if (match.seg != text_seg_index or match.sect != text_sect_index) continue;
5266 const sect_id = sym.n_sect - 1;
5267 if (sect_id != text_sect_index) continue;
59235268
59245269 addresses.appendAssumeCapacity(sym.n_value);
59255270 }
......@@ -5932,7 +5277,7 @@ fn writeFunctionStarts(self: *MachO) !void {
59325277
59335278 var last_off: u32 = 0;
59345279 for (addresses.items) |addr| {
5935 const offset = @intCast(u32, addr - text_seg.inner.vmaddr);
5280 const offset = @intCast(u32, addr - text_seg.vmaddr);
59365281 const diff = offset - last_off;
59375282
59385283 if (diff == 0) continue;
......@@ -5951,22 +5296,22 @@ fn writeFunctionStarts(self: *MachO) !void {
59515296 try std.leb.writeULEB128(buffer.writer(), offset);
59525297 }
59535298
5954 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
5955 const fn_cmd = &self.load_commands.items[self.function_starts_cmd_index.?].linkedit_data;
5299 const link_seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
5300 const offset = mem.alignForwardGeneric(u64, link_seg.fileoff + link_seg.filesize, @alignOf(u64));
5301 const needed_size = buffer.items.len;
5302 link_seg.filesize = offset + needed_size - link_seg.fileoff;
59565303
5957 const dataoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64));
5958 const datasize = buffer.items.len;
5959 fn_cmd.dataoff = @intCast(u32, dataoff);
5960 fn_cmd.datasize = @intCast(u32, datasize);
5961 seg.inner.filesize = fn_cmd.dataoff + fn_cmd.datasize - seg.inner.fileoff;
5304 log.debug("writing function starts info from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
59625305
5963 log.debug("writing function starts info from 0x{x} to 0x{x}", .{
5964 fn_cmd.dataoff,
5965 fn_cmd.dataoff + fn_cmd.datasize,
5966 });
5306 try self.base.file.?.pwriteAll(buffer.items, offset);
59675307
5968 try self.base.file.?.pwriteAll(buffer.items, fn_cmd.dataoff);
5969 self.load_commands_dirty = true;
5308 try lc_writer.writeStruct(macho.linkedit_data_command{
5309 .cmd = .FUNCTION_STARTS,
5310 .cmdsize = @sizeOf(macho.linkedit_data_command),
5311 .dataoff = @intCast(u32, offset),
5312 .datasize = @intCast(u32, needed_size),
5313 });
5314 ncmds.* += 1;
59705315}
59715316
59725317fn filterDataInCode(
......@@ -5988,17 +5333,15 @@ fn filterDataInCode(
59885333 return dices[start..end];
59895334}
59905335
5991fn writeDataInCode(self: *MachO) !void {
5336fn writeDataInCode(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
59925337 const tracy = trace(@src());
59935338 defer tracy.end();
59945339
59955340 var out_dice = std.ArrayList(macho.data_in_code_entry).init(self.base.allocator);
59965341 defer out_dice.deinit();
59975342
5998 const text_sect = self.getSection(.{
5999 .seg = self.text_segment_cmd_index orelse return,
6000 .sect = self.text_section_index orelse return,
6001 });
5343 const text_sect_id = self.text_section_index orelse return;
5344 const text_sect_header = self.sections.items(.header)[text_sect_id];
60025345
60035346 for (self.objects.items) |object| {
60045347 const dice = object.parseDataInCode() orelse continue;
......@@ -6008,15 +5351,15 @@ fn writeDataInCode(self: *MachO) !void {
60085351 const sym = atom.getSymbol(self);
60095352 if (sym.n_desc == N_DESC_GCED) continue;
60105353
6011 const match = self.getMatchingSectionFromOrdinal(sym.n_sect);
6012 if (match.seg != self.text_segment_cmd_index.? and match.sect != self.text_section_index.?) {
5354 const sect_id = sym.n_sect - 1;
5355 if (sect_id != self.text_section_index.?) {
60135356 continue;
60145357 }
60155358
60165359 const source_sym = object.getSourceSymbol(atom.sym_index) orelse continue;
60175360 const source_addr = math.cast(u32, source_sym.n_value) orelse return error.Overflow;
60185361 const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size);
6019 const base = math.cast(u32, sym.n_value - text_sect.addr + text_sect.offset) orelse
5362 const base = math.cast(u32, sym.n_value - text_sect_header.addr + text_sect_header.offset) orelse
60205363 return error.Overflow;
60215364
60225365 for (filtered_dice) |single| {
......@@ -6030,33 +5373,63 @@ fn writeDataInCode(self: *MachO) !void {
60305373 }
60315374 }
60325375
6033 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
6034 const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].linkedit_data;
5376 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
5377 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
5378 const needed_size = out_dice.items.len * @sizeOf(macho.data_in_code_entry);
5379 seg.filesize = offset + needed_size - seg.fileoff;
60355380
6036 const dataoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64));
6037 const datasize = out_dice.items.len * @sizeOf(macho.data_in_code_entry);
6038 dice_cmd.dataoff = @intCast(u32, dataoff);
6039 dice_cmd.datasize = @intCast(u32, datasize);
6040 seg.inner.filesize = dice_cmd.dataoff + dice_cmd.datasize - seg.inner.fileoff;
5381 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
60415382
6042 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{
6043 dice_cmd.dataoff,
6044 dice_cmd.dataoff + dice_cmd.datasize,
5383 try self.base.file.?.pwriteAll(mem.sliceAsBytes(out_dice.items), offset);
5384 try lc_writer.writeStruct(macho.linkedit_data_command{
5385 .cmd = .DATA_IN_CODE,
5386 .cmdsize = @sizeOf(macho.linkedit_data_command),
5387 .dataoff = @intCast(u32, offset),
5388 .datasize = @intCast(u32, needed_size),
60455389 });
6046
6047 try self.base.file.?.pwriteAll(mem.sliceAsBytes(out_dice.items), dice_cmd.dataoff);
6048 self.load_commands_dirty = true;
5390 ncmds.* += 1;
60495391}
60505392
6051fn writeSymtab(self: *MachO) !void {
6052 const tracy = trace(@src());
6053 defer tracy.end();
5393fn writeSymtabs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
5394 var symtab_cmd = macho.symtab_command{
5395 .cmdsize = @sizeOf(macho.symtab_command),
5396 .symoff = 0,
5397 .nsyms = 0,
5398 .stroff = 0,
5399 .strsize = 0,
5400 };
5401 var dysymtab_cmd = macho.dysymtab_command{
5402 .cmdsize = @sizeOf(macho.dysymtab_command),
5403 .ilocalsym = 0,
5404 .nlocalsym = 0,
5405 .iextdefsym = 0,
5406 .nextdefsym = 0,
5407 .iundefsym = 0,
5408 .nundefsym = 0,
5409 .tocoff = 0,
5410 .ntoc = 0,
5411 .modtaboff = 0,
5412 .nmodtab = 0,
5413 .extrefsymoff = 0,
5414 .nextrefsyms = 0,
5415 .indirectsymoff = 0,
5416 .nindirectsyms = 0,
5417 .extreloff = 0,
5418 .nextrel = 0,
5419 .locreloff = 0,
5420 .nlocrel = 0,
5421 };
5422 var ctx = try self.writeSymtab(&symtab_cmd);
5423 defer ctx.imports_table.deinit();
5424 try self.writeDysymtab(ctx, &dysymtab_cmd);
5425 try self.writeStrtab(&symtab_cmd);
5426 try lc_writer.writeStruct(symtab_cmd);
5427 try lc_writer.writeStruct(dysymtab_cmd);
5428 ncmds.* += 2;
5429}
60545430
5431fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {
60555432 const gpa = self.base.allocator;
6056 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
6057 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
6058 const symoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(macho.nlist_64));
6059 symtab.symoff = @intCast(u32, symoff);
60605433
60615434 var locals = std.ArrayList(macho.nlist_64).init(gpa);
60625435 defer locals.deinit();
......@@ -6101,8 +5474,8 @@ fn writeSymtab(self: *MachO) !void {
61015474
61025475 var imports = std.ArrayList(macho.nlist_64).init(gpa);
61035476 defer imports.deinit();
5477
61045478 var imports_table = std.AutoHashMap(SymbolWithLoc, u32).init(gpa);
6105 defer imports_table.deinit();
61065479
61075480 for (self.globals.values()) |global| {
61085481 const sym = self.getSymbol(global);
......@@ -6115,56 +5488,84 @@ fn writeSymtab(self: *MachO) !void {
61155488 try imports_table.putNoClobber(global, new_index);
61165489 }
61175490
6118 const nlocals = locals.items.len;
6119 const nexports = exports.items.len;
6120 const nimports = imports.items.len;
6121 symtab.nsyms = @intCast(u32, nlocals + nexports + nimports);
5491 const nlocals = @intCast(u32, locals.items.len);
5492 const nexports = @intCast(u32, exports.items.len);
5493 const nimports = @intCast(u32, imports.items.len);
5494 const nsyms = nlocals + nexports + nimports;
5495
5496 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
5497 const offset = mem.alignForwardGeneric(
5498 u64,
5499 seg.fileoff + seg.filesize,
5500 @alignOf(macho.nlist_64),
5501 );
5502 const needed_size = nsyms * @sizeOf(macho.nlist_64);
5503 seg.filesize = offset + needed_size - seg.fileoff;
61225504
61235505 var buffer = std.ArrayList(u8).init(gpa);
61245506 defer buffer.deinit();
6125 try buffer.ensureTotalCapacityPrecise(symtab.nsyms * @sizeOf(macho.nlist_64));
5507 try buffer.ensureTotalCapacityPrecise(needed_size);
61265508 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(locals.items));
61275509 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(exports.items));
61285510 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(imports.items));
61295511
6130 log.debug("writing symtab from 0x{x} to 0x{x}", .{ symtab.symoff, symtab.symoff + buffer.items.len });
6131 try self.base.file.?.pwriteAll(buffer.items, symtab.symoff);
5512 log.debug("writing symtab from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
5513 try self.base.file.?.pwriteAll(buffer.items, offset);
5514
5515 lc.symoff = @intCast(u32, offset);
5516 lc.nsyms = nsyms;
61325517
6133 seg.inner.filesize = symtab.symoff + buffer.items.len - seg.inner.fileoff;
5518 return SymtabCtx{
5519 .nlocalsym = nlocals,
5520 .nextdefsym = nexports,
5521 .nundefsym = nimports,
5522 .imports_table = imports_table,
5523 };
5524}
5525
5526fn writeStrtab(self: *MachO, lc: *macho.symtab_command) !void {
5527 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
5528 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
5529 const needed_size = self.strtab.buffer.items.len;
5530 seg.filesize = offset + needed_size - seg.fileoff;
5531
5532 log.debug("writing string table from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
61345533
6135 // Update dynamic symbol table.
6136 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].dysymtab;
6137 dysymtab.nlocalsym = @intCast(u32, nlocals);
6138 dysymtab.iextdefsym = dysymtab.nlocalsym;
6139 dysymtab.nextdefsym = @intCast(u32, nexports);
6140 dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym;
6141 dysymtab.nundefsym = @intCast(u32, nimports);
5534 try self.base.file.?.pwriteAll(self.strtab.buffer.items, offset);
5535
5536 lc.stroff = @intCast(u32, offset);
5537 lc.strsize = @intCast(u32, needed_size);
5538}
5539
5540const SymtabCtx = struct {
5541 nlocalsym: u32,
5542 nextdefsym: u32,
5543 nundefsym: u32,
5544 imports_table: std.AutoHashMap(SymbolWithLoc, u32),
5545};
61425546
5547fn writeDysymtab(self: *MachO, ctx: SymtabCtx, lc: *macho.dysymtab_command) !void {
5548 const gpa = self.base.allocator;
61435549 const nstubs = @intCast(u32, self.stubs_table.count());
61445550 const ngot_entries = @intCast(u32, self.got_entries_table.count());
5551 const nindirectsyms = nstubs * 2 + ngot_entries;
5552 const iextdefsym = ctx.nlocalsym;
5553 const iundefsym = iextdefsym + ctx.nextdefsym;
61455554
6146 const indirectsymoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64));
6147 dysymtab.indirectsymoff = @intCast(u32, indirectsymoff);
6148 dysymtab.nindirectsyms = nstubs * 2 + ngot_entries;
6149
6150 seg.inner.filesize = dysymtab.indirectsymoff + dysymtab.nindirectsyms * @sizeOf(u32) - seg.inner.fileoff;
5555 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
5556 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
5557 const needed_size = nindirectsyms * @sizeOf(u32);
5558 seg.filesize = offset + needed_size - seg.fileoff;
61515559
6152 log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{
6153 dysymtab.indirectsymoff,
6154 dysymtab.indirectsymoff + dysymtab.nindirectsyms * @sizeOf(u32),
6155 });
5560 log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
61565561
61575562 var buf = std.ArrayList(u8).init(gpa);
61585563 defer buf.deinit();
6159 try buf.ensureTotalCapacity(dysymtab.nindirectsyms * @sizeOf(u32));
5564 try buf.ensureTotalCapacity(needed_size);
61605565 const writer = buf.writer();
61615566
6162 if (self.text_segment_cmd_index) |text_segment_cmd_index| blk: {
6163 const stubs_section_index = self.stubs_section_index orelse break :blk;
6164 const stubs = self.getSectionPtr(.{
6165 .seg = text_segment_cmd_index,
6166 .sect = stubs_section_index,
6167 });
5567 if (self.stubs_section_index) |sect_id| {
5568 const stubs = &self.sections.items(.header)[sect_id];
61685569 stubs.reserved1 = 0;
61695570 for (self.stubs.items) |entry| {
61705571 if (entry.sym_index == 0) continue;
......@@ -6172,16 +5573,12 @@ fn writeSymtab(self: *MachO) !void {
61725573 if (atom_sym.n_desc == N_DESC_GCED) continue;
61735574 const target_sym = self.getSymbol(entry.target);
61745575 assert(target_sym.undf());
6175 try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(entry.target).?);
5576 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
61765577 }
61775578 }
61785579
6179 if (self.data_const_segment_cmd_index) |data_const_segment_cmd_index| blk: {
6180 const got_section_index = self.got_section_index orelse break :blk;
6181 const got = self.getSectionPtr(.{
6182 .seg = data_const_segment_cmd_index,
6183 .sect = got_section_index,
6184 });
5580 if (self.got_section_index) |sect_id| {
5581 const got = &self.sections.items(.header)[sect_id];
61855582 got.reserved1 = nstubs;
61865583 for (self.got_entries.items) |entry| {
61875584 if (entry.sym_index == 0) continue;
......@@ -6189,19 +5586,15 @@ fn writeSymtab(self: *MachO) !void {
61895586 if (atom_sym.n_desc == N_DESC_GCED) continue;
61905587 const target_sym = self.getSymbol(entry.target);
61915588 if (target_sym.undf()) {
6192 try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(entry.target).?);
5589 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
61935590 } else {
61945591 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
61955592 }
61965593 }
61975594 }
61985595
6199 if (self.data_segment_cmd_index) |data_segment_cmd_index| blk: {
6200 const la_symbol_ptr_section_index = self.la_symbol_ptr_section_index orelse break :blk;
6201 const la_symbol_ptr = self.getSectionPtr(.{
6202 .seg = data_segment_cmd_index,
6203 .sect = la_symbol_ptr_section_index,
6204 });
5596 if (self.la_symbol_ptr_section_index) |sect_id| {
5597 const la_symbol_ptr = &self.sections.items(.header)[sect_id];
62055598 la_symbol_ptr.reserved1 = nstubs + ngot_entries;
62065599 for (self.stubs.items) |entry| {
62075600 if (entry.sym_index == 0) continue;
......@@ -6209,131 +5602,76 @@ fn writeSymtab(self: *MachO) !void {
62095602 if (atom_sym.n_desc == N_DESC_GCED) continue;
62105603 const target_sym = self.getSymbol(entry.target);
62115604 assert(target_sym.undf());
6212 try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(entry.target).?);
5605 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
62135606 }
62145607 }
62155608
6216 assert(buf.items.len == dysymtab.nindirectsyms * @sizeOf(u32));
6217
6218 try self.base.file.?.pwriteAll(buf.items, dysymtab.indirectsymoff);
6219 self.load_commands_dirty = true;
6220}
6221
6222fn writeStrtab(self: *MachO) !void {
6223 const tracy = trace(@src());
6224 defer tracy.end();
6225
6226 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
6227 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
6228 const stroff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64));
6229
6230 const strsize = self.strtab.buffer.items.len;
6231 symtab.stroff = @intCast(u32, stroff);
6232 symtab.strsize = @intCast(u32, strsize);
6233 seg.inner.filesize = symtab.stroff + symtab.strsize - seg.inner.fileoff;
6234
6235 log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });
6236
6237 try self.base.file.?.pwriteAll(self.strtab.buffer.items, symtab.stroff);
6238
6239 self.load_commands_dirty = true;
6240}
6241
6242fn writeLinkeditSegment(self: *MachO) !void {
6243 const tracy = trace(@src());
6244 defer tracy.end();
5609 assert(buf.items.len == needed_size);
5610 try self.base.file.?.pwriteAll(buf.items, offset);
62455611
6246 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
6247 seg.inner.filesize = 0;
6248
6249 try self.writeDyldInfoData();
6250 try self.writeFunctionStarts();
6251 try self.writeDataInCode();
6252 try self.writeSymtab();
6253 try self.writeStrtab();
6254
6255 seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size);
5612 lc.nlocalsym = ctx.nlocalsym;
5613 lc.iextdefsym = iextdefsym;
5614 lc.nextdefsym = ctx.nextdefsym;
5615 lc.iundefsym = iundefsym;
5616 lc.nundefsym = ctx.nundefsym;
5617 lc.indirectsymoff = @intCast(u32, offset);
5618 lc.nindirectsyms = nindirectsyms;
62565619}
62575620
6258fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {
6259 const tracy = trace(@src());
6260 defer tracy.end();
6261
6262 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
6263 const cs_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].linkedit_data;
5621fn writeCodeSignaturePadding(
5622 self: *MachO,
5623 code_sig: *CodeSignature,
5624 ncmds: *u32,
5625 lc_writer: anytype,
5626) !u32 {
5627 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
62645628 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file
62655629 // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271
6266 const dataoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, 16);
6267 const datasize = code_sig.estimateSize(dataoff);
6268 cs_cmd.dataoff = @intCast(u32, dataoff);
6269 cs_cmd.datasize = @intCast(u32, code_sig.estimateSize(dataoff));
6270
6271 // Advance size of __LINKEDIT segment
6272 seg.inner.filesize = cs_cmd.dataoff + cs_cmd.datasize - seg.inner.fileoff;
6273 seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size);
6274 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ dataoff, dataoff + datasize });
5630 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, 16);
5631 const needed_size = code_sig.estimateSize(offset);
5632 seg.filesize = offset + needed_size - seg.fileoff;
5633 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
5634 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
62755635 // Pad out the space. We need to do this to calculate valid hashes for everything in the file
62765636 // except for code signature data.
6277 try self.base.file.?.pwriteAll(&[_]u8{0}, dataoff + datasize - 1);
6278 self.load_commands_dirty = true;
6279}
5637 try self.base.file.?.pwriteAll(&[_]u8{0}, offset + needed_size - 1);
62805638
6281fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature) !void {
6282 const tracy = trace(@src());
6283 defer tracy.end();
5639 try lc_writer.writeStruct(macho.linkedit_data_command{
5640 .cmd = .CODE_SIGNATURE,
5641 .cmdsize = @sizeOf(macho.linkedit_data_command),
5642 .dataoff = @intCast(u32, offset),
5643 .datasize = @intCast(u32, needed_size),
5644 });
5645 ncmds.* += 1;
5646
5647 return @intCast(u32, offset);
5648}
62845649
6285 const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].linkedit_data;
6286 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
5650fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature, offset: u32) !void {
5651 const seg = self.segments.items[self.text_segment_cmd_index.?];
62875652
62885653 var buffer = std.ArrayList(u8).init(self.base.allocator);
62895654 defer buffer.deinit();
62905655 try buffer.ensureTotalCapacityPrecise(code_sig.size());
62915656 try code_sig.writeAdhocSignature(self.base.allocator, .{
62925657 .file = self.base.file.?,
6293 .exec_seg_base = seg.inner.fileoff,
6294 .exec_seg_limit = seg.inner.filesize,
6295 .code_sig_cmd = code_sig_cmd,
5658 .exec_seg_base = seg.fileoff,
5659 .exec_seg_limit = seg.filesize,
5660 .file_size = offset,
62965661 .output_mode = self.base.options.output_mode,
62975662 }, buffer.writer());
62985663 assert(buffer.items.len == code_sig.size());
62995664
63005665 log.debug("writing code signature from 0x{x} to 0x{x}", .{
6301 code_sig_cmd.dataoff,
6302 code_sig_cmd.dataoff + buffer.items.len,
5666 offset,
5667 offset + buffer.items.len,
63035668 });
63045669
6305 try self.base.file.?.pwriteAll(buffer.items, code_sig_cmd.dataoff);
6306}
6307
6308/// Writes all load commands and section headers.
6309fn writeLoadCommands(self: *MachO) !void {
6310 if (!self.load_commands_dirty) return;
6311
6312 var sizeofcmds: u32 = 0;
6313 for (self.load_commands.items) |lc| {
6314 if (lc.cmd() == .NONE) continue;
6315 sizeofcmds += lc.cmdsize();
6316 }
6317
6318 var buffer = try self.base.allocator.alloc(u8, sizeofcmds);
6319 defer self.base.allocator.free(buffer);
6320 var fib = std.io.fixedBufferStream(buffer);
6321 const writer = fib.writer();
6322 for (self.load_commands.items) |lc| {
6323 if (lc.cmd() == .NONE) continue;
6324 try lc.write(writer);
6325 }
6326
6327 const off = @sizeOf(macho.mach_header_64);
6328
6329 log.debug("writing load commands from 0x{x} to 0x{x}", .{ off, off + sizeofcmds });
6330
6331 try self.base.file.?.pwriteAll(buffer, off);
6332 self.load_commands_dirty = false;
5670 try self.base.file.?.pwriteAll(buffer.items, offset);
63335671}
63345672
63355673/// Writes Mach-O file header.
6336fn writeHeader(self: *MachO) !void {
5674fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {
63375675 var header: macho.mach_header_64 = .{};
63385676 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;
63395677
......@@ -6361,18 +5699,12 @@ fn writeHeader(self: *MachO) !void {
63615699 else => unreachable,
63625700 }
63635701
6364 if (self.tlv_section_index) |_| {
5702 if (self.getSectionByName("__DATA", "__thread_vars")) |_| {
63655703 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
63665704 }
63675705
6368 header.ncmds = 0;
6369 header.sizeofcmds = 0;
6370
6371 for (self.load_commands.items) |cmd| {
6372 if (cmd.cmd() == .NONE) continue;
6373 header.sizeofcmds += cmd.cmdsize();
6374 header.ncmds += 1;
6375 }
5706 header.ncmds = ncmds;
5707 header.sizeofcmds = sizeofcmds;
63765708
63775709 log.debug("writing Mach-O header {}", .{header});
63785710
......@@ -6392,33 +5724,27 @@ pub fn makeStaticString(bytes: []const u8) [16]u8 {
63925724 return buf;
63935725}
63945726
6395pub fn getSectionOrdinal(self: *MachO, match: MatchingSection) u8 {
6396 return @intCast(u8, self.section_ordinals.getIndex(match).?) + 1;
6397}
6398
6399pub fn getMatchingSectionFromOrdinal(self: *MachO, ord: u8) MatchingSection {
6400 const index = ord - 1;
6401 assert(index < self.section_ordinals.count());
6402 return self.section_ordinals.keys()[index];
6403}
6404
6405pub fn getSegmentPtr(self: *MachO, match: MatchingSection) *macho.SegmentCommand {
6406 assert(match.seg < self.load_commands.items.len);
6407 return &self.load_commands.items[match.seg].segment;
6408}
6409
6410pub fn getSegment(self: *MachO, match: MatchingSection) macho.SegmentCommand {
6411 return self.getSegmentPtr(match).*;
5727fn getSegmentByName(self: MachO, segname: []const u8) ?u8 {
5728 for (self.segments.items) |seg, i| {
5729 if (mem.eql(u8, segname, seg.segName())) return @intCast(u8, i);
5730 } else return null;
64125731}
64135732
6414pub fn getSectionPtr(self: *MachO, match: MatchingSection) *macho.section_64 {
6415 const seg = self.getSegmentPtr(match);
6416 assert(match.sect < seg.sections.items.len);
6417 return &seg.sections.items[match.sect];
5733pub fn getSectionByName(self: MachO, segname: []const u8, sectname: []const u8) ?u8 {
5734 // TODO investigate caching with a hashmap
5735 for (self.sections.items(.header)) |header, i| {
5736 if (mem.eql(u8, header.segName(), segname) and mem.eql(u8, header.sectName(), sectname))
5737 return @intCast(u8, i);
5738 } else return null;
64185739}
64195740
6420pub fn getSection(self: *MachO, match: MatchingSection) macho.section_64 {
6421 return self.getSectionPtr(match).*;
5741pub fn getSectionIndexes(self: MachO, segment_index: u8) struct { start: u8, end: u8 } {
5742 var start: u8 = 0;
5743 const nsects = for (self.segments.items) |seg, i| {
5744 if (i == segment_index) break @intCast(u8, seg.nsects);
5745 start += @intCast(u8, seg.nsects);
5746 } else 0;
5747 return .{ .start = start, .end = start + nsects };
64225748}
64235749
64245750pub fn symbolIsTemp(self: *MachO, sym_with_loc: SymbolWithLoc) bool {
......@@ -6512,72 +5838,6 @@ pub fn findFirst(comptime T: type, haystack: []const T, start: usize, predicate:
65125838 return i;
65135839}
65145840
6515const DebugInfo = struct {
6516 inner: dwarf.DwarfInfo,
6517 debug_info: []const u8,
6518 debug_abbrev: []const u8,
6519 debug_str: []const u8,
6520 debug_line: []const u8,
6521 debug_line_str: []const u8,
6522 debug_ranges: []const u8,
6523
6524 pub fn parse(allocator: Allocator, object: Object) !?DebugInfo {
6525 var debug_info = blk: {
6526 const index = object.dwarf_debug_info_index orelse return null;
6527 break :blk try object.getSectionContents(index);
6528 };
6529 var debug_abbrev = blk: {
6530 const index = object.dwarf_debug_abbrev_index orelse return null;
6531 break :blk try object.getSectionContents(index);
6532 };
6533 var debug_str = blk: {
6534 const index = object.dwarf_debug_str_index orelse return null;
6535 break :blk try object.getSectionContents(index);
6536 };
6537 var debug_line = blk: {
6538 const index = object.dwarf_debug_line_index orelse return null;
6539 break :blk try object.getSectionContents(index);
6540 };
6541 var debug_line_str = blk: {
6542 if (object.dwarf_debug_line_str_index) |ind| {
6543 break :blk try object.getSectionContents(ind);
6544 }
6545 break :blk &[0]u8{};
6546 };
6547 var debug_ranges = blk: {
6548 if (object.dwarf_debug_ranges_index) |ind| {
6549 break :blk try object.getSectionContents(ind);
6550 }
6551 break :blk &[0]u8{};
6552 };
6553
6554 var inner: dwarf.DwarfInfo = .{
6555 .endian = .Little,
6556 .debug_info = debug_info,
6557 .debug_abbrev = debug_abbrev,
6558 .debug_str = debug_str,
6559 .debug_line = debug_line,
6560 .debug_line_str = debug_line_str,
6561 .debug_ranges = debug_ranges,
6562 };
6563 try dwarf.openDwarfDebugInfo(&inner, allocator);
6564
6565 return DebugInfo{
6566 .inner = inner,
6567 .debug_info = debug_info,
6568 .debug_abbrev = debug_abbrev,
6569 .debug_str = debug_str,
6570 .debug_line = debug_line,
6571 .debug_line_str = debug_line_str,
6572 .debug_ranges = debug_ranges,
6573 };
6574 }
6575
6576 pub fn deinit(self: *DebugInfo, allocator: Allocator) void {
6577 self.inner.deinit(allocator);
6578 }
6579};
6580
65815841pub fn generateSymbolStabs(
65825842 self: *MachO,
65835843 object: Object,
......@@ -6585,14 +5845,15 @@ pub fn generateSymbolStabs(
65855845) !void {
65865846 assert(!self.base.options.strip);
65875847
6588 const gpa = self.base.allocator;
6589
65905848 log.debug("parsing debug info in '{s}'", .{object.name});
65915849
6592 var debug_info = (try DebugInfo.parse(gpa, object)) orelse return;
5850 const gpa = self.base.allocator;
5851 var debug_info = try object.parseDwarfInfo();
5852 defer debug_info.deinit(gpa);
5853 try dwarf.openDwarfDebugInfo(&debug_info, gpa);
65935854
65945855 // We assume there is only one CU.
6595 const compile_unit = debug_info.inner.findCompileUnit(0x0) catch |err| switch (err) {
5856 const compile_unit = debug_info.findCompileUnit(0x0) catch |err| switch (err) {
65965857 error.MissingDebugInfo => {
65975858 // TODO audit cases with missing debug info and audit our dwarf.zig module.
65985859 log.debug("invalid or missing debug info in {s}; skipping", .{object.name});
......@@ -6600,8 +5861,8 @@ pub fn generateSymbolStabs(
66005861 },
66015862 else => |e| return e,
66025863 };
6603 const tu_name = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT.name);
6604 const tu_comp_dir = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT.comp_dir);
5864 const tu_name = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.name);
5865 const tu_comp_dir = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.comp_dir);
66055866
66065867 // Open scope
66075868 try locals.ensureUnusedCapacity(3);
......@@ -6664,7 +5925,7 @@ pub fn generateSymbolStabs(
66645925fn generateSymbolStabsForSymbol(
66655926 self: *MachO,
66665927 sym_loc: SymbolWithLoc,
6667 debug_info: DebugInfo,
5928 debug_info: dwarf.DwarfInfo,
66685929 buf: *[4]macho.nlist_64,
66695930) ![]const macho.nlist_64 {
66705931 const gpa = self.base.allocator;
......@@ -6679,7 +5940,7 @@ fn generateSymbolStabsForSymbol(
66795940 const source_sym = object.getSourceSymbol(sym_loc.sym_index) orelse return buf[0..0];
66805941 const size: ?u64 = size: {
66815942 if (source_sym.tentative()) break :size null;
6682 for (debug_info.inner.func_list.items) |func| {
5943 for (debug_info.func_list.items) |func| {
66835944 if (func.pc_range) |range| {
66845945 if (source_sym.n_value >= range.start and source_sym.n_value < range.end) {
66855946 break :size range.end - range.start;
......@@ -6731,259 +5992,272 @@ fn generateSymbolStabsForSymbol(
67315992 }
67325993}
67335994
6734fn snapshotState(self: *MachO) !void {
6735 const emit = self.base.options.emit orelse {
6736 log.debug("no emit directory found; skipping snapshot...", .{});
6737 return;
6738 };
6739
6740 const Snapshot = struct {
6741 const Node = struct {
6742 const Tag = enum {
6743 section_start,
6744 section_end,
6745 atom_start,
6746 atom_end,
6747 relocation,
6748
6749 pub fn jsonStringify(
6750 tag: Tag,
6751 options: std.json.StringifyOptions,
6752 out_stream: anytype,
6753 ) !void {
6754 _ = options;
6755 switch (tag) {
6756 .section_start => try out_stream.writeAll("\"section_start\""),
6757 .section_end => try out_stream.writeAll("\"section_end\""),
6758 .atom_start => try out_stream.writeAll("\"atom_start\""),
6759 .atom_end => try out_stream.writeAll("\"atom_end\""),
6760 .relocation => try out_stream.writeAll("\"relocation\""),
6761 }
6762 }
6763 };
6764 const Payload = struct {
6765 name: []const u8 = "",
6766 aliases: [][]const u8 = &[0][]const u8{},
6767 is_global: bool = false,
6768 target: u64 = 0,
6769 };
6770 address: u64,
6771 tag: Tag,
6772 payload: Payload,
6773 };
6774 timestamp: i128,
6775 nodes: []Node,
6776 };
6777
6778 var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
6779 defer arena_allocator.deinit();
6780 const arena = arena_allocator.allocator();
6781
6782 const out_file = try emit.directory.handle.createFile("snapshots.json", .{
6783 .truncate = false,
6784 .read = true,
6785 });
6786 defer out_file.close();
6787
6788 if (out_file.seekFromEnd(-1)) {
6789 try out_file.writer().writeByte(',');
6790 } else |err| switch (err) {
6791 error.Unseekable => try out_file.writer().writeByte('['),
6792 else => |e| return e,
6793 }
6794 const writer = out_file.writer();
6795
6796 var snapshot = Snapshot{
6797 .timestamp = std.time.nanoTimestamp(),
6798 .nodes = undefined,
6799 };
6800 var nodes = std.ArrayList(Snapshot.Node).init(arena);
6801
6802 for (self.section_ordinals.keys()) |key| {
6803 const sect = self.getSection(key);
6804 const sect_name = try std.fmt.allocPrint(arena, "{s},{s}", .{ sect.segName(), sect.sectName() });
6805 try nodes.append(.{
6806 .address = sect.addr,
6807 .tag = .section_start,
6808 .payload = .{ .name = sect_name },
6809 });
6810
6811 const is_tlv = sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;
6812
6813 var atom: *Atom = self.atoms.get(key) orelse {
6814 try nodes.append(.{
6815 .address = sect.addr + sect.size,
6816 .tag = .section_end,
6817 .payload = .{},
6818 });
6819 continue;
6820 };
6821
6822 while (atom.prev) |prev| {
6823 atom = prev;
6824 }
6825
6826 while (true) {
6827 const atom_sym = atom.getSymbol(self);
6828 var node = Snapshot.Node{
6829 .address = atom_sym.n_value,
6830 .tag = .atom_start,
6831 .payload = .{
6832 .name = atom.getName(self),
6833 .is_global = self.globals.contains(atom.getName(self)),
6834 },
6835 };
6836
6837 var aliases = std.ArrayList([]const u8).init(arena);
6838 for (atom.contained.items) |sym_off| {
6839 if (sym_off.offset == 0) {
6840 try aliases.append(self.getSymbolName(.{
6841 .sym_index = sym_off.sym_index,
6842 .file = atom.file,
6843 }));
6844 }
6845 }
6846 node.payload.aliases = aliases.toOwnedSlice();
6847 try nodes.append(node);
6848
6849 var relocs = try std.ArrayList(Snapshot.Node).initCapacity(arena, atom.relocs.items.len);
6850 for (atom.relocs.items) |rel| {
6851 const source_addr = blk: {
6852 const source_sym = atom.getSymbol(self);
6853 break :blk source_sym.n_value + rel.offset;
6854 };
6855 const target_addr = blk: {
6856 const target_atom = rel.getTargetAtom(self) orelse {
6857 // If there is no atom for target, we still need to check for special, atom-less
6858 // symbols such as `___dso_handle`.
6859 const target_name = self.getSymbolName(rel.target);
6860 if (self.globals.contains(target_name)) {
6861 const atomless_sym = self.getSymbol(rel.target);
6862 break :blk atomless_sym.n_value;
6863 }
6864 break :blk 0;
6865 };
6866 const target_sym = if (target_atom.isSymbolContained(rel.target, self))
6867 self.getSymbol(rel.target)
6868 else
6869 target_atom.getSymbol(self);
6870 const base_address: u64 = if (is_tlv) base_address: {
6871 const sect_id: u16 = sect_id: {
6872 if (self.tlv_data_section_index) |i| {
6873 break :sect_id i;
6874 } else if (self.tlv_bss_section_index) |i| {
6875 break :sect_id i;
6876 } else unreachable;
6877 };
6878 break :base_address self.getSection(.{
6879 .seg = self.data_segment_cmd_index.?,
6880 .sect = sect_id,
6881 }).addr;
6882 } else 0;
6883 break :blk target_sym.n_value - base_address;
6884 };
6885
6886 relocs.appendAssumeCapacity(.{
6887 .address = source_addr,
6888 .tag = .relocation,
6889 .payload = .{ .target = target_addr },
6890 });
6891 }
6892
6893 if (atom.contained.items.len == 0) {
6894 try nodes.appendSlice(relocs.items);
6895 } else {
6896 // Need to reverse iteration order of relocs since by default for relocatable sources
6897 // they come in reverse. For linking, this doesn't matter in any way, however, for
6898 // arranging the memoryline for displaying it does.
6899 std.mem.reverse(Snapshot.Node, relocs.items);
6900
6901 var next_i: usize = 0;
6902 var last_rel: usize = 0;
6903 while (next_i < atom.contained.items.len) : (next_i += 1) {
6904 const loc = SymbolWithLoc{
6905 .sym_index = atom.contained.items[next_i].sym_index,
6906 .file = atom.file,
6907 };
6908 const cont_sym = self.getSymbol(loc);
6909 const cont_sym_name = self.getSymbolName(loc);
6910 var contained_node = Snapshot.Node{
6911 .address = cont_sym.n_value,
6912 .tag = .atom_start,
6913 .payload = .{
6914 .name = cont_sym_name,
6915 .is_global = self.globals.contains(cont_sym_name),
6916 },
6917 };
6918
6919 // Accumulate aliases
6920 var inner_aliases = std.ArrayList([]const u8).init(arena);
6921 while (true) {
6922 if (next_i + 1 >= atom.contained.items.len) break;
6923 const next_sym_loc = SymbolWithLoc{
6924 .sym_index = atom.contained.items[next_i + 1].sym_index,
6925 .file = atom.file,
6926 };
6927 const next_sym = self.getSymbol(next_sym_loc);
6928 if (next_sym.n_value != cont_sym.n_value) break;
6929 const next_sym_name = self.getSymbolName(next_sym_loc);
6930 if (self.globals.contains(next_sym_name)) {
6931 try inner_aliases.append(contained_node.payload.name);
6932 contained_node.payload.name = next_sym_name;
6933 contained_node.payload.is_global = true;
6934 } else try inner_aliases.append(next_sym_name);
6935 next_i += 1;
6936 }
6937
6938 const cont_size = if (next_i + 1 < atom.contained.items.len)
6939 self.getSymbol(.{
6940 .sym_index = atom.contained.items[next_i + 1].sym_index,
6941 .file = atom.file,
6942 }).n_value - cont_sym.n_value
6943 else
6944 atom_sym.n_value + atom.size - cont_sym.n_value;
6945
6946 contained_node.payload.aliases = inner_aliases.toOwnedSlice();
6947 try nodes.append(contained_node);
6948
6949 for (relocs.items[last_rel..]) |rel| {
6950 if (rel.address >= cont_sym.n_value + cont_size) {
6951 break;
6952 }
6953 try nodes.append(rel);
6954 last_rel += 1;
6955 }
6956
6957 try nodes.append(.{
6958 .address = cont_sym.n_value + cont_size,
6959 .tag = .atom_end,
6960 .payload = .{},
6961 });
6962 }
6963 }
6964
6965 try nodes.append(.{
6966 .address = atom_sym.n_value + atom.size,
6967 .tag = .atom_end,
6968 .payload = .{},
6969 });
6970
6971 if (atom.next) |next| {
6972 atom = next;
6973 } else break;
6974 }
6975
6976 try nodes.append(.{
6977 .address = sect.addr + sect.size,
6978 .tag = .section_end,
6979 .payload = .{},
5995// fn snapshotState(self: *MachO) !void {
5996// const emit = self.base.options.emit orelse {
5997// log.debug("no emit directory found; skipping snapshot...", .{});
5998// return;
5999// };
6000
6001// const Snapshot = struct {
6002// const Node = struct {
6003// const Tag = enum {
6004// section_start,
6005// section_end,
6006// atom_start,
6007// atom_end,
6008// relocation,
6009
6010// pub fn jsonStringify(
6011// tag: Tag,
6012// options: std.json.StringifyOptions,
6013// out_stream: anytype,
6014// ) !void {
6015// _ = options;
6016// switch (tag) {
6017// .section_start => try out_stream.writeAll("\"section_start\""),
6018// .section_end => try out_stream.writeAll("\"section_end\""),
6019// .atom_start => try out_stream.writeAll("\"atom_start\""),
6020// .atom_end => try out_stream.writeAll("\"atom_end\""),
6021// .relocation => try out_stream.writeAll("\"relocation\""),
6022// }
6023// }
6024// };
6025// const Payload = struct {
6026// name: []const u8 = "",
6027// aliases: [][]const u8 = &[0][]const u8{},
6028// is_global: bool = false,
6029// target: u64 = 0,
6030// };
6031// address: u64,
6032// tag: Tag,
6033// payload: Payload,
6034// };
6035// timestamp: i128,
6036// nodes: []Node,
6037// };
6038
6039// var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
6040// defer arena_allocator.deinit();
6041// const arena = arena_allocator.allocator();
6042
6043// const out_file = try emit.directory.handle.createFile("snapshots.json", .{
6044// .truncate = false,
6045// .read = true,
6046// });
6047// defer out_file.close();
6048
6049// if (out_file.seekFromEnd(-1)) {
6050// try out_file.writer().writeByte(',');
6051// } else |err| switch (err) {
6052// error.Unseekable => try out_file.writer().writeByte('['),
6053// else => |e| return e,
6054// }
6055// const writer = out_file.writer();
6056
6057// var snapshot = Snapshot{
6058// .timestamp = std.time.nanoTimestamp(),
6059// .nodes = undefined,
6060// };
6061// var nodes = std.ArrayList(Snapshot.Node).init(arena);
6062
6063// for (self.section_ordinals.keys()) |key| {
6064// const sect = self.getSection(key);
6065// const sect_name = try std.fmt.allocPrint(arena, "{s},{s}", .{ sect.segName(), sect.sectName() });
6066// try nodes.append(.{
6067// .address = sect.addr,
6068// .tag = .section_start,
6069// .payload = .{ .name = sect_name },
6070// });
6071
6072// const is_tlv = sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;
6073
6074// var atom: *Atom = self.atoms.get(key) orelse {
6075// try nodes.append(.{
6076// .address = sect.addr + sect.size,
6077// .tag = .section_end,
6078// .payload = .{},
6079// });
6080// continue;
6081// };
6082
6083// while (atom.prev) |prev| {
6084// atom = prev;
6085// }
6086
6087// while (true) {
6088// const atom_sym = atom.getSymbol(self);
6089// var node = Snapshot.Node{
6090// .address = atom_sym.n_value,
6091// .tag = .atom_start,
6092// .payload = .{
6093// .name = atom.getName(self),
6094// .is_global = self.globals.contains(atom.getName(self)),
6095// },
6096// };
6097
6098// var aliases = std.ArrayList([]const u8).init(arena);
6099// for (atom.contained.items) |sym_off| {
6100// if (sym_off.offset == 0) {
6101// try aliases.append(self.getSymbolName(.{
6102// .sym_index = sym_off.sym_index,
6103// .file = atom.file,
6104// }));
6105// }
6106// }
6107// node.payload.aliases = aliases.toOwnedSlice();
6108// try nodes.append(node);
6109
6110// var relocs = try std.ArrayList(Snapshot.Node).initCapacity(arena, atom.relocs.items.len);
6111// for (atom.relocs.items) |rel| {
6112// const source_addr = blk: {
6113// const source_sym = atom.getSymbol(self);
6114// break :blk source_sym.n_value + rel.offset;
6115// };
6116// const target_addr = blk: {
6117// const target_atom = rel.getTargetAtom(self) orelse {
6118// // If there is no atom for target, we still need to check for special, atom-less
6119// // symbols such as `___dso_handle`.
6120// const target_name = self.getSymbolName(rel.target);
6121// if (self.globals.contains(target_name)) {
6122// const atomless_sym = self.getSymbol(rel.target);
6123// break :blk atomless_sym.n_value;
6124// }
6125// break :blk 0;
6126// };
6127// const target_sym = if (target_atom.isSymbolContained(rel.target, self))
6128// self.getSymbol(rel.target)
6129// else
6130// target_atom.getSymbol(self);
6131// const base_address: u64 = if (is_tlv) base_address: {
6132// const sect_id: u16 = sect_id: {
6133// if (self.tlv_data_section_index) |i| {
6134// break :sect_id i;
6135// } else if (self.tlv_bss_section_index) |i| {
6136// break :sect_id i;
6137// } else unreachable;
6138// };
6139// break :base_address self.getSection(.{
6140// .seg = self.data_segment_cmd_index.?,
6141// .sect = sect_id,
6142// }).addr;
6143// } else 0;
6144// break :blk target_sym.n_value - base_address;
6145// };
6146
6147// relocs.appendAssumeCapacity(.{
6148// .address = source_addr,
6149// .tag = .relocation,
6150// .payload = .{ .target = target_addr },
6151// });
6152// }
6153
6154// if (atom.contained.items.len == 0) {
6155// try nodes.appendSlice(relocs.items);
6156// } else {
6157// // Need to reverse iteration order of relocs since by default for relocatable sources
6158// // they come in reverse. For linking, this doesn't matter in any way, however, for
6159// // arranging the memoryline for displaying it does.
6160// std.mem.reverse(Snapshot.Node, relocs.items);
6161
6162// var next_i: usize = 0;
6163// var last_rel: usize = 0;
6164// while (next_i < atom.contained.items.len) : (next_i += 1) {
6165// const loc = SymbolWithLoc{
6166// .sym_index = atom.contained.items[next_i].sym_index,
6167// .file = atom.file,
6168// };
6169// const cont_sym = self.getSymbol(loc);
6170// const cont_sym_name = self.getSymbolName(loc);
6171// var contained_node = Snapshot.Node{
6172// .address = cont_sym.n_value,
6173// .tag = .atom_start,
6174// .payload = .{
6175// .name = cont_sym_name,
6176// .is_global = self.globals.contains(cont_sym_name),
6177// },
6178// };
6179
6180// // Accumulate aliases
6181// var inner_aliases = std.ArrayList([]const u8).init(arena);
6182// while (true) {
6183// if (next_i + 1 >= atom.contained.items.len) break;
6184// const next_sym_loc = SymbolWithLoc{
6185// .sym_index = atom.contained.items[next_i + 1].sym_index,
6186// .file = atom.file,
6187// };
6188// const next_sym = self.getSymbol(next_sym_loc);
6189// if (next_sym.n_value != cont_sym.n_value) break;
6190// const next_sym_name = self.getSymbolName(next_sym_loc);
6191// if (self.globals.contains(next_sym_name)) {
6192// try inner_aliases.append(contained_node.payload.name);
6193// contained_node.payload.name = next_sym_name;
6194// contained_node.payload.is_global = true;
6195// } else try inner_aliases.append(next_sym_name);
6196// next_i += 1;
6197// }
6198
6199// const cont_size = if (next_i + 1 < atom.contained.items.len)
6200// self.getSymbol(.{
6201// .sym_index = atom.contained.items[next_i + 1].sym_index,
6202// .file = atom.file,
6203// }).n_value - cont_sym.n_value
6204// else
6205// atom_sym.n_value + atom.size - cont_sym.n_value;
6206
6207// contained_node.payload.aliases = inner_aliases.toOwnedSlice();
6208// try nodes.append(contained_node);
6209
6210// for (relocs.items[last_rel..]) |rel| {
6211// if (rel.address >= cont_sym.n_value + cont_size) {
6212// break;
6213// }
6214// try nodes.append(rel);
6215// last_rel += 1;
6216// }
6217
6218// try nodes.append(.{
6219// .address = cont_sym.n_value + cont_size,
6220// .tag = .atom_end,
6221// .payload = .{},
6222// });
6223// }
6224// }
6225
6226// try nodes.append(.{
6227// .address = atom_sym.n_value + atom.size,
6228// .tag = .atom_end,
6229// .payload = .{},
6230// });
6231
6232// if (atom.next) |next| {
6233// atom = next;
6234// } else break;
6235// }
6236
6237// try nodes.append(.{
6238// .address = sect.addr + sect.size,
6239// .tag = .section_end,
6240// .payload = .{},
6241// });
6242// }
6243
6244// snapshot.nodes = nodes.toOwnedSlice();
6245
6246// try std.json.stringify(snapshot, .{}, writer);
6247// try writer.writeByte(']');
6248// }
6249
6250fn logSections(self: *MachO) void {
6251 log.debug("sections:", .{});
6252 for (self.sections.items(.header)) |header, i| {
6253 log.debug(" sect({d}): {s},{s} @{x}, sizeof({x})", .{
6254 i + 1,
6255 header.segName(),
6256 header.sectName(),
6257 header.offset,
6258 header.size,
69806259 });
69816260 }
6982
6983 snapshot.nodes = nodes.toOwnedSlice();
6984
6985 try std.json.stringify(snapshot, .{}, writer);
6986 try writer.writeByte(']');
69876261}
69886262
69896263fn logSymAttributes(sym: macho.nlist_64, buf: *[9]u8) []const u8 {
......@@ -7104,26 +6378,19 @@ fn logSymtab(self: *MachO) void {
71046378 }
71056379}
71066380
7107fn logSectionOrdinals(self: *MachO) void {
7108 for (self.section_ordinals.keys()) |match, i| {
7109 const sect = self.getSection(match);
7110 log.debug("sect({d}, '{s},{s}')", .{ i + 1, sect.segName(), sect.sectName() });
7111 }
7112}
7113
71146381fn logAtoms(self: *MachO) void {
71156382 log.debug("atoms:", .{});
7116 var it = self.atoms.iterator();
7117 while (it.next()) |entry| {
7118 const match = entry.key_ptr.*;
7119 var atom = entry.value_ptr.*;
6383
6384 const slice = self.sections.slice();
6385 for (slice.items(.last_atom)) |last, i| {
6386 var atom = last orelse continue;
6387 const header = slice.items(.header)[i];
71206388
71216389 while (atom.prev) |prev| {
71226390 atom = prev;
71236391 }
71246392
7125 const sect = self.getSection(match);
7126 log.debug("{s},{s}", .{ sect.segName(), sect.sectName() });
6393 log.debug("{s},{s}", .{ header.segName(), header.sectName() });
71276394
71286395 while (true) {
71296396 self.logAtom(atom);
src/link/MachO/Archive.zig+33-25
......@@ -6,19 +6,14 @@ const fs = std.fs;
66const log = std.log.scoped(.link);
77const macho = std.macho;
88const mem = std.mem;
9const fat = @import("fat.zig");
109
1110const Allocator = mem.Allocator;
1211const Object = @import("Object.zig");
1312
1413file: fs.File,
14fat_offset: u64,
1515name: []const u8,
16
17header: ?ar_hdr = null,
18
19// The actual contents we care about linking with will be embedded at
20// an offset within a file if we are linking against a fat lib
21library_offset: u64 = 0,
16header: ar_hdr = undefined,
2217
2318/// Parsed table of contents.
2419/// Each symbol name points to a list of all definition
......@@ -103,11 +98,7 @@ pub fn deinit(self: *Archive, allocator: Allocator) void {
10398 allocator.free(self.name);
10499}
105100
106pub fn parse(self: *Archive, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) !void {
107 const reader = self.file.reader();
108 self.library_offset = try fat.getLibraryOffset(reader, cpu_arch);
109 try self.file.seekTo(self.library_offset);
110
101pub fn parse(self: *Archive, allocator: Allocator, reader: anytype) !void {
111102 const magic = try reader.readBytesNoEof(SARMAG);
112103 if (!mem.eql(u8, &magic, ARMAG)) {
113104 log.debug("invalid magic: expected '{s}', found '{s}'", .{ ARMAG, magic });
......@@ -115,21 +106,23 @@ pub fn parse(self: *Archive, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch
115106 }
116107
117108 self.header = try reader.readStruct(ar_hdr);
118 if (!mem.eql(u8, &self.header.?.ar_fmag, ARFMAG)) {
119 log.debug("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, self.header.?.ar_fmag });
109 if (!mem.eql(u8, &self.header.ar_fmag, ARFMAG)) {
110 log.debug("invalid header delimiter: expected '{s}', found '{s}'", .{
111 ARFMAG,
112 self.header.ar_fmag,
113 });
120114 return error.NotArchive;
121115 }
122116
123 var embedded_name = try parseName(allocator, self.header.?, reader);
117 const name_or_length = try self.header.nameOrLength();
118 var embedded_name = try parseName(allocator, name_or_length, reader);
124119 log.debug("parsing archive '{s}' at '{s}'", .{ embedded_name, self.name });
125120 defer allocator.free(embedded_name);
126121
127122 try self.parseTableOfContents(allocator, reader);
128 try reader.context.seekTo(0);
129123}
130124
131fn parseName(allocator: Allocator, header: ar_hdr, reader: anytype) ![]u8 {
132 const name_or_length = try header.nameOrLength();
125fn parseName(allocator: Allocator, name_or_length: ar_hdr.NameOrLength, reader: anytype) ![]u8 {
133126 var name: []u8 = undefined;
134127 switch (name_or_length) {
135128 .Name => |n| {
......@@ -187,9 +180,14 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) !
187180 }
188181}
189182
190pub fn parseObject(self: Archive, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch, offset: u32) !Object {
183pub fn parseObject(
184 self: Archive,
185 allocator: Allocator,
186 cpu_arch: std.Target.Cpu.Arch,
187 offset: u32,
188) !Object {
191189 const reader = self.file.reader();
192 try reader.context.seekTo(offset + self.library_offset);
190 try reader.context.seekTo(self.fat_offset + offset);
193191
194192 const object_header = try reader.readStruct(ar_hdr);
195193
......@@ -198,7 +196,8 @@ pub fn parseObject(self: Archive, allocator: Allocator, cpu_arch: std.Target.Cpu
198196 return error.MalformedArchive;
199197 }
200198
201 const object_name = try parseName(allocator, object_header, reader);
199 const name_or_length = try object_header.nameOrLength();
200 const object_name = try parseName(allocator, name_or_length, reader);
202201 defer allocator.free(object_name);
203202
204203 log.debug("extracting object '{s}' from archive '{s}'", .{ object_name, self.name });
......@@ -209,15 +208,24 @@ pub fn parseObject(self: Archive, allocator: Allocator, cpu_arch: std.Target.Cpu
209208 break :name try std.fmt.allocPrint(allocator, "{s}({s})", .{ path, object_name });
210209 };
211210
211 const object_name_len = switch (name_or_length) {
212 .Name => 0,
213 .Length => |len| len,
214 };
215 const object_size = (try object_header.size()) - object_name_len;
216 const contents = try allocator.allocWithOptions(u8, object_size, @alignOf(u64), null);
217 const amt = try reader.readAll(contents);
218 if (amt != object_size) {
219 return error.InputOutput;
220 }
221
212222 var object = Object{
213 .file = try fs.cwd().openFile(self.name, .{}),
214223 .name = name,
215 .file_offset = @intCast(u32, try reader.context.getPos()),
216 .mtime = try self.header.?.date(),
224 .mtime = try self.header.date(),
225 .contents = contents,
217226 };
218227
219228 try object.parse(allocator, cpu_arch);
220 try reader.context.seekTo(0);
221229
222230 return object;
223231}
src/link/MachO/Atom.zig+21-19
......@@ -246,7 +246,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
246246 else => {
247247 log.err("unexpected relocation type after ARM64_RELOC_ADDEND", .{});
248248 log.err(" expected ARM64_RELOC_PAGE21 or ARM64_RELOC_PAGEOFF12", .{});
249 log.err(" found {}", .{next});
249 log.err(" found {s}", .{@tagName(next)});
250250 return error.UnexpectedRelocationType;
251251 },
252252 }
......@@ -285,7 +285,9 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
285285 else => {
286286 log.err("unexpected relocation type after ARM64_RELOC_ADDEND", .{});
287287 log.err(" expected ARM64_RELOC_UNSIGNED", .{});
288 log.err(" found {}", .{@intToEnum(macho.reloc_type_arm64, relocs[i + 1].r_type)});
288 log.err(" found {s}", .{
289 @tagName(@intToEnum(macho.reloc_type_arm64, relocs[i + 1].r_type)),
290 });
289291 return error.UnexpectedRelocationType;
290292 },
291293 },
......@@ -294,7 +296,9 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
294296 else => {
295297 log.err("unexpected relocation type after X86_64_RELOC_ADDEND", .{});
296298 log.err(" expected X86_64_RELOC_UNSIGNED", .{});
297 log.err(" found {}", .{@intToEnum(macho.reloc_type_x86_64, relocs[i + 1].r_type)});
299 log.err(" found {s}", .{
300 @tagName(@intToEnum(macho.reloc_type_x86_64, relocs[i + 1].r_type)),
301 });
298302 return error.UnexpectedRelocationType;
299303 },
300304 },
......@@ -309,13 +313,13 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
309313 const sect_id = @intCast(u16, rel.r_symbolnum - 1);
310314 const sym_index = object.sections_as_symbols.get(sect_id) orelse blk: {
311315 const sect = object.getSourceSection(sect_id);
312 const match = (try context.macho_file.getMatchingSection(sect)) orelse
316 const match = (try context.macho_file.getOutputSection(sect)) orelse
313317 unreachable;
314318 const sym_index = @intCast(u32, object.symtab.items.len);
315319 try object.symtab.append(gpa, .{
316320 .n_strx = 0,
317321 .n_type = macho.N_SECT,
318 .n_sect = context.macho_file.getSectionOrdinal(match),
322 .n_sect = match + 1,
319323 .n_desc = 0,
320324 .n_value = sect.addr,
321325 });
......@@ -459,9 +463,10 @@ fn addPtrBindingOrRebase(
459463 });
460464 } else {
461465 const source_sym = self.getSymbol(context.macho_file);
462 const match = context.macho_file.getMatchingSectionFromOrdinal(source_sym.n_sect);
463 const sect = context.macho_file.getSection(match);
464 const sect_type = sect.type_();
466 const section = context.macho_file.sections.get(source_sym.n_sect - 1);
467 const header = section.header;
468 const segment_index = section.segment_index;
469 const sect_type = header.@"type"();
465470
466471 const should_rebase = rebase: {
467472 if (rel.r_length != 3) break :rebase false;
......@@ -470,12 +475,12 @@ fn addPtrBindingOrRebase(
470475 // that the segment is writable should be enough here.
471476 const is_right_segment = blk: {
472477 if (context.macho_file.data_segment_cmd_index) |idx| {
473 if (match.seg == idx) {
478 if (segment_index == idx) {
474479 break :blk true;
475480 }
476481 }
477482 if (context.macho_file.data_const_segment_cmd_index) |idx| {
478 if (match.seg == idx) {
483 if (segment_index == idx) {
479484 break :blk true;
480485 }
481486 }
......@@ -524,6 +529,7 @@ fn addStub(target: MachO.SymbolWithLoc, context: RelocContext) !void {
524529 if (context.macho_file.stubs_table.contains(target)) return;
525530
526531 const stub_index = try context.macho_file.allocateStubEntry(target);
532
527533 const stub_helper_atom = try context.macho_file.createStubHelperAtom();
528534 const laptr_atom = try context.macho_file.createLazyPointerAtom(stub_helper_atom.sym_index, target);
529535 const stub_atom = try context.macho_file.createStubAtom(laptr_atom.sym_index);
......@@ -565,9 +571,8 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
565571 };
566572 const is_tlv = is_tlv: {
567573 const source_sym = self.getSymbol(macho_file);
568 const match = macho_file.getMatchingSectionFromOrdinal(source_sym.n_sect);
569 const sect = macho_file.getSection(match);
570 break :is_tlv sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;
574 const header = macho_file.sections.items(.header)[source_sym.n_sect - 1];
575 break :is_tlv header.@"type"() == macho.S_THREAD_LOCAL_VARIABLES;
571576 };
572577 const target_addr = blk: {
573578 const target_atom = rel.getTargetAtom(macho_file) orelse {
......@@ -597,9 +602,9 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
597602 // * wrt to __thread_data if defined, then
598603 // * wrt to __thread_bss
599604 const sect_id: u16 = sect_id: {
600 if (macho_file.tlv_data_section_index) |i| {
605 if (macho_file.getSectionByName("__DATA", "__thread_data")) |i| {
601606 break :sect_id i;
602 } else if (macho_file.tlv_bss_section_index) |i| {
607 } else if (macho_file.getSectionByName("__DATA", "__thread_bss")) |i| {
603608 break :sect_id i;
604609 } else {
605610 log.err("threadlocal variables present but no initializer sections found", .{});
......@@ -608,10 +613,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
608613 return error.FailedToResolveRelocationTarget;
609614 }
610615 };
611 break :base_address macho_file.getSection(.{
612 .seg = macho_file.data_segment_cmd_index.?,
613 .sect = sect_id,
614 }).addr;
616 break :base_address macho_file.sections.items(.header)[sect_id].addr;
615617 } else 0;
616618 break :blk target_sym.n_value - base_address;
617619 };
src/link/MachO/CodeSignature.zig+7-5
......@@ -252,7 +252,7 @@ pub const WriteOpts = struct {
252252 file: fs.File,
253253 exec_seg_base: u64,
254254 exec_seg_limit: u64,
255 code_sig_cmd: macho.linkedit_data_command,
255 file_size: u32,
256256 output_mode: std.builtin.OutputMode,
257257};
258258
......@@ -274,10 +274,9 @@ pub fn writeAdhocSignature(
274274 self.code_directory.inner.execSegBase = opts.exec_seg_base;
275275 self.code_directory.inner.execSegLimit = opts.exec_seg_limit;
276276 self.code_directory.inner.execSegFlags = if (opts.output_mode == .Exe) macho.CS_EXECSEG_MAIN_BINARY else 0;
277 const file_size = opts.code_sig_cmd.dataoff;
278 self.code_directory.inner.codeLimit = file_size;
277 self.code_directory.inner.codeLimit = opts.file_size;
279278
280 const total_pages = mem.alignForward(file_size, self.page_size) / self.page_size;
279 const total_pages = mem.alignForward(opts.file_size, self.page_size) / self.page_size;
281280
282281 var buffer = try allocator.alloc(u8, self.page_size);
283282 defer allocator.free(buffer);
......@@ -289,7 +288,10 @@ pub fn writeAdhocSignature(
289288 var i: usize = 0;
290289 while (i < total_pages) : (i += 1) {
291290 const fstart = i * self.page_size;
292 const fsize = if (fstart + self.page_size > file_size) file_size - fstart else self.page_size;
291 const fsize = if (fstart + self.page_size > opts.file_size)
292 opts.file_size - fstart
293 else
294 self.page_size;
293295 const len = try opts.file.preadAll(buffer, fstart);
294296 assert(fsize <= len);
295297
src/link/MachO/DebugSymbols.zig+199-331
......@@ -25,35 +25,18 @@ base: *MachO,
2525dwarf: Dwarf,
2626file: fs.File,
2727
28/// Table of all load commands
29load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},
30/// __PAGEZERO segment
31pagezero_segment_cmd_index: ?u16 = null,
32/// __TEXT segment
33text_segment_cmd_index: ?u16 = null,
34/// __DATA_CONST segment
35data_const_segment_cmd_index: ?u16 = null,
36/// __DATA segment
37data_segment_cmd_index: ?u16 = null,
38/// __LINKEDIT segment
39linkedit_segment_cmd_index: ?u16 = null,
40/// __DWARF segment
41dwarf_segment_cmd_index: ?u16 = null,
42/// Symbol table
43symtab_cmd_index: ?u16 = null,
44/// UUID load command
45uuid_cmd_index: ?u16 = null,
46
47/// Index into __TEXT,__text section.
48text_section_index: ?u16 = null,
49
50debug_info_section_index: ?u16 = null,
51debug_abbrev_section_index: ?u16 = null,
52debug_str_section_index: ?u16 = null,
53debug_aranges_section_index: ?u16 = null,
54debug_line_section_index: ?u16 = null,
55
56load_commands_dirty: bool = false,
28segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
29sections: std.ArrayListUnmanaged(macho.section_64) = .{},
30
31linkedit_segment_cmd_index: ?u8 = null,
32dwarf_segment_cmd_index: ?u8 = null,
33
34debug_info_section_index: ?u8 = null,
35debug_abbrev_section_index: ?u8 = null,
36debug_str_section_index: ?u8 = null,
37debug_aranges_section_index: ?u8 = null,
38debug_line_section_index: ?u8 = null,
39
5740debug_string_table_dirty: bool = false,
5841debug_abbrev_section_dirty: bool = false,
5942debug_aranges_section_dirty: bool = false,
......@@ -78,98 +61,44 @@ pub const Reloc = struct {
7861/// You must call this function *after* `MachO.populateMissingMetadata()`
7962/// has been called to get a viable debug symbols output.
8063pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void {
81 if (self.uuid_cmd_index == null) {
82 const base_cmd = self.base.load_commands.items[self.base.uuid_cmd_index.?];
83 self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len);
84 try self.load_commands.append(allocator, base_cmd);
85 self.load_commands_dirty = true;
86 }
87
88 if (self.symtab_cmd_index == null) {
89 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
90 try self.load_commands.append(self.base.base.allocator, .{
91 .symtab = .{
92 .cmdsize = @sizeOf(macho.symtab_command),
93 .symoff = 0,
94 .nsyms = 0,
95 .stroff = 0,
96 .strsize = 0,
97 },
98 });
99 try self.strtab.buffer.append(allocator, 0);
100 self.load_commands_dirty = true;
101 }
102
103 if (self.pagezero_segment_cmd_index == null) {
104 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
105 const base_cmd = self.base.load_commands.items[self.base.pagezero_segment_cmd_index.?].segment;
106 const cmd = try self.copySegmentCommand(allocator, base_cmd);
107 try self.load_commands.append(allocator, .{ .segment = cmd });
108 self.load_commands_dirty = true;
109 }
110
111 if (self.text_segment_cmd_index == null) {
112 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
113 const base_cmd = self.base.load_commands.items[self.base.text_segment_cmd_index.?].segment;
114 const cmd = try self.copySegmentCommand(allocator, base_cmd);
115 try self.load_commands.append(allocator, .{ .segment = cmd });
116 self.load_commands_dirty = true;
117 }
118
119 if (self.data_const_segment_cmd_index == null) outer: {
120 if (self.base.data_const_segment_cmd_index == null) break :outer; // __DATA_CONST is optional
121 self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
122 const base_cmd = self.base.load_commands.items[self.base.data_const_segment_cmd_index.?].segment;
123 const cmd = try self.copySegmentCommand(allocator, base_cmd);
124 try self.load_commands.append(allocator, .{ .segment = cmd });
125 self.load_commands_dirty = true;
126 }
127
128 if (self.data_segment_cmd_index == null) outer: {
129 if (self.base.data_segment_cmd_index == null) break :outer; // __DATA is optional
130 self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
131 const base_cmd = self.base.load_commands.items[self.base.data_segment_cmd_index.?].segment;
132 const cmd = try self.copySegmentCommand(allocator, base_cmd);
133 try self.load_commands.append(allocator, .{ .segment = cmd });
134 self.load_commands_dirty = true;
135 }
136
13764 if (self.linkedit_segment_cmd_index == null) {
138 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
139 const base_cmd = self.base.load_commands.items[self.base.linkedit_segment_cmd_index.?].segment;
140 var cmd = try self.copySegmentCommand(allocator, base_cmd);
65 self.linkedit_segment_cmd_index = @intCast(u8, self.segments.items.len);
66 log.debug("found __LINKEDIT segment free space 0x{x} to 0x{x}", .{
67 self.base.page_size,
68 self.base.page_size * 2,
69 });
14170 // TODO this needs reworking
142 cmd.inner.vmsize = self.base.page_size;
143 cmd.inner.fileoff = self.base.page_size;
144 cmd.inner.filesize = self.base.page_size;
145 try self.load_commands.append(allocator, .{ .segment = cmd });
146 self.load_commands_dirty = true;
71 try self.segments.append(allocator, .{
72 .segname = makeStaticString("__LINKEDIT"),
73 .vmaddr = self.base.page_size,
74 .vmsize = self.base.page_size,
75 .fileoff = self.base.page_size,
76 .filesize = self.base.page_size,
77 .maxprot = macho.PROT.READ,
78 .initprot = macho.PROT.READ,
79 .cmdsize = @sizeOf(macho.segment_command_64),
80 });
14781 }
14882
14983 if (self.dwarf_segment_cmd_index == null) {
150 self.dwarf_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
84 self.dwarf_segment_cmd_index = @intCast(u8, self.segments.items.len);
15185
152 const linkedit = self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
86 const linkedit = self.segments.items[self.linkedit_segment_cmd_index.?];
15387 const ideal_size: u16 = 200 + 128 + 160 + 250;
15488 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.base.page_size);
155 const fileoff = linkedit.inner.fileoff + linkedit.inner.filesize;
156 const vmaddr = linkedit.inner.vmaddr + linkedit.inner.vmsize;
89 const fileoff = linkedit.fileoff + linkedit.filesize;
90 const vmaddr = linkedit.vmaddr + linkedit.vmsize;
15791
15892 log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ fileoff, fileoff + needed_size });
15993
160 try self.load_commands.append(allocator, .{
161 .segment = .{
162 .inner = .{
163 .segname = makeStaticString("__DWARF"),
164 .vmaddr = vmaddr,
165 .vmsize = needed_size,
166 .fileoff = fileoff,
167 .filesize = needed_size,
168 .cmdsize = @sizeOf(macho.segment_command_64),
169 },
170 },
94 try self.segments.append(allocator, .{
95 .segname = makeStaticString("__DWARF"),
96 .vmaddr = vmaddr,
97 .vmsize = needed_size,
98 .fileoff = fileoff,
99 .filesize = needed_size,
100 .cmdsize = @sizeOf(macho.segment_command_64),
171101 });
172 self.load_commands_dirty = true;
173102 }
174103
175104 if (self.debug_str_section_index == null) {
......@@ -203,18 +132,18 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
203132 }
204133}
205134
206fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignment: u16) !u16 {
207 const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
135fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignment: u16) !u8 {
136 const segment = &self.segments.items[self.dwarf_segment_cmd_index.?];
208137 var sect = macho.section_64{
209138 .sectname = makeStaticString(sectname),
210 .segname = seg.inner.segname,
139 .segname = segment.segname,
211140 .size = @intCast(u32, size),
212141 .@"align" = alignment,
213142 };
214143 const alignment_pow_2 = try math.powi(u32, 2, alignment);
215144 const off = self.findFreeSpace(size, alignment_pow_2);
216145
217 assert(off + size <= seg.inner.fileoff + seg.inner.filesize); // TODO expand
146 assert(off + size <= segment.fileoff + segment.filesize); // TODO expand
218147
219148 log.debug("found {s},{s} section free space 0x{x} to 0x{x}", .{
220149 sect.segName(),
......@@ -223,31 +152,20 @@ fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignme
223152 off + size,
224153 });
225154
226 sect.addr = seg.inner.vmaddr + off - seg.inner.fileoff;
155 sect.addr = segment.vmaddr + off - segment.fileoff;
227156 sect.offset = @intCast(u32, off);
228157
229 const index = @intCast(u16, seg.sections.items.len);
230 try seg.sections.append(self.base.base.allocator, sect);
231 seg.inner.cmdsize += @sizeOf(macho.section_64);
232 seg.inner.nsects += 1;
233
234 // TODO
235 // const match = MatchingSection{
236 // .seg = segment_id,
237 // .sect = index,
238 // };
239 // _ = try self.section_ordinals.getOrPut(self.base.allocator, match);
240 // try self.block_free_lists.putNoClobber(self.base.allocator, match, .{});
241
242 self.load_commands_dirty = true;
158 const index = @intCast(u8, self.sections.items.len);
159 try self.sections.append(self.base.base.allocator, sect);
160 segment.cmdsize += @sizeOf(macho.section_64);
161 segment.nsects += 1;
243162
244163 return index;
245164}
246165
247166fn detectAllocCollision(self: *DebugSymbols, start: u64, size: u64) ?u64 {
248 const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
249167 const end = start + padToIdeal(size);
250 for (seg.sections.items) |section| {
168 for (self.sections.items) |section| {
251169 const increased_size = padToIdeal(section.size);
252170 const test_end = section.offset + increased_size;
253171 if (end > section.offset and start < test_end) {
......@@ -258,8 +176,8 @@ fn detectAllocCollision(self: *DebugSymbols, start: u64, size: u64) ?u64 {
258176}
259177
260178pub fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64) u64 {
261 const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
262 var offset: u64 = seg.inner.fileoff;
179 const segment = self.segments.items[self.dwarf_segment_cmd_index.?];
180 var offset: u64 = segment.fileoff;
263181 while (self.detectAllocCollision(offset, object_size)) |item_end| {
264182 offset = mem.alignForwardGeneric(u64, item_end, min_alignment);
265183 }
......@@ -296,8 +214,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
296214 break :blk got_entry.getName(self.base);
297215 },
298216 };
299 const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
300 const sect = &seg.sections.items[self.debug_info_section_index.?];
217 const sect = &self.sections.items[self.debug_info_section_index.?];
301218 const file_offset = sect.offset + reloc.offset;
302219 log.debug("resolving relocation: {d}@{x} ('{s}') at offset {x}", .{
303220 reloc.target,
......@@ -311,15 +228,13 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
311228
312229 if (self.debug_abbrev_section_dirty) {
313230 try self.dwarf.writeDbgAbbrev(&self.base.base);
314 self.load_commands_dirty = true;
315231 self.debug_abbrev_section_dirty = false;
316232 }
317233
318234 if (self.debug_info_header_dirty) {
319235 // Currently only one compilation unit is supported, so the address range is simply
320236 // identical to the main program header virtual address and memory size.
321 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;
322 const text_section = text_segment.sections.items[self.text_section_index.?];
237 const text_section = self.base.sections.items(.header)[self.base.text_section_index.?];
323238 const low_pc = text_section.addr;
324239 const high_pc = text_section.addr + text_section.size;
325240 try self.dwarf.writeDbgInfoHeader(&self.base.base, module, low_pc, high_pc);
......@@ -329,10 +244,8 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
329244 if (self.debug_aranges_section_dirty) {
330245 // Currently only one compilation unit is supported, so the address range is simply
331246 // identical to the main program header virtual address and memory size.
332 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;
333 const text_section = text_segment.sections.items[self.text_section_index.?];
247 const text_section = self.base.sections.items(.header)[self.base.text_section_index.?];
334248 try self.dwarf.writeDbgAranges(&self.base.base, text_section.addr, text_section.size);
335 self.load_commands_dirty = true;
336249 self.debug_aranges_section_dirty = false;
337250 }
338251
......@@ -342,8 +255,8 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
342255 }
343256
344257 {
345 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
346 const debug_strtab_sect = &dwarf_segment.sections.items[self.debug_str_section_index.?];
258 const dwarf_segment = &self.segments.items[self.dwarf_segment_cmd_index.?];
259 const debug_strtab_sect = &self.sections.items[self.debug_str_section_index.?];
347260 if (self.debug_string_table_dirty or self.dwarf.strtab.items.len != debug_strtab_sect.size) {
348261 const allocated_size = self.allocatedSize(debug_strtab_sect.offset);
349262 const needed_size = self.dwarf.strtab.items.len;
......@@ -351,7 +264,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
351264 if (needed_size > allocated_size) {
352265 debug_strtab_sect.size = 0; // free the space
353266 const new_offset = self.findFreeSpace(needed_size, 1);
354 debug_strtab_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;
267 debug_strtab_sect.addr = dwarf_segment.vmaddr + new_offset - dwarf_segment.fileoff;
355268 debug_strtab_sect.offset = @intCast(u32, new_offset);
356269 }
357270 debug_strtab_sect.size = @intCast(u32, needed_size);
......@@ -362,28 +275,40 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
362275 });
363276
364277 try self.file.pwriteAll(self.dwarf.strtab.items, debug_strtab_sect.offset);
365 self.load_commands_dirty = true;
366278 self.debug_string_table_dirty = false;
367279 }
368280 }
369281
282 var lc_buffer = std.ArrayList(u8).init(allocator);
283 defer lc_buffer.deinit();
284 const lc_writer = lc_buffer.writer();
285 var ncmds: u32 = 0;
286
287 try self.writeLinkeditSegmentData(&ncmds, lc_writer);
370288 self.updateDwarfSegment();
371 try self.writeLinkeditSegment();
372 try self.updateVirtualMemoryMapping();
373 try self.writeLoadCommands(allocator);
374 try self.writeHeader();
375289
376 assert(!self.load_commands_dirty);
290 {
291 try lc_writer.writeStruct(self.base.uuid);
292 ncmds += 1;
293 }
294
295 var headers_buf = std.ArrayList(u8).init(allocator);
296 defer headers_buf.deinit();
297 try self.writeSegmentHeaders(&ncmds, headers_buf.writer());
298
299 try self.file.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));
300 try self.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);
301
302 try self.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len));
303
377304 assert(!self.debug_abbrev_section_dirty);
378305 assert(!self.debug_aranges_section_dirty);
379306 assert(!self.debug_string_table_dirty);
380307}
381308
382309pub fn deinit(self: *DebugSymbols, allocator: Allocator) void {
383 for (self.load_commands.items) |*lc| {
384 lc.deinit(allocator);
385 }
386 self.load_commands.deinit(allocator);
310 self.segments.deinit(allocator);
311 self.sections.deinit(allocator);
387312 self.dwarf.deinit();
388313 self.strtab.deinit(allocator);
389314 self.relocs.deinit(allocator);
......@@ -402,59 +327,20 @@ pub fn swapRemoveRelocs(self: *DebugSymbols, target: u32) void {
402327 }
403328}
404329
405fn copySegmentCommand(
406 self: *DebugSymbols,
407 allocator: Allocator,
408 base_cmd: macho.SegmentCommand,
409) !macho.SegmentCommand {
410 var cmd = macho.SegmentCommand{
411 .inner = .{
412 .segname = undefined,
413 .cmdsize = base_cmd.inner.cmdsize,
414 .vmaddr = base_cmd.inner.vmaddr,
415 .vmsize = base_cmd.inner.vmsize,
416 .maxprot = base_cmd.inner.maxprot,
417 .initprot = base_cmd.inner.initprot,
418 .nsects = base_cmd.inner.nsects,
419 .flags = base_cmd.inner.flags,
420 },
421 };
422 mem.copy(u8, &cmd.inner.segname, &base_cmd.inner.segname);
423
424 try cmd.sections.ensureTotalCapacity(allocator, cmd.inner.nsects);
425 for (base_cmd.sections.items) |base_sect, i| {
426 var sect = macho.section_64{
427 .sectname = undefined,
428 .segname = undefined,
429 .addr = base_sect.addr,
430 .size = base_sect.size,
431 .offset = 0,
432 .@"align" = base_sect.@"align",
433 .reloff = 0,
434 .nreloc = 0,
435 .flags = base_sect.flags,
436 .reserved1 = base_sect.reserved1,
437 .reserved2 = base_sect.reserved2,
438 .reserved3 = base_sect.reserved3,
439 };
440 mem.copy(u8, &sect.sectname, &base_sect.sectname);
441 mem.copy(u8, &sect.segname, &base_sect.segname);
442
443 if (self.base.text_section_index.? == i) {
444 self.text_section_index = @intCast(u16, i);
445 }
330fn updateDwarfSegment(self: *DebugSymbols) void {
331 const linkedit = self.segments.items[self.linkedit_segment_cmd_index.?];
332 const dwarf_segment = &self.segments.items[self.dwarf_segment_cmd_index.?];
446333
447 cmd.sections.appendAssumeCapacity(sect);
334 const new_start_aligned = linkedit.vmaddr + linkedit.vmsize;
335 const old_start_aligned = dwarf_segment.vmaddr;
336 const diff = new_start_aligned - old_start_aligned;
337 if (diff > 0) {
338 dwarf_segment.vmaddr = new_start_aligned;
448339 }
449340
450 return cmd;
451}
452
453fn updateDwarfSegment(self: *DebugSymbols) void {
454 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
455
456341 var max_offset: u64 = 0;
457 for (dwarf_segment.sections.items) |sect| {
342 for (self.sections.items) |*sect| {
343 sect.addr += diff;
458344 log.debug(" {s},{s} - 0x{x}-0x{x} - 0x{x}-0x{x}", .{
459345 sect.segName(),
460346 sect.sectName(),
......@@ -468,42 +354,59 @@ fn updateDwarfSegment(self: *DebugSymbols) void {
468354 }
469355 }
470356
471 const file_size = max_offset - dwarf_segment.inner.fileoff;
357 const file_size = max_offset - dwarf_segment.fileoff;
472358 log.debug("__DWARF size 0x{x}", .{file_size});
473359
474 if (file_size != dwarf_segment.inner.filesize) {
475 dwarf_segment.inner.filesize = file_size;
476 if (dwarf_segment.inner.vmsize < dwarf_segment.inner.filesize) {
477 dwarf_segment.inner.vmsize = mem.alignForwardGeneric(u64, dwarf_segment.inner.filesize, self.base.page_size);
478 }
479 self.load_commands_dirty = true;
360 if (file_size != dwarf_segment.filesize) {
361 dwarf_segment.filesize = file_size;
362 dwarf_segment.vmsize = mem.alignForwardGeneric(u64, dwarf_segment.filesize, self.base.page_size);
480363 }
481364}
482365
483/// Writes all load commands and section headers.
484fn writeLoadCommands(self: *DebugSymbols, allocator: Allocator) !void {
485 if (!self.load_commands_dirty) return;
366fn writeSegmentHeaders(self: *DebugSymbols, ncmds: *u32, writer: anytype) !void {
367 // Write segment/section headers from the binary file first.
368 const end = self.base.linkedit_segment_cmd_index.?;
369 for (self.base.segments.items[0..end]) |seg, i| {
370 const indexes = self.base.getSectionIndexes(@intCast(u8, i));
371 var out_seg = seg;
372 out_seg.fileoff = 0;
373 out_seg.filesize = 0;
374 out_seg.cmdsize = @sizeOf(macho.segment_command_64);
375 out_seg.nsects = 0;
376
377 // Update section headers count; any section with size of 0 is excluded
378 // since it doesn't have any data in the final binary file.
379 for (self.base.sections.items(.header)[indexes.start..indexes.end]) |header| {
380 if (header.size == 0) continue;
381 out_seg.cmdsize += @sizeOf(macho.section_64);
382 out_seg.nsects += 1;
383 }
486384
487 var sizeofcmds: u32 = 0;
488 for (self.load_commands.items) |lc| {
489 sizeofcmds += lc.cmdsize();
490 }
385 if (out_seg.nsects == 0 and
386 (mem.eql(u8, out_seg.segName(), "__DATA_CONST") or
387 mem.eql(u8, out_seg.segName(), "__DATA"))) continue;
491388
492 var buffer = try allocator.alloc(u8, sizeofcmds);
493 defer allocator.free(buffer);
494 var fib = std.io.fixedBufferStream(buffer);
495 const writer = fib.writer();
496 for (self.load_commands.items) |lc| {
497 try lc.write(writer);
498 }
389 try writer.writeStruct(out_seg);
390 for (self.base.sections.items(.header)[indexes.start..indexes.end]) |header| {
391 if (header.size == 0) continue;
392 var out_header = header;
393 out_header.offset = 0;
394 try writer.writeStruct(out_header);
395 }
499396
500 const off = @sizeOf(macho.mach_header_64);
501 log.debug("writing {} load commands from 0x{x} to 0x{x}", .{ self.load_commands.items.len, off, off + sizeofcmds });
502 try self.file.pwriteAll(buffer, off);
503 self.load_commands_dirty = false;
397 ncmds.* += 1;
398 }
399 // Next, commit DSYM's __LINKEDIT and __DWARF segments headers.
400 for (self.segments.items) |seg| {
401 try writer.writeStruct(seg);
402 ncmds.* += 1;
403 }
404 for (self.sections.items) |header| {
405 try writer.writeStruct(header);
406 }
504407}
505408
506fn writeHeader(self: *DebugSymbols) !void {
409fn writeHeader(self: *DebugSymbols, ncmds: u32, sizeofcmds: u32) !void {
507410 var header: macho.mach_header_64 = .{};
508411 header.filetype = macho.MH_DSYM;
509412
......@@ -519,12 +422,8 @@ fn writeHeader(self: *DebugSymbols) !void {
519422 else => return error.UnsupportedCpuArchitecture,
520423 }
521424
522 header.ncmds = @intCast(u32, self.load_commands.items.len);
523 header.sizeofcmds = 0;
524
525 for (self.load_commands.items) |cmd| {
526 header.sizeofcmds += cmd.cmdsize();
527 }
425 header.ncmds = ncmds;
426 header.sizeofcmds = sizeofcmds;
528427
529428 log.debug("writing Mach-O header {}", .{header});
530429
......@@ -532,79 +431,46 @@ fn writeHeader(self: *DebugSymbols) !void {
532431}
533432
534433pub fn allocatedSize(self: *DebugSymbols, start: u64) u64 {
535 const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
536 assert(start >= seg.inner.fileoff);
434 const seg = self.segments.items[self.dwarf_segment_cmd_index.?];
435 assert(start >= seg.fileoff);
537436 var min_pos: u64 = std.math.maxInt(u64);
538 for (seg.sections.items) |section| {
437 for (self.sections.items) |section| {
539438 if (section.offset <= start) continue;
540439 if (section.offset < min_pos) min_pos = section.offset;
541440 }
542441 return min_pos - start;
543442}
544443
545fn updateVirtualMemoryMapping(self: *DebugSymbols) !void {
546 const macho_file = self.base;
547 const allocator = macho_file.base.allocator;
548
549 const IndexTuple = std.meta.Tuple(&[_]type{ *?u16, *?u16 });
550 const indices = &[_]IndexTuple{
551 .{ &macho_file.text_segment_cmd_index, &self.text_segment_cmd_index },
552 .{ &macho_file.data_const_segment_cmd_index, &self.data_const_segment_cmd_index },
553 .{ &macho_file.data_segment_cmd_index, &self.data_segment_cmd_index },
554 };
555
556 for (indices) |tuple| {
557 const orig_cmd = macho_file.load_commands.items[tuple[0].*.?].segment;
558 const cmd = try self.copySegmentCommand(allocator, orig_cmd);
559 const comp_cmd = &self.load_commands.items[tuple[1].*.?];
560 comp_cmd.deinit(allocator);
561 self.load_commands.items[tuple[1].*.?] = .{ .segment = cmd };
562 }
563
564 // TODO should we set the linkedit vmsize to that of the binary?
565 const orig_cmd = macho_file.load_commands.items[macho_file.linkedit_segment_cmd_index.?].segment;
566 const orig_vmaddr = orig_cmd.inner.vmaddr;
567 const linkedit_cmd = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
568 linkedit_cmd.inner.vmaddr = orig_vmaddr;
569
570 // Update VM address for the DWARF segment and sections including re-running relocations.
571 // TODO re-run relocations
572 const dwarf_cmd = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
573 const new_start_aligned = orig_vmaddr + linkedit_cmd.inner.vmsize;
574 const old_start_aligned = dwarf_cmd.inner.vmaddr;
575 const diff = new_start_aligned - old_start_aligned;
576 if (diff > 0) {
577 dwarf_cmd.inner.vmaddr = new_start_aligned;
578
579 for (dwarf_cmd.sections.items) |*sect| {
580 sect.addr += (new_start_aligned - old_start_aligned);
581 }
582 }
583
584 self.load_commands_dirty = true;
585}
586
587fn writeLinkeditSegment(self: *DebugSymbols) !void {
444fn writeLinkeditSegmentData(self: *DebugSymbols, ncmds: *u32, lc_writer: anytype) !void {
588445 const tracy = trace(@src());
589446 defer tracy.end();
590447
591 try self.writeSymbolTable();
592 try self.writeStringTable();
448 const source_vmaddr = self.base.segments.items[self.base.linkedit_segment_cmd_index.?].vmaddr;
449 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
450 seg.vmaddr = source_vmaddr;
593451
594 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
595 const aligned_size = mem.alignForwardGeneric(u64, seg.inner.filesize, self.base.page_size);
596 seg.inner.filesize = aligned_size;
597 seg.inner.vmsize = aligned_size;
452 var symtab_cmd = macho.symtab_command{
453 .cmdsize = @sizeOf(macho.symtab_command),
454 .symoff = 0,
455 .nsyms = 0,
456 .stroff = 0,
457 .strsize = 0,
458 };
459 try self.writeSymtab(&symtab_cmd);
460 try self.writeStrtab(&symtab_cmd);
461 try lc_writer.writeStruct(symtab_cmd);
462 ncmds.* += 1;
463
464 const aligned_size = mem.alignForwardGeneric(u64, seg.filesize, self.base.page_size);
465 seg.filesize = aligned_size;
466 seg.vmsize = aligned_size;
598467}
599468
600fn writeSymbolTable(self: *DebugSymbols) !void {
469fn writeSymtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
601470 const tracy = trace(@src());
602471 defer tracy.end();
603472
604473 const gpa = self.base.base.allocator;
605 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
606 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
607 symtab.symoff = @intCast(u32, seg.inner.fileoff);
608474
609475 var locals = std.ArrayList(macho.nlist_64).init(gpa);
610476 defer locals.deinit();
......@@ -634,34 +500,32 @@ fn writeSymbolTable(self: *DebugSymbols) !void {
634500
635501 const nlocals = locals.items.len;
636502 const nexports = exports.items.len;
637 const locals_off = symtab.symoff;
638 const locals_size = nlocals * @sizeOf(macho.nlist_64);
639 const exports_off = locals_off + locals_size;
640 const exports_size = nexports * @sizeOf(macho.nlist_64);
503 const nsyms = nlocals + nexports;
641504
642 symtab.nsyms = @intCast(u32, nlocals + nexports);
643 const needed_size = (nlocals + nexports) * @sizeOf(macho.nlist_64);
505 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
506 const offset = mem.alignForwardGeneric(u64, seg.fileoff, @alignOf(macho.nlist_64));
507 const needed_size = nsyms * @sizeOf(macho.nlist_64);
644508
645 if (needed_size > seg.inner.filesize) {
509 if (needed_size > seg.filesize) {
646510 const aligned_size = mem.alignForwardGeneric(u64, needed_size, self.base.page_size);
647 const diff = @intCast(u32, aligned_size - seg.inner.filesize);
648 const dwarf_seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
649 seg.inner.filesize = aligned_size;
511 const diff = @intCast(u32, aligned_size - seg.filesize);
512 const dwarf_seg = &self.segments.items[self.dwarf_segment_cmd_index.?];
513 seg.filesize = aligned_size;
650514
651515 try MachO.copyRangeAllOverlappingAlloc(
652516 self.base.base.allocator,
653517 self.file,
654 dwarf_seg.inner.fileoff,
655 dwarf_seg.inner.fileoff + diff,
656 math.cast(usize, dwarf_seg.inner.filesize) orelse return error.Overflow,
518 dwarf_seg.fileoff,
519 dwarf_seg.fileoff + diff,
520 math.cast(usize, dwarf_seg.filesize) orelse return error.Overflow,
657521 );
658522
659 const old_seg_fileoff = dwarf_seg.inner.fileoff;
660 dwarf_seg.inner.fileoff += diff;
523 const old_seg_fileoff = dwarf_seg.fileoff;
524 dwarf_seg.fileoff += diff;
661525
662 log.debug(" (moving __DWARF segment from 0x{x} to 0x{x})", .{ old_seg_fileoff, dwarf_seg.inner.fileoff });
526 log.debug(" (moving __DWARF segment from 0x{x} to 0x{x})", .{ old_seg_fileoff, dwarf_seg.fileoff });
663527
664 for (dwarf_seg.sections.items) |*sect| {
528 for (self.sections.items) |*sect| {
665529 const old_offset = sect.offset;
666530 sect.offset += diff;
667531
......@@ -674,47 +538,53 @@ fn writeSymbolTable(self: *DebugSymbols) !void {
674538 }
675539 }
676540
541 lc.symoff = @intCast(u32, offset);
542 lc.nsyms = @intCast(u32, nsyms);
543
544 const locals_off = lc.symoff;
545 const locals_size = nlocals * @sizeOf(macho.nlist_64);
546 const exports_off = locals_off + locals_size;
547 const exports_size = nexports * @sizeOf(macho.nlist_64);
548
677549 log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off });
678550 try self.file.pwriteAll(mem.sliceAsBytes(locals.items), locals_off);
679551
680552 log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off });
681553 try self.file.pwriteAll(mem.sliceAsBytes(exports.items), exports_off);
682
683 self.load_commands_dirty = true;
684554}
685555
686fn writeStringTable(self: *DebugSymbols) !void {
556fn writeStrtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
687557 const tracy = trace(@src());
688558 defer tracy.end();
689559
690 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
691 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
692 const symtab_size = @intCast(u32, symtab.nsyms * @sizeOf(macho.nlist_64));
693 symtab.stroff = symtab.symoff + symtab_size;
560 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
561 const symtab_size = @intCast(u32, lc.nsyms * @sizeOf(macho.nlist_64));
562 const offset = mem.alignForwardGeneric(u64, lc.symoff + symtab_size, @alignOf(u64));
563 lc.stroff = @intCast(u32, offset);
694564
695565 const needed_size = mem.alignForwardGeneric(u64, self.strtab.buffer.items.len, @alignOf(u64));
696 symtab.strsize = @intCast(u32, needed_size);
566 lc.strsize = @intCast(u32, needed_size);
697567
698 if (symtab_size + needed_size > seg.inner.filesize) {
699 const aligned_size = mem.alignForwardGeneric(u64, symtab_size + needed_size, self.base.page_size);
700 const diff = @intCast(u32, aligned_size - seg.inner.filesize);
701 const dwarf_seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
702 seg.inner.filesize = aligned_size;
568 if (symtab_size + needed_size > seg.filesize) {
569 const aligned_size = mem.alignForwardGeneric(u64, offset + needed_size, self.base.page_size);
570 const diff = @intCast(u32, aligned_size - seg.filesize);
571 const dwarf_seg = &self.segments.items[self.dwarf_segment_cmd_index.?];
572 seg.filesize = aligned_size;
703573
704574 try MachO.copyRangeAllOverlappingAlloc(
705575 self.base.base.allocator,
706576 self.file,
707 dwarf_seg.inner.fileoff,
708 dwarf_seg.inner.fileoff + diff,
709 math.cast(usize, dwarf_seg.inner.filesize) orelse return error.Overflow,
577 dwarf_seg.fileoff,
578 dwarf_seg.fileoff + diff,
579 math.cast(usize, dwarf_seg.filesize) orelse return error.Overflow,
710580 );
711581
712 const old_seg_fileoff = dwarf_seg.inner.fileoff;
713 dwarf_seg.inner.fileoff += diff;
582 const old_seg_fileoff = dwarf_seg.fileoff;
583 dwarf_seg.fileoff += diff;
714584
715 log.debug(" (moving __DWARF segment from 0x{x} to 0x{x})", .{ old_seg_fileoff, dwarf_seg.inner.fileoff });
585 log.debug(" (moving __DWARF segment from 0x{x} to 0x{x})", .{ old_seg_fileoff, dwarf_seg.fileoff });
716586
717 for (dwarf_seg.sections.items) |*sect| {
587 for (self.sections.items) |*sect| {
718588 const old_offset = sect.offset;
719589 sect.offset += diff;
720590
......@@ -727,9 +597,7 @@ fn writeStringTable(self: *DebugSymbols) !void {
727597 }
728598 }
729599
730 log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });
731
732 try self.file.pwriteAll(self.strtab.buffer.items, symtab.stroff);
600 log.debug("writing string table from 0x{x} to 0x{x}", .{ lc.stroff, lc.stroff + lc.strsize });
733601
734 self.load_commands_dirty = true;
602 try self.file.pwriteAll(self.strtab.buffer.items, lc.stroff);
735603}
src/link/MachO/Dylib.zig+53-106
......@@ -13,23 +13,9 @@ const fat = @import("fat.zig");
1313const Allocator = mem.Allocator;
1414const CrossTarget = std.zig.CrossTarget;
1515const LibStub = @import("../tapi.zig").LibStub;
16const LoadCommandIterator = macho.LoadCommandIterator;
1617const MachO = @import("../MachO.zig");
1718
18file: fs.File,
19name: []const u8,
20
21header: ?macho.mach_header_64 = null,
22
23// The actual dylib contents we care about linking with will be embedded at
24// an offset within a file if we are linking against a fat lib
25library_offset: u64 = 0,
26
27load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},
28
29symtab_cmd_index: ?u16 = null,
30dysymtab_cmd_index: ?u16 = null,
31id_cmd_index: ?u16 = null,
32
3319id: ?Id = null,
3420weak: bool = false,
3521
......@@ -53,16 +39,12 @@ pub const Id = struct {
5339 };
5440 }
5541
56 pub fn fromLoadCommand(allocator: Allocator, lc: macho.GenericCommandWithData(macho.dylib_command)) !Id {
57 const dylib = lc.inner.dylib;
58 const dylib_name = @ptrCast([*:0]const u8, lc.data[dylib.name - @sizeOf(macho.dylib_command) ..]);
59 const name = try allocator.dupe(u8, mem.sliceTo(dylib_name, 0));
60
42 pub fn fromLoadCommand(allocator: Allocator, lc: macho.dylib_command, name: []const u8) !Id {
6143 return Id{
62 .name = name,
63 .timestamp = dylib.timestamp,
64 .current_version = dylib.current_version,
65 .compatibility_version = dylib.compatibility_version,
44 .name = try allocator.dupe(u8, name),
45 .timestamp = lc.dylib.timestamp,
46 .current_version = lc.dylib.current_version,
47 .compatibility_version = lc.dylib.compatibility_version,
6648 };
6749 }
6850
......@@ -126,125 +108,89 @@ pub const Id = struct {
126108};
127109
128110pub fn deinit(self: *Dylib, allocator: Allocator) void {
129 for (self.load_commands.items) |*lc| {
130 lc.deinit(allocator);
131 }
132 self.load_commands.deinit(allocator);
133
134111 for (self.symbols.keys()) |key| {
135112 allocator.free(key);
136113 }
137114 self.symbols.deinit(allocator);
138
139 allocator.free(self.name);
140
141115 if (self.id) |*id| {
142116 id.deinit(allocator);
143117 }
144118}
145119
146pub fn parse(
120pub fn parseFromBinary(
147121 self: *Dylib,
148122 allocator: Allocator,
149123 cpu_arch: std.Target.Cpu.Arch,
150124 dylib_id: u16,
151125 dependent_libs: anytype,
126 name: []const u8,
127 data: []align(@alignOf(u64)) const u8,
152128) !void {
153 log.debug("parsing shared library '{s}'", .{self.name});
154
155 self.library_offset = try fat.getLibraryOffset(self.file.reader(), cpu_arch);
129 var stream = std.io.fixedBufferStream(data);
130 const reader = stream.reader();
156131
157 try self.file.seekTo(self.library_offset);
132 log.debug("parsing shared library '{s}'", .{name});
158133
159 var reader = self.file.reader();
160 self.header = try reader.readStruct(macho.mach_header_64);
134 const header = try reader.readStruct(macho.mach_header_64);
161135
162 if (self.header.?.filetype != macho.MH_DYLIB) {
163 log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{ macho.MH_DYLIB, self.header.?.filetype });
136 if (header.filetype != macho.MH_DYLIB) {
137 log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{ macho.MH_DYLIB, header.filetype });
164138 return error.NotDylib;
165139 }
166140
167 const this_arch: std.Target.Cpu.Arch = try fat.decodeArch(self.header.?.cputype, true);
141 const this_arch: std.Target.Cpu.Arch = try fat.decodeArch(header.cputype, true);
168142
169143 if (this_arch != cpu_arch) {
170 log.err("mismatched cpu architecture: expected {}, found {}", .{ cpu_arch, this_arch });
144 log.err("mismatched cpu architecture: expected {s}, found {s}", .{
145 @tagName(cpu_arch),
146 @tagName(this_arch),
147 });
171148 return error.MismatchedCpuArchitecture;
172149 }
173150
174 try self.readLoadCommands(allocator, reader, dylib_id, dependent_libs);
175 try self.parseId(allocator);
176 try self.parseSymbols(allocator);
177}
178
179fn readLoadCommands(
180 self: *Dylib,
181 allocator: Allocator,
182 reader: anytype,
183 dylib_id: u16,
184 dependent_libs: anytype,
185) !void {
186 const should_lookup_reexports = self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0;
187
188 try self.load_commands.ensureUnusedCapacity(allocator, self.header.?.ncmds);
189
190 var i: u16 = 0;
191 while (i < self.header.?.ncmds) : (i += 1) {
192 var cmd = try macho.LoadCommand.read(allocator, reader);
151 const should_lookup_reexports = header.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0;
152 var it = LoadCommandIterator{
153 .ncmds = header.ncmds,
154 .buffer = data[@sizeOf(macho.mach_header_64)..][0..header.sizeofcmds],
155 };
156 while (it.next()) |cmd| {
193157 switch (cmd.cmd()) {
194158 .SYMTAB => {
195 self.symtab_cmd_index = i;
196 },
197 .DYSYMTAB => {
198 self.dysymtab_cmd_index = i;
159 const symtab_cmd = cmd.cast(macho.symtab_command).?;
160 const symtab = @ptrCast(
161 [*]const macho.nlist_64,
162 @alignCast(@alignOf(macho.nlist_64), &data[symtab_cmd.symoff]),
163 )[0..symtab_cmd.nsyms];
164 const strtab = data[symtab_cmd.stroff..][0..symtab_cmd.strsize];
165
166 for (symtab) |sym| {
167 const add_to_symtab = sym.ext() and (sym.sect() or sym.indr());
168 if (!add_to_symtab) continue;
169
170 const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0);
171 try self.symbols.putNoClobber(allocator, try allocator.dupe(u8, sym_name), {});
172 }
199173 },
200174 .ID_DYLIB => {
201 self.id_cmd_index = i;
175 self.id = try Id.fromLoadCommand(
176 allocator,
177 cmd.cast(macho.dylib_command).?,
178 cmd.getDylibPathName(),
179 );
202180 },
203181 .REEXPORT_DYLIB => {
204182 if (should_lookup_reexports) {
205183 // Parse install_name to dependent dylib.
206 var id = try Id.fromLoadCommand(allocator, cmd.dylib);
184 var id = try Id.fromLoadCommand(
185 allocator,
186 cmd.cast(macho.dylib_command).?,
187 cmd.getDylibPathName(),
188 );
207189 try dependent_libs.writeItem(.{ .id = id, .parent = dylib_id });
208190 }
209191 },
210 else => {
211 log.debug("Unknown load command detected: 0x{x}.", .{@enumToInt(cmd.cmd())});
212 },
192 else => {},
213193 }
214 self.load_commands.appendAssumeCapacity(cmd);
215 }
216}
217
218fn parseId(self: *Dylib, allocator: Allocator) !void {
219 const index = self.id_cmd_index orelse {
220 log.debug("no LC_ID_DYLIB load command found; using hard-coded defaults...", .{});
221 self.id = try Id.default(allocator, self.name);
222 return;
223 };
224 self.id = try Id.fromLoadCommand(allocator, self.load_commands.items[index].dylib);
225}
226
227fn parseSymbols(self: *Dylib, allocator: Allocator) !void {
228 const index = self.symtab_cmd_index orelse return;
229 const symtab_cmd = self.load_commands.items[index].symtab;
230
231 const symtab = try allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms);
232 defer allocator.free(symtab);
233 _ = try self.file.preadAll(symtab, symtab_cmd.symoff + self.library_offset);
234 const slice = @alignCast(@alignOf(macho.nlist_64), mem.bytesAsSlice(macho.nlist_64, symtab));
235
236 const strtab = try allocator.alloc(u8, symtab_cmd.strsize);
237 defer allocator.free(strtab);
238 _ = try self.file.preadAll(strtab, symtab_cmd.stroff + self.library_offset);
239
240 for (slice) |sym| {
241 const add_to_symtab = sym.ext() and (sym.sect() or sym.indr());
242
243 if (!add_to_symtab) continue;
244
245 const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0);
246 const name = try allocator.dupe(u8, sym_name);
247 try self.symbols.putNoClobber(allocator, name, {});
248194 }
249195}
250196
......@@ -356,10 +302,11 @@ pub fn parseFromStub(
356302 lib_stub: LibStub,
357303 dylib_id: u16,
358304 dependent_libs: anytype,
305 name: []const u8,
359306) !void {
360307 if (lib_stub.inner.len == 0) return error.EmptyStubFile;
361308
362 log.debug("parsing shared library from stub '{s}'", .{self.name});
309 log.debug("parsing shared library from stub '{s}'", .{name});
363310
364311 const umbrella_lib = lib_stub.inner[0];
365312
src/link/MachO/Object.zig+151-176
......@@ -3,6 +3,7 @@ const Object = @This();
33const std = @import("std");
44const build_options = @import("build_options");
55const assert = std.debug.assert;
6const dwarf = std.dwarf;
67const fs = std.fs;
78const io = std.io;
89const log = std.log.scoped(.link);
......@@ -14,43 +15,20 @@ const trace = @import("../../tracy.zig").trace;
1415
1516const Allocator = mem.Allocator;
1617const Atom = @import("Atom.zig");
18const LoadCommandIterator = macho.LoadCommandIterator;
1719const MachO = @import("../MachO.zig");
18const MatchingSection = MachO.MatchingSection;
1920const SymbolWithLoc = MachO.SymbolWithLoc;
2021
21file: fs.File,
2222name: []const u8,
2323mtime: u64,
24
25/// Data contents of the file. Includes sections, and data of load commands.
26/// Excludes the backing memory for the header and load commands.
27/// Initialized in `parse`.
28contents: []const u8 = undefined,
29
30file_offset: ?u32 = null,
24contents: []align(@alignOf(u64)) const u8,
3125
3226header: macho.mach_header_64 = undefined,
33
34load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},
35
36segment_cmd_index: ?u16 = null,
37text_section_index: ?u16 = null,
38symtab_cmd_index: ?u16 = null,
39dysymtab_cmd_index: ?u16 = null,
40build_version_cmd_index: ?u16 = null,
41data_in_code_cmd_index: ?u16 = null,
42
43// __DWARF segment sections
44dwarf_debug_info_index: ?u16 = null,
45dwarf_debug_abbrev_index: ?u16 = null,
46dwarf_debug_str_index: ?u16 = null,
47dwarf_debug_line_index: ?u16 = null,
48dwarf_debug_line_str_index: ?u16 = null,
49dwarf_debug_ranges_index: ?u16 = null,
27in_symtab: []const macho.nlist_64 = undefined,
28in_strtab: []const u8 = undefined,
5029
5130symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
52strtab: []const u8 = &.{},
53data_in_code_entries: []const macho.data_in_code_entry = &.{},
31sections: std.ArrayListUnmanaged(macho.section_64) = .{},
5432
5533sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{},
5634
......@@ -61,12 +39,8 @@ managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
6139atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{},
6240
6341pub fn deinit(self: *Object, gpa: Allocator) void {
64 for (self.load_commands.items) |*lc| {
65 lc.deinit(gpa);
66 }
67 self.load_commands.deinit(gpa);
68 gpa.free(self.contents);
6942 self.symtab.deinit(gpa);
43 self.sections.deinit(gpa);
7044 self.sections_as_symbols.deinit(gpa);
7145 self.atom_by_index_table.deinit(gpa);
7246
......@@ -77,22 +51,15 @@ pub fn deinit(self: *Object, gpa: Allocator) void {
7751 self.managed_atoms.deinit(gpa);
7852
7953 gpa.free(self.name);
54 gpa.free(self.contents);
8055}
8156
8257pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) !void {
83 const file_stat = try self.file.stat();
84 const file_size = math.cast(usize, file_stat.size) orelse return error.Overflow;
85 self.contents = try self.file.readToEndAlloc(allocator, file_size);
86
8758 var stream = std.io.fixedBufferStream(self.contents);
8859 const reader = stream.reader();
8960
90 const file_offset = self.file_offset orelse 0;
91 if (file_offset > 0) {
92 try reader.context.seekTo(file_offset);
93 }
94
9561 self.header = try reader.readStruct(macho.mach_header_64);
62
9663 if (self.header.filetype != macho.MH_OBJECT) {
9764 log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{
9865 macho.MH_OBJECT,
......@@ -110,92 +77,54 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
11077 },
11178 };
11279 if (this_arch != cpu_arch) {
113 log.err("mismatched cpu architecture: expected {}, found {}", .{ cpu_arch, this_arch });
80 log.err("mismatched cpu architecture: expected {s}, found {s}", .{
81 @tagName(cpu_arch),
82 @tagName(this_arch),
83 });
11484 return error.MismatchedCpuArchitecture;
11585 }
11686
117 try self.load_commands.ensureUnusedCapacity(allocator, self.header.ncmds);
118
119 var i: u16 = 0;
120 while (i < self.header.ncmds) : (i += 1) {
121 var cmd = try macho.LoadCommand.read(allocator, reader);
87 var it = LoadCommandIterator{
88 .ncmds = self.header.ncmds,
89 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
90 };
91 while (it.next()) |cmd| {
12292 switch (cmd.cmd()) {
12393 .SEGMENT_64 => {
124 self.segment_cmd_index = i;
125 var seg = cmd.segment;
126 for (seg.sections.items) |*sect, j| {
127 const index = @intCast(u16, j);
128 const segname = sect.segName();
129 const sectname = sect.sectName();
130 if (mem.eql(u8, segname, "__DWARF")) {
131 if (mem.eql(u8, sectname, "__debug_info")) {
132 self.dwarf_debug_info_index = index;
133 } else if (mem.eql(u8, sectname, "__debug_abbrev")) {
134 self.dwarf_debug_abbrev_index = index;
135 } else if (mem.eql(u8, sectname, "__debug_str")) {
136 self.dwarf_debug_str_index = index;
137 } else if (mem.eql(u8, sectname, "__debug_line")) {
138 self.dwarf_debug_line_index = index;
139 } else if (mem.eql(u8, sectname, "__debug_line_str")) {
140 self.dwarf_debug_line_str_index = index;
141 } else if (mem.eql(u8, sectname, "__debug_ranges")) {
142 self.dwarf_debug_ranges_index = index;
143 }
144 } else if (mem.eql(u8, segname, "__TEXT")) {
145 if (mem.eql(u8, sectname, "__text")) {
146 self.text_section_index = index;
147 }
148 }
149
150 sect.offset += file_offset;
151 if (sect.reloff > 0) {
152 sect.reloff += file_offset;
153 }
94 const segment = cmd.cast(macho.segment_command_64).?;
95 try self.sections.ensureUnusedCapacity(allocator, segment.nsects);
96 for (cmd.getSections()) |sect| {
97 self.sections.appendAssumeCapacity(sect);
15498 }
155
156 seg.inner.fileoff += file_offset;
15799 },
158100 .SYMTAB => {
159 self.symtab_cmd_index = i;
160 cmd.symtab.symoff += file_offset;
161 cmd.symtab.stroff += file_offset;
162 },
163 .DYSYMTAB => {
164 self.dysymtab_cmd_index = i;
165 },
166 .BUILD_VERSION => {
167 self.build_version_cmd_index = i;
168 },
169 .DATA_IN_CODE => {
170 self.data_in_code_cmd_index = i;
171 cmd.linkedit_data.dataoff += file_offset;
172 },
173 else => {
174 log.debug("Unknown load command detected: 0x{x}.", .{@enumToInt(cmd.cmd())});
101 const symtab = cmd.cast(macho.symtab_command).?;
102 self.in_symtab = @ptrCast(
103 [*]const macho.nlist_64,
104 @alignCast(@alignOf(macho.nlist_64), &self.contents[symtab.symoff]),
105 )[0..symtab.nsyms];
106 self.in_strtab = self.contents[symtab.stroff..][0..symtab.strsize];
107 try self.symtab.appendSlice(allocator, self.in_symtab);
175108 },
109 else => {},
176110 }
177 self.load_commands.appendAssumeCapacity(cmd);
178111 }
179
180 try self.parseSymtab(allocator);
181112}
182113
183114const Context = struct {
184 symtab: []const macho.nlist_64,
185 strtab: []const u8,
115 object: *const Object,
186116};
187117
188118const SymbolAtIndex = struct {
189119 index: u32,
190120
191121 fn getSymbol(self: SymbolAtIndex, ctx: Context) macho.nlist_64 {
192 return ctx.symtab[self.index];
122 return ctx.object.getSourceSymbol(self.index).?;
193123 }
194124
195125 fn getSymbolName(self: SymbolAtIndex, ctx: Context) []const u8 {
196126 const sym = self.getSymbol(ctx);
197 assert(sym.n_strx < ctx.strtab.len);
198 return mem.sliceTo(@ptrCast([*:0]const u8, ctx.strtab.ptr + sym.n_strx), 0);
127 return ctx.object.getString(sym.n_strx);
199128 }
200129
201130 /// Returns whether lhs is less than rhs by allocated address in object file.
......@@ -285,6 +214,23 @@ fn filterRelocs(
285214 return relocs[start..end];
286215}
287216
217pub fn scanInputSections(self: Object, macho_file: *MachO) !void {
218 for (self.sections.items) |sect| {
219 const match = (try macho_file.getOutputSection(sect)) orelse {
220 log.debug(" unhandled section", .{});
221 continue;
222 };
223 const output = macho_file.sections.items(.header)[match];
224 log.debug("mapping '{s},{s}' into output sect({d}, '{s},{s}')", .{
225 sect.segName(),
226 sect.sectName(),
227 match + 1,
228 output.segName(),
229 output.sectName(),
230 });
231 }
232}
233
288234/// Splits object into atoms assuming one-shot linking mode.
289235pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32) !void {
290236 assert(macho_file.mode == .one_shot);
......@@ -293,7 +239,6 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
293239 defer tracy.end();
294240
295241 const gpa = macho_file.base.allocator;
296 const seg = self.load_commands.items[self.segment_cmd_index.?].segment;
297242
298243 log.debug("splitting object({d}, {s}) into atoms: one-shot mode", .{ object_id, self.name });
299244
......@@ -302,13 +247,12 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
302247 // the GO compiler does not necessarily respect that therefore we sort immediately by type
303248 // and address within.
304249 const context = Context{
305 .symtab = self.getSourceSymtab(),
306 .strtab = self.strtab,
250 .object = self,
307251 };
308 var sorted_all_syms = try std.ArrayList(SymbolAtIndex).initCapacity(gpa, context.symtab.len);
252 var sorted_all_syms = try std.ArrayList(SymbolAtIndex).initCapacity(gpa, self.in_symtab.len);
309253 defer sorted_all_syms.deinit();
310254
311 for (context.symtab) |_, index| {
255 for (self.in_symtab) |_, index| {
312256 sorted_all_syms.appendAssumeCapacity(.{ .index = @intCast(u32, index) });
313257 }
314258
......@@ -320,53 +264,48 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
320264
321265 // Well, shit, sometimes compilers skip the dysymtab load command altogether, meaning we
322266 // have to infer the start of undef section in the symtab ourselves.
323 const iundefsym = if (self.dysymtab_cmd_index) |cmd_index| blk: {
324 const dysymtab = self.load_commands.items[cmd_index].dysymtab;
267 const iundefsym = blk: {
268 const dysymtab = self.parseDysymtab() orelse {
269 var iundefsym: usize = sorted_all_syms.items.len;
270 while (iundefsym > 0) : (iundefsym -= 1) {
271 const sym = sorted_all_syms.items[iundefsym - 1].getSymbol(context);
272 if (sym.sect()) break;
273 }
274 break :blk iundefsym;
275 };
325276 break :blk dysymtab.iundefsym;
326 } else blk: {
327 var iundefsym: usize = sorted_all_syms.items.len;
328 while (iundefsym > 0) : (iundefsym -= 1) {
329 const sym = sorted_all_syms.items[iundefsym - 1].getSymbol(context);
330 if (sym.sect()) break;
331 }
332 break :blk iundefsym;
333277 };
334278
335279 // We only care about defined symbols, so filter every other out.
336280 const sorted_syms = sorted_all_syms.items[0..iundefsym];
337281 const subsections_via_symbols = self.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
338282
339 for (seg.sections.items) |sect, id| {
283 for (self.sections.items) |sect, id| {
340284 const sect_id = @intCast(u8, id);
341285 log.debug("splitting section '{s},{s}' into atoms", .{ sect.segName(), sect.sectName() });
342286
343287 // Get matching segment/section in the final artifact.
344 const match = (try macho_file.getMatchingSection(sect)) orelse {
288 const match = (try macho_file.getOutputSection(sect)) orelse {
345289 log.debug(" unhandled section", .{});
346290 continue;
347291 };
348292
349293 log.debug(" output sect({d}, '{s},{s}')", .{
350 macho_file.getSectionOrdinal(match),
351 macho_file.getSection(match).segName(),
352 macho_file.getSection(match).sectName(),
294 match + 1,
295 macho_file.sections.items(.header)[match].segName(),
296 macho_file.sections.items(.header)[match].sectName(),
353297 });
354298
355299 const cpu_arch = macho_file.base.options.target.cpu.arch;
356 const is_zerofill = blk: {
357 const section_type = sect.type_();
358 break :blk section_type == macho.S_ZEROFILL or section_type == macho.S_THREAD_LOCAL_ZEROFILL;
359 };
360300
361301 // Read section's code
362 const code: ?[]const u8 = if (!is_zerofill) try self.getSectionContents(sect_id) else null;
302 const code: ?[]const u8 = if (!sect.isZerofill()) try self.getSectionContents(sect) else null;
363303
364304 // Read section's list of relocations
365 const raw_relocs = self.contents[sect.reloff..][0 .. sect.nreloc * @sizeOf(macho.relocation_info)];
366 const relocs = mem.bytesAsSlice(
367 macho.relocation_info,
368 @alignCast(@alignOf(macho.relocation_info), raw_relocs),
369 );
305 const relocs = @ptrCast(
306 [*]const macho.relocation_info,
307 @alignCast(@alignOf(macho.relocation_info), &self.contents[sect.reloff]),
308 )[0..sect.nreloc];
370309
371310 // Symbols within this section only.
372311 const filtered_syms = filterSymbolsByAddress(
......@@ -387,7 +326,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
387326 try self.symtab.append(gpa, .{
388327 .n_strx = 0,
389328 .n_type = macho.N_SECT,
390 .n_sect = macho_file.getSectionOrdinal(match),
329 .n_sect = match + 1,
391330 .n_desc = 0,
392331 .n_value = sect.addr,
393332 });
......@@ -476,7 +415,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
476415 try self.symtab.append(gpa, .{
477416 .n_strx = 0,
478417 .n_type = macho.N_SECT,
479 .n_sect = macho_file.getSectionOrdinal(match),
418 .n_sect = match + 1,
480419 .n_desc = 0,
481420 .n_value = addr,
482421 });
......@@ -501,7 +440,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
501440 try self.symtab.append(gpa, .{
502441 .n_strx = 0,
503442 .n_type = macho.N_SECT,
504 .n_sect = macho_file.getSectionOrdinal(match),
443 .n_sect = match + 1,
505444 .n_desc = 0,
506445 .n_value = sect.addr,
507446 });
......@@ -535,21 +474,21 @@ fn createAtomFromSubsection(
535474 code: ?[]const u8,
536475 relocs: []const macho.relocation_info,
537476 indexes: []const SymbolAtIndex,
538 match: MatchingSection,
477 match: u8,
539478 sect: macho.section_64,
540479) !*Atom {
541480 const gpa = macho_file.base.allocator;
542481 const sym = self.symtab.items[sym_index];
543482 const atom = try MachO.createEmptyAtom(gpa, sym_index, size, alignment);
544483 atom.file = object_id;
545 self.symtab.items[sym_index].n_sect = macho_file.getSectionOrdinal(match);
484 self.symtab.items[sym_index].n_sect = match + 1;
546485
547486 log.debug("creating ATOM(%{d}, '{s}') in sect({d}, '{s},{s}') in object({d})", .{
548487 sym_index,
549488 self.getString(sym.n_strx),
550 macho_file.getSectionOrdinal(match),
551 macho_file.getSection(match).segName(),
552 macho_file.getSection(match).sectName(),
489 match + 1,
490 macho_file.sections.items(.header)[match].segName(),
491 macho_file.sections.items(.header)[match].sectName(),
553492 object_id,
554493 });
555494
......@@ -577,7 +516,7 @@ fn createAtomFromSubsection(
577516 try atom.contained.ensureTotalCapacity(gpa, indexes.len);
578517 for (indexes) |inner_sym_index| {
579518 const inner_sym = &self.symtab.items[inner_sym_index.index];
580 inner_sym.n_sect = macho_file.getSectionOrdinal(match);
519 inner_sym.n_sect = match + 1;
581520 atom.contained.appendAssumeCapacity(.{
582521 .sym_index = inner_sym_index.index,
583522 .offset = inner_sym.n_value - sym.n_value,
......@@ -589,48 +528,84 @@ fn createAtomFromSubsection(
589528 return atom;
590529}
591530
592fn parseSymtab(self: *Object, allocator: Allocator) !void {
593 const index = self.symtab_cmd_index orelse return;
594 const symtab = self.load_commands.items[index].symtab;
595 try self.symtab.appendSlice(allocator, self.getSourceSymtab());
596 self.strtab = self.contents[symtab.stroff..][0..symtab.strsize];
531pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 {
532 if (index >= self.in_symtab.len) return null;
533 return self.in_symtab[index];
597534}
598535
599pub fn getSourceSymtab(self: Object) []const macho.nlist_64 {
600 const index = self.symtab_cmd_index orelse return &[0]macho.nlist_64{};
601 const symtab = self.load_commands.items[index].symtab;
602 const symtab_size = @sizeOf(macho.nlist_64) * symtab.nsyms;
603 const raw_symtab = self.contents[symtab.symoff..][0..symtab_size];
604 return mem.bytesAsSlice(
605 macho.nlist_64,
606 @alignCast(@alignOf(macho.nlist_64), raw_symtab),
607 );
536pub fn getSourceSection(self: Object, index: u16) macho.section_64 {
537 assert(index < self.sections.items.len);
538 return self.sections.items[index];
608539}
609540
610pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 {
611 const symtab = self.getSourceSymtab();
612 if (index >= symtab.len) return null;
613 return symtab[index];
541pub fn parseDataInCode(self: Object) ?[]const macho.data_in_code_entry {
542 var it = LoadCommandIterator{
543 .ncmds = self.header.ncmds,
544 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
545 };
546 while (it.next()) |cmd| {
547 switch (cmd.cmd()) {
548 .DATA_IN_CODE => {
549 const dice = cmd.cast(macho.linkedit_data_command).?;
550 const ndice = @divExact(dice.datasize, @sizeOf(macho.data_in_code_entry));
551 return @ptrCast(
552 [*]const macho.data_in_code_entry,
553 @alignCast(@alignOf(macho.data_in_code_entry), &self.contents[dice.dataoff]),
554 )[0..ndice];
555 },
556 else => {},
557 }
558 } else return null;
614559}
615560
616pub fn getSourceSection(self: Object, index: u16) macho.section_64 {
617 const seg = self.load_commands.items[self.segment_cmd_index.?].segment;
618 assert(index < seg.sections.items.len);
619 return seg.sections.items[index];
561fn parseDysymtab(self: Object) ?macho.dysymtab_command {
562 var it = LoadCommandIterator{
563 .ncmds = self.header.ncmds,
564 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
565 };
566 while (it.next()) |cmd| {
567 switch (cmd.cmd()) {
568 .DYSYMTAB => {
569 return cmd.cast(macho.dysymtab_command).?;
570 },
571 else => {},
572 }
573 } else return null;
620574}
621575
622pub fn parseDataInCode(self: Object) ?[]const macho.data_in_code_entry {
623 const index = self.data_in_code_cmd_index orelse return null;
624 const data_in_code = self.load_commands.items[index].linkedit_data;
625 const raw_dice = self.contents[data_in_code.dataoff..][0..data_in_code.datasize];
626 return mem.bytesAsSlice(
627 macho.data_in_code_entry,
628 @alignCast(@alignOf(macho.data_in_code_entry), raw_dice),
629 );
576pub fn parseDwarfInfo(self: Object) error{Overflow}!dwarf.DwarfInfo {
577 var di = dwarf.DwarfInfo{
578 .endian = .Little,
579 .debug_info = &[0]u8{},
580 .debug_abbrev = &[0]u8{},
581 .debug_str = &[0]u8{},
582 .debug_line = &[0]u8{},
583 .debug_line_str = &[0]u8{},
584 .debug_ranges = &[0]u8{},
585 };
586 for (self.sections.items) |sect| {
587 const segname = sect.segName();
588 const sectname = sect.sectName();
589 if (mem.eql(u8, segname, "__DWARF")) {
590 if (mem.eql(u8, sectname, "__debug_info")) {
591 di.debug_info = try self.getSectionContents(sect);
592 } else if (mem.eql(u8, sectname, "__debug_abbrev")) {
593 di.debug_abbrev = try self.getSectionContents(sect);
594 } else if (mem.eql(u8, sectname, "__debug_str")) {
595 di.debug_str = try self.getSectionContents(sect);
596 } else if (mem.eql(u8, sectname, "__debug_line")) {
597 di.debug_line = try self.getSectionContents(sect);
598 } else if (mem.eql(u8, sectname, "__debug_line_str")) {
599 di.debug_line_str = try self.getSectionContents(sect);
600 } else if (mem.eql(u8, sectname, "__debug_ranges")) {
601 di.debug_ranges = try self.getSectionContents(sect);
602 }
603 }
604 }
605 return di;
630606}
631607
632pub fn getSectionContents(self: Object, index: u16) error{Overflow}![]const u8 {
633 const sect = self.getSourceSection(index);
608pub fn getSectionContents(self: Object, sect: macho.section_64) error{Overflow}![]const u8 {
634609 const size = math.cast(usize, sect.size) orelse return error.Overflow;
635610 log.debug("getting {s},{s} data at 0x{x} - 0x{x}", .{
636611 sect.segName(),
......@@ -642,8 +617,8 @@ pub fn getSectionContents(self: Object, index: u16) error{Overflow}![]const u8 {
642617}
643618
644619pub fn getString(self: Object, off: u32) []const u8 {
645 assert(off < self.strtab.len);
646 return mem.sliceTo(@ptrCast([*:0]const u8, self.strtab.ptr + off), 0);
620 assert(off < self.in_strtab.len);
621 return mem.sliceTo(@ptrCast([*:0]const u8, self.in_strtab.ptr + off), 0);
647622}
648623
649624pub fn getAtomForSymbol(self: Object, sym_index: u32) ?*Atom {
src/link/MachO/dead_strip.zig+26-24
......@@ -8,7 +8,6 @@ const mem = std.mem;
88const Allocator = mem.Allocator;
99const Atom = @import("Atom.zig");
1010const MachO = @import("../MachO.zig");
11const MatchingSection = MachO.MatchingSection;
1211
1312pub fn gcAtoms(macho_file: *MachO) !void {
1413 const gpa = macho_file.base.allocator;
......@@ -25,12 +24,12 @@ pub fn gcAtoms(macho_file: *MachO) !void {
2524 try prune(arena, alive, macho_file);
2625}
2726
28fn removeAtomFromSection(atom: *Atom, match: MatchingSection, macho_file: *MachO) void {
29 const sect = macho_file.getSectionPtr(match);
27fn removeAtomFromSection(atom: *Atom, match: u8, macho_file: *MachO) void {
28 var section = macho_file.sections.get(match);
3029
3130 // If we want to enable GC for incremental codepath, we need to take into
3231 // account any padding that might have been left here.
33 sect.size -= atom.size;
32 section.header.size -= atom.size;
3433
3534 if (atom.prev) |prev| {
3635 prev.next = atom.next;
......@@ -38,15 +37,16 @@ fn removeAtomFromSection(atom: *Atom, match: MatchingSection, macho_file: *MachO
3837 if (atom.next) |next| {
3938 next.prev = atom.prev;
4039 } else {
41 const last = macho_file.atoms.getPtr(match).?;
4240 if (atom.prev) |prev| {
43 last.* = prev;
41 section.last_atom = prev;
4442 } else {
4543 // The section will be GCed in the next step.
46 last.* = undefined;
47 sect.size = 0;
44 section.last_atom = null;
45 section.header.size = 0;
4846 }
4947 }
48
49 macho_file.sections.set(match, section);
5050}
5151
5252fn collectRoots(roots: *std.AutoHashMap(*Atom, void), macho_file: *MachO) !void {
......@@ -93,7 +93,7 @@ fn collectRoots(roots: *std.AutoHashMap(*Atom, void), macho_file: *MachO) !void
9393 const is_gc_root = blk: {
9494 if (source_sect.isDontDeadStrip()) break :blk true;
9595 if (mem.eql(u8, "__StaticInit", source_sect.sectName())) break :blk true;
96 switch (source_sect.type_()) {
96 switch (source_sect.@"type"()) {
9797 macho.S_MOD_INIT_FUNC_POINTERS,
9898 macho.S_MOD_TERM_FUNC_POINTERS,
9999 => break :blk true,
......@@ -173,19 +173,19 @@ fn mark(
173173fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *MachO) !void {
174174 // Any section that ends up here will be updated, that is,
175175 // its size and alignment recalculated.
176 var gc_sections = std.AutoHashMap(MatchingSection, void).init(arena);
176 var gc_sections = std.AutoHashMap(u8, void).init(arena);
177177 var loop: bool = true;
178178 while (loop) {
179179 loop = false;
180180
181181 for (macho_file.objects.items) |object| {
182 for (object.getSourceSymtab()) |_, source_index| {
182 for (object.in_symtab) |_, source_index| {
183183 const atom = object.getAtomForSymbol(@intCast(u32, source_index)) orelse continue;
184184 if (alive.contains(atom)) continue;
185185
186186 const global = atom.getSymbolWithLoc();
187187 const sym = atom.getSymbolPtr(macho_file);
188 const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);
188 const match = sym.n_sect - 1;
189189
190190 if (sym.n_desc == MachO.N_DESC_GCED) continue;
191191 if (!sym.ext() and !refersDead(atom, macho_file)) continue;
......@@ -232,7 +232,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
232232
233233 // TODO tombstone
234234 const atom = entry.getAtom(macho_file);
235 const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);
235 const match = sym.n_sect - 1;
236236 removeAtomFromSection(atom, match, macho_file);
237237 _ = try gc_sections.put(match, {});
238238 _ = macho_file.got_entries_table.remove(entry.target);
......@@ -244,7 +244,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
244244
245245 // TODO tombstone
246246 const atom = entry.getAtom(macho_file);
247 const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);
247 const match = sym.n_sect - 1;
248248 removeAtomFromSection(atom, match, macho_file);
249249 _ = try gc_sections.put(match, {});
250250 _ = macho_file.stubs_table.remove(entry.target);
......@@ -256,7 +256,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
256256
257257 // TODO tombstone
258258 const atom = entry.getAtom(macho_file);
259 const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);
259 const match = sym.n_sect - 1;
260260 removeAtomFromSection(atom, match, macho_file);
261261 _ = try gc_sections.put(match, {});
262262 _ = macho_file.tlv_ptr_entries_table.remove(entry.target);
......@@ -265,13 +265,13 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
265265 var gc_sections_it = gc_sections.iterator();
266266 while (gc_sections_it.next()) |entry| {
267267 const match = entry.key_ptr.*;
268 const sect = macho_file.getSectionPtr(match);
269 if (sect.size == 0) continue; // Pruning happens automatically in next step.
268 var section = macho_file.sections.get(match);
269 if (section.header.size == 0) continue; // Pruning happens automatically in next step.
270270
271 sect.@"align" = 0;
272 sect.size = 0;
271 section.header.@"align" = 0;
272 section.header.size = 0;
273273
274 var atom = macho_file.atoms.get(match).?;
274 var atom = section.last_atom.?;
275275
276276 while (atom.prev) |prev| {
277277 atom = prev;
......@@ -279,14 +279,16 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
279279
280280 while (true) {
281281 const atom_alignment = try math.powi(u32, 2, atom.alignment);
282 const aligned_end_addr = mem.alignForwardGeneric(u64, sect.size, atom_alignment);
283 const padding = aligned_end_addr - sect.size;
284 sect.size += padding + atom.size;
285 sect.@"align" = @maximum(sect.@"align", atom.alignment);
282 const aligned_end_addr = mem.alignForwardGeneric(u64, section.header.size, atom_alignment);
283 const padding = aligned_end_addr - section.header.size;
284 section.header.size += padding + atom.size;
285 section.header.@"align" = @maximum(section.header.@"align", atom.alignment);
286286
287287 if (atom.next) |next| {
288288 atom = next;
289289 } else break;
290290 }
291
292 macho_file.sections.set(match, section);
291293 }
292294}
src/link/MachO/fat.zig+3-1
......@@ -46,7 +46,9 @@ pub fn getLibraryOffset(reader: anytype, cpu_arch: std.Target.Cpu.Arch) !u64 {
4646 return fat_arch.offset;
4747 }
4848 } else {
49 log.err("Could not find matching cpu architecture in fat library: expected {}", .{cpu_arch});
49 log.err("Could not find matching cpu architecture in fat library: expected {s}", .{
50 @tagName(cpu_arch),
51 });
5052 return error.MismatchedCpuArchitecture;
5153 }
5254}
test/behavior/bitcast.zig-16
......@@ -90,22 +90,6 @@ test "nested bitcast" {
9090 comptime try S.foo(42);
9191}
9292
93test "@bitCast enum to its integer type" {
94 const SOCK = enum(c_int) {
95 A,
96 B,
97
98 fn testBitCastExternEnum() !void {
99 var SOCK_DGRAM = @This().B;
100 var sock_dgram = @bitCast(c_int, SOCK_DGRAM);
101 try expect(sock_dgram == 1);
102 }
103 };
104
105 try SOCK.testBitCastExternEnum();
106 comptime try SOCK.testBitCastExternEnum();
107}
108
10993// issue #3010: compiler segfault
11094test "bitcast literal [4]u8 param to u32" {
11195 const ip = @bitCast(u32, [_]u8{ 255, 255, 255, 255 });
test/behavior/packed-struct.zig+2-26
......@@ -6,6 +6,8 @@ const expectEqual = std.testing.expectEqual;
66const native_endian = builtin.cpu.arch.endian();
77
88test "correct size of packed structs" {
9 // Stage2 has different packed struct semantics.
10 if (builtin.zig_backend != .stage1) return error.SkipZigTest;
911 const T1 = packed struct { one: u8, three: [3]u8 };
1012
1113 try expectEqual(4, @sizeOf(T1));
......@@ -118,18 +120,6 @@ test "flags in packed structs" {
118120 try expectEqual(32, @bitSizeOf(Flags3));
119121}
120122
121test "arrays in packed structs" {
122 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
123
124 const T1 = packed struct { array: [3][3]u8 };
125 const T2 = packed struct { array: [9]u8 };
126
127 try expectEqual(@sizeOf(u72), @sizeOf(T1));
128 try expectEqual(72, @bitSizeOf(T1));
129 try expectEqual(@sizeOf(u72), @sizeOf(T2));
130 try expectEqual(72, @bitSizeOf(T2));
131}
132
133123test "consistent size of packed structs" {
134124 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
135125
......@@ -145,23 +135,15 @@ test "consistent size of packed structs" {
145135 try expectEqual(register_size_bits, @bitSizeOf(TxData2));
146136 try expectEqual(register_size_bytes, @sizeOf(TxData2));
147137
148 const TxData3 = packed struct { a: u32, b: [3]u8 };
149138 const TxData4 = packed struct { a: u32, b: u24 };
150 const TxData5 = packed struct { a: [3]u8, b: u32 };
151139 const TxData6 = packed struct { a: u24, b: u32 };
152140
153141 const expectedBitSize = 56;
154142 const expectedByteSize = @sizeOf(u56);
155143
156 try expectEqual(expectedBitSize, @bitSizeOf(TxData3));
157 try expectEqual(expectedByteSize, @sizeOf(TxData3));
158
159144 try expectEqual(expectedBitSize, @bitSizeOf(TxData4));
160145 try expectEqual(expectedByteSize, @sizeOf(TxData4));
161146
162 try expectEqual(expectedBitSize, @bitSizeOf(TxData5));
163 try expectEqual(expectedByteSize, @sizeOf(TxData5));
164
165147 try expectEqual(expectedBitSize, @bitSizeOf(TxData6));
166148 try expectEqual(expectedByteSize, @sizeOf(TxData6));
167149}
......@@ -234,12 +216,6 @@ test "correct sizeOf and offsets in packed structs" {
234216 try expectEqual(@as(u7, 0b1111010), s2.y);
235217 try expectEqual(@as(u24, 0xd5c71f), s2.z);
236218 }
237
238 const S = packed struct { a: u32, pad: [3]u32, b: u32 };
239
240 try expectEqual(16, @offsetOf(S, "b"));
241 try expectEqual(128, @bitOffsetOf(S, "b"));
242 try expectEqual(@sizeOf(u160), @sizeOf(S));
243219}
244220
245221test "nested packed structs" {
test/behavior/sizeof_and_typeof.zig+2
......@@ -105,6 +105,8 @@ test "@offsetOf" {
105105}
106106
107107test "@offsetOf packed struct, array length not power of 2 or multiple of native pointer width in bytes" {
108 // Stage2 has different packed struct semantics.
109 if (builtin.zig_backend != .stage1) return error.SkipZigTest;
108110 const p3a_len = 3;
109111 const P3 = packed struct {
110112 a: [p3a_len]u8,
test/behavior/struct.zig+2-4
......@@ -704,10 +704,8 @@ const FooArray24Bits = packed struct {
704704};
705705
706706test "aligned array of packed struct" {
707 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
708 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
709 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
710 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
707 // Stage2 has different packed struct semantics.
708 if (builtin.zig_backend != .stage1) return error.SkipZigTest;
711709
712710 comptime {
713711 try expect(@sizeOf(FooStructAligned) == 2);
test/behavior/type.zig+40-17
......@@ -247,26 +247,17 @@ fn add(a: i32, b: i32) i32 {
247247}
248248
249249test "Type.ErrorSet" {
250 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
251 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
252
250253 try testing.expect(@Type(.{ .ErrorSet = null }) == anyerror);
251254
252255 // error sets don't compare equal so just check if they compile
253 _ = @Type(@typeInfo(error{}));
254 _ = @Type(@typeInfo(error{A}));
255 _ = @Type(@typeInfo(error{ A, B, C }));
256 _ = @Type(.{
257 .ErrorSet = &[_]Type.Error{
258 .{ .name = "A" },
259 .{ .name = "B" },
260 .{ .name = "C" },
261 },
262 });
263 _ = @Type(.{
264 .ErrorSet = &.{
265 .{ .name = "C" },
266 .{ .name = "B" },
267 .{ .name = "A" },
268 },
269 });
256 inline for (.{ error{}, error{A}, error{ A, B, C } }) |T| {
257 const info = @typeInfo(T);
258 const T2 = @Type(info);
259 try testing.expect(T == T2);
260 }
270261}
271262
272263test "Type.Struct" {
......@@ -517,3 +508,35 @@ test "Type.Union from regular enum" {
517508 _ = T;
518509 _ = @typeInfo(T).Union;
519510}
511
512test "Type.Fn" {
513 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
514 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
515
516 const some_opaque = opaque {};
517 const some_ptr = *some_opaque;
518 const T = fn (c_int, some_ptr) callconv(.C) void;
519
520 {
521 const fn_info = std.builtin.Type{ .Fn = .{
522 .calling_convention = .C,
523 .alignment = 0,
524 .is_generic = false,
525 .is_var_args = false,
526 .return_type = void,
527 .args = &.{
528 .{ .is_generic = false, .is_noalias = false, .arg_type = c_int },
529 .{ .is_generic = false, .is_noalias = false, .arg_type = some_ptr },
530 },
531 } };
532
533 const fn_type = @Type(fn_info);
534 try std.testing.expectEqual(T, fn_type);
535 }
536
537 {
538 const fn_info = @typeInfo(T);
539 const fn_type = @Type(fn_info);
540 try std.testing.expectEqual(T, fn_type);
541 }
542}
test/cases/compile_errors/accessing_runtime_parameter_from_outer_function.zig created+20
......@@ -0,0 +1,20 @@
1fn outer(y: u32) *const fn (u32) u32 {
2 const st = struct {
3 fn get(z: u32) u32 {
4 return z + y;
5 }
6 };
7 return st.get;
8}
9export fn entry() void {
10 var func = outer(10);
11 var x = func(3);
12 _ = x;
13}
14
15// error
16// backend=stage2
17// target=native
18//
19// :4:24: error: 'y' not accessible from inner function
20// :3:9: note: crossed function definition here
test/cases/compile_errors/bitCast_to_enum_type.zig+1-1
......@@ -9,4 +9,4 @@ export fn entry() void {
99// target=native
1010//
1111// :3:24: error: cannot @bitCast to 'tmp.entry.E'
12// :3:24: note: use @intToEnum for type coercion
12// :3:24: note: use @intToEnum to cast from 'u32'
test/cases/compile_errors/cannot_break_out_of_defer_expression.zig created+14
......@@ -0,0 +1,14 @@
1export fn foo() void {
2 while (true) {
3 defer {
4 break;
5 }
6 }
7}
8
9// error
10// backend=stage2
11// target=native
12//
13// :4:13: error: cannot break out of defer expression
14// :3:9: note: defer expression here
test/cases/compile_errors/cannot_continue_out_of_defer_expression.zig created+14
......@@ -0,0 +1,14 @@
1export fn foo() void {
2 while (true) {
3 defer {
4 continue;
5 }
6 }
7}
8
9// error
10// backend=stage2
11// target=native
12//
13// :4:13: error: cannot continue out of defer expression
14// :3:9: note: defer expression here
test/cases/compile_errors/direct_struct_loop.zig created+9
......@@ -0,0 +1,9 @@
1const A = struct { a : A, };
2export fn entry() usize { return @sizeOf(A); }
3
4// error
5// backend=stage2
6// target=native
7//
8// :1:11: error: struct 'tmp.A' depends on itself
9// :1:20: note: while checking this field
test/cases/compile_errors/for_loop_body_expression_ignored.zig created+35
......@@ -0,0 +1,35 @@
1fn returns() usize {
2 return 2;
3}
4export fn f1() void {
5 for ("hello") |_| returns();
6}
7export fn f2() void {
8 var x: anyerror!i32 = error.Bad;
9 for ("hello") |_| returns() else unreachable;
10 _ = x;
11}
12export fn f3() void {
13 for ("hello") |_| {} else true;
14}
15export fn f4() void {
16 const foo = for ("hello") |_| returns() else true;
17 _ = foo;
18}
19
20// error
21// backend=stage2
22// target=native
23//
24// :5:30: error: value of type 'usize' ignored
25// :5:30: note: all non-void values must be used
26// :5:30: note: this error can be suppressed by assigning the value to '_'
27// :9:30: error: value of type 'usize' ignored
28// :9:30: note: all non-void values must be used
29// :9:30: note: this error can be suppressed by assigning the value to '_'
30// :13:31: error: value of type 'bool' ignored
31// :13:31: note: all non-void values must be used
32// :13:31: note: this error can be suppressed by assigning the value to '_'
33// :16:42: error: value of type 'usize' ignored
34// :16:42: note: all non-void values must be used
35// :16:42: note: this error can be suppressed by assigning the value to '_'
test/cases/compile_errors/global_variable_alignment_non_power_of_2.zig created+8
......@@ -0,0 +1,8 @@
1const some_data: [100]u8 align(3) = undefined;
2export fn entry() usize { return @sizeOf(@TypeOf(some_data)); }
3
4// error
5// backend=stage2
6// target=native
7//
8// :1:32: error: alignment value '3' is not a power of two
test/cases/compile_errors/indirect_struct_loop.zig created+13
......@@ -0,0 +1,13 @@
1const A = struct { b : B, };
2const B = struct { c : C, };
3const C = struct { a : A, };
4export fn entry() usize { return @sizeOf(A); }
5
6// error
7// backend=stage2
8// target=native
9//
10// :1:11: error: struct 'tmp.A' depends on itself
11// :3:20: note: while checking this field
12// :2:20: note: while checking this field
13// :1:20: note: while checking this field
test/cases/compile_errors/instantiating_an_undefined_value_for_an_invalid_struct_that_contains_itself.zig created+16
......@@ -0,0 +1,16 @@
1const Foo = struct {
2 x: Foo,
3};
4
5var foo: Foo = undefined;
6
7export fn entry() usize {
8 return @sizeOf(@TypeOf(foo.x));
9}
10
11// error
12// backend=stage2
13// target=native
14//
15// :1:13: error: struct 'tmp.Foo' depends on itself
16// :2:5: note: while checking this field
test/cases/compile_errors/instantiating_an_undefined_value_for_an_invalid_union_that_contains_itself.zig created+16
......@@ -0,0 +1,16 @@
1const Foo = union {
2 x: Foo,
3};
4
5var foo: Foo = undefined;
6
7export fn entry() usize {
8 return @sizeOf(@TypeOf(foo.x));
9}
10
11// error
12// backend=stage2
13// target=native
14//
15// :1:13: error: union 'tmp.Foo' depends on itself
16// :2:5: note: while checking this field
test/cases/compile_errors/intToPtr_with_misaligned_address.zig created+10
......@@ -0,0 +1,10 @@
1pub export fn entry() void {
2 var y = @intToPtr([*]align(4) u8, 5);
3 _ = y;
4}
5
6// error
7// backend=stage2
8// target=native
9//
10// :2:39: error: pointer type '[*]align(4) u8' requires aligned address
test/cases/compile_errors/issue_3818_bitcast_from_parray-slice_to_u16.zig created+19
......@@ -0,0 +1,19 @@
1export fn foo1() void {
2 var bytes = [_]u8{1, 2};
3 const word: u16 = @bitCast(u16, bytes[0..]);
4 _ = word;
5}
6export fn foo2() void {
7 var bytes: []const u8 = &[_]u8{1, 2};
8 const word: u16 = @bitCast(u16, bytes);
9 _ = word;
10}
11
12// error
13// backend=stage2
14// target=native
15//
16// :3:42: error: cannot @bitCast from '*[2]u8'
17// :3:42: note: use @ptrToInt to cast to 'u16'
18// :8:37: error: cannot @bitCast from '[]const u8'
19// :8:37: note: use @ptrToInt to cast to 'u16'
test/cases/compile_errors/packed_struct_with_fields_of_not_allowed_types.zig created+84
......@@ -0,0 +1,84 @@
1export fn entry1() void {
2 _ = @sizeOf(packed struct {
3 x: anyerror,
4 });
5}
6export fn entry2() void {
7 _ = @sizeOf(packed struct {
8 x: [2]u24,
9 });
10}
11export fn entry3() void {
12 _ = @sizeOf(packed struct {
13 x: anyerror!u32,
14 });
15}
16export fn entry4() void {
17 _ = @sizeOf(packed struct {
18 x: S,
19 });
20}
21export fn entry5() void {
22 _ = @sizeOf(packed struct {
23 x: U,
24 });
25}
26export fn entry6() void {
27 _ = @sizeOf(packed struct {
28 x: ?anyerror,
29 });
30}
31export fn entry7() void {
32 _ = @sizeOf(packed struct {
33 x: enum { A, B },
34 });
35}
36export fn entry8() void {
37 _ = @sizeOf(packed struct {
38 x: fn () void,
39 });
40}
41export fn entry9() void {
42 _ = @sizeOf(packed struct {
43 x: *const fn () void,
44 });
45}
46export fn entry10() void {
47 _ = @sizeOf(packed struct {
48 x: packed struct { x: i32 },
49 });
50}
51export fn entry11() void {
52 _ = @sizeOf(packed struct {
53 x: packed union { A: i32, B: u32 },
54 });
55}
56const S = struct {
57 x: i32,
58};
59const U = extern union {
60 A: i32,
61 B: u32,
62};
63
64// error
65// backend=llvm
66// target=native
67//
68// :3:9: error: packed structs cannot contain fields of type 'anyerror'
69// :3:9: note: type has no guaranteed in-memory representation
70// :8:9: error: packed structs cannot contain fields of type '[2]u24'
71// :8:9: note: type has no guaranteed in-memory representation
72// :13:9: error: packed structs cannot contain fields of type 'anyerror!u32'
73// :13:9: note: type has no guaranteed in-memory representation
74// :18:9: error: packed structs cannot contain fields of type 'tmp.S'
75// :18:9: note: only packed structs layout are allowed in packed types
76// :56:11: note: struct declared here
77// :23:9: error: packed structs cannot contain fields of type 'tmp.U'
78// :23:9: note: only packed unions layout are allowed in packed types
79// :59:18: note: union declared here
80// :28:9: error: packed structs cannot contain fields of type '?anyerror'
81// :28:9: note: type has no guaranteed in-memory representation
82// :38:9: error: packed structs cannot contain fields of type 'fn() void'
83// :38:9: note: type has no guaranteed in-memory representation
84// :38:9: note: use '*const ' to make a function pointer type
test/cases/compile_errors/packed_union_given_enum_tag_type.zig created+20
......@@ -0,0 +1,20 @@
1const Letter = enum {
2 A,
3 B,
4 C,
5};
6const Payload = packed union(Letter) {
7 A: i32,
8 B: f64,
9 C: bool,
10};
11export fn entry() void {
12 var a = Payload { .A = 1234 };
13 _ = a;
14}
15
16// error
17// backend=stage2
18// target=native
19//
20// :6:30: error: packed union does not support enum tag type
test/cases/compile_errors/packed_union_with_automatic_layout_field.zig created+20
......@@ -0,0 +1,20 @@
1const Foo = struct {
2 a: u32,
3 b: f32,
4};
5const Payload = packed union {
6 A: Foo,
7 B: bool,
8};
9export fn entry() void {
10 var a = Payload { .B = true };
11 _ = a;
12}
13
14// error
15// backend=stage2
16// target=native
17//
18// :6:5: error: packed unions cannot contain fields of type 'tmp.Foo'
19// :6:5: note: only packed structs layout are allowed in packed types
20// :1:13: note: struct declared here
test/cases/compile_errors/reify_type_for_tagged_union_with_extra_union_field.zig+1-1
......@@ -31,5 +31,5 @@ export fn entry() void {
3131// backend=stage2
3232// target=native
3333//
34// :13:16: error: no field named 'arst' in enum 'tmp.Tag__enum_264'
34// :13:16: error: no field named 'arst' in enum 'tmp.Tag__enum_266'
3535// :1:13: note: enum declared here
test/cases/compile_errors/specify_non-integer_enum_tag_type.zig created+16
......@@ -0,0 +1,16 @@
1const Small = enum (f32) {
2 One,
3 Two,
4 Three,
5};
6
7export fn entry() void {
8 var x = Small.One;
9 _ = x;
10}
11
12// error
13// backend=stage2
14// target=native
15//
16// :1:21: error: expected integer tag type, found 'f32'
test/cases/compile_errors/src_outside_function.zig created+9
......@@ -0,0 +1,9 @@
1comptime {
2 @src();
3}
4
5// error
6// backend=stage2
7// target=native
8//
9// :2:5: error: @src outside function
test/cases/compile_errors/stage1/obj/accessing_runtime_parameter_from_outer_function.zig deleted-21
......@@ -1,21 +0,0 @@
1fn outer(y: u32) fn (u32) u32 {
2 const st = struct {
3 fn get(z: u32) u32 {
4 return z + y;
5 }
6 };
7 return st.get;
8}
9export fn entry() void {
10 var func = outer(10);
11 var x = func(3);
12 _ = x;
13}
14
15// error
16// backend=stage1
17// target=native
18//
19// tmp.zig:4:24: error: 'y' not accessible from inner function
20// tmp.zig:3:28: note: crossed function definition here
21// tmp.zig:1:10: note: declared here
test/cases/compile_errors/stage1/obj/cannot_break_out_of_defer_expression.zig deleted-13
......@@ -1,13 +0,0 @@
1export fn foo() void {
2 while (true) {
3 defer {
4 break;
5 }
6 }
7}
8
9// error
10// backend=stage1
11// target=native
12//
13// tmp.zig:4:13: error: cannot break out of defer expression
test/cases/compile_errors/stage1/obj/cannot_continue_out_of_defer_expression.zig deleted-13
......@@ -1,13 +0,0 @@
1export fn foo() void {
2 while (true) {
3 defer {
4 continue;
5 }
6 }
7}
8
9// error
10// backend=stage1
11// target=native
12//
13// tmp.zig:4:13: error: cannot continue out of defer expression
test/cases/compile_errors/stage1/obj/direct_struct_loop.zig deleted-8
......@@ -1,8 +0,0 @@
1const A = struct { a : A, };
2export fn entry() usize { return @sizeOf(A); }
3
4// error
5// backend=stage1
6// target=native
7//
8// tmp.zig:1:11: error: struct 'A' depends on itself
test/cases/compile_errors/stage1/obj/for_loop_body_expression_ignored.zig deleted-18
......@@ -1,18 +0,0 @@
1fn returns() usize {
2 return 2;
3}
4export fn f1() void {
5 for ("hello") |_| returns();
6}
7export fn f2() void {
8 var x: anyerror!i32 = error.Bad;
9 for ("hello") |_| returns() else unreachable;
10 _ = x;
11}
12
13// error
14// backend=stage1
15// target=native
16//
17// tmp.zig:5:30: error: expression value is ignored
18// tmp.zig:9:30: error: expression value is ignored
test/cases/compile_errors/stage1/obj/global_variable_alignment_non_power_of_2.zig deleted-8
......@@ -1,8 +0,0 @@
1const some_data: [100]u8 align(3) = undefined;
2export fn entry() usize { return @sizeOf(@TypeOf(some_data)); }
3
4// error
5// backend=stage1
6// target=native
7//
8// tmp.zig:1:32: error: alignment value 3 is not a power of 2
test/cases/compile_errors/stage1/obj/indirect_struct_loop.zig deleted-10
......@@ -1,10 +0,0 @@
1const A = struct { b : B, };
2const B = struct { c : C, };
3const C = struct { a : A, };
4export fn entry() usize { return @sizeOf(A); }
5
6// error
7// backend=stage1
8// target=native
9//
10// tmp.zig:1:11: error: struct 'A' depends on itself
test/cases/compile_errors/stage1/obj/instantiating_an_undefined_value_for_an_invalid_struct_that_contains_itself.zig deleted-15
......@@ -1,15 +0,0 @@
1const Foo = struct {
2 x: Foo,
3};
4
5var foo: Foo = undefined;
6
7export fn entry() usize {
8 return @sizeOf(@TypeOf(foo.x));
9}
10
11// error
12// backend=stage1
13// target=native
14//
15// tmp.zig:1:13: error: struct 'Foo' depends on itself
test/cases/compile_errors/stage1/obj/intToPtr_with_misaligned_address.zig deleted-10
......@@ -1,10 +0,0 @@
1pub fn main() void {
2 var y = @intToPtr([*]align(4) u8, 5);
3 _ = y;
4}
5
6// error
7// backend=stage1
8// target=native
9//
10// tmp.zig:2:13: error: pointer type '[*]align(4) u8' requires aligned address
test/cases/compile_errors/stage1/obj/issue_3818_bitcast_from_parray-slice_to_u16.zig deleted-17
......@@ -1,17 +0,0 @@
1export fn foo1() void {
2 var bytes = [_]u8{1, 2};
3 const word: u16 = @bitCast(u16, bytes[0..]);
4 _ = word;
5}
6export fn foo2() void {
7 var bytes: []const u8 = &[_]u8{1, 2};
8 const word: u16 = @bitCast(u16, bytes);
9 _ = word;
10}
11
12// error
13// backend=stage1
14// target=native
15//
16// tmp.zig:3:42: error: unable to @bitCast from pointer type '*[2]u8'
17// tmp.zig:8:32: error: destination type 'u16' has size 2 but source type '[]const u8' has size 16
test/cases/compile_errors/stage1/obj/packed_union_given_enum_tag_type.zig deleted-20
......@@ -1,20 +0,0 @@
1const Letter = enum {
2 A,
3 B,
4 C,
5};
6const Payload = packed union(Letter) {
7 A: i32,
8 B: f64,
9 C: bool,
10};
11export fn entry() void {
12 var a = Payload { .A = 1234 };
13 _ = a;
14}
15
16// error
17// backend=stage1
18// target=native
19//
20// tmp.zig:6:30: error: packed union does not support enum tag type
test/cases/compile_errors/stage1/obj/packed_union_with_automatic_layout_field.zig deleted-18
......@@ -1,18 +0,0 @@
1const Foo = struct {
2 a: u32,
3 b: f32,
4};
5const Payload = packed union {
6 A: Foo,
7 B: bool,
8};
9export fn entry() void {
10 var a = Payload { .B = true };
11 _ = a;
12}
13
14// error
15// backend=stage1
16// target=native
17//
18// tmp.zig:6:5: error: non-packed, non-extern struct 'Foo' not allowed in packed union; no guaranteed in-memory representation
test/cases/compile_errors/stage1/obj/specify_non-integer_enum_tag_type.zig deleted-16
......@@ -1,16 +0,0 @@
1const Small = enum (f32) {
2 One,
3 Two,
4 Three,
5};
6
7export fn entry() void {
8 var x = Small.One;
9 _ = x;
10}
11
12// error
13// backend=stage1
14// target=native
15//
16// tmp.zig:1:21: error: expected integer, found 'f32'
test/cases/compile_errors/stage1/obj/src_outside_function.zig deleted-9
......@@ -1,9 +0,0 @@
1comptime {
2 @src();
3}
4
5// error
6// backend=stage1
7// target=native
8//
9// tmp.zig:2:5: error: @src outside function
test/cases/compile_errors/stage1/obj/struct_depends_on_itself_via_optional_field.zig deleted-19
......@@ -1,19 +0,0 @@
1const LhsExpr = struct {
2 rhsExpr: ?AstObject,
3};
4const AstObject = union {
5 lhsExpr: LhsExpr,
6};
7export fn entry() void {
8 const lhsExpr = LhsExpr{ .rhsExpr = null };
9 const obj = AstObject{ .lhsExpr = lhsExpr };
10 _ = obj;
11}
12
13// error
14// backend=stage1
15// target=native
16//
17// tmp.zig:1:17: error: struct 'LhsExpr' depends on itself
18// tmp.zig:5:5: note: while checking this field
19// tmp.zig:2:5: note: while checking this field
test/cases/compile_errors/stage1/obj/top_level_decl_dependency_loop.zig deleted-12
......@@ -1,12 +0,0 @@
1const a : @TypeOf(b) = 0;
2const b : @TypeOf(a) = 0;
3export fn entry() void {
4 const c = a + b;
5 _ = c;
6}
7
8// error
9// backend=stage1
10// target=native
11//
12// tmp.zig:2:19: error: dependency loop detected
test/cases/compile_errors/stage1/obj/unused_variable_error_on_errdefer.zig deleted-13
......@@ -1,13 +0,0 @@
1fn foo() !void {
2 errdefer |a| unreachable;
3 return error.A;
4}
5export fn entry() void {
6 foo() catch unreachable;
7}
8
9// error
10// backend=stage1
11// target=native
12//
13// tmp.zig:2:15: error: unused variable: 'a'
test/cases/compile_errors/stage1/obj/vector_index_out_of_bounds.zig deleted-10
......@@ -1,10 +0,0 @@
1export fn entry() void {
2 const x = @import("std").meta.Vector(3, f32){ 25, 75, 5, 0 };
3 _ = x;
4}
5
6// error
7// backend=stage1
8// target=native
9//
10// tmp.zig:2:62: error: index 3 outside vector of size 3
test/cases/compile_errors/stage1/obj/while_expected_error_union_got_bool.zig deleted-10
......@@ -1,10 +0,0 @@
1export fn foo() void {
2 while (bar()) |x| {_ = x;} else |err| {_ = err;}
3}
4fn bar() bool { return true; }
5
6// error
7// backend=stage1
8// target=native
9//
10// tmp.zig:2:15: error: expected error union type, found 'bool'
test/cases/compile_errors/stage1/obj/while_expected_error_union_got_optional.zig deleted-10
......@@ -1,10 +0,0 @@
1export fn foo() void {
2 while (bar()) |x| {_ = x;} else |err| {_ = err;}
3}
4fn bar() ?i32 { return 1; }
5
6// error
7// backend=stage1
8// target=native
9//
10// tmp.zig:2:15: error: expected error union type, found '?i32'
test/cases/compile_errors/stage1/obj/while_expected_optional_got_bool.zig deleted-10
......@@ -1,10 +0,0 @@
1export fn foo() void {
2 while (bar()) |x| {_ = x;}
3}
4fn bar() bool { return true; }
5
6// error
7// backend=stage1
8// target=native
9//
10// tmp.zig:2:15: error: expected optional type, found 'bool'
test/cases/compile_errors/stage1/obj/while_expected_optional_got_error_union.zig deleted-10
......@@ -1,10 +0,0 @@
1export fn foo() void {
2 while (bar()) |x| {_ = x;}
3}
4fn bar() anyerror!i32 { return 1; }
5
6// error
7// backend=stage1
8// target=native
9//
10// tmp.zig:2:15: error: expected optional type, found 'anyerror!i32'
test/cases/compile_errors/stage1/obj/while_loop_body_expression_ignored.zig deleted-22
......@@ -1,22 +0,0 @@
1fn returns() usize {
2 return 2;
3}
4export fn f1() void {
5 while (true) returns();
6}
7export fn f2() void {
8 var x: ?i32 = null;
9 while (x) |_| returns();
10}
11export fn f3() void {
12 var x: anyerror!i32 = error.Bad;
13 while (x) |_| returns() else |_| unreachable;
14}
15
16// error
17// backend=stage1
18// target=native
19//
20// tmp.zig:5:25: error: expression value is ignored
21// tmp.zig:9:26: error: expression value is ignored
22// tmp.zig:13:26: error: expression value is ignored
test/cases/compile_errors/stage1/test/packed_struct_with_fields_of_not_allowed_types.zig deleted-74
......@@ -1,74 +0,0 @@
1const A = packed struct {
2 x: anyerror,
3};
4const B = packed struct {
5 x: [2]u24,
6};
7const C = packed struct {
8 x: [1]anyerror,
9};
10const D = packed struct {
11 x: [1]S,
12};
13const E = packed struct {
14 x: [1]U,
15};
16const F = packed struct {
17 x: ?anyerror,
18};
19const G = packed struct {
20 x: Enum,
21};
22export fn entry1() void {
23 var a: A = undefined;
24 _ = a;
25}
26export fn entry2() void {
27 var b: B = undefined;
28 _ = b;
29}
30export fn entry3() void {
31 var r: C = undefined;
32 _ = r;
33}
34export fn entry4() void {
35 var d: D = undefined;
36 _ = d;
37}
38export fn entry5() void {
39 var e: E = undefined;
40 _ = e;
41}
42export fn entry6() void {
43 var f: F = undefined;
44 _ = f;
45}
46export fn entry7() void {
47 var g: G = undefined;
48 _ = g;
49}
50const S = struct {
51 x: i32,
52};
53const U = struct {
54 A: i32,
55 B: u32,
56};
57const Enum = enum {
58 A,
59 B,
60};
61
62// error
63// backend=stage1
64// target=native
65// is_test=1
66//
67// tmp.zig:2:5: error: type 'anyerror' not allowed in packed struct; no guaranteed in-memory representation
68// tmp.zig:5:5: error: array of 'u24' not allowed in packed struct due to padding bits (must be padded from 48 to 64 bits)
69// tmp.zig:8:5: error: type 'anyerror' not allowed in packed struct; no guaranteed in-memory representation
70// tmp.zig:11:5: error: non-packed, non-extern struct 'S' not allowed in packed struct; no guaranteed in-memory representation
71// tmp.zig:14:5: error: non-packed, non-extern struct 'U' not allowed in packed struct; no guaranteed in-memory representation
72// tmp.zig:17:5: error: type '?anyerror' not allowed in packed struct; no guaranteed in-memory representation
73// tmp.zig:20:5: error: type 'Enum' not allowed in packed struct; no guaranteed in-memory representation
74// tmp.zig:57:14: note: enum declaration does not specify an integer tag type
test/cases/compile_errors/struct_depends_on_itself_via_optional_field.zig created+19
......@@ -0,0 +1,19 @@
1const LhsExpr = struct {
2 rhsExpr: ?AstObject,
3};
4const AstObject = union {
5 lhsExpr: LhsExpr,
6};
7export fn entry() void {
8 const lhsExpr = LhsExpr{ .rhsExpr = null };
9 const obj = AstObject{ .lhsExpr = lhsExpr };
10 _ = obj;
11}
12
13// error
14// backend=stage2
15// target=native
16//
17// :1:17: error: struct 'tmp.LhsExpr' depends on itself
18// :5:5: note: while checking this field
19// :2:5: note: while checking this field
test/cases/compile_errors/top_level_decl_dependency_loop.zig created+13
......@@ -0,0 +1,13 @@
1const a : @TypeOf(b) = 0;
2const b : @TypeOf(a) = 0;
3export fn entry() void {
4 const c = a + b;
5 _ = c;
6}
7
8// error
9// backend=stage2
10// target=native
11//
12// :1:1: error: dependency loop detected
13// :2:19: note: referenced here
test/cases/compile_errors/type_variables_must_be_constant.zig+2-2
......@@ -7,5 +7,5 @@ export fn entry() foo {
77// backend=stage2
88// target=native
99//
10// :1:1: error: variable of type 'type' must be const or comptime
11// :1:1: note: types are not available at runtime
10// :1:5: error: variable of type 'type' must be const or comptime
11// :1:5: note: types are not available at runtime
test/cases/compile_errors/unused_variable_error_on_errdefer.zig created+13
......@@ -0,0 +1,13 @@
1fn foo() !void {
2 errdefer |a| unreachable;
3 return error.A;
4}
5export fn entry() void {
6 foo() catch unreachable;
7}
8
9// error
10// backend=stage2
11// target=native
12//
13// :2:15: error: unused capture
test/cases/compile_errors/use_invalid_number_literal_as_array_index.zig+2-2
......@@ -8,5 +8,5 @@ export fn entry() void {
88// backend=stage2
99// target=native
1010//
11// :1:1: error: variable of type 'comptime_int' must be const or comptime
12// :1:1: note: to modify this variable at runtime, it must be given an explicit fixed-size number type
11// :1:5: error: variable of type 'comptime_int' must be const or comptime
12// :1:5: note: to modify this variable at runtime, it must be given an explicit fixed-size number type
test/cases/compile_errors/usingnamespace_with_wrong_type.zig+1-1
......@@ -4,4 +4,4 @@ usingnamespace void;
44// backend=stage2
55// target=native
66//
7// :1:1: error: type void has no namespace
7// :1:16: error: type void has no namespace
test/cases/compile_errors/vector_index_out_of_bounds.zig created+10
......@@ -0,0 +1,10 @@
1export fn entry() void {
2 const x = @import("std").meta.Vector(3, f32){ 25, 75, 5, 0 };
3 _ = x;
4}
5
6// error
7// backend=stage2
8// target=native
9//
10// :2:49: error: expected 3 vector elements; found 4
test/cases/compile_errors/while_expected_error_union_got_bool.zig created+10
......@@ -0,0 +1,10 @@
1export fn foo() void {
2 while (bar()) |x| {_ = x;} else |err| {_ = err;}
3}
4fn bar() bool { return true; }
5
6// error
7// backend=stage2
8// target=native
9//
10// :2:15: error: expected error union type, found 'bool'
test/cases/compile_errors/while_expected_error_union_got_optional.zig created+10
......@@ -0,0 +1,10 @@
1export fn foo() void {
2 while (bar()) |x| {_ = x;} else |err| {_ = err;}
3}
4fn bar() ?i32 { return 1; }
5
6// error
7// backend=stage2
8// target=native
9//
10// :2:15: error: expected error union type, found '?i32'
test/cases/compile_errors/while_expected_optional_got_bool.zig created+10
......@@ -0,0 +1,10 @@
1export fn foo() void {
2 while (bar()) |x| {_ = x;}
3}
4fn bar() bool { return true; }
5
6// error
7// backend=stage2
8// target=native
9//
10// :2:15: error: expected optional type, found 'bool'
test/cases/compile_errors/while_expected_optional_got_error_union.zig created+10
......@@ -0,0 +1,10 @@
1export fn foo() void {
2 while (bar()) |x| {_ = x;}
3}
4fn bar() anyerror!i32 { return 1; }
5
6// error
7// backend=stage2
8// target=native
9//
10// :2:15: error: expected optional type, found 'anyerror!i32'
test/cases/compile_errors/while_loop_body_expression_ignored.zig created+43
......@@ -0,0 +1,43 @@
1fn returns() usize {
2 return 2;
3}
4export fn f1() void {
5 while (true) returns();
6}
7export fn f2() void {
8 var x: ?i32 = null;
9 while (x) |_| returns();
10}
11export fn f3() void {
12 var x: anyerror!i32 = error.Bad;
13 while (x) |_| returns() else |_| unreachable;
14}
15export fn f4() void {
16 var a = true;
17 while (a) {} else true;
18}
19export fn f5() void {
20 var a = true;
21 const foo = while (a) returns() else true;
22 _ = foo;
23}
24
25// error
26// backend=stage2
27// target=native
28//
29// :5:25: error: value of type 'usize' ignored
30// :5:25: note: all non-void values must be used
31// :5:25: note: this error can be suppressed by assigning the value to '_'
32// :9:26: error: value of type 'usize' ignored
33// :9:26: note: all non-void values must be used
34// :9:26: note: this error can be suppressed by assigning the value to '_'
35// :13:26: error: value of type 'usize' ignored
36// :13:26: note: all non-void values must be used
37// :13:26: note: this error can be suppressed by assigning the value to '_'
38// :17:23: error: value of type 'bool' ignored
39// :17:23: note: all non-void values must be used
40// :17:23: note: this error can be suppressed by assigning the value to '_'
41// :21:34: error: value of type 'usize' ignored
42// :21:34: note: all non-void values must be used
43// :21:34: note: this error can be suppressed by assigning the value to '_'
test/cases/safety/array slice sentinel mismatch non-scalar.zig created+21
......@@ -0,0 +1,21 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "sentinel mismatch: expected tmp.main.S{ .a = 1 }, found tmp.main.S{ .a = 2 }")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10
11pub fn main() !void {
12 const S = struct { a: u32 };
13 var arr = [_]S{ .{ .a = 1 }, .{ .a = 2 } };
14 var s = arr[0..1 :.{ .a = 1 }];
15 _ = s;
16 return error.TestFailed;
17}
18
19// run
20// backend=llvm
21// target=native
test/cases/safety/array slice sentinel mismatch vector.zig created+19
......@@ -0,0 +1,19 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "sentinel mismatch: expected { 0, 0 }, found { 4, 4 }")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10
11pub fn main() !void {
12 var buf: [4]@Vector(2, u32) = .{ .{ 1, 1 }, .{ 2, 2 }, .{ 3, 3 }, .{ 4, 4 } };
13 const slice = buf[0..3 :.{ 0, 0 }];
14 _ = slice;
15 return error.TestFailed;
16}
17// run
18// backend=llvm
19// target=native
test/cases/safety/array slice sentinel mismatch.zig +4-3
......@@ -2,17 +2,18 @@ const std = @import("std");
22
33pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
44 _ = stack_trace;
5 if (std.mem.eql(u8, message, "sentinel mismatch")) {
5 if (std.mem.eql(u8, message, "sentinel mismatch: expected 0, found 4")) {
66 std.process.exit(0);
77 }
88 std.process.exit(1);
99}
10
1011pub fn main() !void {
11 var buf: [4]u8 = undefined;
12 var buf: [4]u8 = .{ 1, 2, 3, 4 };
1213 const slice = buf[0..3 :0];
1314 _ = slice;
1415 return error.TestFailed;
1516}
1617// run
17// backend=stage1
18// backend=llvm
1819// target=native