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 @@...@@ -284,6 +284,16 @@
284 overflow-x: hidden;284 overflow-x: hidden;
285 }285 }
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
287 /* help dialog */297 /* help dialog */
288 .help-modal {298 .help-modal {
289 display: flex;299 display: flex;
lib/docs/main.js+45-16
...@@ -618,15 +618,18 @@ var zigAnalysis;...@@ -618,15 +618,18 @@ var zigAnalysis;
618 for (let i = 0; i < fields.length; i += 1) {618 for (let i = 0; i < fields.length; i += 1) {
619 let field = fields[i];619 let field = fields[i];
620 let fieldNode = zigAnalysis.astNodes[field];620 let fieldNode = zigAnalysis.astNodes[field];
621 let docs = fieldNode.docs;
621 if (fieldNode.docs == null) {622 if (fieldNode.docs == null) {
622 continue;623 continue;
623 }624 }
625 let docsNonEmpty = docs !== "";
624 let divDom = domListParams.children[domIndex];626 let divDom = domListParams.children[domIndex];
625 domIndex += 1;627 domIndex += 1;
626628
627629
628 let value = typeObj.params[i];630 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)) + ": ";
630 if (isVarArgs && i === typeObj.params.length - 1) {633 if (isVarArgs && i === typeObj.params.length - 1) {
631 html += '...';634 html += '...';
632 } else {635 } else {
...@@ -636,9 +639,8 @@ var zigAnalysis;...@@ -636,9 +639,8 @@ var zigAnalysis;
636639
637 html += ',</pre>';640 html += ',</pre>';
638641
639 let docs = fieldNode.docs;642 if (docsNonEmpty) {
640 if (docs != null) {643 html += '<div class="fieldDocs">' + markdown(docs) + '</div>';
641 html += markdown(docs);
642 }644 }
643 divDom.innerHTML = html;645 divDom.innerHTML = html;
644 }646 }
...@@ -824,7 +826,13 @@ var zigAnalysis;...@@ -824,7 +826,13 @@ var zigAnalysis;
824 826
825 function exprName(expr, opts) {827 function exprName(expr, opts) {
826 switch (Object.keys(expr)[0]) {828 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 }
828 case "&": {836 case "&": {
829 return "&" + exprName(zigAnalysis.exprs[expr["&"]]);837 return "&" + exprName(zigAnalysis.exprs[expr["&"]]);
830 }838 }
...@@ -929,8 +937,7 @@ var zigAnalysis;...@@ -929,8 +937,7 @@ var zigAnalysis;
929 return exprName(switchIndex, opts);937 return exprName(switchIndex, opts);
930 }938 }
931 case "refPath" : {939 case "refPath" : {
932 const declRef = expr.refPath[0].declRef;940 let name = exprName(expr.refPath[0]);
933 let name = zigAnalysis.decls[declRef].name;
934 for (let i = 1; i < expr.refPath.length; i++) {941 for (let i = 1; i < expr.refPath.length; i++) {
935 let component = undefined;942 let component = undefined;
936 if ("string" in expr.refPath[i]) {943 if ("string" in expr.refPath[i]) {
...@@ -1402,6 +1409,11 @@ var zigAnalysis;...@@ -1402,6 +1409,11 @@ var zigAnalysis;
1402 let payloadHtml = "@TypeOf(" + exprName(typeRefArg, {wantHtml: true, wantLink:true}) + ")";1409 let payloadHtml = "@TypeOf(" + exprName(typeRefArg, {wantHtml: true, wantLink:true}) + ")";
1403 return payloadHtml;1410 return payloadHtml;
1404 }1411 }
1412 case "typeInfo": {
1413 const typeRefArg = zigAnalysis.exprs[expr.typeInfo];
1414 let payloadHtml = "@typeInfo(" + exprName(typeRefArg, {wantHtml: true, wantLink:true}) + ")";
1415 return payloadHtml;
1416 }
1405 case "null": {1417 case "null": {
1406 return "null";1418 return "null";
1407 }1419 }
...@@ -1693,6 +1705,12 @@ var zigAnalysis;...@@ -1693,6 +1705,12 @@ var zigAnalysis;
1693 let rhs = exprName(errUnionObj.rhs, opts);1705 let rhs = exprName(errUnionObj.rhs, opts);
1694 return lhs + "!" + rhs;1706 return lhs + "!" + rhs;
1695 }1707 }
1708 case typeKinds.InferredErrorUnion:
1709 {
1710 let errUnionObj = (typeObj);
1711 let payload = exprName(errUnionObj.payload, opts);
1712 return "!" + payload;
1713 }
1696 case typeKinds.Fn:1714 case typeKinds.Fn:
1697 {1715 {
1698 let fnObj = (typeObj);1716 let fnObj = (typeObj);
...@@ -2270,8 +2288,11 @@ var zigAnalysis;...@@ -2270,8 +2288,11 @@ var zigAnalysis;
2270 let fieldNode = zigAnalysis.astNodes[containerNode.fields[i]];2288 let fieldNode = zigAnalysis.astNodes[containerNode.fields[i]];
2271 let divDom = domListFields.children[i];2289 let divDom = domListFields.children[i];
2272 let fieldName = (fieldNode.name);2290 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
2276 if (container.kind === typeKinds.Enum) {2297 if (container.kind === typeKinds.Enum) {
2277 html += ' = <span class="tok-number">' + fieldName + '</span>';2298 html += ' = <span class="tok-number">' + fieldName + '</span>';
...@@ -2289,9 +2310,8 @@ var zigAnalysis;...@@ -2289,9 +2310,8 @@ var zigAnalysis;
22892310
2290 html += ',</pre></div>';2311 html += ',</pre></div>';
22912312
2292 let docs = fieldNode.docs;2313 if (docsNonEmpty) {
2293 if (docs != null) {2314 html += '<div class="fieldDocs">' + markdown(docs) + '</div>';
2294 html += markdown(docs);
2295 }2315 }
2296 divDom.innerHTML = html;2316 divDom.innerHTML = html;
2297 }2317 }
...@@ -2639,14 +2659,23 @@ var zigAnalysis;...@@ -2639,14 +2659,23 @@ var zigAnalysis;
2639 });2659 });
2640 }2660 }
26412661
2642 2662
2643 function shortDescMarkdown(docs) {2663 function shortDescMarkdown(docs) {
2644 let parts = docs.trim().split("\n");2664 const trimmed_docs = docs.trim();
2645 let firstLine = parts[0];2665 let index = trimmed_docs.indexOf('.');
2646 return markdown(firstLine);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);
2647 }2676 }
26482677
2649 2678
2650 function markdown(input) {2679 function markdown(input) {
2651 const raw_lines = input.split('\n'); // zig allows no '\r', so we don't need to split on CR2680 const raw_lines = input.split('\n'); // zig allows no '\r', so we don't need to split on CR
2652 2681
lib/init-exe/src/main.zig+19-6
...@@ -1,11 +1,24 @@...@@ -1,11 +1,24 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn main() anyerror!void {3pub fn main() !void {
4 // Note that info level log messages are by default printed only in Debug4 // Prints to stderr (it's a shortcut based on `std.io.getStdErr()`)
5 // and ReleaseSafe build modes.5 std.debug.print("All your {s} are belong to us.\n", .{"codebase"});
6 std.log.info("All your codebase are belong to us.", .{});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!
7}17}
818
9test "basic test" {19test "simple test" {
10 try std.testing.expectEqual(10, 3 + 7);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());
11}24}
lib/std/build/CheckObjectStep.zig+47-42
...@@ -283,7 +283,14 @@ fn make(step: *Step) !void {...@@ -283,7 +283,14 @@ fn make(step: *Step) !void {
283283
284 const gpa = self.builder.allocator;284 const gpa = self.builder.allocator;
285 const src_path = self.source.getPath(self.builder);285 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
288 const output = switch (self.obj_format) {295 const output = switch (self.obj_format) {
289 .macho => try MachODumper.parseAndDump(contents, .{296 .macho => try MachODumper.parseAndDump(contents, .{
...@@ -370,9 +377,10 @@ const Opts = struct {...@@ -370,9 +377,10 @@ const Opts = struct {
370};377};
371378
372const MachODumper = struct {379const MachODumper = struct {
380 const LoadCommandIterator = macho.LoadCommandIterator;
373 const symtab_label = "symtab";381 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 {
376 const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator384 const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator
377 var stream = std.io.fixedBufferStream(bytes);385 var stream = std.io.fixedBufferStream(bytes);
378 const reader = stream.reader();386 const reader = stream.reader();
...@@ -385,55 +393,54 @@ const MachODumper = struct {...@@ -385,55 +393,54 @@ const MachODumper = struct {
385 var output = std.ArrayList(u8).init(gpa);393 var output = std.ArrayList(u8).init(gpa);
386 const writer = output.writer();394 const writer = output.writer();
387395
388 var load_commands = std.ArrayList(macho.LoadCommand).init(gpa);396 var symtab: []const macho.nlist_64 = undefined;
389 try load_commands.ensureTotalCapacity(hdr.ncmds);397 var strtab: []const u8 = undefined;
390398 var sections = std.ArrayList(macho.section_64).init(gpa);
391 var sections = std.ArrayList(struct { seg: u16, sect: u16 }).init(gpa);399 var imports = std.ArrayList([]const u8).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);
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| {
400 switch (cmd.cmd()) {407 switch (cmd.cmd()) {
401 .SEGMENT_64 => {408 .SEGMENT_64 => {
402 const seg = cmd.segment;409 const seg = cmd.cast(macho.segment_command_64).?;
403 for (seg.sections.items) |_, j| {410 try sections.ensureUnusedCapacity(seg.nsects);
404 try sections.append(.{ .seg = i, .sect = @intCast(u16, j) });411 for (cmd.getSections()) |sect| {
412 sections.appendAssumeCapacity(sect);
405 }413 }
406 },414 },
407 .SYMTAB => {415 .SYMTAB => if (opts.dump_symtab) {
408 symtab_cmd = i;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];
409 },422 },
410 .LOAD_DYLIB,423 .LOAD_DYLIB,
411 .LOAD_WEAK_DYLIB,424 .LOAD_WEAK_DYLIB,
412 .REEXPORT_DYLIB,425 .REEXPORT_DYLIB,
413 => {426 => {
414 try imports.append(i);427 try imports.append(cmd.getDylibPathName());
415 },428 },
416 else => {},429 else => {},
417 }430 }
418431
419 try dumpLoadCommand(cmd, i, writer);432 try dumpLoadCommand(cmd, i, writer);
420 try writer.writeByte('\n');433 try writer.writeByte('\n');
434
435 i += 1;
421 }436 }
422437
423 if (opts.dump_symtab) {438 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
430 for (symtab) |sym| {439 for (symtab) |sym| {
431 if (sym.stab()) continue;440 if (sym.stab()) continue;
432 const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0);441 const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0);
433 if (sym.sect()) {442 if (sym.sect()) {
434 const map = sections.items[sym.n_sect - 1];443 const sect = sections.items[sym.n_sect - 1];
435 const seg = load_commands.items[map.seg].segment;
436 const sect = seg.sections.items[map.sect];
437 try writer.print("{x} ({s},{s})", .{444 try writer.print("{x} ({s},{s})", .{
438 sym.n_value,445 sym.n_value,
439 sect.segName(),446 sect.segName(),
...@@ -455,9 +462,7 @@ const MachODumper = struct {...@@ -455,9 +462,7 @@ const MachODumper = struct {
455 break :blk "flat lookup";462 break :blk "flat lookup";
456 unreachable;463 unreachable;
457 }464 }
458 const import_id = imports.items[@bitCast(u16, ordinal) - 1];465 const full_path = imports.items[@bitCast(u16, ordinal) - 1];
459 const import = load_commands.items[import_id].dylib;
460 const full_path = mem.sliceTo(import.data, 0);
461 const basename = fs.path.basename(full_path);466 const basename = fs.path.basename(full_path);
462 assert(basename.len > 0);467 assert(basename.len > 0);
463 const ext = mem.lastIndexOfScalar(u8, basename, '.') orelse basename.len;468 const ext = mem.lastIndexOfScalar(u8, basename, '.') orelse basename.len;
...@@ -481,7 +486,7 @@ const MachODumper = struct {...@@ -481,7 +486,7 @@ const MachODumper = struct {
481 return output.toOwnedSlice();486 return output.toOwnedSlice();
482 }487 }
483488
484 fn dumpLoadCommand(lc: macho.LoadCommand, index: u16, writer: anytype) !void {489 fn dumpLoadCommand(lc: macho.LoadCommandIterator.LoadCommand, index: usize, writer: anytype) !void {
485 // print header first490 // print header first
486 try writer.print(491 try writer.print(
487 \\LC {d}492 \\LC {d}
...@@ -491,8 +496,7 @@ const MachODumper = struct {...@@ -491,8 +496,7 @@ const MachODumper = struct {
491496
492 switch (lc.cmd()) {497 switch (lc.cmd()) {
493 .SEGMENT_64 => {498 .SEGMENT_64 => {
494 // TODO dump section headers499 const seg = lc.cast(macho.segment_command_64).?;
495 const seg = lc.segment.inner;
496 try writer.writeByte('\n');500 try writer.writeByte('\n');
497 try writer.print(501 try writer.print(
498 \\segname {s}502 \\segname {s}
...@@ -508,7 +512,7 @@ const MachODumper = struct {...@@ -508,7 +512,7 @@ const MachODumper = struct {
508 seg.filesize,512 seg.filesize,
509 });513 });
510514
511 for (lc.segment.sections.items) |sect| {515 for (lc.getSections()) |sect| {
512 try writer.writeByte('\n');516 try writer.writeByte('\n');
513 try writer.print(517 try writer.print(
514 \\sectname {s}518 \\sectname {s}
...@@ -531,7 +535,7 @@ const MachODumper = struct {...@@ -531,7 +535,7 @@ const MachODumper = struct {
531 .LOAD_WEAK_DYLIB,535 .LOAD_WEAK_DYLIB,
532 .REEXPORT_DYLIB,536 .REEXPORT_DYLIB,
533 => {537 => {
534 const dylib = lc.dylib.inner.dylib;538 const dylib = lc.cast(macho.dylib_command).?;
535 try writer.writeByte('\n');539 try writer.writeByte('\n');
536 try writer.print(540 try writer.print(
537 \\name {s}541 \\name {s}
...@@ -539,19 +543,20 @@ const MachODumper = struct {...@@ -539,19 +543,20 @@ const MachODumper = struct {
539 \\current version {x}543 \\current version {x}
540 \\compatibility version {x}544 \\compatibility version {x}
541 , .{545 , .{
542 mem.sliceTo(lc.dylib.data, 0),546 lc.getDylibPathName(),
543 dylib.timestamp,547 dylib.dylib.timestamp,
544 dylib.current_version,548 dylib.dylib.current_version,
545 dylib.compatibility_version,549 dylib.dylib.compatibility_version,
546 });550 });
547 },551 },
548552
549 .MAIN => {553 .MAIN => {
554 const main = lc.cast(macho.entry_point_command).?;
550 try writer.writeByte('\n');555 try writer.writeByte('\n');
551 try writer.print(556 try writer.print(
552 \\entryoff {x}557 \\entryoff {x}
553 \\stacksize {x}558 \\stacksize {x}
554 , .{ lc.main.entryoff, lc.main.stacksize });559 , .{ main.entryoff, main.stacksize });
555 },560 },
556561
557 .RPATH => {562 .RPATH => {
...@@ -559,7 +564,7 @@ const MachODumper = struct {...@@ -559,7 +564,7 @@ const MachODumper = struct {
559 try writer.print(564 try writer.print(
560 \\path {s}565 \\path {s}
561 , .{566 , .{
562 mem.sliceTo(lc.rpath.data, 0),567 lc.getRpathPathName(),
563 });568 });
564 },569 },
565570
lib/std/builtin.zig+11
...@@ -846,6 +846,17 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn...@@ -846,6 +846,17 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn
846 }846 }
847}847}
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
849pub fn panicUnwrapError(st: ?*StackTrace, err: anyerror) noreturn {860pub fn panicUnwrapError(st: ?*StackTrace, err: anyerror) noreturn {
850 @setCold(true);861 @setCold(true);
851 std.debug.panicExtra(st, "attempt to unwrap error: {s}", .{@errorName(err)});862 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 {...@@ -229,15 +229,14 @@ pub const Ed25519 = struct {
229 blind_secret_key: BlindSecretKey,229 blind_secret_key: BlindSecretKey,
230 };230 };
231231
232 /// Blind an existing key pair with a blinding seed.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) !BlindKeyPair {233 pub fn blind(key_pair: Ed25519.KeyPair, blind_seed: [blind_seed_length]u8, ctx: []const u8) !BlindKeyPair {
234 var h: [Sha512.digest_length]u8 = undefined;234 var h: [Sha512.digest_length]u8 = undefined;
235 Sha512.hash(key_pair.secret_key[0..32], &h, .{});235 Sha512.hash(key_pair.secret_key[0..32], &h, .{});
236 Curve.scalar.clamp(h[0..32]);236 Curve.scalar.clamp(h[0..32]);
237 const scalar = Curve.scalar.reduce(h[0..32].*);237 const scalar = Curve.scalar.reduce(h[0..32].*);
238238
239 var blind_h: [Sha512.digest_length]u8 = undefined;239 const blind_h = blindCtx(blind_seed, ctx);
240 Sha512.hash(blind_seed[0..], &blind_h, .{});
241 const blind_factor = Curve.scalar.reduce(blind_h[0..32].*);240 const blind_factor = Curve.scalar.reduce(blind_h[0..32].*);
242241
243 const blind_scalar = Curve.scalar.mul(scalar, blind_factor);242 const blind_scalar = Curve.scalar.mul(scalar, blind_factor);
...@@ -259,9 +258,8 @@ pub const Ed25519 = struct {...@@ -259,9 +258,8 @@ pub const Ed25519 = struct {
259 }258 }
260259
261 /// Recover a public key from a blind version of it.260 /// 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 {261 pub fn unblindPublicKey(blind_public_key: [public_length]u8, blind_seed: [blind_seed_length]u8, ctx: []const u8) ![public_length]u8 {
263 var blind_h: [Sha512.digest_length]u8 = undefined;262 const blind_h = blindCtx(blind_seed, ctx);
264 Sha512.hash(&blind_seed, &blind_h, .{});
265 const inv_blind_factor = Scalar.fromBytes(blind_h[0..32].*).invert().toBytes();263 const inv_blind_factor = Scalar.fromBytes(blind_h[0..32].*).invert().toBytes();
266 const public_key = try (try Curve.fromBytes(blind_public_key)).mul(inv_blind_factor);264 const public_key = try (try Curve.fromBytes(blind_public_key)).mul(inv_blind_factor);
267 return public_key.toBytes();265 return public_key.toBytes();
...@@ -297,6 +295,17 @@ pub const Ed25519 = struct {...@@ -297,6 +295,17 @@ pub const Ed25519 = struct {
297 mem.copy(u8, sig[32..], s[0..]);295 mem.copy(u8, sig[32..], s[0..]);
298 return sig;296 return sig;
299 }297 }
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 }
300 };309 };
301};310};
302311
...@@ -458,7 +467,7 @@ test "ed25519 with blind keys" {...@@ -458,7 +467,7 @@ test "ed25519 with blind keys" {
458 crypto.random.bytes(&blind);467 crypto.random.bytes(&blind);
459468
460 // Blind the key pair469 // Blind the key pair
461 const blind_kp = try BlindKeySignatures.blind(kp, blind);470 const blind_kp = try BlindKeySignatures.blind(kp, blind, "ctx");
462471
463 // Sign a message and check that it can be verified with the blind public key472 // Sign a message and check that it can be verified with the blind public key
464 const msg = "test";473 const msg = "test";
...@@ -466,6 +475,6 @@ test "ed25519 with blind keys" {...@@ -466,6 +475,6 @@ test "ed25519 with blind keys" {
466 try Ed25519.verify(sig, msg, blind_kp.blind_public_key);475 try Ed25519.verify(sig, msg, blind_kp.blind_public_key);
467476
468 // Unblind the public key477 // 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");
470 try std.testing.expectEqualSlices(u8, &pk, &kp.public_key);479 try std.testing.expectEqualSlices(u8, &pk, &kp.public_key);
471}480}
lib/std/fs.zig+29-2
...@@ -595,6 +595,19 @@ pub const IterableDir = struct {...@@ -595,6 +595,19 @@ pub const IterableDir = struct {
595 /// Memory such as file names referenced in this returned entry becomes invalid595 /// Memory such as file names referenced in this returned entry becomes invalid
596 /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.596 /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.
597 pub fn next(self: *Self) Error!?Entry {597 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 {
598 start_over: while (true) {611 start_over: while (true) {
599 if (self.index >= self.end_index) {612 if (self.index >= self.end_index) {
600 if (self.first_iter) {613 if (self.first_iter) {
...@@ -607,7 +620,7 @@ pub const IterableDir = struct {...@@ -607,7 +620,7 @@ pub const IterableDir = struct {
607 .BADF => unreachable, // Dir is invalid or was opened without iteration ability620 .BADF => unreachable, // Dir is invalid or was opened without iteration ability
608 .FAULT => unreachable,621 .FAULT => unreachable,
609 .NOTDIR => unreachable,622 .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.
611 .INVAL => return error.Unexpected, // Linux may in some cases return EINVAL when reading /proc/$PID/net.624 .INVAL => return error.Unexpected, // Linux may in some cases return EINVAL when reading /proc/$PID/net.
612 else => |err| return os.unexpectedErrno(err),625 else => |err| return os.unexpectedErrno(err),
613 }626 }
...@@ -729,6 +742,20 @@ pub const IterableDir = struct {...@@ -729,6 +742,20 @@ pub const IterableDir = struct {
729 /// Memory such as file names referenced in this returned entry becomes invalid742 /// Memory such as file names referenced in this returned entry becomes invalid
730 /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.743 /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.
731 pub fn next(self: *Self) Error!?Entry {744 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 {
732 // We intentinally use fd_readdir even when linked with libc,759 // We intentinally use fd_readdir even when linked with libc,
733 // since its implementation is exactly the same as below,760 // since its implementation is exactly the same as below,
734 // and we avoid the code complexity here.761 // and we avoid the code complexity here.
...@@ -742,7 +769,7 @@ pub const IterableDir = struct {...@@ -742,7 +769,7 @@ pub const IterableDir = struct {
742 .FAULT => unreachable,769 .FAULT => unreachable,
743 .NOTDIR => unreachable,770 .NOTDIR => unreachable,
744 .INVAL => unreachable,771 .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.
746 .NOTCAPABLE => return error.AccessDenied,773 .NOTCAPABLE => return error.AccessDenied,
747 else => |err| return os.unexpectedErrno(err),774 else => |err| return os.unexpectedErrno(err),
748 }775 }
lib/std/fs/test.zig+5
...@@ -241,6 +241,11 @@ test "Dir.Iterator but dir is deleted during iteration" {...@@ -241,6 +241,11 @@ test "Dir.Iterator but dir is deleted during iteration" {
241 // Now, when we try to iterate, the next call should return null immediately.241 // Now, when we try to iterate, the next call should return null immediately.
242 const entry = try iterator.next();242 const entry = try iterator.next();
243 try std.testing.expect(entry == null);243 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 }
244}249}
245250
246fn entryEql(lhs: IterableDir.Entry, rhs: IterableDir.Entry) bool {251fn entryEql(lhs: IterableDir.Entry, rhs: IterableDir.Entry) bool {
lib/std/macho.zig+55-409
...@@ -780,7 +780,7 @@ pub const section_64 = extern struct {...@@ -780,7 +780,7 @@ pub const section_64 = extern struct {
780 return parseName(&sect.segname);780 return parseName(&sect.segname);
781 }781 }
782782
783 pub fn type_(sect: section_64) u8 {783 pub fn @"type"(sect: section_64) u8 {
784 return @truncate(u8, sect.flags & 0xff);784 return @truncate(u8, sect.flags & 0xff);
785 }785 }
786786
...@@ -793,6 +793,11 @@ pub const section_64 = extern struct {...@@ -793,6 +793,11 @@ pub const section_64 = extern struct {
793 return attr & S_ATTR_PURE_INSTRUCTIONS != 0 or attr & S_ATTR_SOME_INSTRUCTIONS != 0;793 return attr & S_ATTR_PURE_INSTRUCTIONS != 0 or attr & S_ATTR_SOME_INSTRUCTIONS != 0;
794 }794 }
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
796 pub fn isDebug(sect: section_64) bool {801 pub fn isDebug(sect: section_64) bool {
797 return sect.attrs() & S_ATTR_DEBUG != 0;802 return sect.attrs() & S_ATTR_DEBUG != 0;
798 }803 }
...@@ -1835,429 +1840,70 @@ pub const data_in_code_entry = extern struct {...@@ -1835,429 +1840,70 @@ pub const data_in_code_entry = extern struct {
1835 kind: u16,1840 kind: u16,
1836};1841};
18371842
1838/// A Zig wrapper for all known MachO load commands.1843pub const LoadCommandIterator = struct {
1839/// Provides interface to read and write the load command data to a buffer.1844 ncmds: usize,
1840pub const LoadCommand = union(enum) {1845 buffer: []align(@alignOf(u64)) const u8,
1841 segment: SegmentCommand,1846 index: usize = 0,
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) = .{},
20091847
2010 pub fn read(allocator: Allocator, reader: anytype) !SegmentCommand {1848 pub const LoadCommand = struct {
2011 const inner = try reader.readStruct(segment_command_64);1849 hdr: load_command,
2012 var segment = SegmentCommand{1850 data: []const u8,
2013 .inner = inner,
2014 };
2015 try segment.sections.ensureTotalCapacityPrecise(allocator, inner.nsects);
20161851
2017 var i: usize = 0;1852 pub fn cmd(lc: LoadCommand) LC {
2018 while (i < inner.nsects) : (i += 1) {1853 return lc.hdr.cmd;
2019 const sect = try reader.readStruct(section_64);
2020 segment.sections.appendAssumeCapacity(sect);
2021 }1854 }
20221855
2023 return segment;1856 pub fn cmdsize(lc: LoadCommand) u32 {
2024 }1857 return lc.hdr.cmdsize;
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;
2044 }1858 }
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.1860 pub fn cast(lc: LoadCommand, comptime Cmd: type) ?Cmd {
2054pub fn GenericCommandWithData(comptime Cmd: type) type {1861 if (lc.data.len < @sizeOf(Cmd)) return null;
2055 return struct {1862 return @ptrCast(*const Cmd, @alignCast(@alignOf(Cmd), &lc.data[0])).*;
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 };
2071 }1863 }
20721864
2073 pub fn write(self: Self, writer: anytype) !void {1865 /// Asserts LoadCommand is of type segment_command_64.
2074 try writer.writeAll(mem.asBytes(&self.inner));1866 pub fn getSections(lc: LoadCommand) []const section_64 {
2075 try writer.writeAll(self.data);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;
2076 }1875 }
20771876
2078 pub fn deinit(self: *Self, allocator: Allocator) void {1877 /// Asserts LoadCommand is of type dylib_command.
2079 allocator.free(self.data);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);
2080 }1882 }
20811883
2082 pub fn eql(self: Self, other: Self) bool {1884 /// Asserts LoadCommand is of type rpath_command.
2083 if (!meta.eql(self.inner, other.inner)) return false;1885 pub fn getRpathPathName(lc: LoadCommand) []const u8 {
2084 return mem.eql(u8, self.data, other.data);1886 const rpath_lc = lc.cast(rpath_command).?;
1887 const data = lc.data[rpath_lc.path..];
1888 return mem.sliceTo(data, 0);
2085 }1889 }
2086 };1890 };
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" {1892 pub fn next(it: *LoadCommandIterator) ?LoadCommand {
2201 // TODO compiling for macOS from big-endian arch1893 if (it.index >= it.ncmds) return null;
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}
22411894
2242test "read-write C struct command" {1895 const hdr = @ptrCast(
2243 // TODO compiling for macOS from big-endian arch1896 *const load_command,
2244 if (builtin.target.cpu.arch.endian() != .Little) return error.SkipZigTest;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;1904 it.buffer = @alignCast(@alignOf(u64), it.buffer[hdr.cmdsize..]);
2247 const in_buffer = &[_]u8{1905 it.index += 1;
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 });
22601906
2261 var out_buffer: [in_buffer.len]u8 = undefined;1907 return cmd;
2262 try testWrite(&out_buffer, LoadCommand{ .main = cmd }, in_buffer);1908 }
2263}1909};
src/AstGen.zig+113-24
...@@ -768,12 +768,12 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr...@@ -768,12 +768,12 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
768 .if_simple => return ifExpr(gz, scope, rl.br(), node, tree.ifSimple(node)),768 .if_simple => return ifExpr(gz, scope, rl.br(), node, tree.ifSimple(node)),
769 .@"if" => return ifExpr(gz, scope, rl.br(), node, tree.ifFull(node)),769 .@"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)),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)),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)),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)),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)),776 .@"for" => return forExpr(gz, scope, rl.br(), node, tree.forFull(node), false),
777777
778 .slice_open => {778 .slice_open => {
779 const lhs = try expr(gz, scope, .ref, node_datas[node].lhs);779 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...@@ -1899,6 +1899,17 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
1899 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,1899 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
1900 .namespace => break,1900 .namespace => break,
1901 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,1901 .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 },
1902 .top => unreachable,1913 .top => unreachable,
1903 }1914 }
1904 }1915 }
...@@ -1958,6 +1969,17 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)...@@ -1958,6 +1969,17 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)
1958 try unusedResultDeferExpr(parent_gz, defer_scope, defer_scope.parent, expr_node);1969 try unusedResultDeferExpr(parent_gz, defer_scope, defer_scope.parent, expr_node);
1959 },1970 },
1960 .defer_error => scope = scope.cast(Scope.Defer).?.parent,1971 .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 },
1961 .namespace => break,1983 .namespace => break,
1962 .top => unreachable,1984 .top => unreachable,
1963 }1985 }
...@@ -2022,6 +2044,7 @@ fn checkLabelRedefinition(astgen: *AstGen, parent_scope: *Scope, label: Ast.Toke...@@ -2022,6 +2044,7 @@ fn checkLabelRedefinition(astgen: *AstGen, parent_scope: *Scope, label: Ast.Toke
2022 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,2044 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
2023 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,2045 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
2024 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,2046 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
2047 .defer_gen => scope = scope.cast(Scope.DeferGen).?.parent,
2025 .namespace => break,2048 .namespace => break,
2026 .top => unreachable,2049 .top => unreachable,
2027 }2050 }
...@@ -2129,6 +2152,7 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod...@@ -2129,6 +2152,7 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
2129 const astgen = gz.astgen;2152 const astgen = gz.astgen;
2130 const tree = astgen.tree;2153 const tree = astgen.tree;
2131 const node_tags = tree.nodes.items(.tag);2154 const node_tags = tree.nodes.items(.tag);
2155 const node_data = tree.nodes.items(.data);
21322156
2133 if (statements.len == 0) return;2157 if (statements.len == 0) return;
21342158
...@@ -2155,8 +2179,10 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod...@@ -2155,8 +2179,10 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
2155 },2179 },
2156 );2180 );
2157 }2181 }
2158 switch (node_tags[statement]) {2182 var inner_node = statement;
2159 // zig fmt: off2183 while (true) {
2184 switch (node_tags[inner_node]) {
2185 // zig fmt: off
2160 .global_var_decl => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.globalVarDecl(statement)),2186 .global_var_decl => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.globalVarDecl(statement)),
2161 .local_var_decl => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.localVarDecl(statement)),2187 .local_var_decl => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.localVarDecl(statement)),
2162 .simple_var_decl => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.simpleVarDecl(statement)),2188 .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...@@ -2181,9 +2207,23 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
2181 .assign_add_wrap => try assignOp(gz, scope, statement, .addwrap),2207 .assign_add_wrap => try assignOp(gz, scope, statement, .addwrap),
2182 .assign_mul => try assignOp(gz, scope, statement, .mul),2208 .assign_mul => try assignOp(gz, scope, statement, .mul),
2183 .assign_mul_wrap => try assignOp(gz, scope, statement, .mulwrap),2209 .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),
2186 // zig fmt: on2224 // zig fmt: on
2225 }
2226 break;
2187 }2227 }
2188 }2228 }
21892229
...@@ -2206,7 +2246,13 @@ fn unusedResultDeferExpr(gz: *GenZir, defer_scope: *Scope.Defer, expr_scope: *Sc...@@ -2206,7 +2246,13 @@ fn unusedResultDeferExpr(gz: *GenZir, defer_scope: *Scope.Defer, expr_scope: *Sc
2206 astgen.source_offset = defer_scope.source_offset;2246 astgen.source_offset = defer_scope.source_offset;
2207 astgen.source_line = defer_scope.source_line;2247 astgen.source_line = defer_scope.source_line;
2208 astgen.source_column = defer_scope.source_column;2248 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);
2210}2256}
22112257
2212/// Returns AST source node of the thing that is noreturn if the statement is2258/// 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...@@ -2216,6 +2262,10 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
2216 // We need to emit an error if the result is not `noreturn` or `void`, but2262 // We need to emit an error if the result is not `noreturn` or `void`, but
2217 // we want to avoid adding the ZIR instruction if possible for performance.2263 // we want to avoid adding the ZIR instruction if possible for performance.
2218 const maybe_unused_result = try expr(gz, scope, .none, statement);2264 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 {
2219 var noreturn_src_node: Ast.Node.Index = 0;2269 var noreturn_src_node: Ast.Node.Index = 0;
2220 const elide_check = if (refToIndex(maybe_unused_result)) |inst| b: {2270 const elide_check = if (refToIndex(maybe_unused_result)) |inst| b: {
2221 // Note that this array becomes invalid after appending more items to it2271 // 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...@@ -2553,6 +2603,7 @@ fn countDefers(astgen: *AstGen, outer_scope: *Scope, inner_scope: *Scope) struct
2553 .gen_zir => scope = scope.cast(GenZir).?.parent,2603 .gen_zir => scope = scope.cast(GenZir).?.parent,
2554 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,2604 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
2555 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,2605 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
2606 .defer_gen => scope = scope.cast(Scope.DeferGen).?.parent,
2556 .defer_normal => {2607 .defer_normal => {
2557 const defer_scope = scope.cast(Scope.Defer).?;2608 const defer_scope = scope.cast(Scope.Defer).?;
2558 scope = defer_scope.parent;2609 scope = defer_scope.parent;
...@@ -2602,6 +2653,7 @@ fn genDefers(...@@ -2602,6 +2653,7 @@ fn genDefers(
2602 .gen_zir => scope = scope.cast(GenZir).?.parent,2653 .gen_zir => scope = scope.cast(GenZir).?.parent,
2603 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,2654 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
2604 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,2655 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
2656 .defer_gen => scope = scope.cast(Scope.DeferGen).?.parent,
2605 .defer_normal => {2657 .defer_normal => {
2606 const defer_scope = scope.cast(Scope.Defer).?;2658 const defer_scope = scope.cast(Scope.Defer).?;
2607 scope = defer_scope.parent;2659 scope = defer_scope.parent;
...@@ -2644,6 +2696,7 @@ fn genDefers(...@@ -2644,6 +2696,7 @@ fn genDefers(
2644 break :blk &local_val_scope.base;2696 break :blk &local_val_scope.base;
2645 };2697 };
2646 try unusedResultDeferExpr(gz, defer_scope, sub_scope, expr_node);2698 try unusedResultDeferExpr(gz, defer_scope, sub_scope, expr_node);
2699 try checkUsed(gz, scope, sub_scope);
2647 try gz.addDbgBlockEnd();2700 try gz.addDbgBlockEnd();
2648 },2701 },
2649 .normal_only => continue,2702 .normal_only => continue,
...@@ -2681,6 +2734,7 @@ fn checkUsed(...@@ -2681,6 +2734,7 @@ fn checkUsed(
2681 scope = s.parent;2734 scope = s.parent;
2682 },2735 },
2683 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,2736 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
2737 .defer_gen => scope = scope.cast(Scope.DeferGen).?.parent,
2684 .namespace => unreachable,2738 .namespace => unreachable,
2685 .top => unreachable,2739 .top => unreachable,
2686 }2740 }
...@@ -4040,6 +4094,7 @@ fn testDecl(...@@ -4040,6 +4094,7 @@ fn testDecl(
4040 .local_val, .local_ptr => unreachable, // a test cannot be in a local scope4094 .local_val, .local_ptr => unreachable, // a test cannot be in a local scope
4041 .gen_zir => s = s.cast(GenZir).?.parent,4095 .gen_zir => s = s.cast(GenZir).?.parent,
4042 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,4096 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
4097 .defer_gen => s = s.cast(Scope.DeferGen).?.parent,
4043 .namespace => {4098 .namespace => {
4044 const ns = s.cast(Scope.Namespace).?;4099 const ns = s.cast(Scope.Namespace).?;
4045 if (ns.decls.get(name_str_index)) |i| {4100 if (ns.decls.get(name_str_index)) |i| {
...@@ -5330,7 +5385,7 @@ fn ifExpr(...@@ -5330,7 +5385,7 @@ fn ifExpr(
5330 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;5385 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
5331 break :c .{5386 break :c .{
5332 .inst = err_union,5387 .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),
5334 };5389 };
5335 } else if (if_full.payload_token) |_| {5390 } else if (if_full.payload_token) |_| {
5336 const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none;5391 const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none;
...@@ -5338,7 +5393,7 @@ fn ifExpr(...@@ -5338,7 +5393,7 @@ fn ifExpr(
5338 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null;5393 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null;
5339 break :c .{5394 break :c .{
5340 .inst = optional,5395 .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),
5342 };5397 };
5343 } else {5398 } else {
5344 const cond = try expr(&block_scope, &block_scope.base, bool_rl, if_full.ast.cond_expr);5399 const cond = try expr(&block_scope, &block_scope.base, bool_rl, if_full.ast.cond_expr);
...@@ -5369,7 +5424,7 @@ fn ifExpr(...@@ -5369,7 +5424,7 @@ fn ifExpr(
5369 .err_union_payload_unsafe_ptr5424 .err_union_payload_unsafe_ptr
5370 else5425 else
5371 .err_union_payload_unsafe;5426 .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);
5373 const token_name_index = payload_token + @boolToInt(payload_is_ref);5428 const token_name_index = payload_token + @boolToInt(payload_is_ref);
5374 const ident_name = try astgen.identAsString(token_name_index);5429 const ident_name = try astgen.identAsString(token_name_index);
5375 const token_name_str = tree.tokenSlice(token_name_index);5430 const token_name_str = tree.tokenSlice(token_name_index);
...@@ -5398,7 +5453,7 @@ fn ifExpr(...@@ -5398,7 +5453,7 @@ fn ifExpr(
5398 const ident_bytes = tree.tokenSlice(ident_token);5453 const ident_bytes = tree.tokenSlice(ident_token);
5399 if (mem.eql(u8, "_", ident_bytes))5454 if (mem.eql(u8, "_", ident_bytes))
5400 break :s &then_scope.base;5455 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);
5402 const ident_name = try astgen.identAsString(ident_token);5457 const ident_name = try astgen.identAsString(ident_token);
5403 try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes);5458 try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes);
5404 payload_val_scope = .{5459 payload_val_scope = .{
...@@ -5441,7 +5496,7 @@ fn ifExpr(...@@ -5441,7 +5496,7 @@ fn ifExpr(
5441 .err_union_code_ptr5496 .err_union_code_ptr
5442 else5497 else
5443 .err_union_code;5498 .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);
5445 const ident_name = try astgen.identAsString(error_token);5500 const ident_name = try astgen.identAsString(error_token);
5446 const error_token_str = tree.tokenSlice(error_token);5501 const error_token_str = tree.tokenSlice(error_token);
5447 if (mem.eql(u8, "_", error_token_str))5502 if (mem.eql(u8, "_", error_token_str))
...@@ -5615,6 +5670,7 @@ fn whileExpr(...@@ -5615,6 +5670,7 @@ fn whileExpr(
5615 rl: ResultLoc,5670 rl: ResultLoc,
5616 node: Ast.Node.Index,5671 node: Ast.Node.Index,
5617 while_full: Ast.full.While,5672 while_full: Ast.full.While,
5673 is_statement: bool,
5618) InnerError!Zir.Inst.Ref {5674) InnerError!Zir.Inst.Ref {
5619 const astgen = parent_gz.astgen;5675 const astgen = parent_gz.astgen;
5620 const tree = astgen.tree;5676 const tree = astgen.tree;
...@@ -5654,7 +5710,7 @@ fn whileExpr(...@@ -5654,7 +5710,7 @@ fn whileExpr(
5654 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;5710 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
5655 break :c .{5711 break :c .{
5656 .inst = err_union,5712 .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),
5658 };5714 };
5659 } else if (while_full.payload_token) |_| {5715 } else if (while_full.payload_token) |_| {
5660 const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none;5716 const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none;
...@@ -5662,7 +5718,7 @@ fn whileExpr(...@@ -5662,7 +5718,7 @@ fn whileExpr(
5662 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null;5718 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null;
5663 break :c .{5719 break :c .{
5664 .inst = optional,5720 .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),
5666 };5722 };
5667 } else {5723 } else {
5668 const cond = try expr(&continue_scope, &continue_scope.base, bool_rl, while_full.ast.cond_expr);5724 const cond = try expr(&continue_scope, &continue_scope.base, bool_rl, while_full.ast.cond_expr);
...@@ -5700,7 +5756,7 @@ fn whileExpr(...@@ -5700,7 +5756,7 @@ fn whileExpr(
5700 else5756 else
5701 .err_union_payload_unsafe;5757 .err_union_payload_unsafe;
5702 // will add this instruction to then_scope.instructions below5758 // 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);
5704 const ident_token = if (payload_is_ref) payload_token + 1 else payload_token;5760 const ident_token = if (payload_is_ref) payload_token + 1 else payload_token;
5705 const ident_bytes = tree.tokenSlice(ident_token);5761 const ident_bytes = tree.tokenSlice(ident_token);
5706 if (mem.eql(u8, "_", ident_bytes))5762 if (mem.eql(u8, "_", ident_bytes))
...@@ -5729,7 +5785,7 @@ fn whileExpr(...@@ -5729,7 +5785,7 @@ fn whileExpr(
5729 else5785 else
5730 .optional_payload_unsafe;5786 .optional_payload_unsafe;
5731 // will add this instruction to then_scope.instructions below5787 // 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);
5733 const ident_name = try astgen.identAsString(ident_token);5789 const ident_name = try astgen.identAsString(ident_token);
5734 const ident_bytes = tree.tokenSlice(ident_token);5790 const ident_bytes = tree.tokenSlice(ident_token);
5735 if (mem.eql(u8, "_", ident_bytes))5791 if (mem.eql(u8, "_", ident_bytes))
...@@ -5785,6 +5841,8 @@ fn whileExpr(...@@ -5785,6 +5841,8 @@ fn whileExpr(
5785 try then_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst);5841 try then_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst);
5786 }5842 }
5787 const then_result = try expr(&then_scope, then_sub_scope, loop_scope.break_result_loc, while_full.ast.then_expr);5843 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
5788 try checkUsed(parent_gz, &then_scope.base, then_sub_scope);5846 try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
5789 try then_scope.addDbgBlockEnd();5847 try then_scope.addDbgBlockEnd();
57905848
...@@ -5803,7 +5861,7 @@ fn whileExpr(...@@ -5803,7 +5861,7 @@ fn whileExpr(
5803 .err_union_code_ptr5861 .err_union_code_ptr
5804 else5862 else
5805 .err_union_code;5863 .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);
5807 const ident_name = try astgen.identAsString(error_token);5865 const ident_name = try astgen.identAsString(error_token);
5808 const ident_bytes = tree.tokenSlice(error_token);5866 const ident_bytes = tree.tokenSlice(error_token);
5809 if (mem.eql(u8, ident_bytes, "_"))5867 if (mem.eql(u8, ident_bytes, "_"))
...@@ -5827,7 +5885,11 @@ fn whileExpr(...@@ -5827,7 +5885,11 @@ fn whileExpr(
5827 // control flow apply to outer loops; not this one.5885 // control flow apply to outer loops; not this one.
5828 loop_scope.continue_block = 0;5886 loop_scope.continue_block = 0;
5829 loop_scope.break_block = 0;5887 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
5831 if (!else_scope.endsWithNoReturn()) {5893 if (!else_scope.endsWithNoReturn()) {
5832 loop_scope.break_count += 1;5894 loop_scope.break_count += 1;
5833 }5895 }
...@@ -5835,7 +5897,7 @@ fn whileExpr(...@@ -5835,7 +5897,7 @@ fn whileExpr(
5835 try else_scope.addDbgBlockEnd();5897 try else_scope.addDbgBlockEnd();
5836 break :blk .{5898 break :blk .{
5837 .src = else_node,5899 .src = else_node,
5838 .result = e,5900 .result = else_result,
5839 };5901 };
5840 } else .{5902 } else .{
5841 .src = while_full.ast.then_expr,5903 .src = while_full.ast.then_expr,
...@@ -5848,7 +5910,7 @@ fn whileExpr(...@@ -5848,7 +5910,7 @@ fn whileExpr(
5848 }5910 }
5849 }5911 }
5850 const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break";5912 const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
5851 return finishThenElseBlock(5913 const result = try finishThenElseBlock(
5852 parent_gz,5914 parent_gz,
5853 rl,5915 rl,
5854 node,5916 node,
...@@ -5863,6 +5925,10 @@ fn whileExpr(...@@ -5863,6 +5925,10 @@ fn whileExpr(
5863 cond_block,5925 cond_block,
5864 break_tag,5926 break_tag,
5865 );5927 );
5928 if (is_statement) {
5929 _ = try parent_gz.addUnNode(.ensure_result_used, result, node);
5930 }
5931 return result;
5866}5932}
58675933
5868fn forExpr(5934fn forExpr(
...@@ -5871,6 +5937,7 @@ fn forExpr(...@@ -5871,6 +5937,7 @@ fn forExpr(
5871 rl: ResultLoc,5937 rl: ResultLoc,
5872 node: Ast.Node.Index,5938 node: Ast.Node.Index,
5873 for_full: Ast.full.While,5939 for_full: Ast.full.While,
5940 is_statement: bool,
5874) InnerError!Zir.Inst.Ref {5941) InnerError!Zir.Inst.Ref {
5875 const astgen = parent_gz.astgen;5942 const astgen = parent_gz.astgen;
58765943
...@@ -6014,6 +6081,8 @@ fn forExpr(...@@ -6014,6 +6081,8 @@ fn forExpr(
6014 };6081 };
60156082
6016 const then_result = try expr(&then_scope, then_sub_scope, loop_scope.break_result_loc, for_full.ast.then_expr);6083 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
6017 try checkUsed(parent_gz, &then_scope.base, then_sub_scope);6086 try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
6018 try then_scope.addDbgBlockEnd();6087 try then_scope.addDbgBlockEnd();
60196088
...@@ -6031,6 +6100,10 @@ fn forExpr(...@@ -6031,6 +6100,10 @@ fn forExpr(
6031 loop_scope.continue_block = 0;6100 loop_scope.continue_block = 0;
6032 loop_scope.break_block = 0;6101 loop_scope.break_block = 0;
6033 const else_result = try expr(&else_scope, sub_scope, loop_scope.break_result_loc, else_node);6102 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
6034 if (!else_scope.endsWithNoReturn()) {6107 if (!else_scope.endsWithNoReturn()) {
6035 loop_scope.break_count += 1;6108 loop_scope.break_count += 1;
6036 }6109 }
...@@ -6049,7 +6122,7 @@ fn forExpr(...@@ -6049,7 +6122,7 @@ fn forExpr(
6049 }6122 }
6050 }6123 }
6051 const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break";6124 const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
6052 return finishThenElseBlock(6125 const result = try finishThenElseBlock(
6053 parent_gz,6126 parent_gz,
6054 rl,6127 rl,
6055 node,6128 node,
...@@ -6064,6 +6137,10 @@ fn forExpr(...@@ -6064,6 +6137,10 @@ fn forExpr(
6064 cond_block,6137 cond_block,
6065 break_tag,6138 break_tag,
6066 );6139 );
6140 if (is_statement) {
6141 _ = try parent_gz.addUnNode(.ensure_result_used, result, node);
6142 }
6143 return result;
6067}6144}
60686145
6069fn switchExpr(6146fn switchExpr(
...@@ -6730,6 +6807,7 @@ fn localVarRef(...@@ -6730,6 +6807,7 @@ fn localVarRef(
6730 },6807 },
6731 .gen_zir => s = s.cast(GenZir).?.parent,6808 .gen_zir => s = s.cast(GenZir).?.parent,
6732 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,6809 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
6810 .defer_gen => s = s.cast(Scope.DeferGen).?.parent,
6733 .namespace => {6811 .namespace => {
6734 const ns = s.cast(Scope.Namespace).?;6812 const ns = s.cast(Scope.Namespace).?;
6735 if (ns.decls.get(name_str_index)) |i| {6813 if (ns.decls.get(name_str_index)) |i| {
...@@ -7351,6 +7429,7 @@ fn builtinCall(...@@ -7351,6 +7429,7 @@ fn builtinCall(
7351 },7429 },
7352 .gen_zir => s = s.cast(GenZir).?.parent,7430 .gen_zir => s = s.cast(GenZir).?.parent,
7353 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,7431 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
7432 .defer_gen => s = s.cast(Scope.DeferGen).?.parent,
7354 .namespace => {7433 .namespace => {
7355 const ns = s.cast(Scope.Namespace).?;7434 const ns = s.cast(Scope.Namespace).?;
7356 if (ns.decls.get(decl_name)) |i| {7435 if (ns.decls.get(decl_name)) |i| {
...@@ -7424,7 +7503,8 @@ fn builtinCall(...@@ -7424,7 +7503,8 @@ fn builtinCall(
7424 const token_starts = tree.tokens.items(.start);7503 const token_starts = tree.tokens.items(.start);
7425 const node_start = token_starts[tree.firstToken(node)];7504 const node_start = token_starts[tree.firstToken(node)];
7426 astgen.advanceSourceCursor(node_start);7505 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),
7428 .line = astgen.source_line,7508 .line = astgen.source_line,
7429 .column = astgen.source_column,7509 .column = astgen.source_column,
7430 });7510 });
...@@ -9807,6 +9887,7 @@ const Scope = struct {...@@ -9807,6 +9887,7 @@ const Scope = struct {
9807 local_ptr,9887 local_ptr,
9808 defer_normal,9888 defer_normal,
9809 defer_error,9889 defer_error,
9890 defer_gen,
9810 namespace,9891 namespace,
9811 top,9892 top,
9812 };9893 };
...@@ -9904,6 +9985,13 @@ const Scope = struct {...@@ -9904,6 +9985,13 @@ const Scope = struct {
9904 const base_tag: Scope.Tag = .top;9985 const base_tag: Scope.Tag = .top;
9905 base: Scope = Scope{ .tag = base_tag },9986 base: Scope = Scope{ .tag = base_tag },
9906 };9987 };
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 };
9907};9995};
99089996
9909/// This is a temporary structure; references to it are valid only9997/// This is a temporary structure; references to it are valid only
...@@ -11414,6 +11502,7 @@ fn detectLocalShadowing(...@@ -11414,6 +11502,7 @@ fn detectLocalShadowing(
11414 },11502 },
11415 .gen_zir => s = s.cast(GenZir).?.parent,11503 .gen_zir => s = s.cast(GenZir).?.parent,
11416 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,11504 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
11505 .defer_gen => s = s.cast(Scope.DeferGen).?.parent,
11417 .top => break,11506 .top => break,
11418 };11507 };
11419}11508}
src/Autodoc.zig+20-7
...@@ -468,7 +468,7 @@ const DocData = struct {...@@ -468,7 +468,7 @@ const DocData = struct {
468 child: Expr,468 child: Expr,
469 },469 },
470 ErrorUnion: struct { lhs: Expr, rhs: Expr },470 ErrorUnion: struct { lhs: Expr, rhs: Expr },
471 // ErrorUnion: struct { name: []const u8 },471 InferredErrorUnion: struct { payload: Expr },
472 ErrorSet: struct {472 ErrorSet: struct {
473 name: []const u8,473 name: []const u8,
474 fields: ?[]const Field = null,474 fields: ?[]const Field = null,
...@@ -580,8 +580,9 @@ const DocData = struct {...@@ -580,8 +580,9 @@ const DocData = struct {
580 enumLiteral: []const u8, // direct value580 enumLiteral: []const u8, // direct value
581 alignOf: usize, // index in `exprs`581 alignOf: usize, // index in `exprs`
582 typeOf: usize, // index in `exprs`582 typeOf: usize, // index in `exprs`
583 typeInfo: usize, // index in `exprs`
583 typeOf_peer: []usize,584 typeOf_peer: []usize,
584 errorUnion: usize, // index in `exprs`585 errorUnion: usize, // index in `types`
585 as: As,586 as: As,
586 sizeOf: usize, // index in `exprs`587 sizeOf: usize, // index in `exprs`
587 bitSizeOf: usize, // index in `exprs`588 bitSizeOf: usize, // index in `exprs`
...@@ -1398,7 +1399,6 @@ fn walkInstruction(...@@ -1398,7 +1399,6 @@ fn walkInstruction(
1398 const extra = file.zir.extraData(Zir.Inst.PtrType, ptr.payload_index);1399 const extra = file.zir.extraData(Zir.Inst.PtrType, ptr.payload_index);
1399 var extra_index = extra.end;1400 var extra_index = extra.end;
14001401
1401 const type_slot_index = self.types.items.len;
1402 const elem_type_ref = try self.walkRef(1402 const elem_type_ref = try self.walkRef(
1403 file,1403 file,
1404 parent_scope,1404 parent_scope,
...@@ -1445,6 +1445,7 @@ fn walkInstruction(...@@ -1445,6 +1445,7 @@ fn walkInstruction(
1445 host_size = ref_result.expr;1445 host_size = ref_result.expr;
1446 }1446 }
14471447
1448 const type_slot_index = self.types.items.len;
1448 try self.types.append(self.arena, .{1449 try self.types.append(self.arena, .{
1449 .Pointer = .{1450 .Pointer = .{
1450 .size = ptr.size,1451 .size = ptr.size,
...@@ -1788,7 +1789,7 @@ fn walkInstruction(...@@ -1788,7 +1789,7 @@ fn walkInstruction(
17881789
1789 return DocData.WalkResult{1790 return DocData.WalkResult{
1790 .typeRef = operand.typeRef,1791 .typeRef = operand.typeRef,
1791 .expr = .{ .typeOf = operand_index },1792 .expr = .{ .typeInfo = operand_index },
1792 };1793 };
1793 },1794 },
1794 .as_node => {1795 .as_node => {
...@@ -1928,7 +1929,7 @@ fn walkInstruction(...@@ -1928,7 +1929,7 @@ fn walkInstruction(
1928 .comptimeExpr = self.comptime_exprs.items.len,1929 .comptimeExpr = self.comptime_exprs.items.len,
1929 } };1930 } };
1930 try self.comptime_exprs.append(self.arena, .{1931 try self.comptime_exprs.append(self.arena, .{
1931 .code = "if(banana) 1 else 0",1932 .code = "if (...) { ... }",
1932 });1933 });
1933 return res;1934 return res;
1934 },1935 },
...@@ -2118,6 +2119,7 @@ fn walkInstruction(...@@ -2118,6 +2119,7 @@ fn walkInstruction(
2118 inst_index,2119 inst_index,
2119 self_ast_node_index,2120 self_ast_node_index,
2120 type_slot_index,2121 type_slot_index,
2122 tags[inst_index] == .func_inferred,
2121 );2123 );
21222124
2123 return result;2125 return result;
...@@ -2993,7 +2995,7 @@ fn tryResolveRefPath(...@@ -2993,7 +2995,7 @@ fn tryResolveRefPath(
2993 "TODO: handle `{s}`in tryResolveRefPath\nInfo: {}",2995 "TODO: handle `{s}`in tryResolveRefPath\nInfo: {}",
2994 .{ @tagName(resolved_parent), resolved_parent },2996 .{ @tagName(resolved_parent), resolved_parent },
2995 );2997 );
2996 path[i + 1] = (try self.cteTodo("match failure")).expr;2998 path[i + 1] = (try self.cteTodo("<match failure>")).expr;
2997 continue :outer;2999 continue :outer;
2998 },3000 },
2999 .comptimeExpr, .call, .typeOf => {3001 .comptimeExpr, .call, .typeOf => {
...@@ -3415,6 +3417,7 @@ fn analyzeFunction(...@@ -3415,6 +3417,7 @@ fn analyzeFunction(
3415 inst_index: usize,3417 inst_index: usize,
3416 self_ast_node_index: usize,3418 self_ast_node_index: usize,
3417 type_slot_index: usize,3419 type_slot_index: usize,
3420 ret_is_inferred_error_set: bool,
3418) AutodocErrors!DocData.WalkResult {3421) AutodocErrors!DocData.WalkResult {
3419 const tags = file.zir.instructions.items(.tag);3422 const tags = file.zir.instructions.items(.tag);
3420 const data = file.zir.instructions.items(.data);3423 const data = file.zir.instructions.items(.data);
...@@ -3521,13 +3524,23 @@ fn analyzeFunction(...@@ -3521,13 +3524,23 @@ fn analyzeFunction(
3521 else => null,3524 else => null,
3522 };3525 };
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
3524 self.ast_nodes.items[self_ast_node_index].fields = param_ast_indexes.items;3537 self.ast_nodes.items[self_ast_node_index].fields = param_ast_indexes.items;
3525 self.types.items[type_slot_index] = .{3538 self.types.items[type_slot_index] = .{
3526 .Fn = .{3539 .Fn = .{
3527 .name = "todo_name func",3540 .name = "todo_name func",
3528 .src = self_ast_node_index,3541 .src = self_ast_node_index,
3529 .params = param_type_refs.items,3542 .params = param_type_refs.items,
3530 .ret = ret_type_ref,3543 .ret = ret_type,
3531 .generic_ret = generic_ret,3544 .generic_ret = generic_ret,
3532 },3545 },
3533 };3546 };
src/Module.zig+94-13
...@@ -2161,6 +2161,10 @@ pub const SrcLoc = struct {...@@ -2161,6 +2161,10 @@ pub const SrcLoc = struct {
2161 .local_var_decl => tree.localVarDecl(node),2161 .local_var_decl => tree.localVarDecl(node),
2162 .simple_var_decl => tree.simpleVarDecl(node),2162 .simple_var_decl => tree.simpleVarDecl(node),
2163 .aligned_var_decl => tree.alignedVarDecl(node),2163 .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 },
2164 else => unreachable,2168 else => unreachable,
2165 };2169 };
2166 if (full.ast.type_node != 0) {2170 if (full.ast.type_node != 0) {
...@@ -2171,6 +2175,58 @@ pub const SrcLoc = struct {...@@ -2171,6 +2175,58 @@ pub const SrcLoc = struct {
2171 const end = start + @intCast(u32, tree.tokenSlice(tok_index).len);2175 const end = start + @intCast(u32, tree.tokenSlice(tok_index).len);
2172 return Span{ .start = start, .end = end, .main = start };2176 return Span{ .start = start, .end = end, .main = start };
2173 },2177 },
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 },
2174 .node_offset_builtin_call_arg0 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 0),2230 .node_offset_builtin_call_arg0 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 0),
2175 .node_offset_builtin_call_arg1 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 1),2231 .node_offset_builtin_call_arg1 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 1),
2176 .node_offset_builtin_call_arg2 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 2),2232 .node_offset_builtin_call_arg2 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 2),
...@@ -2857,6 +2913,18 @@ pub const LazySrcLoc = union(enum) {...@@ -2857,6 +2913,18 @@ pub const LazySrcLoc = union(enum) {
2857 /// to the type expression.2913 /// to the type expression.
2858 /// The Decl is determined contextually.2914 /// The Decl is determined contextually.
2859 node_offset_var_decl_ty: i32,2915 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,
2860 /// The source location points to a for loop condition expression,2928 /// The source location points to a for loop condition expression,
2861 /// found by taking this AST node index offset from the containing2929 /// found by taking this AST node index offset from the containing
2862 /// Decl AST node, which points to a for loop AST node. Next, navigate2930 /// Decl AST node, which points to a for loop AST node. Next, navigate
...@@ -3098,6 +3166,10 @@ pub const LazySrcLoc = union(enum) {...@@ -3098,6 +3166,10 @@ pub const LazySrcLoc = union(enum) {
3098 .node_offset,3166 .node_offset,
3099 .node_offset_initializer,3167 .node_offset_initializer,
3100 .node_offset_var_decl_ty,3168 .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,
3101 .node_offset_for_cond,3173 .node_offset_for_cond,
3102 .node_offset_builtin_call_arg0,3174 .node_offset_builtin_call_arg0,
3103 .node_offset_builtin_call_arg1,3175 .node_offset_builtin_call_arg1,
...@@ -4414,17 +4486,26 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {...@@ -4414,17 +4486,26 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
4414 const body = zir.extra[extra.end..][0..extra.data.body_len];4486 const body = zir.extra[extra.end..][0..extra.data.body_len];
4415 const result_ref = (try sema.analyzeBodyBreak(&block_scope, body)).?.operand;4487 const result_ref = (try sema.analyzeBodyBreak(&block_scope, body)).?.operand;
4416 try wip_captures.finalize();4488 try wip_captures.finalize();
4417 const src = LazySrcLoc.nodeOffset(0);4489 const align_src: LazySrcLoc = .{ .node_offset_var_decl_align = 0 };
4418 const decl_tv = try sema.resolveInstValue(&block_scope, .unneeded, result_ref, undefined);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);
4419 const decl_align: u32 = blk: {4495 const decl_align: u32 = blk: {
4420 const align_ref = decl.zirAlignRef();4496 const align_ref = decl.zirAlignRef();
4421 if (align_ref == .none) break :blk 0;4497 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);
4423 };4499 };
4424 const decl_linksection: ?[*:0]const u8 = blk: {4500 const decl_linksection: ?[*:0]const u8 = blk: {
4425 const linksection_ref = decl.zirLinksectionRef();4501 const linksection_ref = decl.zirLinksectionRef();
4426 if (linksection_ref == .none) break :blk null;4502 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 }
4428 break :blk (try decl_arena_allocator.dupeZ(u8, bytes)).ptr;4509 break :blk (try decl_arena_allocator.dupeZ(u8, bytes)).ptr;
4429 };4510 };
4430 const target = sema.mod.getTarget();4511 const target = sema.mod.getTarget();
...@@ -4442,27 +4523,27 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {...@@ -4442,27 +4523,27 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
4442 .constant => target_util.defaultAddressSpace(target, .global_constant),4523 .constant => target_util.defaultAddressSpace(target, .global_constant),
4443 else => unreachable,4524 else => unreachable,
4444 },4525 },
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),
4446 };4527 };
4447 };4528 };
44484529
4449 // Note this resolves the type of the Decl, not the value; if this Decl4530 // Note this resolves the type of the Decl, not the value; if this Decl
4450 // is a struct, for example, this resolves `type` (which needs no resolution),4531 // is a struct, for example, this resolves `type` (which needs no resolution),
4451 // not the struct itself.4532 // 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
4454 const decl_arena_state = try decl_arena_allocator.create(std.heap.ArenaAllocator.State);4535 const decl_arena_state = try decl_arena_allocator.create(std.heap.ArenaAllocator.State);
44554536
4456 if (decl.is_usingnamespace) {4537 if (decl.is_usingnamespace) {
4457 if (!decl_tv.ty.eql(Type.type, mod)) {4538 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 {}", .{
4459 decl_tv.ty.fmt(mod),4540 decl_tv.ty.fmt(mod),
4460 });4541 });
4461 }4542 }
4462 var buffer: Value.ToTypeBuffer = undefined;4543 var buffer: Value.ToTypeBuffer = undefined;
4463 const ty = try decl_tv.val.toType(&buffer).copy(decl_arena_allocator);4544 const ty = try decl_tv.val.toType(&buffer).copy(decl_arena_allocator);
4464 if (ty.getNamespace() == null) {4545 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)});
4466 }4547 }
44674548
4468 decl.ty = Type.type;4549 decl.ty = Type.type;
...@@ -4508,7 +4589,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {...@@ -4508,7 +4589,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
4508 decl.analysis = .complete;4589 decl.analysis = .complete;
4509 decl.generation = mod.generation;4590 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
4513 if (has_runtime_bits) {4594 if (has_runtime_bits) {
4514 // We don't fully codegen the decl until later, but we do need to reserve a global4595 // 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 {...@@ -4525,7 +4606,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
45254606
4526 const is_inline = decl.ty.fnCallingConvention() == .Inline;4607 const is_inline = decl.ty.fnCallingConvention() == .Inline;
4527 if (decl.is_exported) {4608 if (decl.is_exported) {
4528 const export_src = src; // TODO make this point at `export` token4609 const export_src: LazySrcLoc = .{ .token_offset = @boolToInt(decl.is_pub) };
4529 if (is_inline) {4610 if (is_inline) {
4530 return sema.fail(&block_scope, export_src, "export of inline function", .{});4611 return sema.fail(&block_scope, export_src, "export of inline function", .{});
4531 }4612 }
...@@ -4588,14 +4669,14 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {...@@ -4588,14 +4669,14 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
4588 decl.generation = mod.generation;4669 decl.generation = mod.generation;
45894670
4590 const has_runtime_bits = is_extern or4671 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
4593 if (has_runtime_bits) {4674 if (has_runtime_bits) {
4594 log.debug("queue linker work for {*} ({s})", .{ decl, decl.name });4675 log.debug("queue linker work for {*} ({s})", .{ decl, decl.name });
45954676
4596 // Needed for codegen_decl which will call updateDecl and then the4677 // Needed for codegen_decl which will call updateDecl and then the
4597 // codegen backend wants full access to the Decl Type.4678 // 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
4600 try mod.comp.bin_file.allocateDeclIndexes(decl_index);4681 try mod.comp.bin_file.allocateDeclIndexes(decl_index);
4601 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl_index });4682 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl_index });
...@@ -4606,7 +4687,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {...@@ -4606,7 +4687,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
4606 }4687 }
46074688
4608 if (decl.is_exported) {4689 if (decl.is_exported) {
4609 const export_src = src; // TODO point to the export token4690 const export_src: LazySrcLoc = .{ .token_offset = @boolToInt(decl.is_pub) };
4610 // The scope needs to have the decl in it.4691 // The scope needs to have the decl in it.
4611 const options: std.builtin.ExportOptions = .{ .name = mem.sliceTo(decl.name, 0) };4692 const options: std.builtin.ExportOptions = .{ .name = mem.sliceTo(decl.name, 0) };
4612 try sema.analyzeExport(&block_scope, export_src, options, decl_index);4693 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) = .{},...@@ -74,6 +74,8 @@ types_to_resolve: std.ArrayListUnmanaged(Air.Inst.Ref) = .{},
74/// Sema must convert comptime control flow to runtime control flow, which means74/// Sema must convert comptime control flow to runtime control flow, which means
75/// breaking from a block.75/// breaking from a block.
76post_hoc_blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, *LabeledBlock) = .{},76post_hoc_blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, *LabeledBlock) = .{},
77/// Populated with the last compile error created.
78err: ?*Module.ErrorMsg = null,
7779
78const std = @import("std");80const std = @import("std");
79const mem = std.mem;81const mem = std.mem;
...@@ -174,7 +176,6 @@ pub const Block = struct {...@@ -174,7 +176,6 @@ pub const Block = struct {
174 pub const Inlining = struct {176 pub const Inlining = struct {
175 comptime_result: Air.Inst.Ref,177 comptime_result: Air.Inst.Ref,
176 merges: Merges,178 merges: Merges,
177 err: ?*Module.ErrorMsg = null,
178 };179 };
179180
180 pub const Merges = struct {181 pub const Merges = struct {
...@@ -1159,7 +1160,7 @@ fn analyzeBodyInner(...@@ -1159,7 +1160,7 @@ fn analyzeBodyInner(
1159 try sema.errNote(block, runtime_src, msg, "runtime control flow here", .{});1160 try sema.errNote(block, runtime_src, msg, "runtime control flow here", .{});
1160 break :msg msg;1161 break :msg msg;
1161 };1162 };
1162 return sema.failWithOwnedErrorMsg(block, msg);1163 return sema.failWithOwnedErrorMsg(msg);
1163 }1164 }
1164 }1165 }
1165 i += 1;1166 i += 1;
...@@ -1738,7 +1739,7 @@ fn failWithNeededComptime(sema: *Sema, block: *Block, src: LazySrcLoc, reason: [...@@ -1738,7 +1739,7 @@ fn failWithNeededComptime(sema: *Sema, block: *Block, src: LazySrcLoc, reason: [
1738 try sema.errNote(block, src, msg, "{s}", .{reason});1739 try sema.errNote(block, src, msg, "{s}", .{reason});
1739 break :msg msg;1740 break :msg msg;
1740 };1741 };
1741 return sema.failWithOwnedErrorMsg(block, msg);1742 return sema.failWithOwnedErrorMsg(msg);
1742}1743}
17431744
1744fn failWithUseOfUndef(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError {1745fn failWithUseOfUndef(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError {
...@@ -1770,7 +1771,7 @@ fn failWithArrayInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty...@@ -1770,7 +1771,7 @@ fn failWithArrayInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty
1770 }1771 }
1771 break :msg msg;1772 break :msg msg;
1772 };1773 };
1773 return sema.failWithOwnedErrorMsg(block, msg);1774 return sema.failWithOwnedErrorMsg(msg);
1774}1775}
17751776
1776fn failWithStructInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError {1777fn failWithStructInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError {
...@@ -1801,7 +1802,7 @@ fn failWithIntegerOverflow(sema: *Sema, block: *Block, src: LazySrcLoc, int_ty:...@@ -1801,7 +1802,7 @@ fn failWithIntegerOverflow(sema: *Sema, block: *Block, src: LazySrcLoc, int_ty:
1801 try sema.errNote(block, src, msg, "when computing vector element at index '{d}'", .{vector_index});1802 try sema.errNote(block, src, msg, "when computing vector element at index '{d}'", .{vector_index});
1802 break :msg msg;1803 break :msg msg;
1803 };1804 };
1804 return sema.failWithOwnedErrorMsg(block, msg);1805 return sema.failWithOwnedErrorMsg(msg);
1805 }1806 }
1806 return sema.fail(block, src, "overflow of integer type '{}' with value '{}'", .{1807 return sema.fail(block, src, "overflow of integer type '{}' with value '{}'", .{
1807 int_ty.fmt(sema.mod), val.fmtValue(int_ty, sema.mod),1808 int_ty.fmt(sema.mod), val.fmtValue(int_ty, sema.mod),
...@@ -1823,7 +1824,7 @@ fn failWithInvalidComptimeFieldStore(sema: *Sema, block: *Block, init_src: LazyS...@@ -1823,7 +1824,7 @@ fn failWithInvalidComptimeFieldStore(sema: *Sema, block: *Block, init_src: LazyS
1823 try sema.errNote(block, default_value_src, msg, "default value set here", .{});1824 try sema.errNote(block, default_value_src, msg, "default value set here", .{});
1824 break :msg msg;1825 break :msg msg;
1825 };1826 };
1826 return sema.failWithOwnedErrorMsg(block, msg);1827 return sema.failWithOwnedErrorMsg(msg);
1827}1828}
18281829
1829/// We don't return a pointer to the new error note because the pointer1830/// We don't return a pointer to the new error note because the pointer
...@@ -1878,10 +1879,10 @@ pub fn fail(...@@ -1878,10 +1879,10 @@ pub fn fail(
1878 args: anytype,1879 args: anytype,
1879) CompileError {1880) CompileError {
1880 const err_msg = try sema.errMsg(block, src, format, args);1881 const err_msg = try sema.errMsg(block, src, format, args);
1881 return sema.failWithOwnedErrorMsg(block, err_msg);1882 return sema.failWithOwnedErrorMsg(err_msg);
1882}1883}
18831884
1884fn failWithOwnedErrorMsg(sema: *Sema, block: *Block, err_msg: *Module.ErrorMsg) CompileError {1885fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError {
1885 @setCold(true);1886 @setCold(true);
18861887
1887 if (crash_report.is_enabled and sema.mod.comp.debug_compile_errors) {1888 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)...@@ -1894,7 +1895,7 @@ fn failWithOwnedErrorMsg(sema: *Sema, block: *Block, err_msg: *Module.ErrorMsg)
1894 }1895 }
18951896
1896 const mod = sema.mod;1897 const mod = sema.mod;
1897 if (block.inlining) |some| some.err = err_msg;1898 sema.err = err_msg;
18981899
1899 {1900 {
1900 errdefer err_msg.destroy(mod.gpa);1901 errdefer err_msg.destroy(mod.gpa);
...@@ -2591,7 +2592,7 @@ fn zirEnumDecl(...@@ -2591,7 +2592,7 @@ fn zirEnumDecl(
2591 try sema.errNote(block, other_tag_src, msg, "other field here", .{});2592 try sema.errNote(block, other_tag_src, msg, "other field here", .{});
2592 break :msg msg;2593 break :msg msg;
2593 };2594 };
2594 return sema.failWithOwnedErrorMsg(block, msg);2595 return sema.failWithOwnedErrorMsg(msg);
2595 }2596 }
25962597
2597 if (has_tag_value) {2598 if (has_tag_value) {
...@@ -2886,7 +2887,7 @@ fn ensureResultUsed(...@@ -2886,7 +2887,7 @@ fn ensureResultUsed(
2886 try sema.errNote(block, src, msg, "consider using `try`, `catch`, or `if`", .{});2887 try sema.errNote(block, src, msg, "consider using `try`, `catch`, or `if`", .{});
2887 break :msg msg;2888 break :msg msg;
2888 };2889 };
2889 return sema.failWithOwnedErrorMsg(block, msg);2890 return sema.failWithOwnedErrorMsg(msg);
2890 },2891 },
2891 else => {2892 else => {
2892 const msg = msg: {2893 const msg = msg: {
...@@ -2896,7 +2897,7 @@ fn ensureResultUsed(...@@ -2896,7 +2897,7 @@ fn ensureResultUsed(
2896 try sema.errNote(block, src, msg, "this error can be suppressed by assigning the value to '_'", .{});2897 try sema.errNote(block, src, msg, "this error can be suppressed by assigning the value to '_'", .{});
2897 break :msg msg;2898 break :msg msg;
2898 };2899 };
2899 return sema.failWithOwnedErrorMsg(block, msg);2900 return sema.failWithOwnedErrorMsg(msg);
2900 },2901 },
2901 }2902 }
2902}2903}
...@@ -2917,7 +2918,7 @@ fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com...@@ -2917,7 +2918,7 @@ fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
2917 try sema.errNote(block, src, msg, "consider using `try`, `catch`, or `if`", .{});2918 try sema.errNote(block, src, msg, "consider using `try`, `catch`, or `if`", .{});
2918 break :msg msg;2919 break :msg msg;
2919 };2920 };
2920 return sema.failWithOwnedErrorMsg(block, msg);2921 return sema.failWithOwnedErrorMsg(msg);
2921 },2922 },
2922 else => return,2923 else => return,
2923 }2924 }
...@@ -2957,7 +2958,7 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE...@@ -2957,7 +2958,7 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
2957 );2958 );
2958 break :msg msg;2959 break :msg msg;
2959 };2960 };
2960 return sema.failWithOwnedErrorMsg(block, msg);2961 return sema.failWithOwnedErrorMsg(msg);
2961 }2962 }
29622963
2963 return sema.fieldVal(block, src, object, "len", src);2964 return sema.fieldVal(block, src, object, "len", src);
...@@ -2971,7 +2972,7 @@ fn zirAllocExtended(...@@ -2971,7 +2972,7 @@ fn zirAllocExtended(
2971 const extra = sema.code.extraData(Zir.Inst.AllocExtended, extended.operand);2972 const extra = sema.code.extraData(Zir.Inst.AllocExtended, extended.operand);
2972 const src = LazySrcLoc.nodeOffset(extra.data.src_node);2973 const src = LazySrcLoc.nodeOffset(extra.data.src_node);
2973 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = extra.data.src_node };2974 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = extra.data.src_node };
2974 const align_src = src; // TODO better source location2975 const align_src: LazySrcLoc = .{ .node_offset_var_decl_align = extra.data.src_node };
2975 const small = @bitCast(Zir.Inst.AllocExtended.Small, extended.small);2976 const small = @bitCast(Zir.Inst.AllocExtended.Small, extended.small);
29762977
2977 var extra_index: usize = extra.end;2978 var extra_index: usize = extra.end;
...@@ -3615,7 +3616,7 @@ fn validateUnionInit(...@@ -3615,7 +3616,7 @@ fn validateUnionInit(
3615 try sema.addDeclaredHereNote(msg, union_ty);3616 try sema.addDeclaredHereNote(msg, union_ty);
3616 break :msg msg;3617 break :msg msg;
3617 };3618 };
3618 return sema.failWithOwnedErrorMsg(block, msg);3619 return sema.failWithOwnedErrorMsg(msg);
3619 }3620 }
36203621
3621 if ((is_comptime or block.is_comptime) and3622 if ((is_comptime or block.is_comptime) and
...@@ -3747,7 +3748,7 @@ fn validateStructInit(...@@ -3747,7 +3748,7 @@ fn validateStructInit(
3747 try sema.errNote(block, other_field_src, msg, "other field here", .{});3748 try sema.errNote(block, other_field_src, msg, "other field here", .{});
3748 break :msg msg;3749 break :msg msg;
3749 };3750 };
3750 return sema.failWithOwnedErrorMsg(block, msg);3751 return sema.failWithOwnedErrorMsg(msg);
3751 }3752 }
3752 found_fields[field_index] = field_ptr;3753 found_fields[field_index] = field_ptr;
3753 }3754 }
...@@ -3808,7 +3809,7 @@ fn validateStructInit(...@@ -3808,7 +3809,7 @@ fn validateStructInit(
3808 .{fqn},3809 .{fqn},
3809 );3810 );
3810 }3811 }
3811 return sema.failWithOwnedErrorMsg(block, msg);3812 return sema.failWithOwnedErrorMsg(msg);
3812 }3813 }
38133814
3814 return;3815 return;
...@@ -3938,7 +3939,7 @@ fn validateStructInit(...@@ -3938,7 +3939,7 @@ fn validateStructInit(
3938 .{fqn},3939 .{fqn},
3939 );3940 );
3940 }3941 }
3941 return sema.failWithOwnedErrorMsg(block, msg);3942 return sema.failWithOwnedErrorMsg(msg);
3942 }3943 }
39433944
3944 if (struct_is_comptime) {3945 if (struct_is_comptime) {
...@@ -4000,7 +4001,7 @@ fn zirValidateArrayInit(...@@ -4000,7 +4001,7 @@ fn zirValidateArrayInit(
4000 }4001 }
40014002
4002 if (root_msg) |msg| {4003 if (root_msg) |msg| {
4003 return sema.failWithOwnedErrorMsg(block, msg);4004 return sema.failWithOwnedErrorMsg(msg);
4004 }4005 }
4005 }4006 }
40064007
...@@ -4180,7 +4181,7 @@ fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr...@@ -4180,7 +4181,7 @@ fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
4180 try sema.explainWhyTypeIsComptime(block, src, msg, src.toSrcLoc(src_decl), elem_ty);4181 try sema.explainWhyTypeIsComptime(block, src, msg, src.toSrcLoc(src_decl), elem_ty);
4181 break :msg msg;4182 break :msg msg;
4182 };4183 };
4183 return sema.failWithOwnedErrorMsg(block, msg);4184 return sema.failWithOwnedErrorMsg(msg);
4184 }4185 }
4185}4186}
41864187
...@@ -4206,7 +4207,7 @@ fn failWithBadMemberAccess(...@@ -4206,7 +4207,7 @@ fn failWithBadMemberAccess(
4206 try sema.addDeclaredHereNote(msg, agg_ty);4207 try sema.addDeclaredHereNote(msg, agg_ty);
4207 break :msg msg;4208 break :msg msg;
4208 };4209 };
4209 return sema.failWithOwnedErrorMsg(block, msg);4210 return sema.failWithOwnedErrorMsg(msg);
4210}4211}
42114212
4212fn failWithBadStructFieldAccess(4213fn failWithBadStructFieldAccess(
...@@ -4232,7 +4233,7 @@ fn failWithBadStructFieldAccess(...@@ -4232,7 +4233,7 @@ fn failWithBadStructFieldAccess(
4232 try sema.mod.errNoteNonLazy(struct_obj.srcLoc(sema.mod), msg, "struct declared here", .{});4233 try sema.mod.errNoteNonLazy(struct_obj.srcLoc(sema.mod), msg, "struct declared here", .{});
4233 break :msg msg;4234 break :msg msg;
4234 };4235 };
4235 return sema.failWithOwnedErrorMsg(block, msg);4236 return sema.failWithOwnedErrorMsg(msg);
4236}4237}
42374238
4238fn failWithBadUnionFieldAccess(4239fn failWithBadUnionFieldAccess(
...@@ -4258,7 +4259,7 @@ fn failWithBadUnionFieldAccess(...@@ -4258,7 +4259,7 @@ fn failWithBadUnionFieldAccess(
4258 try sema.mod.errNoteNonLazy(union_obj.srcLoc(sema.mod), msg, "union declared here", .{});4259 try sema.mod.errNoteNonLazy(union_obj.srcLoc(sema.mod), msg, "union declared here", .{});
4259 break :msg msg;4260 break :msg msg;
4260 };4261 };
4261 return sema.failWithOwnedErrorMsg(block, msg);4262 return sema.failWithOwnedErrorMsg(msg);
4262}4263}
42634264
4264fn addDeclaredHereNote(sema: *Sema, parent: *Module.ErrorMsg, decl_ty: Type) !void {4265fn 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...@@ -4747,7 +4748,7 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
4747 @import("clang.zig").Stage2ErrorMsg.delete(c_import_res.errors.ptr, c_import_res.errors.len);4748 @import("clang.zig").Stage2ErrorMsg.delete(c_import_res.errors.ptr, c_import_res.errors.len);
4748 break :msg msg;4749 break :msg msg;
4749 };4750 };
4750 return sema.failWithOwnedErrorMsg(parent_block, msg);4751 return sema.failWithOwnedErrorMsg(msg);
4751 }4752 }
4752 const c_import_pkg = Package.create(4753 const c_import_pkg = Package.create(
4753 sema.gpa,4754 sema.gpa,
...@@ -4921,7 +4922,7 @@ fn analyzeBlockBody(...@@ -4921,7 +4922,7 @@ fn analyzeBlockBody(
49214922
4922 break :msg msg;4923 break :msg msg;
4923 };4924 };
4924 return sema.failWithOwnedErrorMsg(child_block, msg);4925 return sema.failWithOwnedErrorMsg(msg);
4925 }4926 }
4926 const ty_inst = try sema.addType(resolved_ty);4927 const ty_inst = try sema.addType(resolved_ty);
4927 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +4928 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
...@@ -5049,18 +5050,18 @@ pub fn analyzeExport(...@@ -5049,18 +5050,18 @@ pub fn analyzeExport(
5049 try mod.ensureDeclAnalyzed(exported_decl_index);5050 try mod.ensureDeclAnalyzed(exported_decl_index);
5050 const exported_decl = mod.declPtr(exported_decl_index);5051 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)) {
5053 const msg = msg: {5054 const msg = msg: {
5054 const msg = try sema.errMsg(block, src, "unable to export type '{}'", .{exported_decl.ty.fmt(sema.mod)});5055 const msg = try sema.errMsg(block, src, "unable to export type '{}'", .{exported_decl.ty.fmt(sema.mod)});
5055 errdefer msg.destroy(sema.gpa);5056 errdefer msg.destroy(sema.gpa);
50565057
5057 const src_decl = sema.mod.declPtr(block.src_decl);5058 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
5060 try sema.addDeclaredHereNote(msg, exported_decl.ty);5061 try sema.addDeclaredHereNote(msg, exported_decl.ty);
5061 break :msg msg;5062 break :msg msg;
5062 };5063 };
5063 return sema.failWithOwnedErrorMsg(block, msg);5064 return sema.failWithOwnedErrorMsg(msg);
5064 }5065 }
50655066
5066 const gpa = mod.gpa;5067 const gpa = mod.gpa;
...@@ -5150,7 +5151,7 @@ fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst...@@ -5150,7 +5151,7 @@ fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst
5150 try sema.errNote(block, gop.value_ptr.src, msg, "other instance here", .{});5151 try sema.errNote(block, gop.value_ptr.src, msg, "other instance here", .{});
5151 break :msg msg;5152 break :msg msg;
5152 };5153 };
5153 return sema.failWithOwnedErrorMsg(block, msg);5154 return sema.failWithOwnedErrorMsg(msg);
5154 }5155 }
5155 gop.value_ptr.* = .{ .alignment = alignment, .src = src };5156 gop.value_ptr.* = .{ .alignment = alignment, .src = src };
5156}5157}
...@@ -5311,7 +5312,14 @@ fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -5311,7 +5312,14 @@ fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
5311 const src = inst_data.src();5312 const src = inst_data.src();
5312 const decl_name = inst_data.get(sema.code);5313 const decl_name = inst_data.get(sema.code);
5313 const decl_index = try sema.lookupIdentifier(block, src, decl_name);5314 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 };
5315}5323}
53165324
5317fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {5325fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -5413,7 +5421,7 @@ fn lookupInNamespace(...@@ -5413,7 +5421,7 @@ fn lookupInNamespace(
5413 }5421 }
5414 break :msg msg;5422 break :msg msg;
5415 };5423 };
5416 return sema.failWithOwnedErrorMsg(block, msg);5424 return sema.failWithOwnedErrorMsg(msg);
5417 },5425 },
5418 }5426 }
5419 } else if (namespace.decls.getKeyAdapted(ident_name, Module.DeclAdapter{ .mod = mod })) |decl_index| {5427 } else if (namespace.decls.getKeyAdapted(ident_name, Module.DeclAdapter{ .mod = mod })) |decl_index| {
...@@ -5872,9 +5880,8 @@ fn analyzeCall(...@@ -5872,9 +5880,8 @@ fn analyzeCall(
5872 sema.analyzeBody(&child_block, fn_info.body) catch |err| switch (err) {5880 sema.analyzeBody(&child_block, fn_info.body) catch |err| switch (err) {
5873 error.ComptimeReturn => break :result inlining.comptime_result,5881 error.ComptimeReturn => break :result inlining.comptime_result,
5874 error.AnalysisFail => {5882 error.AnalysisFail => {
5875 const err_msg = inlining.err orelse return err;5883 const err_msg = sema.err orelse return err;
5876 try sema.errNote(block, call_src, err_msg, "called from here", .{});5884 try sema.errNote(block, call_src, err_msg, "called from here", .{});
5877 if (block.inlining) |some| some.err = err_msg;
5878 return err;5885 return err;
5879 },5886 },
5880 else => |e| return e,5887 else => |e| return e,
...@@ -6802,7 +6809,7 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr...@@ -6802,7 +6809,7 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
6802 try sema.errNote(block, src, msg, "'||' merges error sets; 'or' performs boolean OR", .{});6809 try sema.errNote(block, src, msg, "'||' merges error sets; 'or' performs boolean OR", .{});
6803 break :msg msg;6810 break :msg msg;
6804 };6811 };
6805 return sema.failWithOwnedErrorMsg(block, msg);6812 return sema.failWithOwnedErrorMsg(msg);
6806 }6813 }
6807 const lhs_ty = try sema.analyzeAsType(block, lhs_src, lhs);6814 const lhs_ty = try sema.analyzeAsType(block, lhs_src, lhs);
6808 const rhs_ty = try sema.analyzeAsType(block, rhs_src, rhs);6815 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...@@ -6927,7 +6934,7 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
6927 try sema.addDeclaredHereNote(msg, dest_ty);6934 try sema.addDeclaredHereNote(msg, dest_ty);
6928 break :msg msg;6935 break :msg msg;
6929 };6936 };
6930 return sema.failWithOwnedErrorMsg(block, msg);6937 return sema.failWithOwnedErrorMsg(msg);
6931 }6938 }
6932 return sema.addConstant(dest_ty, int_val);6939 return sema.addConstant(dest_ty, int_val);
6933 }6940 }
...@@ -7632,9 +7639,9 @@ fn funcCommon(...@@ -7632,9 +7639,9 @@ fn funcCommon(
7632 try sema.addDeclaredHereNote(msg, bare_return_type);7639 try sema.addDeclaredHereNote(msg, bare_return_type);
7633 break :msg msg;7640 break :msg msg;
7634 };7641 };
7635 return sema.failWithOwnedErrorMsg(block, msg);7642 return sema.failWithOwnedErrorMsg(msg);
7636 }7643 }
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)) {
7638 const msg = msg: {7645 const msg = msg: {
7639 const msg = try sema.errMsg(block, ret_ty_src, "return type '{}' not allowed in function with calling convention '{s}'", .{7646 const msg = try sema.errMsg(block, ret_ty_src, "return type '{}' not allowed in function with calling convention '{s}'", .{
7640 return_type.fmt(sema.mod), @tagName(cc_workaround),7647 return_type.fmt(sema.mod), @tagName(cc_workaround),
...@@ -7642,12 +7649,12 @@ fn funcCommon(...@@ -7642,12 +7649,12 @@ fn funcCommon(
7642 errdefer msg.destroy(sema.gpa);7649 errdefer msg.destroy(sema.gpa);
76437650
7644 const src_decl = sema.mod.declPtr(block.src_decl);7651 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
7647 try sema.addDeclaredHereNote(msg, return_type);7654 try sema.addDeclaredHereNote(msg, return_type);
7648 break :msg msg;7655 break :msg msg;
7649 };7656 };
7650 return sema.failWithOwnedErrorMsg(block, msg);7657 return sema.failWithOwnedErrorMsg(msg);
7651 }7658 }
76527659
7653 const arch = sema.mod.getTarget().cpu.arch;7660 const arch = sema.mod.getTarget().cpu.arch;
...@@ -7812,7 +7819,7 @@ fn analyzeParameter(...@@ -7812,7 +7819,7 @@ fn analyzeParameter(
7812 try sema.errNote(block, param_src, msg, "function is generic because of this parameter", .{});7819 try sema.errNote(block, param_src, msg, "function is generic because of this parameter", .{});
7813 break :msg msg;7820 break :msg msg;
7814 };7821 };
7815 return sema.failWithOwnedErrorMsg(block, msg);7822 return sema.failWithOwnedErrorMsg(msg);
7816 }7823 }
7817 if (this_generic and !Type.fnCallingConventionAllowsZigTypes(cc)) {7824 if (this_generic and !Type.fnCallingConventionAllowsZigTypes(cc)) {
7818 return sema.fail(block, param_src, "generic parameters not allowed in function with calling convention '{s}'", .{@tagName(cc)});7825 return sema.fail(block, param_src, "generic parameters not allowed in function with calling convention '{s}'", .{@tagName(cc)});
...@@ -7828,9 +7835,9 @@ fn analyzeParameter(...@@ -7828,9 +7835,9 @@ fn analyzeParameter(
7828 try sema.addDeclaredHereNote(msg, param.ty);7835 try sema.addDeclaredHereNote(msg, param.ty);
7829 break :msg msg;7836 break :msg msg;
7830 };7837 };
7831 return sema.failWithOwnedErrorMsg(block, msg);7838 return sema.failWithOwnedErrorMsg(msg);
7832 }7839 }
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)) {
7834 const msg = msg: {7841 const msg = msg: {
7835 const msg = try sema.errMsg(block, param_src, "parameter of type '{}' not allowed in function with calling convention '{s}'", .{7842 const msg = try sema.errMsg(block, param_src, "parameter of type '{}' not allowed in function with calling convention '{s}'", .{
7836 param.ty.fmt(sema.mod), @tagName(cc),7843 param.ty.fmt(sema.mod), @tagName(cc),
...@@ -7838,12 +7845,12 @@ fn analyzeParameter(...@@ -7838,12 +7845,12 @@ fn analyzeParameter(
7838 errdefer msg.destroy(sema.gpa);7845 errdefer msg.destroy(sema.gpa);
78397846
7840 const src_decl = sema.mod.declPtr(block.src_decl);7847 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
7843 try sema.addDeclaredHereNote(msg, param.ty);7850 try sema.addDeclaredHereNote(msg, param.ty);
7844 break :msg msg;7851 break :msg msg;
7845 };7852 };
7846 return sema.failWithOwnedErrorMsg(block, msg);7853 return sema.failWithOwnedErrorMsg(msg);
7847 }7854 }
7848 if (requires_comptime and !param.is_comptime) {7855 if (requires_comptime and !param.is_comptime) {
7849 const msg = msg: {7856 const msg = msg: {
...@@ -7855,7 +7862,7 @@ fn analyzeParameter(...@@ -7855,7 +7862,7 @@ fn analyzeParameter(
7855 try sema.addDeclaredHereNote(msg, param.ty);7862 try sema.addDeclaredHereNote(msg, param.ty);
7856 break :msg msg;7863 break :msg msg;
7857 };7864 };
7858 return sema.failWithOwnedErrorMsg(block, msg);7865 return sema.failWithOwnedErrorMsg(msg);
7859 }7866 }
7860}7867}
78617868
...@@ -8046,7 +8053,7 @@ fn zirPtrToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -8046,7 +8053,7 @@ fn zirPtrToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
8046 if (try sema.resolveMaybeUndefValIntable(block, ptr_src, ptr)) |ptr_val| {8053 if (try sema.resolveMaybeUndefValIntable(block, ptr_src, ptr)) |ptr_val| {
8047 return sema.addConstant(Type.usize, ptr_val);8054 return sema.addConstant(Type.usize, ptr_val);
8048 }8055 }
8049 try sema.requireRuntimeBlock(block, ptr_src, ptr_src);8056 try sema.requireRuntimeBlock(block, inst_data.src(), ptr_src);
8050 return block.addUnOp(.ptrtoint, ptr);8057 return block.addUnOp(.ptrtoint, ptr);
8051}8058}
80528059
...@@ -8288,6 +8295,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -8288,6 +8295,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
82888295
8289 const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);8296 const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
8290 const operand = try sema.resolveInst(extra.rhs);8297 const operand = try sema.resolveInst(extra.rhs);
8298 const operand_ty = sema.typeOf(operand);
8291 switch (dest_ty.zigTypeTag()) {8299 switch (dest_ty.zigTypeTag()) {
8292 .AnyFrame,8300 .AnyFrame,
8293 .ComptimeFloat,8301 .ComptimeFloat,
...@@ -8310,19 +8318,30 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -8310,19 +8318,30 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
8310 const msg = msg: {8318 const msg = msg: {
8311 const msg = try sema.errMsg(block, dest_ty_src, "cannot @bitCast to '{}'", .{dest_ty.fmt(sema.mod)});8319 const msg = try sema.errMsg(block, dest_ty_src, "cannot @bitCast to '{}'", .{dest_ty.fmt(sema.mod)});
8312 errdefer msg.destroy(sema.gpa);8320 errdefer msg.destroy(sema.gpa);
8313 switch (sema.typeOf(operand).zigTypeTag()) {8321 switch (operand_ty.zigTypeTag()) {
8314 .Int, .ComptimeInt => try sema.errNote(block, dest_ty_src, msg, "use @intToEnum for type coercion", .{}),8322 .Int, .ComptimeInt => try sema.errNote(block, dest_ty_src, msg, "use @intToEnum to cast from '{}'", .{operand_ty.fmt(sema.mod)}),
8315 else => {},8323 else => {},
8316 }8324 }
83178325
8318 break :msg msg;8326 break :msg msg;
8319 };8327 };
8320 return sema.failWithOwnedErrorMsg(block, msg);8328 return sema.failWithOwnedErrorMsg(msg);
8321 },8329 },
83228330
8323 .Pointer => return sema.fail(block, dest_ty_src, "cannot @bitCast to '{}', use @ptrCast to cast to a pointer", .{8331 .Pointer => {
8324 dest_ty.fmt(sema.mod),8332 const msg = msg: {
8325 }),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 },
8326 .Struct, .Union => if (dest_ty.containerLayout() == .Auto) {8345 .Struct, .Union => if (dest_ty.containerLayout() == .Auto) {
8327 const container = switch (dest_ty.zigTypeTag()) {8346 const container = switch (dest_ty.zigTypeTag()) {
8328 .Struct => "struct",8347 .Struct => "struct",
...@@ -8342,6 +8361,70 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -8342,6 +8361,70 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
8342 .Vector,8361 .Vector,
8343 => {},8362 => {},
8344 }8363 }
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 }
8345 return sema.bitCast(block, dest_ty, operand, operand_src);8428 return sema.bitCast(block, dest_ty, operand, operand_src);
8346}8429}
83478430
...@@ -8587,7 +8670,7 @@ fn zirSwitchCapture(...@@ -8587,7 +8670,7 @@ fn zirSwitchCapture(
8587 try sema.errNote(block, item_src, msg, "type '{}' here", .{field.ty.fmt(sema.mod)});8670 try sema.errNote(block, item_src, msg, "type '{}' here", .{field.ty.fmt(sema.mod)});
8588 break :msg msg;8671 break :msg msg;
8589 };8672 };
8590 return sema.failWithOwnedErrorMsg(block, msg);8673 return sema.failWithOwnedErrorMsg(msg);
8591 }8674 }
8592 }8675 }
85938676
...@@ -8710,7 +8793,7 @@ fn zirSwitchCond(...@@ -8710,7 +8793,7 @@ fn zirSwitchCond(
8710 }8793 }
8711 break :msg msg;8794 break :msg msg;
8712 };8795 };
8713 return sema.failWithOwnedErrorMsg(block, msg);8796 return sema.failWithOwnedErrorMsg(msg);
8714 };8797 };
8715 return sema.unionToTag(block, enum_ty, operand, src);8798 return sema.unionToTag(block, enum_ty, operand, src);
8716 },8799 },
...@@ -8799,7 +8882,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -8799,7 +8882,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
8799 );8882 );
8800 break :msg msg;8883 break :msg msg;
8801 };8884 };
8802 return sema.failWithOwnedErrorMsg(block, msg);8885 return sema.failWithOwnedErrorMsg(msg);
8803 }8886 }
88048887
8805 const target = sema.mod.getTarget();8888 const target = sema.mod.getTarget();
...@@ -8903,7 +8986,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -8903,7 +8986,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
8903 );8986 );
8904 break :msg msg;8987 break :msg msg;
8905 };8988 };
8906 return sema.failWithOwnedErrorMsg(block, msg);8989 return sema.failWithOwnedErrorMsg(msg);
8907 } else if (special_prong == .none and operand_ty.isNonexhaustiveEnum() and !union_originally) {8990 } else if (special_prong == .none and operand_ty.isNonexhaustiveEnum() and !union_originally) {
8908 return sema.fail(8991 return sema.fail(
8909 block,8992 block,
...@@ -9003,7 +9086,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9003,7 +9086,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9003 if (maybe_msg) |msg| {9086 if (maybe_msg) |msg| {
9004 maybe_msg = null;9087 maybe_msg = null;
9005 try sema.addDeclaredHereNote(msg, operand_ty);9088 try sema.addDeclaredHereNote(msg, operand_ty);
9006 return sema.failWithOwnedErrorMsg(block, msg);9089 return sema.failWithOwnedErrorMsg(msg);
9007 }9090 }
90089091
9009 if (special_prong == .@"else" and seen_errors.count() == operand_ty.errorSetNames().len) {9092 if (special_prong == .@"else" and seen_errors.count() == operand_ty.errorSetNames().len) {
...@@ -9812,7 +9895,7 @@ fn validateSwitchDupe(...@@ -9812,7 +9895,7 @@ fn validateSwitchDupe(
9812 );9895 );
9813 break :msg msg;9896 break :msg msg;
9814 };9897 };
9815 return sema.failWithOwnedErrorMsg(block, msg);9898 return sema.failWithOwnedErrorMsg(msg);
9816}9899}
98179900
9818fn validateSwitchItemBool(9901fn validateSwitchItemBool(
...@@ -9882,7 +9965,7 @@ fn validateSwitchNoRange(...@@ -9882,7 +9965,7 @@ fn validateSwitchNoRange(
9882 );9965 );
9883 break :msg msg;9966 break :msg msg;
9884 };9967 };
9885 return sema.failWithOwnedErrorMsg(block, msg);9968 return sema.failWithOwnedErrorMsg(msg);
9886}9969}
98879970
9888fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {9971fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -12806,7 +12889,7 @@ fn analyzeCmpUnionTag(...@@ -12806,7 +12889,7 @@ fn analyzeCmpUnionTag(
12806 try sema.mod.errNoteNonLazy(union_ty.declSrcLoc(sema.mod), msg, "union '{}' is not a tagged union", .{union_ty.fmt(sema.mod)});12889 try sema.mod.errNoteNonLazy(union_ty.declSrcLoc(sema.mod), msg, "union '{}' is not a tagged union", .{union_ty.fmt(sema.mod)});
12807 break :msg msg;12890 break :msg msg;
12808 };12891 };
12809 return sema.failWithOwnedErrorMsg(block, msg);12892 return sema.failWithOwnedErrorMsg(msg);
12810 };12893 };
12811 // Coerce both the union and the tag to the union's tag type, and then execute the12894 // Coerce both the union and the tag to the union's tag type, and then execute the
12812 // enum comparison codepath.12895 // enum comparison codepath.
...@@ -13067,6 +13150,36 @@ fn zirClosureGet(...@@ -13067,6 +13150,36 @@ fn zirClosureGet(
13067 scope = scope.parent.?;13150 scope = scope.parent.?;
13068 } else unreachable;13151 } 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
13070 return sema.addConstant(tv.ty, tv.val);13183 return sema.addConstant(tv.ty, tv.val);
13071}13184}
1307213185
...@@ -13098,8 +13211,8 @@ fn zirBuiltinSrc(...@@ -13098,8 +13211,8 @@ fn zirBuiltinSrc(
13098 const tracy = trace(@src());13211 const tracy = trace(@src());
13099 defer tracy.end();13212 defer tracy.end();
1310013213
13101 const src = sema.src; // TODO better source location13214 const extra = sema.code.extraData(Zir.Inst.Src, extended.operand).data;
13102 const extra = sema.code.extraData(Zir.Inst.LineColumn, extended.operand).data;13215 const src = LazySrcLoc.nodeOffset(extra.node);
13103 const func = sema.func orelse return sema.fail(block, src, "@src outside function", .{});13216 const func = sema.func orelse return sema.fail(block, src, "@src outside function", .{});
13104 const fn_owner_decl = sema.mod.declPtr(func.owner_decl);13217 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...@@ -14790,18 +14903,18 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
14790 } else if (inst_data.size == .Many and elem_ty.zigTypeTag() == .Opaque) {14903 } else if (inst_data.size == .Many and elem_ty.zigTypeTag() == .Opaque) {
14791 return sema.fail(block, elem_ty_src, "unknown-length pointer to opaque not allowed", .{});14904 return sema.fail(block, elem_ty_src, "unknown-length pointer to opaque not allowed", .{});
14792 } else if (inst_data.size == .C) {14905 } else if (inst_data.size == .C) {
14793 if (!(try sema.validateExternType(elem_ty, .other))) {14906 if (!sema.validateExternType(elem_ty, .other)) {
14794 const msg = msg: {14907 const msg = msg: {
14795 const msg = try sema.errMsg(block, elem_ty_src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(sema.mod)});14908 const msg = try sema.errMsg(block, elem_ty_src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(sema.mod)});
14796 errdefer msg.destroy(sema.gpa);14909 errdefer msg.destroy(sema.gpa);
1479714910
14798 const src_decl = sema.mod.declPtr(block.src_decl);14911 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
14801 try sema.addDeclaredHereNote(msg, elem_ty);14914 try sema.addDeclaredHereNote(msg, elem_ty);
14802 break :msg msg;14915 break :msg msg;
14803 };14916 };
14804 return sema.failWithOwnedErrorMsg(block, msg);14917 return sema.failWithOwnedErrorMsg(msg);
14805 }14918 }
14806 if (elem_ty.zigTypeTag() == .Opaque) {14919 if (elem_ty.zigTypeTag() == .Opaque) {
14807 return sema.fail(block, elem_ty_src, "C pointers cannot point to opaque types", .{});14920 return sema.fail(block, elem_ty_src, "C pointers cannot point to opaque types", .{});
...@@ -14972,7 +15085,7 @@ fn zirStructInit(...@@ -14972,7 +15085,7 @@ fn zirStructInit(
14972 try sema.errNote(block, other_field_src, msg, "other field here", .{});15085 try sema.errNote(block, other_field_src, msg, "other field here", .{});
14973 break :msg msg;15086 break :msg msg;
14974 };15087 };
14975 return sema.failWithOwnedErrorMsg(block, msg);15088 return sema.failWithOwnedErrorMsg(msg);
14976 }15089 }
14977 found_fields[field_index] = item.data.field_type;15090 found_fields[field_index] = item.data.field_type;
14978 field_inits[field_index] = try sema.resolveInst(item.data.init);15091 field_inits[field_index] = try sema.resolveInst(item.data.init);
...@@ -15113,7 +15226,7 @@ fn finishStructInit(...@@ -15113,7 +15226,7 @@ fn finishStructInit(
15113 .{fqn},15226 .{fqn},
15114 );15227 );
15115 }15228 }
15116 return sema.failWithOwnedErrorMsg(block, msg);15229 return sema.failWithOwnedErrorMsg(msg);
15117 }15230 }
1511815231
15119 const is_comptime = for (field_inits) |field_init| {15232 const is_comptime = for (field_inits) |field_init| {
...@@ -15188,7 +15301,7 @@ fn zirStructInitAnon(...@@ -15188,7 +15301,7 @@ fn zirStructInitAnon(
15188 try sema.errNote(block, prev_source, msg, "other field here", .{});15301 try sema.errNote(block, prev_source, msg, "other field here", .{});
15189 break :msg msg;15302 break :msg msg;
15190 };15303 };
15191 return sema.failWithOwnedErrorMsg(block, msg);15304 return sema.failWithOwnedErrorMsg(msg);
15192 }15305 }
15193 gop.value_ptr.* = @intCast(u32, i);15306 gop.value_ptr.* = @intCast(u32, i);
1519415307
...@@ -15204,7 +15317,7 @@ fn zirStructInitAnon(...@@ -15204,7 +15317,7 @@ fn zirStructInitAnon(
15204 try sema.addDeclaredHereNote(msg, types[i]);15317 try sema.addDeclaredHereNote(msg, types[i]);
15205 break :msg msg;15318 break :msg msg;
15206 };15319 };
15207 return sema.failWithOwnedErrorMsg(block, msg);15320 return sema.failWithOwnedErrorMsg(msg);
15208 }15321 }
15209 const init_src = src; // TODO better source location15322 const init_src = src; // TODO better source location
15210 if (try sema.resolveMaybeUndefVal(block, init_src, init)) |init_val| {15323 if (try sema.resolveMaybeUndefVal(block, init_src, init)) |init_val| {
...@@ -15402,7 +15515,7 @@ fn zirArrayInitAnon(...@@ -15402,7 +15515,7 @@ fn zirArrayInitAnon(
15402 try sema.addDeclaredHereNote(msg, types[i]);15515 try sema.addDeclaredHereNote(msg, types[i]);
15403 break :msg msg;15516 break :msg msg;
15404 };15517 };
15405 return sema.failWithOwnedErrorMsg(block, msg);15518 return sema.failWithOwnedErrorMsg(msg);
15406 }15519 }
15407 if (try sema.resolveMaybeUndefVal(block, operand_src, elem)) |val| {15520 if (try sema.resolveMaybeUndefVal(block, operand_src, elem)) |val| {
15408 values[i] = val;15521 values[i] = val;
...@@ -15716,7 +15829,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -15716,7 +15829,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
15716 try sema.addDeclaredHereNote(msg, operand_ty);15829 try sema.addDeclaredHereNote(msg, operand_ty);
15717 break :msg msg;15830 break :msg msg;
15718 };15831 };
15719 return sema.failWithOwnedErrorMsg(block, msg);15832 return sema.failWithOwnedErrorMsg(msg);
15720 },15833 },
15721 else => return sema.fail(block, operand_src, "expected enum or union; found '{}'", .{15834 else => return sema.fail(block, operand_src, "expected enum or union; found '{}'", .{
15722 operand_ty.fmt(mod),15835 operand_ty.fmt(mod),
...@@ -15735,7 +15848,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -15735,7 +15848,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
15735 try mod.errNoteNonLazy(enum_decl.srcLoc(), msg, "declared here", .{});15848 try mod.errNoteNonLazy(enum_decl.srcLoc(), msg, "declared here", .{});
15736 break :msg msg;15849 break :msg msg;
15737 };15850 };
15738 return sema.failWithOwnedErrorMsg(block, msg);15851 return sema.failWithOwnedErrorMsg(msg);
15739 };15852 };
15740 const field_name = enum_ty.enumFieldName(field_index);15853 const field_name = enum_ty.enumFieldName(field_index);
15741 return sema.addStrLit(block, field_name);15854 return sema.addStrLit(block, field_name);
...@@ -15874,18 +15987,18 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I...@@ -15874,18 +15987,18 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
15874 } else if (ptr_size == .Many and elem_ty.zigTypeTag() == .Opaque) {15987 } else if (ptr_size == .Many and elem_ty.zigTypeTag() == .Opaque) {
15875 return sema.fail(block, src, "unknown-length pointer to opaque not allowed", .{});15988 return sema.fail(block, src, "unknown-length pointer to opaque not allowed", .{});
15876 } else if (ptr_size == .C) {15989 } else if (ptr_size == .C) {
15877 if (!(try sema.validateExternType(elem_ty, .other))) {15990 if (!sema.validateExternType(elem_ty, .other)) {
15878 const msg = msg: {15991 const msg = msg: {
15879 const msg = try sema.errMsg(block, src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(sema.mod)});15992 const msg = try sema.errMsg(block, src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(sema.mod)});
15880 errdefer msg.destroy(sema.gpa);15993 errdefer msg.destroy(sema.gpa);
1588115994
15882 const src_decl = sema.mod.declPtr(block.src_decl);15995 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
15885 try sema.addDeclaredHereNote(msg, elem_ty);15998 try sema.addDeclaredHereNote(msg, elem_ty);
15886 break :msg msg;15999 break :msg msg;
15887 };16000 };
15888 return sema.failWithOwnedErrorMsg(block, msg);16001 return sema.failWithOwnedErrorMsg(msg);
15889 }16002 }
15890 if (elem_ty.zigTypeTag() == .Opaque) {16003 if (elem_ty.zigTypeTag() == .Opaque) {
15891 return sema.fail(block, src, "C pointers cannot point to opaque types", .{});16004 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...@@ -16288,7 +16401,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
16288 try sema.addDeclaredHereNote(msg, union_obj.tag_ty);16401 try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
16289 break :msg msg;16402 break :msg msg;
16290 };16403 };
16291 return sema.failWithOwnedErrorMsg(block, msg);16404 return sema.failWithOwnedErrorMsg(msg);
16292 }16405 }
16293 }16406 }
1629416407
...@@ -16322,7 +16435,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I...@@ -16322,7 +16435,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
16322 try sema.addDeclaredHereNote(msg, union_obj.tag_ty);16435 try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
16323 break :msg msg;16436 break :msg msg;
16324 };16437 };
16325 return sema.failWithOwnedErrorMsg(block, msg);16438 return sema.failWithOwnedErrorMsg(msg);
16326 }16439 }
16327 }16440 }
1632816441
...@@ -16353,10 +16466,24 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I...@@ -16353,10 +16466,24 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
16353 return sema.fail(block, src, "varargs functions must have C calling convention", .{});16466 return sema.fail(block, src, "varargs functions must have C calling convention", .{});
16354 }16467 }
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 };
16357 var buf: Value.ToTypeBuffer = undefined;16480 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 &.{};
16360 var param_types = try sema.arena.alloc(Type, args.len);16487 var param_types = try sema.arena.alloc(Type, args.len);
16361 var comptime_params = try sema.arena.alloc(bool, args.len);16488 var comptime_params = try sema.arena.alloc(bool, args.len);
16362 var noalias_bits: u32 = 0;16489 var noalias_bits: u32 = 0;
...@@ -16778,7 +16905,7 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat...@@ -16778,7 +16905,7 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat
16778 try sema.addDeclaredHereNote(msg, dest_ty);16905 try sema.addDeclaredHereNote(msg, dest_ty);
16779 break :msg msg;16906 break :msg msg;
16780 };16907 };
16781 return sema.failWithOwnedErrorMsg(block, msg);16908 return sema.failWithOwnedErrorMsg(msg);
16782 }16909 }
1678316910
16784 if (maybe_operand_val) |val| {16911 if (maybe_operand_val) |val| {
...@@ -16796,7 +16923,7 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat...@@ -16796,7 +16923,7 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat
16796 try sema.addDeclaredHereNote(msg, dest_ty);16923 try sema.addDeclaredHereNote(msg, dest_ty);
16797 break :msg msg;16924 break :msg msg;
16798 };16925 };
16799 return sema.failWithOwnedErrorMsg(block, msg);16926 return sema.failWithOwnedErrorMsg(msg);
16800 }16927 }
16801 }16928 }
1680216929
...@@ -16934,7 +17061,7 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -16934,7 +17061,7 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
16934 });17061 });
16935 break :msg msg;17062 break :msg msg;
16936 };17063 };
16937 return sema.failWithOwnedErrorMsg(block, msg);17064 return sema.failWithOwnedErrorMsg(msg);
16938 }17065 }
16939 }17066 }
1694017067
...@@ -17225,7 +17352,7 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6...@@ -17225,7 +17352,7 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6
17225 try sema.addDeclaredHereNote(msg, ty);17352 try sema.addDeclaredHereNote(msg, ty);
17226 break :msg msg;17353 break :msg msg;
17227 };17354 };
17228 return sema.failWithOwnedErrorMsg(block, msg);17355 return sema.failWithOwnedErrorMsg(msg);
17229 },17356 },
17230 }17357 }
1723117358
...@@ -17329,7 +17456,7 @@ fn checkPtrOperand(...@@ -17329,7 +17456,7 @@ fn checkPtrOperand(
1732917456
17330 break :msg msg;17457 break :msg msg;
17331 };17458 };
17332 return sema.failWithOwnedErrorMsg(block, msg);17459 return sema.failWithOwnedErrorMsg(msg);
17333 },17460 },
17334 .Optional => if (ty.isPtrLikeOptional()) return,17461 .Optional => if (ty.isPtrLikeOptional()) return,
17335 else => {},17462 else => {},
...@@ -17359,7 +17486,7 @@ fn checkPtrType(...@@ -17359,7 +17486,7 @@ fn checkPtrType(
1735917486
17360 break :msg msg;17487 break :msg msg;
17361 };17488 };
17362 return sema.failWithOwnedErrorMsg(block, msg);17489 return sema.failWithOwnedErrorMsg(msg);
17363 },17490 },
17364 .Optional => if (ty.isPtrLikeOptional()) return,17491 .Optional => if (ty.isPtrLikeOptional()) return,
17365 else => {},17492 else => {},
...@@ -17495,7 +17622,7 @@ fn checkComptimeVarStore(...@@ -17495,7 +17622,7 @@ fn checkComptimeVarStore(
17495 try sema.errNote(block, cond_src, msg, "runtime condition here", .{});17622 try sema.errNote(block, cond_src, msg, "runtime condition here", .{});
17496 break :msg msg;17623 break :msg msg;
17497 };17624 };
17498 return sema.failWithOwnedErrorMsg(block, msg);17625 return sema.failWithOwnedErrorMsg(msg);
17499 }17626 }
17500 if (block.runtime_loop) |loop_src| {17627 if (block.runtime_loop) |loop_src| {
17501 const msg = msg: {17628 const msg = msg: {
...@@ -17504,7 +17631,7 @@ fn checkComptimeVarStore(...@@ -17504,7 +17631,7 @@ fn checkComptimeVarStore(
17504 try sema.errNote(block, loop_src, msg, "non-inline loop here", .{});17631 try sema.errNote(block, loop_src, msg, "non-inline loop here", .{});
17505 break :msg msg;17632 break :msg msg;
17506 };17633 };
17507 return sema.failWithOwnedErrorMsg(block, msg);17634 return sema.failWithOwnedErrorMsg(msg);
17508 }17635 }
17509 unreachable;17636 unreachable;
17510 }17637 }
...@@ -17641,7 +17768,7 @@ fn checkVectorizableBinaryOperands(...@@ -17641,7 +17768,7 @@ fn checkVectorizableBinaryOperands(
17641 try sema.errNote(block, rhs_src, msg, "length {d} here", .{rhs_len});17768 try sema.errNote(block, rhs_src, msg, "length {d} here", .{rhs_len});
17642 break :msg msg;17769 break :msg msg;
17643 };17770 };
17644 return sema.failWithOwnedErrorMsg(block, msg);17771 return sema.failWithOwnedErrorMsg(msg);
17645 }17772 }
17646 } else {17773 } else {
17647 const msg = msg: {17774 const msg = msg: {
...@@ -17658,7 +17785,7 @@ fn checkVectorizableBinaryOperands(...@@ -17658,7 +17785,7 @@ fn checkVectorizableBinaryOperands(
17658 }17785 }
17659 break :msg msg;17786 break :msg msg;
17660 };17787 };
17661 return sema.failWithOwnedErrorMsg(block, msg);17788 return sema.failWithOwnedErrorMsg(msg);
17662 }17789 }
17663}17790}
1766417791
...@@ -18058,7 +18185,7 @@ fn analyzeShuffle(...@@ -18058,7 +18185,7 @@ fn analyzeShuffle(
1805818185
18059 break :msg msg;18186 break :msg msg;
18060 };18187 };
18061 return sema.failWithOwnedErrorMsg(block, msg);18188 return sema.failWithOwnedErrorMsg(msg);
18062 }18189 }
18063 }18190 }
1806418191
...@@ -18654,7 +18781,7 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr...@@ -18654,7 +18781,7 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
18654 try sema.addDeclaredHereNote(msg, struct_ty);18781 try sema.addDeclaredHereNote(msg, struct_ty);
18655 break :msg msg;18782 break :msg msg;
18656 };18783 };
18657 return sema.failWithOwnedErrorMsg(block, msg);18784 return sema.failWithOwnedErrorMsg(msg);
18658 }18785 }
18659 return sema.addConstant(result_ptr, payload.data.container_ptr);18786 return sema.addConstant(result_ptr, payload.data.container_ptr);
18660 }18787 }
...@@ -18883,10 +19010,8 @@ fn zirVarExtended(...@@ -18883,10 +19010,8 @@ fn zirVarExtended(
18883 extended: Zir.Inst.Extended.InstData,19010 extended: Zir.Inst.Extended.InstData,
18884) CompileError!Air.Inst.Ref {19011) CompileError!Air.Inst.Ref {
18885 const extra = sema.code.extraData(Zir.Inst.ExtendedVar, extended.operand);19012 const extra = sema.code.extraData(Zir.Inst.ExtendedVar, extended.operand);
18886 const src = sema.src;19013 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = 0 };
18887 const ty_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at type19014 const init_src: LazySrcLoc = .{ .node_offset_var_decl_init = 0 };
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
18890 const small = @bitCast(Zir.Inst.ExtendedVar.Small, extended.small);19015 const small = @bitCast(Zir.Inst.ExtendedVar.Small, extended.small);
1889119016
18892 var extra_index: usize = extra.end;19017 var extra_index: usize = extra.end;
...@@ -18900,12 +19025,6 @@ fn zirVarExtended(...@@ -18900,12 +19025,6 @@ fn zirVarExtended(
18900 // ZIR supports encoding this information but it is not used; the information19025 // ZIR supports encoding this information but it is not used; the information
18901 // is encoded via the Decl entry.19026 // is encoded via the Decl entry.
18902 assert(!small.has_align);19027 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
18910 const uncasted_init: Air.Inst.Ref = if (small.has_init) blk: {19029 const uncasted_init: Air.Inst.Ref = if (small.has_init) blk: {
18911 const init_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);19030 const init_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
...@@ -18929,7 +19048,7 @@ fn zirVarExtended(...@@ -18929,7 +19048,7 @@ fn zirVarExtended(
18929 return sema.failWithNeededComptime(block, init_src, "container level variable initializers must be comptime known");19048 return sema.failWithNeededComptime(block, init_src, "container level variable initializers must be comptime known");
18930 } else Value.initTag(.unreachable_value);19049 } 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
18934 const new_var = try sema.gpa.create(Module.Var);19053 const new_var = try sema.gpa.create(Module.Var);
18935 errdefer sema.gpa.destroy(new_var);19054 errdefer sema.gpa.destroy(new_var);
...@@ -19462,7 +19581,7 @@ fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src:...@@ -19462,7 +19581,7 @@ fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src:
19462 }19581 }
19463 break :msg msg;19582 break :msg msg;
19464 };19583 };
19465 return sema.failWithOwnedErrorMsg(block, msg);19584 return sema.failWithOwnedErrorMsg(msg);
19466 }19585 }
19467 try sema.requireFunctionBlock(block, src);19586 try sema.requireFunctionBlock(block, src);
19468}19587}
...@@ -19491,7 +19610,7 @@ fn validateVarType(...@@ -19491,7 +19610,7 @@ fn validateVarType(
1949119610
19492 break :msg msg;19611 break :msg msg;
19493 };19612 };
19494 return sema.failWithOwnedErrorMsg(block, msg);19613 return sema.failWithOwnedErrorMsg(msg);
19495}19614}
1949619615
19497fn validateRunTimeType(19616fn validateRunTimeType(
...@@ -19668,7 +19787,7 @@ const ExternPosition = enum {...@@ -19668,7 +19787,7 @@ const ExternPosition = enum {
1966819787
19669/// Returns true if `ty` is allowed in extern types.19788/// Returns true if `ty` is allowed in extern types.
19670/// Does *NOT* require `ty` to be resolved in any way.19789/// 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 {
19672 switch (ty.zigTypeTag()) {19791 switch (ty.zigTypeTag()) {
19673 .Type,19792 .Type,
19674 .ComptimeFloat,19793 .ComptimeFloat,
...@@ -19713,8 +19832,6 @@ fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) CompileEr...@@ -19713,8 +19832,6 @@ fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) CompileEr
1971319832
19714fn explainWhyTypeIsNotExtern(19833fn explainWhyTypeIsNotExtern(
19715 sema: *Sema,19834 sema: *Sema,
19716 block: *Block,
19717 src: LazySrcLoc,
19718 msg: *Module.ErrorMsg,19835 msg: *Module.ErrorMsg,
19719 src_loc: Module.SrcLoc,19836 src_loc: Module.SrcLoc,
19720 ty: Type,19837 ty: Type,
...@@ -19758,7 +19875,7 @@ fn explainWhyTypeIsNotExtern(...@@ -19758,7 +19875,7 @@ fn explainWhyTypeIsNotExtern(
19758 var buf: Type.Payload.Bits = undefined;19875 var buf: Type.Payload.Bits = undefined;
19759 const tag_ty = ty.intTagType(&buf);19876 const tag_ty = ty.intTagType(&buf);
19760 try mod.errNoteNonLazy(src_loc, msg, "enum tag type '{}' is not extern compatible", .{tag_ty.fmt(sema.mod)});19877 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);
19762 },19879 },
19763 .Struct => try mod.errNoteNonLazy(src_loc, msg, "only structs with packed or extern layout are extern compatible", .{}),19880 .Struct => try mod.errNoteNonLazy(src_loc, msg, "only structs with packed or extern layout are extern compatible", .{}),
19764 .Union => try mod.errNoteNonLazy(src_loc, msg, "only unions with packed or extern layout are extern compatible", .{}),19881 .Union => try mod.errNoteNonLazy(src_loc, msg, "only unions with packed or extern layout are extern compatible", .{}),
...@@ -19768,13 +19885,87 @@ fn explainWhyTypeIsNotExtern(...@@ -19768,13 +19885,87 @@ fn explainWhyTypeIsNotExtern(
19768 } else if (position == .param_ty) {19885 } else if (position == .param_ty) {
19769 return mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a parameter type", .{});19886 return mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a parameter type", .{});
19770 }19887 }
19771 try sema.explainWhyTypeIsNotExtern(block, src, msg, src_loc, ty.elemType2(), position);19888 try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(), position);
19772 },19889 },
19773 .Vector => try sema.explainWhyTypeIsNotExtern(block, src, msg, src_loc, ty.elemType2(), position),19890 .Vector => try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(), position),
19774 .Optional => try mod.errNoteNonLazy(src_loc, msg, "only pointer like optionals are extern compatible", .{}),19891 .Optional => try mod.errNoteNonLazy(src_loc, msg, "only pointer like optionals are extern compatible", .{}),
19775 }19892 }
19776}19893}
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
19778pub const PanicId = enum {19969pub const PanicId = enum {
19779 unreach,19970 unreach,
19780 unwrap_null,19971 unwrap_null,
...@@ -19994,6 +20185,77 @@ fn panicIndexOutOfBounds(...@@ -19994,6 +20185,77 @@ fn panicIndexOutOfBounds(
19994 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);20185 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
19995}20186}
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
19997fn safetyPanic(20259fn safetyPanic(
19998 sema: *Sema,20260 sema: *Sema,
19999 block: *Block,20261 block: *Block,
...@@ -20050,7 +20312,7 @@ fn emitBackwardBranch(sema: *Sema, block: *Block, src: LazySrcLoc) !void {...@@ -20050,7 +20312,7 @@ fn emitBackwardBranch(sema: *Sema, block: *Block, src: LazySrcLoc) !void {
20050 "use @setEvalBranchQuota() to raise the branch limit from {d}",20312 "use @setEvalBranchQuota() to raise the branch limit from {d}",
20051 .{sema.branch_quota},20313 .{sema.branch_quota},
20052 );20314 );
20053 return sema.failWithOwnedErrorMsg(block, msg);20315 return sema.failWithOwnedErrorMsg(msg);
20054 }20316 }
20055}20317}
2005620318
...@@ -20158,7 +20420,7 @@ fn fieldVal(...@@ -20158,7 +20420,7 @@ fn fieldVal(
20158 try sema.addDeclaredHereNote(msg, child_type);20420 try sema.addDeclaredHereNote(msg, child_type);
20159 break :msg msg;20421 break :msg msg;
20160 };20422 };
20161 return sema.failWithOwnedErrorMsg(block, msg);20423 return sema.failWithOwnedErrorMsg(msg);
20162 } else (try sema.mod.getErrorValue(field_name)).key;20424 } else (try sema.mod.getErrorValue(field_name)).key;
2016320425
20164 return sema.addConstant(20426 return sema.addConstant(
...@@ -20213,7 +20475,7 @@ fn fieldVal(...@@ -20213,7 +20475,7 @@ fn fieldVal(
20213 if (child_type.zigTypeTag() == .Array) try sema.errNote(block, src, msg, "array values have 'len' member", .{});20475 if (child_type.zigTypeTag() == .Array) try sema.errNote(block, src, msg, "array values have 'len' member", .{});
20214 break :msg msg;20476 break :msg msg;
20215 };20477 };
20216 return sema.failWithOwnedErrorMsg(block, msg);20478 return sema.failWithOwnedErrorMsg(msg);
20217 },20479 },
20218 }20480 }
20219 },20481 },
...@@ -20551,7 +20813,7 @@ fn fieldCallBind(...@@ -20551,7 +20813,7 @@ fn fieldCallBind(
20551 try sema.addDeclaredHereNote(msg, concrete_ty);20813 try sema.addDeclaredHereNote(msg, concrete_ty);
20552 break :msg msg;20814 break :msg msg;
20553 };20815 };
20554 return sema.failWithOwnedErrorMsg(block, msg);20816 return sema.failWithOwnedErrorMsg(msg);
20555}20817}
2055620818
20557fn finishFieldCallBind(20819fn finishFieldCallBind(
...@@ -20606,7 +20868,7 @@ fn namespaceLookup(...@@ -20606,7 +20868,7 @@ fn namespaceLookup(
20606 try sema.mod.errNoteNonLazy(decl.srcLoc(), msg, "declared here", .{});20868 try sema.mod.errNoteNonLazy(decl.srcLoc(), msg, "declared here", .{});
20607 break :msg msg;20869 break :msg msg;
20608 };20870 };
20609 return sema.failWithOwnedErrorMsg(block, msg);20871 return sema.failWithOwnedErrorMsg(msg);
20610 }20872 }
20611 return decl_index;20873 return decl_index;
20612 }20874 }
...@@ -20621,7 +20883,14 @@ fn namespaceLookupRef(...@@ -20621,7 +20883,14 @@ fn namespaceLookupRef(
20621 decl_name: []const u8,20883 decl_name: []const u8,
20622) CompileError!?Air.Inst.Ref {20884) CompileError!?Air.Inst.Ref {
20623 const decl = (try sema.namespaceLookup(block, src, namespace, decl_name)) orelse return null;20885 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 };
20625}20894}
2062620895
20627fn namespaceLookupVal(20896fn namespaceLookupVal(
...@@ -20939,7 +21208,7 @@ fn unionFieldPtr(...@@ -20939,7 +21208,7 @@ fn unionFieldPtr(
20939 try sema.addDeclaredHereNote(msg, union_ty);21208 try sema.addDeclaredHereNote(msg, union_ty);
20940 break :msg msg;21209 break :msg msg;
20941 };21210 };
20942 return sema.failWithOwnedErrorMsg(block, msg);21211 return sema.failWithOwnedErrorMsg(msg);
20943 }21212 }
20944 },21213 },
20945 .Packed, .Extern => {},21214 .Packed, .Extern => {},
...@@ -21009,7 +21278,7 @@ fn unionFieldVal(...@@ -21009,7 +21278,7 @@ fn unionFieldVal(
21009 try sema.addDeclaredHereNote(msg, union_ty);21278 try sema.addDeclaredHereNote(msg, union_ty);
21010 break :msg msg;21279 break :msg msg;
21011 };21280 };
21012 return sema.failWithOwnedErrorMsg(block, msg);21281 return sema.failWithOwnedErrorMsg(msg);
21013 }21282 }
21014 },21283 },
21015 .Packed, .Extern => {21284 .Packed, .Extern => {
...@@ -21183,7 +21452,7 @@ fn validateRuntimeElemAccess(...@@ -21183,7 +21452,7 @@ fn validateRuntimeElemAccess(
2118321452
21184 break :msg msg;21453 break :msg msg;
21185 };21454 };
21186 return sema.failWithOwnedErrorMsg(block, msg);21455 return sema.failWithOwnedErrorMsg(msg);
21187 }21456 }
21188}21457}
2118921458
...@@ -21959,7 +22228,7 @@ fn coerceExtra(...@@ -21959,7 +22228,7 @@ fn coerceExtra(
21959 try sema.addDeclaredHereNote(msg, dest_ty);22228 try sema.addDeclaredHereNote(msg, dest_ty);
21960 break :msg msg;22229 break :msg msg;
21961 };22230 };
21962 return sema.failWithOwnedErrorMsg(block, msg);22231 return sema.failWithOwnedErrorMsg(msg);
21963 };22232 };
21964 return sema.addConstant(22233 return sema.addConstant(
21965 dest_ty,22234 dest_ty,
...@@ -22087,7 +22356,7 @@ fn coerceExtra(...@@ -22087,7 +22356,7 @@ fn coerceExtra(
22087 try sema.mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl), msg, "'noreturn' declared here", .{});22356 try sema.mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl), msg, "'noreturn' declared here", .{});
22088 break :msg msg;22357 break :msg msg;
22089 };22358 };
22090 return sema.failWithOwnedErrorMsg(block, msg);22359 return sema.failWithOwnedErrorMsg(msg);
22091 }22360 }
2209222361
22093 const msg = msg: {22362 const msg = msg: {
...@@ -22128,7 +22397,7 @@ fn coerceExtra(...@@ -22128,7 +22397,7 @@ fn coerceExtra(
2212822397
22129 break :msg msg;22398 break :msg msg;
22130 };22399 };
22131 return sema.failWithOwnedErrorMsg(block, msg);22400 return sema.failWithOwnedErrorMsg(msg);
22132}22401}
2213322402
22134const InMemoryCoercionResult = union(enum) {22403const InMemoryCoercionResult = union(enum) {
...@@ -24193,7 +24462,7 @@ fn coerceEnumToUnion(...@@ -24193,7 +24462,7 @@ fn coerceEnumToUnion(
24193 try sema.addDeclaredHereNote(msg, union_ty);24462 try sema.addDeclaredHereNote(msg, union_ty);
24194 break :msg msg;24463 break :msg msg;
24195 };24464 };
24196 return sema.failWithOwnedErrorMsg(block, msg);24465 return sema.failWithOwnedErrorMsg(msg);
24197 };24466 };
2419824467
24199 const enum_tag = try sema.coerce(block, tag_ty, inst, inst_src);24468 const enum_tag = try sema.coerce(block, tag_ty, inst, inst_src);
...@@ -24208,7 +24477,7 @@ fn coerceEnumToUnion(...@@ -24208,7 +24477,7 @@ fn coerceEnumToUnion(
24208 try sema.addDeclaredHereNote(msg, union_ty);24477 try sema.addDeclaredHereNote(msg, union_ty);
24209 break :msg msg;24478 break :msg msg;
24210 };24479 };
24211 return sema.failWithOwnedErrorMsg(block, msg);24480 return sema.failWithOwnedErrorMsg(msg);
24212 };24481 };
24213 const field = union_obj.fields.values()[field_index];24482 const field = union_obj.fields.values()[field_index];
24214 const field_ty = try sema.resolveTypeFields(block, inst_src, field.ty);24483 const field_ty = try sema.resolveTypeFields(block, inst_src, field.ty);
...@@ -24224,7 +24493,7 @@ fn coerceEnumToUnion(...@@ -24224,7 +24493,7 @@ fn coerceEnumToUnion(
24224 try sema.addDeclaredHereNote(msg, union_ty);24493 try sema.addDeclaredHereNote(msg, union_ty);
24225 break :msg msg;24494 break :msg msg;
24226 };24495 };
24227 return sema.failWithOwnedErrorMsg(block, msg);24496 return sema.failWithOwnedErrorMsg(msg);
24228 };24497 };
2422924498
24230 return sema.addConstant(union_ty, try Value.Tag.@"union".create(sema.arena, .{24499 return sema.addConstant(union_ty, try Value.Tag.@"union".create(sema.arena, .{
...@@ -24244,7 +24513,7 @@ fn coerceEnumToUnion(...@@ -24244,7 +24513,7 @@ fn coerceEnumToUnion(
24244 try sema.addDeclaredHereNote(msg, tag_ty);24513 try sema.addDeclaredHereNote(msg, tag_ty);
24245 break :msg msg;24514 break :msg msg;
24246 };24515 };
24247 return sema.failWithOwnedErrorMsg(block, msg);24516 return sema.failWithOwnedErrorMsg(msg);
24248 }24517 }
2424924518
24250 // If the union has all fields 0 bits, the union value is just the enum value.24519 // If the union has all fields 0 bits, the union value is just the enum value.
...@@ -24273,7 +24542,7 @@ fn coerceEnumToUnion(...@@ -24273,7 +24542,7 @@ fn coerceEnumToUnion(
24273 try sema.addDeclaredHereNote(msg, union_ty);24542 try sema.addDeclaredHereNote(msg, union_ty);
24274 break :msg msg;24543 break :msg msg;
24275 };24544 };
24276 return sema.failWithOwnedErrorMsg(block, msg);24545 return sema.failWithOwnedErrorMsg(msg);
24277}24546}
2427824547
24279fn coerceAnonStructToUnion(24548fn coerceAnonStructToUnion(
...@@ -24302,7 +24571,7 @@ fn coerceAnonStructToUnion(...@@ -24302,7 +24571,7 @@ fn coerceAnonStructToUnion(
24302 try sema.addDeclaredHereNote(msg, union_ty);24571 try sema.addDeclaredHereNote(msg, union_ty);
24303 break :msg msg;24572 break :msg msg;
24304 };24573 };
24305 return sema.failWithOwnedErrorMsg(block, msg);24574 return sema.failWithOwnedErrorMsg(msg);
24306 }24575 }
2430724576
24308 const field_name = anon_struct.names[0];24577 const field_name = anon_struct.names[0];
...@@ -24362,7 +24631,7 @@ fn coerceArrayLike(...@@ -24362,7 +24631,7 @@ fn coerceArrayLike(
24362 try sema.errNote(block, inst_src, msg, "source has length {d}", .{inst_len});24631 try sema.errNote(block, inst_src, msg, "source has length {d}", .{inst_len});
24363 break :msg msg;24632 break :msg msg;
24364 };24633 };
24365 return sema.failWithOwnedErrorMsg(block, msg);24634 return sema.failWithOwnedErrorMsg(msg);
24366 }24635 }
2436724636
24368 const dest_elem_ty = dest_ty.childType();24637 const dest_elem_ty = dest_ty.childType();
...@@ -24434,7 +24703,7 @@ fn coerceTupleToArray(...@@ -24434,7 +24703,7 @@ fn coerceTupleToArray(
24434 try sema.errNote(block, inst_src, msg, "source has length {d}", .{inst_len});24703 try sema.errNote(block, inst_src, msg, "source has length {d}", .{inst_len});
24435 break :msg msg;24704 break :msg msg;
24436 };24705 };
24437 return sema.failWithOwnedErrorMsg(block, msg);24706 return sema.failWithOwnedErrorMsg(msg);
24438 }24707 }
2443924708
24440 const dest_elems = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLenIncludingSentinel());24709 const dest_elems = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLenIncludingSentinel());
...@@ -24597,7 +24866,7 @@ fn coerceTupleToStruct(...@@ -24597,7 +24866,7 @@ fn coerceTupleToStruct(
2459724866
24598 if (root_msg) |msg| {24867 if (root_msg) |msg| {
24599 try sema.addDeclaredHereNote(msg, struct_ty);24868 try sema.addDeclaredHereNote(msg, struct_ty);
24600 return sema.failWithOwnedErrorMsg(block, msg);24869 return sema.failWithOwnedErrorMsg(msg);
24601 }24870 }
2460224871
24603 if (runtime_src) |rs| {24872 if (runtime_src) |rs| {
...@@ -24701,7 +24970,7 @@ fn coerceTupleToTuple(...@@ -24701,7 +24970,7 @@ fn coerceTupleToTuple(
2470124970
24702 if (root_msg) |msg| {24971 if (root_msg) |msg| {
24703 try sema.addDeclaredHereNote(msg, tuple_ty);24972 try sema.addDeclaredHereNote(msg, tuple_ty);
24704 return sema.failWithOwnedErrorMsg(block, msg);24973 return sema.failWithOwnedErrorMsg(msg);
24705 }24974 }
2470624975
24707 if (runtime_src) |rs| {24976 if (runtime_src) |rs| {
...@@ -24724,7 +24993,14 @@ fn analyzeDeclVal(...@@ -24724,7 +24993,14 @@ fn analyzeDeclVal(
24724 if (sema.decl_val_table.get(decl_index)) |result| {24993 if (sema.decl_val_table.get(decl_index)) |result| {
24725 return result;24994 return result;
24726 }24995 }
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 };
24728 const result = try sema.analyzeLoad(block, src, decl_ref, src);25004 const result = try sema.analyzeLoad(block, src, decl_ref, src);
24729 if (Air.refToIndex(result)) |index| {25005 if (Air.refToIndex(result)) |index| {
24730 if (sema.air_instructions.items(.tag)[index] == .constant and !block.is_typeof) {25006 if (sema.air_instructions.items(.tag)[index] == .constant and !block.is_typeof) {
...@@ -24735,6 +25011,12 @@ fn analyzeDeclVal(...@@ -24735,6 +25011,12 @@ fn analyzeDeclVal(
24735}25011}
2473625012
24737fn ensureDeclAnalyzed(sema: *Sema, decl_index: Decl.Index) CompileError!void {25013fn 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
24738 sema.mod.ensureDeclAnalyzed(decl_index) catch |err| {25020 sema.mod.ensureDeclAnalyzed(decl_index) catch |err| {
24739 if (sema.owner_func) |owner_func| {25021 if (sema.owner_func) |owner_func| {
24740 owner_func.state = .dependency_failure;25022 owner_func.state = .dependency_failure;
...@@ -25214,6 +25496,7 @@ fn analyzeSlice(...@@ -25214,6 +25496,7 @@ fn analyzeSlice(
25214 }25496 }
25215 break :s null;25497 break :s null;
25216 };25498 };
25499 const slice_sentinel = if (sentinel_opt != .none) sentinel else null;
2521725500
25218 // requirement: start <= end25501 // requirement: start <= end
25219 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {25502 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
...@@ -25265,7 +25548,7 @@ fn analyzeSlice(...@@ -25265,7 +25548,7 @@ fn analyzeSlice(
2526525548
25266 break :msg msg;25549 break :msg msg;
25267 };25550 };
25268 return sema.failWithOwnedErrorMsg(block, msg);25551 return sema.failWithOwnedErrorMsg(msg);
25269 }25552 }
25270 }25553 }
25271 }25554 }
...@@ -25293,7 +25576,12 @@ fn analyzeSlice(...@@ -25293,7 +25576,12 @@ fn analyzeSlice(
2529325576
25294 const opt_new_ptr_val = try sema.resolveMaybeUndefVal(block, ptr_src, new_ptr);25577 const opt_new_ptr_val = try sema.resolveMaybeUndefVal(block, ptr_src, new_ptr);
25295 const new_ptr_val = opt_new_ptr_val orelse {25578 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;
25297 };25585 };
2529825586
25299 if (!new_ptr_val.isUndef()) {25587 if (!new_ptr_val.isUndef()) {
...@@ -25357,7 +25645,7 @@ fn analyzeSlice(...@@ -25357,7 +25645,7 @@ fn analyzeSlice(
25357 // requirement: start <= end25645 // requirement: start <= end
25358 try sema.panicIndexOutOfBounds(block, src, start, end, .cmp_lte);25646 try sema.panicIndexOutOfBounds(block, src, start, end, .cmp_lte);
25359 }25647 }
25360 return block.addInst(.{25648 const result = try block.addInst(.{
25361 .tag = .slice,25649 .tag = .slice,
25362 .data = .{ .ty_pl = .{25650 .data = .{ .ty_pl = .{
25363 .ty = try sema.addType(return_ty),25651 .ty = try sema.addType(return_ty),
...@@ -25367,6 +25655,11 @@ fn analyzeSlice(...@@ -25367,6 +25655,11 @@ fn analyzeSlice(
25367 }),25655 }),
25368 } },25656 } },
25369 });25657 });
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;
25370}25663}
2537125664
25372/// Asserts that lhs and rhs types are both numeric.25665/// Asserts that lhs and rhs types are both numeric.
...@@ -26204,7 +26497,7 @@ fn resolvePeerTypes(...@@ -26204,7 +26497,7 @@ fn resolvePeerTypes(
2620426497
26205 break :msg msg;26498 break :msg msg;
26206 };26499 };
26207 return sema.failWithOwnedErrorMsg(block, msg);26500 return sema.failWithOwnedErrorMsg(msg);
26208 }26501 }
2620926502
26210 const chosen_ty = sema.typeOf(chosen);26503 const chosen_ty = sema.typeOf(chosen);
...@@ -26363,21 +26656,41 @@ fn resolveStructLayout(...@@ -26363,21 +26656,41 @@ fn resolveStructLayout(
26363 switch (struct_obj.status) {26656 switch (struct_obj.status) {
26364 .none, .have_field_types => {},26657 .none, .have_field_types => {},
26365 .field_types_wip, .layout_wip => {26658 .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);
26367 },26666 },
26368 .have_layout, .fully_resolved_wip, .fully_resolved => return,26667 .have_layout, .fully_resolved_wip, .fully_resolved => return,
26369 }26668 }
26370 struct_obj.status = .layout_wip;26669 struct_obj.status = .layout_wip;
26371 for (struct_obj.fields.values()) |field| {26670 for (struct_obj.fields.values()) |field, i| {
26372 try sema.resolveTypeLayout(block, src, field.ty);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 };
26373 }26679 }
26374 struct_obj.status = .have_layout;26680 struct_obj.status = .have_layout;
2637526681
26376 // In case of querying the ABI alignment of this struct, we will ask26682 // In case of querying the ABI alignment of this struct, we will ask
26377 // for hasRuntimeBits() of each field, so we need "requires comptime"26683 // for hasRuntimeBits() of each field, so we need "requires comptime"
26378 // to be known already before this function returns.26684 // to be known already before this function returns.
26379 for (struct_obj.fields.values()) |field| {26685 for (struct_obj.fields.values()) |field, i| {
26380 _ = try sema.typeRequiresComptime(block, src, field.ty);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 };
26381 }26694 }
26382 }26695 }
26383 // otherwise it's a tuple; no need to resolve anything26696 // otherwise it's a tuple; no need to resolve anything
...@@ -26394,13 +26707,26 @@ fn resolveUnionLayout(...@@ -26394,13 +26707,26 @@ fn resolveUnionLayout(
26394 switch (union_obj.status) {26707 switch (union_obj.status) {
26395 .none, .have_field_types => {},26708 .none, .have_field_types => {},
26396 .field_types_wip, .layout_wip => {26709 .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);
26398 },26717 },
26399 .have_layout, .fully_resolved_wip, .fully_resolved => return,26718 .have_layout, .fully_resolved_wip, .fully_resolved => return,
26400 }26719 }
26401 union_obj.status = .layout_wip;26720 union_obj.status = .layout_wip;
26402 for (union_obj.fields.values()) |field| {26721 for (union_obj.fields.values()) |field, i| {
26403 try sema.resolveTypeLayout(block, src, field.ty);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 };
26404 }26730 }
26405 union_obj.status = .have_layout;26731 union_obj.status = .have_layout;
26406}26732}
...@@ -26528,12 +26854,12 @@ pub fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type)...@@ -26528,12 +26854,12 @@ pub fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type)
26528 switch (ty.tag()) {26854 switch (ty.tag()) {
26529 .@"struct" => {26855 .@"struct" => {
26530 const struct_obj = ty.castTag(.@"struct").?.data;26856 const struct_obj = ty.castTag(.@"struct").?.data;
26531 try sema.resolveTypeFieldsStruct(block, src, ty, struct_obj);26857 try sema.resolveTypeFieldsStruct(ty, struct_obj);
26532 return ty;26858 return ty;
26533 },26859 },
26534 .@"union", .union_safety_tagged, .union_tagged => {26860 .@"union", .union_safety_tagged, .union_tagged => {
26535 const union_obj = ty.cast(Type.Payload.Union).?.data;26861 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);
26537 return ty;26863 return ty;
26538 },26864 },
26539 .type_info => return sema.resolveBuiltinTypeFields(block, src, "Type"),26865 .type_info => return sema.resolveBuiltinTypeFields(block, src, "Type"),
...@@ -26554,15 +26880,19 @@ pub fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type)...@@ -26554,15 +26880,19 @@ pub fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type)
2655426880
26555fn resolveTypeFieldsStruct(26881fn resolveTypeFieldsStruct(
26556 sema: *Sema,26882 sema: *Sema,
26557 block: *Block,
26558 src: LazySrcLoc,
26559 ty: Type,26883 ty: Type,
26560 struct_obj: *Module.Struct,26884 struct_obj: *Module.Struct,
26561) CompileError!void {26885) CompileError!void {
26562 switch (struct_obj.status) {26886 switch (struct_obj.status) {
26563 .none => {},26887 .none => {},
26564 .field_types_wip => {26888 .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);
26566 },26896 },
26567 .have_field_types,26897 .have_field_types,
26568 .have_layout,26898 .have_layout,
...@@ -26576,17 +26906,17 @@ fn resolveTypeFieldsStruct(...@@ -26576,17 +26906,17 @@ fn resolveTypeFieldsStruct(
26576 try semaStructFields(sema.mod, struct_obj);26906 try semaStructFields(sema.mod, struct_obj);
26577}26907}
2657826908
26579fn resolveTypeFieldsUnion(26909fn resolveTypeFieldsUnion(sema: *Sema, ty: Type, union_obj: *Module.Union) CompileError!void {
26580 sema: *Sema,
26581 block: *Block,
26582 src: LazySrcLoc,
26583 ty: Type,
26584 union_obj: *Module.Union,
26585) CompileError!void {
26586 switch (union_obj.status) {26910 switch (union_obj.status) {
26587 .none => {},26911 .none => {},
26588 .field_types_wip => {26912 .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);
26590 },26920 },
26591 .have_field_types,26921 .have_field_types,
26592 .have_layout,26922 .have_layout,
...@@ -26802,7 +27132,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void...@@ -26802,7 +27132,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
26802 try sema.errNote(&block_scope, src, msg, "struct declared here", .{});27132 try sema.errNote(&block_scope, src, msg, "struct declared here", .{});
26803 break :msg msg;27133 break :msg msg;
26804 };27134 };
26805 return sema.failWithOwnedErrorMsg(&block_scope, msg);27135 return sema.failWithOwnedErrorMsg(msg);
26806 }27136 }
26807 gop.value_ptr.* = .{27137 gop.value_ptr.* = .{
26808 .ty = Type.initTag(.noreturn),27138 .ty = Type.initTag(.noreturn),
...@@ -26851,31 +27181,44 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void...@@ -26851,31 +27181,44 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
26851 const field = &struct_obj.fields.values()[i];27181 const field = &struct_obj.fields.values()[i];
26852 field.ty = try field_ty.copy(decl_arena_allocator);27182 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)) {
26855 const msg = msg: {27197 const msg = msg: {
26856 const tree = try sema.getAstTree(&block_scope);27198 const tree = try sema.getAstTree(&block_scope);
26857 const fields_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, i);27199 const fields_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, i);
26858 const msg = try sema.errMsg(&block_scope, fields_src, "extern structs cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)});27200 const msg = try sema.errMsg(&block_scope, fields_src, "extern structs cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)});
26859 errdefer msg.destroy(sema.gpa);27201 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
26863 try sema.addDeclaredHereNote(msg, field.ty);27205 try sema.addDeclaredHereNote(msg, field.ty);
26864 break :msg msg;27206 break :msg msg;
26865 };27207 };
26866 return sema.failWithOwnedErrorMsg(&block_scope, msg);27208 return sema.failWithOwnedErrorMsg(msg);
26867 }27209 } else if (struct_obj.layout == .Packed and !(validatePackedType(field.ty))) {
26868 if (field_ty.zigTypeTag() == .Opaque) {
26869 const msg = msg: {27210 const msg = msg: {
26870 const tree = try sema.getAstTree(&block_scope);27211 const tree = try sema.getAstTree(&block_scope);
26871 const field_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, i);27212 const fields_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", .{});27213 const msg = try sema.errMsg(&block_scope, fields_src, "packed structs cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)});
26873 errdefer msg.destroy(sema.gpa);27214 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);
26876 break :msg msg;27219 break :msg msg;
26877 };27220 };
26878 return sema.failWithOwnedErrorMsg(&block_scope, msg);27221 return sema.failWithOwnedErrorMsg(msg);
26879 }27222 }
2688027223
26881 if (zir_field.align_body_len > 0) {27224 if (zir_field.align_body_len > 0) {
...@@ -27157,7 +27500,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {...@@ -27157,7 +27500,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
27157 try sema.errNote(&block_scope, src, msg, "union declared here", .{});27500 try sema.errNote(&block_scope, src, msg, "union declared here", .{});
27158 break :msg msg;27501 break :msg msg;
27159 };27502 };
27160 return sema.failWithOwnedErrorMsg(&block_scope, msg);27503 return sema.failWithOwnedErrorMsg(msg);
27161 }27504 }
2716227505
27163 if (tag_ty_field_names) |*names| {27506 if (tag_ty_field_names) |*names| {
...@@ -27171,35 +27514,48 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {...@@ -27171,35 +27514,48 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
27171 try sema.addDeclaredHereNote(msg, union_obj.tag_ty);27514 try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
27172 break :msg msg;27515 break :msg msg;
27173 };27516 };
27174 return sema.failWithOwnedErrorMsg(&block_scope, msg);27517 return sema.failWithOwnedErrorMsg(msg);
27175 }27518 }
27176 }27519 }
2717727520
27178 if (union_obj.layout == .Extern and !(try sema.validateExternType(field_ty, .union_field))) {27521 if (field_ty.zigTypeTag() == .Opaque) {
27179 const msg = msg: {27522 const msg = msg: {
27180 const tree = try sema.getAstTree(&block_scope);27523 const tree = try sema.getAstTree(&block_scope);
27181 const field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i);27524 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", .{});
27183 errdefer msg.destroy(sema.gpa);27526 errdefer msg.destroy(sema.gpa);
2718427527
27185 try sema.explainWhyTypeIsNotExtern(&block_scope, field_src, msg, field_src.toSrcLoc(decl), field_ty, .union_field);
27186
27187 try sema.addDeclaredHereNote(msg, field_ty);27528 try sema.addDeclaredHereNote(msg, field_ty);
27188 break :msg msg;27529 break :msg msg;
27189 };27530 };
27190 return sema.failWithOwnedErrorMsg(&block_scope, msg);27531 return sema.failWithOwnedErrorMsg(msg);
27191 }27532 }
27192 if (field_ty.zigTypeTag() == .Opaque) {27533 if (union_obj.layout == .Extern and !sema.validateExternType(field_ty, .union_field)) {
27193 const msg = msg: {27534 const msg = msg: {
27194 const tree = try sema.getAstTree(&block_scope);27535 const tree = try sema.getAstTree(&block_scope);
27195 const field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i);27536 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)});
27197 errdefer msg.destroy(sema.gpa);27538 errdefer msg.destroy(sema.gpa);
2719827539
27540 try sema.explainWhyTypeIsNotExtern(msg, field_src.toSrcLoc(decl), field_ty, .union_field);
27541
27199 try sema.addDeclaredHereNote(msg, field_ty);27542 try sema.addDeclaredHereNote(msg, field_ty);
27200 break :msg msg;27543 break :msg msg;
27201 };27544 };
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);
27203 }27559 }
2720427560
27205 gop.value_ptr.* = .{27561 gop.value_ptr.* = .{
...@@ -27231,7 +27587,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {...@@ -27231,7 +27587,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
27231 try sema.addDeclaredHereNote(msg, union_obj.tag_ty);27587 try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
27232 break :msg msg;27588 break :msg msg;
27233 };27589 };
27234 return sema.failWithOwnedErrorMsg(&block_scope, msg);27590 return sema.failWithOwnedErrorMsg(msg);
27235 }27591 }
27236 }27592 }
27237}27593}
...@@ -27494,9 +27850,19 @@ pub fn typeHasOnePossibleValue(...@@ -27494,9 +27850,19 @@ pub fn typeHasOnePossibleValue(
27494 .@"struct" => {27850 .@"struct" => {
27495 const resolved_ty = try sema.resolveTypeFields(block, src, ty);27851 const resolved_ty = try sema.resolveTypeFields(block, src, ty);
27496 const s = resolved_ty.castTag(.@"struct").?.data;27852 const s = resolved_ty.castTag(.@"struct").?.data;
27497 for (s.fields.values()) |value| {27853 for (s.fields.values()) |field, i| {
27498 if (value.is_comptime) continue;27854 if (field.is_comptime) continue;
27499 if ((try sema.typeHasOnePossibleValue(block, src, value.ty)) == null) {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) {
27500 return null;27866 return null;
27501 }27867 }
27502 }27868 }
...@@ -27562,6 +27928,16 @@ pub fn typeHasOnePossibleValue(...@@ -27562,6 +27928,16 @@ pub fn typeHasOnePossibleValue(
27562 const tag_val = (try sema.typeHasOnePossibleValue(block, src, union_obj.tag_ty)) orelse27928 const tag_val = (try sema.typeHasOnePossibleValue(block, src, union_obj.tag_ty)) orelse
27563 return null;27929 return null;
27564 const only_field = union_obj.fields.values()[0];27930 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 }
27565 const val_val = (try sema.typeHasOnePossibleValue(block, src, only_field.ty)) orelse27941 const val_val = (try sema.typeHasOnePossibleValue(block, src, only_field.ty)) orelse
27566 return null;27942 return null;
27567 // TODO make this not allocate. The function in `Type.onePossibleValue`27943 // 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...@@ -28201,7 +28577,7 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ
28201 if (struct_obj.status == .field_types_wip)28577 if (struct_obj.status == .field_types_wip)
28202 return false;28578 return false;
2820328579
28204 try sema.resolveTypeFieldsStruct(block, src, ty, struct_obj);28580 try sema.resolveTypeFieldsStruct(ty, struct_obj);
2820528581
28206 struct_obj.requires_comptime = .wip;28582 struct_obj.requires_comptime = .wip;
28207 for (struct_obj.fields.values()) |field| {28583 for (struct_obj.fields.values()) |field| {
...@@ -28226,7 +28602,7 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ...@@ -28226,7 +28602,7 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ
28226 if (union_obj.status == .field_types_wip)28602 if (union_obj.status == .field_types_wip)
28227 return false;28603 return false;
2822828604
28229 try sema.resolveTypeFieldsUnion(block, src, ty, union_obj);28605 try sema.resolveTypeFieldsUnion(ty, union_obj);
2823028606
28231 union_obj.requires_comptime = .wip;28607 union_obj.requires_comptime = .wip;
28232 for (union_obj.fields.values()) |field| {28608 for (union_obj.fields.values()) |field| {
src/Zir.zig+6
...@@ -3548,6 +3548,12 @@ pub const Inst = struct {...@@ -3548,6 +3548,12 @@ pub const Inst = struct {
3548 ty: Ref,3548 ty: Ref,
3549 init_count: u32,3549 init_count: u32,
3550 };3550 };
3551
3552 pub const Src = struct {
3553 node: i32,
3554 line: u32,
3555 column: u32,
3556 };
3551};3557};
35523558
3553pub const SpecialProng = enum { none, @"else", under };3559pub const SpecialProng = enum { none, @"else", under };
src/link/Dwarf.zig+18-26
...@@ -853,8 +853,7 @@ pub fn commitDeclState(...@@ -853,8 +853,7 @@ pub fn commitDeclState(
853 .macho => {853 .macho => {
854 const macho_file = file.cast(File.MachO).?;854 const macho_file = file.cast(File.MachO).?;
855 const d_sym = &macho_file.d_sym.?;855 const d_sym = &macho_file.d_sym.?;
856 const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;856 const debug_line_sect = &d_sym.sections.items[d_sym.debug_line_section_index.?];
857 const debug_line_sect = &dwarf_segment.sections.items[d_sym.debug_line_section_index.?];
858 const file_pos = debug_line_sect.offset + src_fn.off;857 const file_pos = debug_line_sect.offset + src_fn.off;
859 try pwriteDbgLineNops(d_sym.file, file_pos, 0, &[0]u8{}, src_fn.len);858 try pwriteDbgLineNops(d_sym.file, file_pos, 0, &[0]u8{}, src_fn.len);
860 },859 },
...@@ -933,8 +932,8 @@ pub fn commitDeclState(...@@ -933,8 +932,8 @@ pub fn commitDeclState(
933 .macho => {932 .macho => {
934 const macho_file = file.cast(File.MachO).?;933 const macho_file = file.cast(File.MachO).?;
935 const d_sym = &macho_file.d_sym.?;934 const d_sym = &macho_file.d_sym.?;
936 const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;935 const dwarf_segment = d_sym.segments.items[d_sym.dwarf_segment_cmd_index.?];
937 const debug_line_sect = &dwarf_segment.sections.items[d_sym.debug_line_section_index.?];936 const debug_line_sect = &d_sym.sections.items[d_sym.debug_line_section_index.?];
938 if (needed_size != debug_line_sect.size) {937 if (needed_size != debug_line_sect.size) {
939 if (needed_size > d_sym.allocatedSize(debug_line_sect.offset)) {938 if (needed_size > d_sym.allocatedSize(debug_line_sect.offset)) {
940 const new_offset = d_sym.findFreeSpace(needed_size, 1);939 const new_offset = d_sym.findFreeSpace(needed_size, 1);
...@@ -955,10 +954,9 @@ pub fn commitDeclState(...@@ -955,10 +954,9 @@ pub fn commitDeclState(
955 );954 );
956955
957 debug_line_sect.offset = @intCast(u32, new_offset);956 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;
959 }958 }
960 debug_line_sect.size = needed_size;959 debug_line_sect.size = needed_size;
961 d_sym.load_commands_dirty = true; // TODO look into making only the one section dirty
962 d_sym.debug_line_header_dirty = true;960 d_sym.debug_line_header_dirty = true;
963 }961 }
964 const file_pos = debug_line_sect.offset + src_fn.off;962 const file_pos = debug_line_sect.offset + src_fn.off;
...@@ -1137,8 +1135,7 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u3...@@ -1137,8 +1135,7 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u3
1137 .macho => {1135 .macho => {
1138 const macho_file = file.cast(File.MachO).?;1136 const macho_file = file.cast(File.MachO).?;
1139 const d_sym = &macho_file.d_sym.?;1137 const d_sym = &macho_file.d_sym.?;
1140 const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;1138 const debug_info_sect = &d_sym.sections.items[d_sym.debug_info_section_index.?];
1141 const debug_info_sect = &dwarf_segment.sections.items[d_sym.debug_info_section_index.?];
1142 const file_pos = debug_info_sect.offset + atom.off;1139 const file_pos = debug_info_sect.offset + atom.off;
1143 try pwriteDbgInfoNops(d_sym.file, file_pos, 0, &[0]u8{}, atom.len, false);1140 try pwriteDbgInfoNops(d_sym.file, file_pos, 0, &[0]u8{}, atom.len, false);
1144 },1141 },
...@@ -1235,8 +1232,8 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co...@@ -1235,8 +1232,8 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co
1235 .macho => {1232 .macho => {
1236 const macho_file = file.cast(File.MachO).?;1233 const macho_file = file.cast(File.MachO).?;
1237 const d_sym = &macho_file.d_sym.?;1234 const d_sym = &macho_file.d_sym.?;
1238 const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;1235 const dwarf_segment = d_sym.segments.items[d_sym.dwarf_segment_cmd_index.?];
1239 const debug_info_sect = &dwarf_segment.sections.items[d_sym.debug_info_section_index.?];1236 const debug_info_sect = &d_sym.sections.items[d_sym.debug_info_section_index.?];
1240 if (needed_size != debug_info_sect.size) {1237 if (needed_size != debug_info_sect.size) {
1241 if (needed_size > d_sym.allocatedSize(debug_info_sect.offset)) {1238 if (needed_size > d_sym.allocatedSize(debug_info_sect.offset)) {
1242 const new_offset = d_sym.findFreeSpace(needed_size, 1);1239 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...@@ -1257,10 +1254,9 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co
1257 );1254 );
12581255
1259 debug_info_sect.offset = @intCast(u32, new_offset);1256 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;
1261 }1258 }
1262 debug_info_sect.size = needed_size;1259 debug_info_sect.size = needed_size;
1263 d_sym.load_commands_dirty = true; // TODO look into making only the one section dirty
1264 d_sym.debug_line_header_dirty = true;1260 d_sym.debug_line_header_dirty = true;
1265 }1261 }
1266 const file_pos = debug_info_sect.offset + atom.off;1262 const file_pos = debug_info_sect.offset + atom.off;
...@@ -1330,8 +1326,7 @@ pub fn updateDeclLineNumber(self: *Dwarf, file: *File, decl: *const Module.Decl)...@@ -1330,8 +1326,7 @@ pub fn updateDeclLineNumber(self: *Dwarf, file: *File, decl: *const Module.Decl)
1330 .macho => {1326 .macho => {
1331 const macho_file = file.cast(File.MachO).?;1327 const macho_file = file.cast(File.MachO).?;
1332 const d_sym = macho_file.d_sym.?;1328 const d_sym = macho_file.d_sym.?;
1333 const dwarf_seg = d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;1329 const sect = d_sym.sections.items[d_sym.debug_line_section_index.?];
1334 const sect = dwarf_seg.sections.items[d_sym.debug_line_section_index.?];
1335 const file_pos = sect.offset + decl.fn_link.macho.off + self.getRelocDbgLineOff();1330 const file_pos = sect.offset + decl.fn_link.macho.off + self.getRelocDbgLineOff();
1336 try d_sym.file.pwriteAll(&data, file_pos);1331 try d_sym.file.pwriteAll(&data, file_pos);
1337 },1332 },
...@@ -1557,14 +1552,14 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void {...@@ -1557,14 +1552,14 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void {
1557 .macho => {1552 .macho => {
1558 const macho_file = file.cast(File.MachO).?;1553 const macho_file = file.cast(File.MachO).?;
1559 const d_sym = &macho_file.d_sym.?;1554 const d_sym = &macho_file.d_sym.?;
1560 const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;1555 const dwarf_segment = d_sym.segments.items[d_sym.dwarf_segment_cmd_index.?];
1561 const debug_abbrev_sect = &dwarf_segment.sections.items[d_sym.debug_abbrev_section_index.?];1556 const debug_abbrev_sect = &d_sym.sections.items[d_sym.debug_abbrev_section_index.?];
1562 const allocated_size = d_sym.allocatedSize(debug_abbrev_sect.offset);1557 const allocated_size = d_sym.allocatedSize(debug_abbrev_sect.offset);
1563 if (needed_size > allocated_size) {1558 if (needed_size > allocated_size) {
1564 debug_abbrev_sect.size = 0; // free the space1559 debug_abbrev_sect.size = 0; // free the space
1565 const offset = d_sym.findFreeSpace(needed_size, 1);1560 const offset = d_sym.findFreeSpace(needed_size, 1);
1566 debug_abbrev_sect.offset = @intCast(u32, offset);1561 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;
1568 }1563 }
1569 debug_abbrev_sect.size = needed_size;1564 debug_abbrev_sect.size = needed_size;
1570 log.debug("__debug_abbrev start=0x{x} end=0x{x}", .{1565 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...@@ -1681,8 +1676,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, file: *File, module: *Module, low_pc: u6
1681 .macho => {1676 .macho => {
1682 const macho_file = file.cast(File.MachO).?;1677 const macho_file = file.cast(File.MachO).?;
1683 const d_sym = &macho_file.d_sym.?;1678 const d_sym = &macho_file.d_sym.?;
1684 const dwarf_seg = d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;1679 const debug_info_sect = d_sym.sections.items[d_sym.debug_info_section_index.?];
1685 const debug_info_sect = dwarf_seg.sections.items[d_sym.debug_info_section_index.?];
1686 const file_pos = debug_info_sect.offset;1680 const file_pos = debug_info_sect.offset;
1687 try pwriteDbgInfoNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt, false);1681 try pwriteDbgInfoNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt, false);
1688 },1682 },
...@@ -1998,13 +1992,13 @@ pub fn writeDbgAranges(self: *Dwarf, file: *File, addr: u64, size: u64) !void {...@@ -1998,13 +1992,13 @@ pub fn writeDbgAranges(self: *Dwarf, file: *File, addr: u64, size: u64) !void {
1998 .macho => {1992 .macho => {
1999 const macho_file = file.cast(File.MachO).?;1993 const macho_file = file.cast(File.MachO).?;
2000 const d_sym = &macho_file.d_sym.?;1994 const d_sym = &macho_file.d_sym.?;
2001 const dwarf_seg = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;1995 const dwarf_seg = d_sym.segments.items[d_sym.dwarf_segment_cmd_index.?];
2002 const debug_aranges_sect = &dwarf_seg.sections.items[d_sym.debug_aranges_section_index.?];1996 const debug_aranges_sect = &d_sym.sections.items[d_sym.debug_aranges_section_index.?];
2003 const allocated_size = d_sym.allocatedSize(debug_aranges_sect.offset);1997 const allocated_size = d_sym.allocatedSize(debug_aranges_sect.offset);
2004 if (needed_size > allocated_size) {1998 if (needed_size > allocated_size) {
2005 debug_aranges_sect.size = 0; // free the space1999 debug_aranges_sect.size = 0; // free the space
2006 const new_offset = d_sym.findFreeSpace(needed_size, 16);2000 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;
2008 debug_aranges_sect.offset = @intCast(u32, new_offset);2002 debug_aranges_sect.offset = @intCast(u32, new_offset);
2009 }2003 }
2010 debug_aranges_sect.size = needed_size;2004 debug_aranges_sect.size = needed_size;
...@@ -2134,8 +2128,7 @@ pub fn writeDbgLineHeader(self: *Dwarf, file: *File, module: *Module) !void {...@@ -2134,8 +2128,7 @@ pub fn writeDbgLineHeader(self: *Dwarf, file: *File, module: *Module) !void {
2134 .macho => {2128 .macho => {
2135 const macho_file = file.cast(File.MachO).?;2129 const macho_file = file.cast(File.MachO).?;
2136 const d_sym = &macho_file.d_sym.?;2130 const d_sym = &macho_file.d_sym.?;
2137 const dwarf_seg = d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;2131 const debug_line_sect = d_sym.sections.items[d_sym.debug_line_section_index.?];
2138 const debug_line_sect = dwarf_seg.sections.items[d_sym.debug_line_section_index.?];
2139 const file_pos = debug_line_sect.offset;2132 const file_pos = debug_line_sect.offset;
2140 try pwriteDbgLineNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt);2133 try pwriteDbgLineNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt);
2141 },2134 },
...@@ -2264,8 +2257,7 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void {...@@ -2264,8 +2257,7 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void {
2264 .macho => {2257 .macho => {
2265 const macho_file = file.cast(File.MachO).?;2258 const macho_file = file.cast(File.MachO).?;
2266 const d_sym = &macho_file.d_sym.?;2259 const d_sym = &macho_file.d_sym.?;
2267 const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;2260 const debug_info_sect = &d_sym.sections.items[d_sym.debug_info_section_index.?];
2268 const debug_info_sect = &dwarf_segment.sections.items[d_sym.debug_info_section_index.?];
2269 break :blk debug_info_sect.offset;2261 break :blk debug_info_sect.offset;
2270 },2262 },
2271 // for wasm, the offset is always 0 as we write to memory first2263 // 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");...@@ -17,6 +17,7 @@ const aarch64 = @import("../arch/aarch64/bits.zig");
17const bind = @import("MachO/bind.zig");17const bind = @import("MachO/bind.zig");
18const codegen = @import("../codegen.zig");18const codegen = @import("../codegen.zig");
19const dead_strip = @import("MachO/dead_strip.zig");19const dead_strip = @import("MachO/dead_strip.zig");
20const fat = @import("MachO/fat.zig");
20const link = @import("../link.zig");21const link = @import("../link.zig");
21const llvm_backend = @import("../codegen/llvm.zig");22const llvm_backend = @import("../codegen/llvm.zig");
22const target_util = @import("../target.zig");23const target_util = @import("../target.zig");
...@@ -60,6 +61,32 @@ const SystemLib = struct {...@@ -60,6 +61,32 @@ const SystemLib = struct {
60 weak: bool = false,61 weak: bool = false,
61};62};
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
63base: File,90base: File,
6491
65/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.92/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
...@@ -77,81 +104,32 @@ page_size: u16,...@@ -77,81 +104,32 @@ page_size: u16,
77/// fashion (default for LLVM backend).104/// fashion (default for LLVM backend).
78mode: enum { incremental, one_shot },105mode: enum { incremental, one_shot },
79106
80/// The absolute address of the entry point.107uuid: macho.uuid_command = .{
81entry_addr: ?u64 = null,108 .cmdsize = @sizeOf(macho.uuid_command),
82109 .uuid = undefined,
83/// Code signature (if any)110},
84code_signature: ?CodeSignature = null,
85111
86objects: std.ArrayListUnmanaged(Object) = .{},112objects: std.ArrayListUnmanaged(Object) = .{},
87archives: std.ArrayListUnmanaged(Archive) = .{},113archives: std.ArrayListUnmanaged(Archive) = .{},
88
89dylibs: std.ArrayListUnmanaged(Dylib) = .{},114dylibs: std.ArrayListUnmanaged(Dylib) = .{},
90dylibs_map: std.StringHashMapUnmanaged(u16) = .{},115dylibs_map: std.StringHashMapUnmanaged(u16) = .{},
91referenced_dylibs: std.AutoArrayHashMapUnmanaged(u16, void) = .{},116referenced_dylibs: std.AutoArrayHashMapUnmanaged(u16, void) = .{},
92117
93load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},118segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
94119sections: std.MultiArrayList(Section) = .{},
95pagezero_segment_cmd_index: ?u16 = null,120
96text_segment_cmd_index: ?u16 = null,121pagezero_segment_cmd_index: ?u8 = null,
97data_const_segment_cmd_index: ?u16 = null,122text_segment_cmd_index: ?u8 = null,
98data_segment_cmd_index: ?u16 = null,123data_const_segment_cmd_index: ?u8 = null,
99linkedit_segment_cmd_index: ?u16 = null,124data_segment_cmd_index: ?u8 = null,
100dyld_info_cmd_index: ?u16 = null,125linkedit_segment_cmd_index: ?u8 = null,
101symtab_cmd_index: ?u16 = null,126
102dysymtab_cmd_index: ?u16 = null,127text_section_index: ?u8 = null,
103dylinker_cmd_index: ?u16 = null,128stubs_section_index: ?u8 = null,
104data_in_code_cmd_index: ?u16 = null,129stub_helper_section_index: ?u8 = null,
105function_starts_cmd_index: ?u16 = null,130got_section_index: ?u8 = null,
106main_cmd_index: ?u16 = null,131la_symbol_ptr_section_index: ?u8 = null,
107dylib_id_cmd_index: ?u16 = null,132data_section_index: ?u8 = 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,
155133
156locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},134locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
157globals: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{},135globals: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{},
...@@ -188,37 +166,12 @@ stubs_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},...@@ -188,37 +166,12 @@ stubs_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
188166
189error_flags: File.ErrorFlags = File.ErrorFlags{},167error_flags: File.ErrorFlags = File.ErrorFlags{},
190168
191load_commands_dirty: bool = false,
192sections_order_dirty: bool = false,
193
194/// A helper var to indicate if we are at the start of the incremental updates, or169/// A helper var to indicate if we are at the start of the incremental updates, or
195/// already somewhere further along the update-and-run chain.170/// already somewhere further along the update-and-run chain.
196/// TODO once we add opening a prelinked output binary from file, this will become171/// TODO once we add opening a prelinked output binary from file, this will become
197/// obsolete as we will carry on where we left off.172/// obsolete as we will carry on where we left off.
198cold_start: bool = true,173cold_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
222/// List of atoms that are either synthetic or map directly to the Zig source program.175/// List of atoms that are either synthetic or map directly to the Zig source program.
223managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},176managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
224177
...@@ -250,7 +203,7 @@ unnamed_const_atoms: UnnamedConstTable = .{},...@@ -250,7 +203,7 @@ unnamed_const_atoms: UnnamedConstTable = .{},
250/// We store them here so that we can properly dispose of any allocated203/// We store them here so that we can properly dispose of any allocated
251/// memory within the atom in the incremental linker.204/// memory within the atom in the incremental linker.
252/// TODO consolidate this.205/// TODO consolidate this.
253decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, ?MatchingSection) = .{},206decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, ?u8) = .{},
254207
255const Entry = struct {208const Entry = struct {
256 target: SymbolWithLoc,209 target: SymbolWithLoc,
...@@ -408,12 +361,7 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {...@@ -408,12 +361,7 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
408361
409pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {362pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
410 const cpu_arch = options.target.cpu.arch;363 const cpu_arch = options.target.cpu.arch;
411 const os_tag = options.target.os.tag;
412 const abi = options.target.abi;
413 const page_size: u16 = if (cpu_arch == .aarch64) 0x4000 else 0x1000;364 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);
417 const use_llvm = build_options.have_llvm and options.use_llvm;365 const use_llvm = build_options.have_llvm and options.use_llvm;
418 const use_stage1 = build_options.is_stage1 and options.use_stage1;366 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 {...@@ -428,10 +376,6 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
428 .file = null,376 .file = null,
429 },377 },
430 .page_size = page_size,378 .page_size = page_size,
431 .code_signature = if (requires_adhoc_codesig)
432 CodeSignature.init(page_size)
433 else
434 null,
435 .mode = if (use_stage1 or use_llvm or options.module == null or options.cache_mode == .whole)379 .mode = if (use_stage1 or use_llvm or options.module == null or options.cache_mode == .whole)
436 .one_shot380 .one_shot
437 else381 else
...@@ -562,8 +506,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -562,8 +506,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
562 var dependent_libs = std.fifo.LinearFifo(struct {506 var dependent_libs = std.fifo.LinearFifo(struct {
563 id: Dylib.Id,507 id: Dylib.Id,
564 parent: u16,508 parent: u16,
565 }, .Dynamic).init(self.base.allocator);509 }, .Dynamic).init(arena);
566 defer dependent_libs.deinit();510
567 try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs);511 try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs);
568 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);512 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);
569 }513 }
...@@ -573,7 +517,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -573,7 +517,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
573 try self.createDyldPrivateAtom();517 try self.createDyldPrivateAtom();
574 try self.createStubHelperPreambleAtom();518 try self.createStubHelperPreambleAtom();
575 try self.resolveSymbolsInDylibs();519 try self.resolveSymbolsInDylibs();
576 try self.addCodeSignatureLC();
577520
578 if (self.unresolved.count() > 0) {521 if (self.unresolved.count() > 0) {
579 return error.UndefinedSymbolReference;522 return error.UndefinedSymbolReference;
...@@ -583,67 +526,92 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -583,67 +526,92 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
583526
584 if (build_options.enable_logging) {527 if (build_options.enable_logging) {
585 self.logSymtab();528 self.logSymtab();
586 self.logSectionOrdinals();529 self.logSections();
587 self.logAtoms();530 self.logAtoms();
588 }531 }
589532
590 try self.writeAtomsIncremental();533 try self.writeAtomsIncremental();
591534
592 try self.setEntryPoint();535 var lc_buffer = std.ArrayList(u8).init(arena);
593 try self.updateSectionOrdinals();536 const lc_writer = lc_buffer.writer();
594 try self.writeLinkeditSegment();537 var ncmds: u32 = 0;
595538
596 if (self.d_sym) |*d_sym| {539 try self.writeLinkeditSegmentData(&ncmds, lc_writer);
597 // Flush debug symbols bundle.540 try writeDylinkerLC(&ncmds, lc_writer);
598 try d_sym.flushModule(self.base.allocator, self.base.options);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;
599 }558 }
600559
601 // code signature and entitlements560 try self.writeBuildVersionLC(&ncmds, lc_writer);
602 if (self.base.options.entitlements) |path| {561
603 if (self.code_signature) |*csig| {562 {
604 try csig.addEntitlements(self.base.allocator, path);563 std.crypto.random.bytes(&self.uuid.uuid);
605 csig.code_directory.ident = self.base.options.emit.?.sub_path;564 try lc_writer.writeStruct(self.uuid);
606 } else {565 ncmds += 1;
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 }
612 }566 }
613567
614 if (self.code_signature) |*csig| {568 try self.writeLoadDylibLCs(&ncmds, lc_writer);
615 csig.clear(self.base.allocator);569
616 csig.code_directory.ident = self.base.options.emit.?.sub_path;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: {
617 // Preallocate space for the code signature.579 // Preallocate space for the code signature.
618 // We need to do this at this stage so that we have the load commands with proper values580 // We need to do this at this stage so that we have the load commands with proper values
619 // written out to the file.581 // written out to the file.
620 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment582 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
621 // where the code signature goes into.583 // where the code signature goes into.
622 try self.writeCodeSignaturePadding(csig);584 var codesig = CodeSignature.init(self.page_size);
623 }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();593 var headers_buf = std.ArrayList(u8).init(arena);
626 try self.writeHeader();594 try self.writeSegmentHeaders(&ncmds, headers_buf.writer());
627595
628 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {596 try self.base.file.?.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));
629 log.debug("flushing. no_entry_point_found = true", .{});597 try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);
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 }
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| {601 if (codesig) |*csig| {
639 try self.writeCodeSignature(csig); // code signing always comes last602 try self.writeCodeSignature(csig, codesig_offset.?); // code signing always comes last
640 }603 }
641604
642 if (build_options.enable_link_snapshots) {605 if (self.d_sym) |*d_sym| {
643 if (self.base.options.enable_link_snapshots)606 // Flush debug symbols bundle.
644 try self.snapshotState();607 try d_sym.flushModule(self.base.allocator, self.base.options);
645 }608 }
646609
610 // if (build_options.enable_link_snapshots) {
611 // if (self.base.options.enable_link_snapshots)
612 // try self.snapshotState();
613 // }
614
647 if (cache_miss) {615 if (cache_miss) {
648 // Update the file with the digest. If it fails we can continue; it only616 // Update the file with the digest. If it fails we can continue; it only
649 // means that the next invocation will have an unnecessary cache miss.617 // 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)...@@ -708,6 +676,9 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
708 sub_prog_node.context.refresh();676 sub_prog_node.context.refresh();
709 defer sub_prog_node.end();677 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;
711 const is_lib = self.base.options.output_mode == .Lib;682 const is_lib = self.base.options.output_mode == .Lib;
712 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;683 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
713 const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;684 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)...@@ -990,40 +961,6 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
990 }961 }
991 }962 }
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
1027 if (self.base.options.verbose_link) {964 if (self.base.options.verbose_link) {
1028 var argv = std.ArrayList([]const u8).init(arena);965 var argv = std.ArrayList([]const u8).init(arena);
1029966
...@@ -1048,7 +985,7 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)...@@ -1048,7 +985,7 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
1048 try argv.append(syslibroot);985 try argv.append(syslibroot);
1049 }986 }
1050987
1051 for (rpath_table.keys()) |rpath| {988 for (self.base.options.rpath_list) |rpath| {
1052 try argv.append("-rpath");989 try argv.append("-rpath");
1053 try argv.append(rpath);990 try argv.append(rpath);
1054 }991 }
...@@ -1157,25 +1094,22 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)...@@ -1157,25 +1094,22 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
1157 var dependent_libs = std.fifo.LinearFifo(struct {1094 var dependent_libs = std.fifo.LinearFifo(struct {
1158 id: Dylib.Id,1095 id: Dylib.Id,
1159 parent: u16,1096 parent: u16,
1160 }, .Dynamic).init(gpa);1097 }, .Dynamic).init(arena);
1161 defer dependent_libs.deinit();1098
1162 try self.parseInputFiles(positionals.items, self.base.options.sysroot, &dependent_libs);1099 try self.parseInputFiles(positionals.items, self.base.options.sysroot, &dependent_libs);
1163 try self.parseAndForceLoadStaticArchives(must_link_archives.keys());1100 try self.parseAndForceLoadStaticArchives(must_link_archives.keys());
1164 try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs);1101 try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs);
1165 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);1102 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);
11661103
1167 for (self.objects.items) |*object, object_id| {1104 for (self.objects.items) |_, object_id| {
1168 try self.resolveSymbolsInObject(object, @intCast(u16, object_id));1105 try self.resolveSymbolsInObject(@intCast(u16, object_id));
1169 }1106 }
11701107
1171 try self.resolveSymbolsInArchives();1108 try self.resolveSymbolsInArchives();
1172 try self.resolveDyldStubBinder();1109 try self.resolveDyldStubBinder();
1173 try self.createDyldPrivateAtom();
1174 try self.createStubHelperPreambleAtom();
1175 try self.resolveSymbolsInDylibs();1110 try self.resolveSymbolsInDylibs();
1176 try self.createMhExecuteHeaderSymbol();1111 try self.createMhExecuteHeaderSymbol();
1177 try self.createDsoHandleSymbol();1112 try self.createDsoHandleSymbol();
1178 try self.addCodeSignatureLC();
1179 try self.resolveSymbolsAtLoading();1113 try self.resolveSymbolsAtLoading();
11801114
1181 if (self.unresolved.count() > 0) {1115 if (self.unresolved.count() > 0) {
...@@ -1188,7 +1122,13 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)...@@ -1188,7 +1122,13 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
1188 return error.FrameworkNotFound;1122 return error.FrameworkNotFound;
1189 }1123 }
11901124
1125 for (self.objects.items) |*object| {
1126 try object.scanInputSections(self);
1127 }
1128
1129 try self.createDyldPrivateAtom();
1191 try self.createTentativeDefAtoms();1130 try self.createTentativeDefAtoms();
1131 try self.createStubHelperPreambleAtom();
11921132
1193 for (self.objects.items) |*object, object_id| {1133 for (self.objects.items) |*object, object_id| {
1194 try object.splitIntoAtomsOneShot(self, @intCast(u32, object_id));1134 try object.splitIntoAtomsOneShot(self, @intCast(u32, object_id));
...@@ -1198,49 +1138,82 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)...@@ -1198,49 +1138,82 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
1198 try dead_strip.gcAtoms(self);1138 try dead_strip.gcAtoms(self);
1199 }1139 }
12001140
1201 try self.pruneAndSortSections();
1202 try self.allocateSegments();1141 try self.allocateSegments();
1203 try self.allocateSymbols();1142 try self.allocateSymbols();
12041143
1205 try self.allocateSpecialSymbols();1144 try self.allocateSpecialSymbols();
12061145
1207 if (build_options.enable_logging) {1146 if (build_options.enable_logging or true) {
1208 self.logSymtab();1147 self.logSymtab();
1209 self.logSectionOrdinals();1148 self.logSections();
1210 self.logAtoms();1149 self.logAtoms();
1211 }1150 }
12121151
1213 try self.writeAtomsOneShot();1152 try self.writeAtomsOneShot();
12141153
1215 if (self.rustc_section_index) |id| {1154 var lc_buffer = std.ArrayList(u8).init(arena);
1216 const sect = self.getSectionPtr(.{1155 const lc_writer = lc_buffer.writer();
1217 .seg = self.data_segment_cmd_index.?,1156 var ncmds: u32 = 0;
1218 .sect = id,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,
1219 });1168 });
1220 sect.size = self.rustc_section_size;1169 ncmds += 1;
1221 }1170 }
12221171
1223 try self.setEntryPoint();1172 try self.writeBuildVersionLC(&ncmds, lc_writer);
1224 try self.writeLinkeditSegment();
12251173
1226 if (self.code_signature) |*csig| {1174 {
1227 csig.clear(gpa);1175 var uuid_lc = macho.uuid_command{
1228 csig.code_directory.ident = self.base.options.emit.?.sub_path;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: {
1229 // Preallocate space for the code signature.1193 // Preallocate space for the code signature.
1230 // We need to do this at this stage so that we have the load commands with proper values1194 // We need to do this at this stage so that we have the load commands with proper values
1231 // written out to the file.1195 // written out to the file.
1232 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment1196 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
1233 // where the code signature goes into.1197 // where the code signature goes into.
1234 try self.writeCodeSignaturePadding(csig);1198 var codesig = CodeSignature.init(self.page_size);
1235 }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();1210 try self.base.file.?.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));
1238 try self.writeHeader();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| {1215 if (codesig) |*csig| {
1243 try self.writeCodeSignature(csig); // code signing always comes last1216 try self.writeCodeSignature(csig, codesig_offset.?); // code signing always comes last
1244 }1217 }
1245 }1218 }
12461219
...@@ -1395,66 +1368,77 @@ fn resolveFramework(...@@ -1395,66 +1368,77 @@ fn resolveFramework(
1395}1368}
13961369
1397fn parseObject(self: *MachO, path: []const u8) !bool {1370fn parseObject(self: *MachO, path: []const u8) !bool {
1371 const gpa = self.base.allocator;
1398 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {1372 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
1399 error.FileNotFound => return false,1373 error.FileNotFound => return false,
1400 else => |e| return e,1374 else => |e| return e,
1401 };1375 };
1402 errdefer file.close();1376 defer file.close();
1403
1404 const name = try self.base.allocator.dupe(u8, path);
1405 errdefer self.base.allocator.free(name);
14061377
1378 const name = try gpa.dupe(u8, path);
1379 errdefer gpa.free(name);
1380 const cpu_arch = self.base.options.target.cpu.arch;
1407 const mtime: u64 = mtime: {1381 const mtime: u64 = mtime: {
1408 const stat = file.stat() catch break :mtime 0;1382 const stat = file.stat() catch break :mtime 0;
1409 break :mtime @intCast(u64, @divFloor(stat.mtime, 1_000_000_000));1383 break :mtime @intCast(u64, @divFloor(stat.mtime, 1_000_000_000));
1410 };1384 };
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
1412 var object = Object{1389 var object = Object{
1413 .name = name,1390 .name = name,
1414 .file = file,
1415 .mtime = mtime,1391 .mtime = mtime,
1392 .contents = contents,
1416 };1393 };
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) {
1419 error.EndOfStream, error.NotObject => {1396 error.EndOfStream, error.NotObject => {
1420 object.deinit(self.base.allocator);1397 object.deinit(gpa);
1421 return false;1398 return false;
1422 },1399 },
1423 else => |e| return e,1400 else => |e| return e,
1424 };1401 };
14251402
1426 try self.objects.append(self.base.allocator, object);1403 try self.objects.append(gpa, object);
14271404
1428 return true;1405 return true;
1429}1406}
14301407
1431fn parseArchive(self: *MachO, path: []const u8, force_load: bool) !bool {1408fn parseArchive(self: *MachO, path: []const u8, force_load: bool) !bool {
1409 const gpa = self.base.allocator;
1432 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {1410 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
1433 error.FileNotFound => return false,1411 error.FileNotFound => return false,
1434 else => |e| return e,1412 else => |e| return e,
1435 };1413 };
1436 errdefer file.close();1414 errdefer file.close();
14371415
1438 const name = try self.base.allocator.dupe(u8, path);1416 const name = try gpa.dupe(u8, path);
1439 errdefer self.base.allocator.free(name);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
1441 var archive = Archive{1423 var archive = Archive{
1442 .name = name,1424 .name = name,
1425 .fat_offset = fat_offset,
1443 .file = file,1426 .file = file,
1444 };1427 };
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) {
1447 error.EndOfStream, error.NotArchive => {1430 error.EndOfStream, error.NotArchive => {
1448 archive.deinit(self.base.allocator);1431 archive.deinit(gpa);
1449 return false;1432 return false;
1450 },1433 },
1451 else => |e| return e,1434 else => |e| return e,
1452 };1435 };
14531436
1454 if (force_load) {1437 if (force_load) {
1455 defer archive.deinit(self.base.allocator);1438 defer archive.deinit(gpa);
1439 defer file.close();
1456 // Get all offsets from the ToC1440 // 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);
1458 defer offsets.deinit();1442 defer offsets.deinit();
1459 for (archive.toc.values()) |offs| {1443 for (archive.toc.values()) |offs| {
1460 for (offs.items) |off| {1444 for (offs.items) |off| {
...@@ -1462,15 +1446,11 @@ fn parseArchive(self: *MachO, path: []const u8, force_load: bool) !bool {...@@ -1462,15 +1446,11 @@ fn parseArchive(self: *MachO, path: []const u8, force_load: bool) !bool {
1462 }1446 }
1463 }1447 }
1464 for (offsets.keys()) |off| {1448 for (offsets.keys()) |off| {
1465 const object = try self.objects.addOne(self.base.allocator);1449 const object = try archive.parseObject(gpa, cpu_arch, off);
1466 object.* = try archive.parseObject(1450 try self.objects.append(gpa, object);
1467 self.base.allocator,
1468 self.base.options.target.cpu.arch,
1469 off,
1470 );
1471 }1451 }
1472 } else {1452 } else {
1473 try self.archives.append(self.base.allocator, archive);1453 try self.archives.append(gpa, archive);
1474 }1454 }
14751455
1476 return true;1456 return true;
...@@ -1481,6 +1461,7 @@ const ParseDylibError = error{...@@ -1481,6 +1461,7 @@ const ParseDylibError = error{
1481 EmptyStubFile,1461 EmptyStubFile,
1482 MismatchedCpuArchitecture,1462 MismatchedCpuArchitecture,
1483 UnsupportedCpuArchitecture,1463 UnsupportedCpuArchitecture,
1464 EndOfStream,
1484} || fs.File.OpenError || std.os.PReadError || Dylib.Id.ParseError;1465} || fs.File.OpenError || std.os.PReadError || Dylib.Id.ParseError;
14851466
1486const DylibCreateOpts = struct {1467const DylibCreateOpts = struct {
...@@ -1497,43 +1478,53 @@ pub fn parseDylib(...@@ -1497,43 +1478,53 @@ pub fn parseDylib(
1497 dependent_libs: anytype,1478 dependent_libs: anytype,
1498 opts: DylibCreateOpts,1479 opts: DylibCreateOpts,
1499) ParseDylibError!bool {1480) ParseDylibError!bool {
1481 const gpa = self.base.allocator;
1500 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {1482 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
1501 error.FileNotFound => return false,1483 error.FileNotFound => return false,
1502 else => |e| return e,1484 else => |e| return e,
1503 };1485 };
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);1492 const reader = file.reader();
1507 errdefer self.base.allocator.free(name);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
1509 const dylib_id = @intCast(u16, self.dylibs.items.len);1501 const dylib_id = @intCast(u16, self.dylibs.items.len);
1510 var dylib = Dylib{1502 var dylib = Dylib{ .weak = opts.weak };
1511 .name = name,
1512 .file = file,
1513 .weak = opts.weak,
1514 };
15151503
1516 dylib.parse(1504 dylib.parseFromBinary(
1517 self.base.allocator,1505 gpa,
1518 self.base.options.target.cpu.arch,1506 cpu_arch,
1519 dylib_id,1507 dylib_id,
1520 dependent_libs,1508 dependent_libs,
1509 path,
1510 contents,
1521 ) catch |err| switch (err) {1511 ) catch |err| switch (err) {
1522 error.EndOfStream, error.NotDylib => {1512 error.EndOfStream, error.NotDylib => {
1523 try file.seekTo(0);1513 try file.seekTo(0);
15241514
1525 var lib_stub = LibStub.loadFromFile(self.base.allocator, file) catch {1515 var lib_stub = LibStub.loadFromFile(gpa, file) catch {
1526 dylib.deinit(self.base.allocator);1516 dylib.deinit(gpa);
1527 return false;1517 return false;
1528 };1518 };
1529 defer lib_stub.deinit();1519 defer lib_stub.deinit();
15301520
1531 try dylib.parseFromStub(1521 try dylib.parseFromStub(
1532 self.base.allocator,1522 gpa,
1533 self.base.options.target,1523 self.base.options.target,
1534 lib_stub,1524 lib_stub,
1535 dylib_id,1525 dylib_id,
1536 dependent_libs,1526 dependent_libs,
1527 path,
1537 );1528 );
1538 },1529 },
1539 else => |e| return e,1530 else => |e| return e,
...@@ -1547,13 +1538,13 @@ pub fn parseDylib(...@@ -1547,13 +1538,13 @@ pub fn parseDylib(
1547 log.warn(" dylib version: {}", .{dylib.id.?.current_version});1538 log.warn(" dylib version: {}", .{dylib.id.?.current_version});
15481539
1549 // TODO maybe this should be an error and facilitate auto-cleanup?1540 // TODO maybe this should be an error and facilitate auto-cleanup?
1550 dylib.deinit(self.base.allocator);1541 dylib.deinit(gpa);
1551 return false;1542 return false;
1552 }1543 }
1553 }1544 }
15541545
1555 try self.dylibs.append(self.base.allocator, dylib);1546 try self.dylibs.append(gpa, dylib);
1556 try self.dylibs_map.putNoClobber(self.base.allocator, dylib.id.?.name, dylib_id);1547 try self.dylibs_map.putNoClobber(gpa, dylib.id.?.name, dylib_id);
15571548
1558 const should_link_dylib_even_if_unreachable = blk: {1549 const should_link_dylib_even_if_unreachable = blk: {
1559 if (self.base.options.dead_strip_dylibs and !opts.needed) break :blk false;1550 if (self.base.options.dead_strip_dylibs and !opts.needed) break :blk false;
...@@ -1561,8 +1552,7 @@ pub fn parseDylib(...@@ -1561,8 +1552,7 @@ pub fn parseDylib(
1561 };1552 };
15621553
1563 if (should_link_dylib_even_if_unreachable) {1554 if (should_link_dylib_even_if_unreachable) {
1564 try self.addLoadDylibLC(dylib_id);1555 try self.referenced_dylibs.putNoClobber(gpa, dylib_id, {});
1565 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});
1566 }1556 }
15671557
1568 return true;1558 return true;
...@@ -1572,10 +1562,8 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const...@@ -1572,10 +1562,8 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const
1572 for (files) |file_name| {1562 for (files) |file_name| {
1573 const full_path = full_path: {1563 const full_path = full_path: {
1574 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;1564 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
1575 const path = try fs.realpath(file_name, &buffer);1565 break :full_path try fs.realpath(file_name, &buffer);
1576 break :full_path try self.base.allocator.dupe(u8, path);
1577 };1566 };
1578 defer self.base.allocator.free(full_path);
1579 log.debug("parsing input file path '{s}'", .{full_path});1567 log.debug("parsing input file path '{s}'", .{full_path});
15801568
1581 if (try self.parseObject(full_path)) continue;1569 if (try self.parseObject(full_path)) continue;
...@@ -1592,10 +1580,8 @@ fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !voi...@@ -1592,10 +1580,8 @@ fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !voi
1592 for (files) |file_name| {1580 for (files) |file_name| {
1593 const full_path = full_path: {1581 const full_path = full_path: {
1594 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;1582 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
1595 const path = try fs.realpath(file_name, &buffer);1583 break :full_path try fs.realpath(file_name, &buffer);
1596 break :full_path try self.base.allocator.dupe(u8, path);
1597 };1584 };
1598 defer self.base.allocator.free(full_path);
1599 log.debug("parsing and force loading static archive '{s}'", .{full_path});1585 log.debug("parsing and force loading static archive '{s}'", .{full_path});
16001586
1601 if (try self.parseArchive(full_path, true)) continue;1587 if (try self.parseArchive(full_path, true)) continue;
...@@ -1669,639 +1655,232 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any...@@ -1669,639 +1655,232 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any
1669 }1655 }
1670}1656}
16711657
1672pub const MatchingSection = struct {1658pub fn getOutputSection(self: *MachO, sect: macho.section_64) !?u8 {
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 {
1687 const segname = sect.segName();1659 const segname = sect.segName();
1688 const sectname = sect.sectName();1660 const sectname = sect.sectName();
1689 const res: ?MatchingSection = blk: {1661 const res: ?u8 = blk: {
1690 switch (sect.type_()) {1662 if (mem.eql(u8, "__LLVM", segname)) {
1691 macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => {1663 log.debug("TODO LLVM section: type 0x{x}, name '{s},{s}'", .{
1692 if (self.text_const_section_index == null) {1664 sect.flags, segname, sectname,
1693 self.text_const_section_index = try self.initSection(1665 });
1694 self.text_segment_cmd_index.?,1666 break :blk null;
1695 "__const",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,
1696 sect.size,1714 sect.size,
1697 sect.@"align",1715 sect.@"align",
1698 .{},1716 .{},
1699 );1717 );
1700 }1718 }
17011719 break :blk self.getSectionByName("__TEXT", "__cstring") orelse try self.initSection(
1702 break :blk .{1720 "__TEXT",
1703 .seg = self.text_segment_cmd_index.?,1721 "__cstring",
1704 .sect = self.text_const_section_index.?,1722 sect.size,
1705 };1723 sect.@"align",
1724 .{ .flags = macho.S_CSTRING_LITERALS },
1725 );
1706 },1726 },
1707 macho.S_CSTRING_LITERALS => {1727 macho.S_MOD_INIT_FUNC_POINTERS,
1708 if (mem.eql(u8, sectname, "__objc_methname")) {1728 macho.S_MOD_TERM_FUNC_POINTERS,
1709 // TODO it seems the common values within the sections in objects are deduplicated/merged1729 => {
1710 // on merging the sections' contents.1730 break :blk self.getSectionByName("__DATA_CONST", sectname) orelse try self.initSection(
1711 if (self.objc_methname_section_index == null) {1731 "__DATA_CONST",
1712 self.objc_methname_section_index = try self.initSection(1732 sectname,
1713 self.text_segment_cmd_index.?,1733 sect.size,
1714 "__objc_methname",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",
1715 sect.size,1773 sect.size,
1716 sect.@"align",1774 sect.@"align",
1717 .{},1775 .{},
1718 );1776 );
1719 }1777 }
17201778 }
1721 break :blk .{1779 if (mem.eql(u8, segname, "__DATA")) {
1722 .seg = self.text_segment_cmd_index.?,1780 if (mem.eql(u8, sectname, "__const") or
1723 .sect = self.objc_methname_section_index.?,1781 mem.eql(u8, sectname, "__cfstring") or
1724 };1782 mem.eql(u8, sectname, "__objc_classlist") or
1725 } else if (mem.eql(u8, sectname, "__objc_methtype")) {1783 mem.eql(u8, sectname, "__objc_imageinfo"))
1726 if (self.objc_methtype_section_index == null) {1784 {
1727 self.objc_methtype_section_index = try self.initSection(1785 break :blk self.getSectionByName("__DATA_CONST", sectname) orelse
1728 self.text_segment_cmd_index.?,1786 try self.initSection(
1729 "__objc_methtype",1787 "__DATA_CONST",
1788 sectname,
1730 sect.size,1789 sect.size,
1731 sect.@"align",1790 sect.@"align",
1732 .{},1791 .{},
1733 );1792 );
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.?;
1734 }1804 }
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 .{1820pub fn createEmptyAtom(gpa: Allocator, sym_index: u32, size: u64, alignment: u32) !*Atom {
1737 .seg = self.text_segment_cmd_index.?,1821 const size_usize = math.cast(usize, size) orelse return error.Overflow;
1738 .sect = self.objc_methtype_section_index.?,1822 const atom = try gpa.create(Atom);
1739 };1823 errdefer gpa.destroy(atom);
1740 } else if (mem.eql(u8, sectname, "__objc_classname")) {1824 atom.* = Atom.empty;
1741 if (self.objc_classname_section_index == null) {1825 atom.sym_index = sym_index;
1742 self.objc_classname_section_index = try self.initSection(1826 atom.size = size;
1743 self.text_segment_cmd_index.?,1827 atom.alignment = alignment;
1744 "__objc_classname",
1745 sect.size,
1746 sect.@"align",
1747 .{},
1748 );
1749 }
17501828
1751 break :blk .{1829 try atom.code.resize(gpa, size_usize);
1752 .seg = self.text_segment_cmd_index.?,1830 mem.set(u8, atom.code.items, 0);
1753 .sect = self.objc_classname_section_index.?,
1754 };
1755 }
17561831
1757 if (self.cstring_section_index == null) {1832 return atom;
1758 self.cstring_section_index = try self.initSection(1833}
1759 self.text_segment_cmd_index.?,
1760 "__cstring",
1761 sect.size,
1762 sect.@"align",
1763 .{
1764 .flags = macho.S_CSTRING_LITERALS,
1765 },
1766 );
1767 }
17681834
1769 break :blk .{1835pub fn writeAtom(self: *MachO, atom: *Atom, sect_id: u8) !void {
1770 .seg = self.text_segment_cmd_index.?,1836 const section = self.sections.get(sect_id);
1771 .sect = self.cstring_section_index.?,1837 const sym = atom.getSymbol(self);
1772 };1838 const file_offset = section.header.offset + sym.n_value - section.header.addr;
1773 },1839 try atom.resolveRelocs(self);
1774 macho.S_LITERAL_POINTERS => {1840 log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset });
1775 if (mem.eql(u8, segname, "__DATA") and mem.eql(u8, sectname, "__objc_selrefs")) {1841 try self.base.file.?.pwriteAll(atom.code.items, file_offset);
1776 if (self.objc_selrefs_section_index == null) {1842}
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 }
17871843
1788 break :blk .{1844fn allocateSymbols(self: *MachO) !void {
1789 .seg = self.data_segment_cmd_index.?,1845 const slice = self.sections.slice();
1790 .sect = self.objc_selrefs_section_index.?,1846 for (slice.items(.last_atom)) |last_atom, sect_id| {
1791 };1847 const header = slice.items(.header)[sect_id];
1792 } else {1848 var atom = last_atom orelse continue;
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 }
18091849
1810 break :blk .{1850 while (atom.prev) |prev| {
1811 .seg = self.data_const_segment_cmd_index.?,1851 atom = prev;
1812 .sect = self.mod_init_func_section_index.?,1852 }
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 }
18271853
1828 break :blk .{1854 const n_sect = @intCast(u8, sect_id + 1);
1829 .seg = self.data_const_segment_cmd_index.?,1855 var base_vaddr = header.addr;
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 }
18451856
1846 break :blk .{1857 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{
1847 .seg = self.data_segment_cmd_index.?,1858 n_sect,
1848 .sect = self.bss_section_index.?,1859 header.segName(),
1849 };1860 header.sectName(),
1850 },1861 });
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 }
18631862
1864 break :blk .{1863 while (true) {
1865 .seg = self.data_segment_cmd_index.?,1864 const alignment = try math.powi(u32, 2, atom.alignment);
1866 .sect = self.tlv_section_index.?,1865 base_vaddr = mem.alignForwardGeneric(u64, base_vaddr, alignment);
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 }
18811866
1882 break :blk .{1867 const sym = atom.getSymbolPtr(self);
1883 .seg = self.data_segment_cmd_index.?,1868 sym.n_value = base_vaddr;
1884 .sect = self.tlv_ptrs_section_index.?,1869 sym.n_sect = n_sect;
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 }
18991870
1900 break :blk .{1871 log.debug(" ATOM(%{d}, '{s}') @{x}", .{ atom.sym_index, atom.getName(self), base_vaddr });
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 }
19171872
1918 break :blk .{1873 // Update each symbol contained within the atom
1919 .seg = self.data_segment_cmd_index.?,1874 for (atom.contained.items) |sym_at_off| {
1920 .sect = self.tlv_bss_section_index.?,1875 const contained_sym = self.getSymbolPtr(.{
1921 };1876 .sym_index = sym_at_off.sym_index,
1922 },1877 .file = atom.file,
1923 macho.S_COALESCED => {1878 });
1924 if (mem.eql(u8, "__TEXT", segname) and mem.eql(u8, "__eh_frame", sectname)) {1879 contained_sym.n_value = base_vaddr + sym_at_off.offset;
1925 // TODO I believe __eh_frame is currently part of __unwind_info section1880 contained_sym.n_sect = n_sect;
1926 // in the latest ld64 output.1881 }
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 }
19361882
1937 break :blk .{1883 base_vaddr += atom.size;
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;
23051884
2306 if (atom.next) |next| {1885 if (atom.next) |next| {
2307 atom = next;1886 atom = next;
...@@ -2310,24 +1889,6 @@ fn allocateSymbols(self: *MachO) !void {...@@ -2310,24 +1889,6 @@ fn allocateSymbols(self: *MachO) !void {
2310 }1889 }
2311}1890}
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
2331fn allocateSpecialSymbols(self: *MachO) !void {1892fn allocateSpecialSymbols(self: *MachO) !void {
2332 for (&[_][]const u8{1893 for (&[_][]const u8{
2333 "___dso_handle",1894 "___dso_handle",
...@@ -2336,16 +1897,13 @@ fn allocateSpecialSymbols(self: *MachO) !void {...@@ -2336,16 +1897,13 @@ fn allocateSpecialSymbols(self: *MachO) !void {
2336 const global = self.globals.get(name) orelse continue;1897 const global = self.globals.get(name) orelse continue;
2337 if (global.file != null) continue;1898 if (global.file != null) continue;
2338 const sym = self.getSymbolPtr(global);1899 const sym = self.getSymbolPtr(global);
2339 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;1900 const seg = self.segments.items[self.text_segment_cmd_index.?];
2340 sym.n_sect = self.getSectionOrdinal(.{1901 sym.n_sect = 1;
2341 .seg = self.text_segment_cmd_index.?,1902 sym.n_value = seg.vmaddr;
2342 .sect = 0,
2343 });
2344 sym.n_value = seg.inner.vmaddr;
23451903
2346 log.debug("allocating {s} at the start of {s}", .{1904 log.debug("allocating {s} at the start of {s}", .{
2347 name,1905 name,
2348 seg.inner.segName(),1906 seg.segName(),
2349 });1907 });
2350 }1908 }
2351}1909}
...@@ -2353,18 +1911,21 @@ fn allocateSpecialSymbols(self: *MachO) !void {...@@ -2353,18 +1911,21 @@ fn allocateSpecialSymbols(self: *MachO) !void {
2353fn writeAtomsOneShot(self: *MachO) !void {1911fn writeAtomsOneShot(self: *MachO) !void {
2354 assert(self.mode == .one_shot);1912 assert(self.mode == .one_shot);
23551913
2356 var it = self.atoms.iterator();1914 const gpa = self.base.allocator;
2357 while (it.next()) |entry| {1915 const slice = self.sections.slice();
2358 const sect = self.getSection(entry.key_ptr.*);1916
2359 var atom: *Atom = entry.value_ptr.*;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);
2364 defer buffer.deinit();1925 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
2369 while (atom.prev) |prev| {1930 while (atom.prev) |prev| {
2370 atom = prev;1931 atom = prev;
...@@ -2399,18 +1960,18 @@ fn writeAtomsOneShot(self: *MachO) !void {...@@ -2399,18 +1960,18 @@ fn writeAtomsOneShot(self: *MachO) !void {
2399 if (atom.next) |next| {1960 if (atom.next) |next| {
2400 atom = next;1961 atom = next;
2401 } else {1962 } else {
2402 assert(buffer.items.len == sect.size);1963 assert(buffer.items.len == header.size);
2403 log.debug(" (writing at file offset 0x{x})", .{sect.offset});1964 log.debug(" (writing at file offset 0x{x})", .{header.offset});
2404 try self.base.file.?.pwriteAll(buffer.items, sect.offset);1965 try self.base.file.?.pwriteAll(buffer.items, header.offset);
2405 break;1966 break;
2406 }1967 }
2407 }1968 }
2408 }1969 }
2409}1970}
24101971
2411fn writePadding(self: *MachO, match: MatchingSection, size: usize, writer: anytype) !void {1972fn writePadding(self: *MachO, sect_id: u8, size: usize, writer: anytype) !void {
2412 const is_code = match.seg == self.text_segment_cmd_index.? and match.sect == self.text_section_index.?;1973 const header = self.sections.items(.header)[sect_id];
2413 const min_alignment: u3 = if (!is_code)1974 const min_alignment: u3 = if (!header.isCode())
2414 11975 1
2415 else switch (self.base.options.target.cpu.arch) {1976 else switch (self.base.options.target.cpu.arch) {
2416 .aarch64 => @sizeOf(u32),1977 .aarch64 => @sizeOf(u32),
...@@ -2421,7 +1982,7 @@ fn writePadding(self: *MachO, match: MatchingSection, size: usize, writer: anyty...@@ -2421,7 +1982,7 @@ fn writePadding(self: *MachO, match: MatchingSection, size: usize, writer: anyty
2421 const len = @divExact(size, min_alignment);1982 const len = @divExact(size, min_alignment);
2422 var i: usize = 0;1983 var i: usize = 0;
2423 while (i < len) : (i += 1) {1984 while (i < len) : (i += 1) {
2424 if (!is_code) {1985 if (!header.isCode()) {
2425 try writer.writeByte(0);1986 try writer.writeByte(0);
2426 } else switch (self.base.options.target.cpu.arch) {1987 } else switch (self.base.options.target.cpu.arch) {
2427 .aarch64 => {1988 .aarch64 => {
...@@ -2439,20 +2000,19 @@ fn writePadding(self: *MachO, match: MatchingSection, size: usize, writer: anyty...@@ -2439,20 +2000,19 @@ fn writePadding(self: *MachO, match: MatchingSection, size: usize, writer: anyty
2439fn writeAtomsIncremental(self: *MachO) !void {2000fn writeAtomsIncremental(self: *MachO) !void {
2440 assert(self.mode == .incremental);2001 assert(self.mode == .incremental);
24412002
2442 var it = self.atoms.iterator();2003 const slice = self.sections.slice();
2443 while (it.next()) |entry| {2004 for (slice.items(.last_atom)) |last, i| {
2444 const match = entry.key_ptr.*;2005 var atom: *Atom = last orelse continue;
2445 const sect = self.getSection(match);2006 const sect_i = @intCast(u8, i);
2446 var atom: *Atom = entry.value_ptr.*;2007 const header = slice.items(.header)[sect_i];
24472008
2448 // TODO handle zerofill in stage22009 if (header.isZerofill()) continue;
2449 // if (sect.flags == macho.S_ZEROFILL or sect.flags == macho.S_THREAD_LOCAL_ZEROFILL) 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
2453 while (true) {2013 while (true) {
2454 if (atom.dirty) {2014 if (atom.dirty) {
2455 try self.writeAtom(atom, match);2015 try self.writeAtom(atom, sect_i);
2456 atom.dirty = false;2016 atom.dirty = false;
2457 }2017 }
24582018
...@@ -2503,10 +2063,7 @@ pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {...@@ -2503,10 +2063,7 @@ pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
2503 try self.managed_atoms.append(gpa, atom);2063 try self.managed_atoms.append(gpa, atom);
2504 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);2064 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
25052065
2506 try self.allocateAtomCommon(atom, .{2066 try self.allocateAtomCommon(atom, self.got_section_index.?);
2507 .seg = self.data_const_segment_cmd_index.?,
2508 .sect = self.got_section_index.?,
2509 });
25102067
2511 return atom;2068 return atom;
2512}2069}
...@@ -2535,7 +2092,7 @@ pub fn createTlvPtrAtom(self: *MachO, target: SymbolWithLoc) !*Atom {...@@ -2535,7 +2092,7 @@ pub fn createTlvPtrAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
2535 try self.managed_atoms.append(gpa, atom);2092 try self.managed_atoms.append(gpa, atom);
2536 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);2093 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
25372094
2538 const match = (try self.getMatchingSection(.{2095 const match = (try self.getOutputSection(.{
2539 .segname = makeStaticString("__DATA"),2096 .segname = makeStaticString("__DATA"),
2540 .sectname = makeStaticString("__thread_ptrs"),2097 .sectname = makeStaticString("__thread_ptrs"),
2541 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,2098 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
...@@ -2561,10 +2118,7 @@ fn createDyldPrivateAtom(self: *MachO) !void {...@@ -2561,10 +2118,7 @@ fn createDyldPrivateAtom(self: *MachO) !void {
2561 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);2118 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);
2562 self.dyld_private_atom = atom;2119 self.dyld_private_atom = atom;
25632120
2564 try self.allocateAtomCommon(atom, .{2121 try self.allocateAtomCommon(atom, self.data_section_index.?);
2565 .seg = self.data_segment_cmd_index.?,
2566 .sect = self.data_section_index.?,
2567 });
25682122
2569 try self.managed_atoms.append(gpa, atom);2123 try self.managed_atoms.append(gpa, atom);
2570 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);2124 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
...@@ -2692,10 +2246,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -2692,10 +2246,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
2692 }2246 }
2693 self.stub_helper_preamble_atom = atom;2247 self.stub_helper_preamble_atom = atom;
26942248
2695 try self.allocateAtomCommon(atom, .{2249 try self.allocateAtomCommon(atom, self.stub_helper_section_index.?);
2696 .seg = self.text_segment_cmd_index.?,
2697 .sect = self.stub_helper_section_index.?,
2698 });
26992250
2700 try self.managed_atoms.append(gpa, atom);2251 try self.managed_atoms.append(gpa, atom);
2701 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);2252 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
...@@ -2771,10 +2322,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -2771,10 +2322,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
2771 try self.managed_atoms.append(gpa, atom);2322 try self.managed_atoms.append(gpa, atom);
2772 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);2323 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
27732324
2774 try self.allocateAtomCommon(atom, .{2325 try self.allocateAtomCommon(atom, self.stub_helper_section_index.?);
2775 .seg = self.text_segment_cmd_index.?,
2776 .sect = self.stub_helper_section_index.?,
2777 });
27782326
2779 return atom;2327 return atom;
2780}2328}
...@@ -2814,10 +2362,7 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWi...@@ -2814,10 +2362,7 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWi
2814 try self.managed_atoms.append(gpa, atom);2362 try self.managed_atoms.append(gpa, atom);
2815 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);2363 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
28162364
2817 try self.allocateAtomCommon(atom, .{2365 try self.allocateAtomCommon(atom, self.la_symbol_ptr_section_index.?);
2818 .seg = self.data_segment_cmd_index.?,
2819 .sect = self.la_symbol_ptr_section_index.?,
2820 });
28212366
2822 return atom;2367 return atom;
2823}2368}
...@@ -2896,10 +2441,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -2896,10 +2441,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
2896 try self.managed_atoms.append(gpa, atom);2441 try self.managed_atoms.append(gpa, atom);
2897 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);2442 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
28982443
2899 try self.allocateAtomCommon(atom, .{2444 try self.allocateAtomCommon(atom, self.stubs_section_index.?);
2900 .seg = self.text_segment_cmd_index.?,
2901 .sect = self.stubs_section_index.?,
2902 });
29032445
2904 return atom;2446 return atom;
2905}2447}
...@@ -2917,19 +2459,18 @@ fn createTentativeDefAtoms(self: *MachO) !void {...@@ -2917,19 +2459,18 @@ fn createTentativeDefAtoms(self: *MachO) !void {
29172459
2918 // Convert any tentative definition into a regular symbol and allocate2460 // Convert any tentative definition into a regular symbol and allocate
2919 // text blocks for each tentative definition.2461 // 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
2926 const size = sym.n_value;2462 const size = sym.n_value;
2927 const alignment = (sym.n_desc >> 8) & 0x0f;2463 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
2929 sym.* = .{2470 sym.* = .{
2930 .n_strx = sym.n_strx,2471 .n_strx = sym.n_strx,
2931 .n_type = macho.N_SECT | macho.N_EXT,2472 .n_type = macho.N_SECT | macho.N_EXT,
2932 .n_sect = 0,2473 .n_sect = n_sect,
2933 .n_desc = 0,2474 .n_desc = 0,
2934 .n_value = 0,2475 .n_value = 0,
2935 };2476 };
...@@ -2937,7 +2478,7 @@ fn createTentativeDefAtoms(self: *MachO) !void {...@@ -2937,7 +2478,7 @@ fn createTentativeDefAtoms(self: *MachO) !void {
2937 const atom = try MachO.createEmptyAtom(gpa, global.sym_index, size, alignment);2478 const atom = try MachO.createEmptyAtom(gpa, global.sym_index, size, alignment);
2938 atom.file = global.file;2479 atom.file = global.file;
29392480
2940 try self.allocateAtomCommon(atom, match);2481 try self.allocateAtomCommon(atom, n_sect);
29412482
2942 if (global.file) |file| {2483 if (global.file) |file| {
2943 const object = &self.objects.items[file];2484 const object = &self.objects.items[file];
...@@ -3060,7 +2601,8 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {...@@ -3060,7 +2601,8 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
3060 gop.value_ptr.* = current;2601 gop.value_ptr.* = current;
3061}2602}
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];
3064 log.debug("resolving symbols in '{s}'", .{object.name});2606 log.debug("resolving symbols in '{s}'", .{object.name});
30652607
3066 for (object.symtab.items) |sym, index| {2608 for (object.symtab.items) |sym, index| {
...@@ -3115,6 +2657,8 @@ fn resolveSymbolsInObject(self: *MachO, object: *Object, object_id: u16) !void {...@@ -3115,6 +2657,8 @@ fn resolveSymbolsInObject(self: *MachO, object: *Object, object_id: u16) !void {
3115fn resolveSymbolsInArchives(self: *MachO) !void {2657fn resolveSymbolsInArchives(self: *MachO) !void {
3116 if (self.archives.items.len == 0) return;2658 if (self.archives.items.len == 0) return;
31172659
2660 const gpa = self.base.allocator;
2661 const cpu_arch = self.base.options.target.cpu.arch;
3118 var next_sym: usize = 0;2662 var next_sym: usize = 0;
3119 loop: while (next_sym < self.unresolved.count()) {2663 loop: while (next_sym < self.unresolved.count()) {
3120 const global = self.globals.values()[self.unresolved.keys()[next_sym]];2664 const global = self.globals.values()[self.unresolved.keys()[next_sym]];
...@@ -3129,13 +2673,9 @@ fn resolveSymbolsInArchives(self: *MachO) !void {...@@ -3129,13 +2673,9 @@ fn resolveSymbolsInArchives(self: *MachO) !void {
3129 assert(offsets.items.len > 0);2673 assert(offsets.items.len > 0);
31302674
3131 const object_id = @intCast(u16, self.objects.items.len);2675 const object_id = @intCast(u16, self.objects.items.len);
3132 const object = try self.objects.addOne(self.base.allocator);2676 const object = try archive.parseObject(gpa, cpu_arch, offsets.items[0]);
3133 object.* = try archive.parseObject(2677 try self.objects.append(gpa, object);
3134 self.base.allocator,2678 try self.resolveSymbolsInObject(object_id);
3135 self.base.options.target.cpu.arch,
3136 offsets.items[0],
3137 );
3138 try self.resolveSymbolsInObject(object, object_id);
31392679
3140 continue :loop;2680 continue :loop;
3141 }2681 }
...@@ -3159,7 +2699,6 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {...@@ -3159,7 +2699,6 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
31592699
3160 const dylib_id = @intCast(u16, id);2700 const dylib_id = @intCast(u16, id);
3161 if (!self.referenced_dylibs.contains(dylib_id)) {2701 if (!self.referenced_dylibs.contains(dylib_id)) {
3162 try self.addLoadDylibLC(dylib_id);
3163 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});2702 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});
3164 }2703 }
31652704
...@@ -3257,7 +2796,6 @@ fn resolveDyldStubBinder(self: *MachO) !void {...@@ -3257,7 +2796,6 @@ fn resolveDyldStubBinder(self: *MachO) !void {
32572796
3258 const dylib_id = @intCast(u16, id);2797 const dylib_id = @intCast(u16, id);
3259 if (!self.referenced_dylibs.contains(dylib_id)) {2798 if (!self.referenced_dylibs.contains(dylib_id)) {
3260 try self.addLoadDylibLC(dylib_id);
3261 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});2799 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});
3262 }2800 }
32632801
...@@ -3280,47 +2818,192 @@ fn resolveDyldStubBinder(self: *MachO) !void {...@@ -3280,47 +2818,192 @@ fn resolveDyldStubBinder(self: *MachO) !void {
3280 self.got_entries.items[got_index].sym_index = got_atom.sym_index;2818 self.got_entries.items[got_index].sym_index = got_atom.sym_index;
3281}2819}
32822820
3283fn addLoadDylibLC(self: *MachO, id: u16) !void {2821fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
3284 const dylib = self.dylibs.items[id];2822 const name_len = mem.sliceTo(default_dyld_path, 0).len;
3285 const dylib_id = dylib.id orelse unreachable;2823 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
3286 var dylib_cmd = try macho.createLoadDylibCommand(2824 u64,
3287 self.base.allocator,2825 @sizeOf(macho.dylinker_command) + name_len,
3288 if (dylib.weak) .LOAD_WEAK_DYLIB else .LOAD_DYLIB,2826 @sizeOf(u64),
3289 dylib_id.name,2827 ));
3290 dylib_id.timestamp,2828 try lc_writer.writeStruct(macho.dylinker_command{
3291 dylib_id.current_version,2829 .cmd = .LOAD_DYLINKER,
3292 dylib_id.compatibility_version,2830 .cmdsize = cmdsize,
3293 );2831 .name = @sizeOf(macho.dylinker_command),
3294 errdefer dylib_cmd.deinit(self.base.allocator);2832 });
3295 try self.load_commands.append(self.base.allocator, .{ .dylib = dylib_cmd });2833 try lc_writer.writeAll(mem.sliceTo(default_dyld_path, 0));
3296 self.load_commands_dirty = true;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;
3297}2839}
32982840
3299fn addCodeSignatureLC(self: *MachO) !void {2841fn writeMainLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
3300 if (self.code_signature_cmd_index != null or self.code_signature == null) return;2842 if (self.base.options.output_mode != .Exe) return;
3301 self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);2843 const seg = self.segments.items[self.text_segment_cmd_index.?];
3302 try self.load_commands.append(self.base.allocator, .{2844 const global = try self.getEntryPoint();
3303 .linkedit_data = .{2845 const sym = self.getSymbol(global);
3304 .cmd = .CODE_SIGNATURE,2846 try lc_writer.writeStruct(macho.entry_point_command{
3305 .cmdsize = @sizeOf(macho.linkedit_data_command),2847 .cmd = .MAIN,
3306 .dataoff = 0,2848 .cmdsize = @sizeOf(macho.entry_point_command),
3307 .datasize = 0,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,
3308 },2878 },
3309 });2879 });
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;
3311}2887}
33122888
3313fn setEntryPoint(self: *MachO) !void {2889fn writeDylibIdLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
3314 if (self.base.options.output_mode != .Exe) return;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;2910const RpathIterator = struct {
3317 const global = try self.getEntryPoint();2911 buffer: []const []const u8,
3318 const sym = self.getSymbol(global);2912 table: std.StringHashMap(void),
3319 const ec = &self.load_commands.items[self.main_cmd_index.?].main;2913 count: usize = 0,
3320 ec.entryoff = @intCast(u32, sym.n_value - seg.inner.vmaddr);2914
3321 ec.stacksize = self.base.options.stack_size_override orelse 0;2915 fn init(gpa: Allocator, rpaths: []const []const u8) RpathIterator {
3322 self.entry_addr = sym.n_value;2916 return .{ .buffer = rpaths, .table = std.StringHashMap(void).init(gpa) };
3323 self.load_commands_dirty = true;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 }
3324}3007}
33253008
3326pub fn deinit(self: *MachO) void {3009pub fn deinit(self: *MachO) void {
...@@ -3334,7 +3017,6 @@ pub fn deinit(self: *MachO) void {...@@ -3334,7 +3017,6 @@ pub fn deinit(self: *MachO) void {
3334 d_sym.deinit(gpa);3017 d_sym.deinit(gpa);
3335 }3018 }
33363019
3337 self.section_ordinals.deinit(gpa);
3338 self.tlv_ptr_entries.deinit(gpa);3020 self.tlv_ptr_entries.deinit(gpa);
3339 self.tlv_ptr_entries_free_list.deinit(gpa);3021 self.tlv_ptr_entries_free_list.deinit(gpa);
3340 self.tlv_ptr_entries_table.deinit(gpa);3022 self.tlv_ptr_entries_table.deinit(gpa);
...@@ -3371,24 +3053,19 @@ pub fn deinit(self: *MachO) void {...@@ -3371,24 +3053,19 @@ pub fn deinit(self: *MachO) void {
3371 self.dylibs_map.deinit(gpa);3053 self.dylibs_map.deinit(gpa);
3372 self.referenced_dylibs.deinit(gpa);3054 self.referenced_dylibs.deinit(gpa);
33733055
3374 for (self.load_commands.items) |*lc| {3056 self.segments.deinit(gpa);
3375 lc.deinit(gpa);3057
3058 for (self.sections.items(.free_list)) |*list| {
3059 list.deinit(gpa);
3376 }3060 }
3377 self.load_commands.deinit(gpa);3061 self.sections.deinit(gpa);
33783062
3379 for (self.managed_atoms.items) |atom| {3063 for (self.managed_atoms.items) |atom| {
3380 atom.deinit(gpa);3064 atom.deinit(gpa);
3381 gpa.destroy(atom);3065 gpa.destroy(atom);
3382 }3066 }
3383 self.managed_atoms.deinit(gpa);3067 self.managed_atoms.deinit(gpa);
3384 self.atoms.deinit(gpa);3068
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 }
3392 if (self.base.options.module) |mod| {3069 if (self.base.options.module) |mod| {
3393 for (self.decls.keys()) |decl_index| {3070 for (self.decls.keys()) |decl_index| {
3394 const decl = mod.declPtr(decl_index);3071 const decl = mod.declPtr(decl_index);
...@@ -3408,34 +3085,24 @@ pub fn deinit(self: *MachO) void {...@@ -3408,34 +3085,24 @@ pub fn deinit(self: *MachO) void {
3408 }3085 }
34093086
3410 self.atom_by_index_table.deinit(gpa);3087 self.atom_by_index_table.deinit(gpa);
3411
3412 if (self.code_signature) |*csig| {
3413 csig.deinit(gpa);
3414 }
3415}3088}
34163089
3417pub fn closeFiles(self: MachO) void {3090pub fn closeFiles(self: MachO) void {
3418 for (self.objects.items) |object| {
3419 object.file.close();
3420 }
3421 for (self.archives.items) |archive| {3091 for (self.archives.items) |archive| {
3422 archive.file.close();3092 archive.file.close();
3423 }3093 }
3424 for (self.dylibs.items) |dylib| {
3425 dylib.file.close();
3426 }
3427 if (self.d_sym) |ds| {3094 if (self.d_sym) |ds| {
3428 ds.file.close();3095 ds.file.close();
3429 }3096 }
3430}3097}
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 {
3433 log.debug("freeAtom {*}", .{atom});3100 log.debug("freeAtom {*}", .{atom});
3434 if (!owns_atom) {3101 if (!owns_atom) {
3435 atom.deinit(self.base.allocator);3102 atom.deinit(self.base.allocator);
3436 }3103 }
34373104
3438 const free_list = self.atom_free_lists.getPtr(match).?;3105 const free_list = &self.sections.items(.free_list)[sect_id];
3439 var already_have_free_list_node = false;3106 var already_have_free_list_node = false;
3440 {3107 {
3441 var i: usize = 0;3108 var i: usize = 0;
...@@ -3452,13 +3119,14 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool)...@@ -3452,13 +3119,14 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool)
3452 }3119 }
3453 }3120 }
34543121
3455 if (self.atoms.getPtr(match)) |last_atom| {3122 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];
3456 if (last_atom.* == atom) {3123 if (maybe_last_atom.*) |last_atom| {
3124 if (last_atom == atom) {
3457 if (atom.prev) |prev| {3125 if (atom.prev) |prev| {
3458 // TODO shrink the section size here3126 // TODO shrink the section size here
3459 last_atom.* = prev;3127 maybe_last_atom.* = prev;
3460 } else {3128 } else {
3461 _ = self.atoms.fetchRemove(match);3129 maybe_last_atom.* = null;
3462 }3130 }
3463 }3131 }
3464 }3132 }
...@@ -3486,21 +3154,21 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool)...@@ -3486,21 +3154,21 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool)
3486 }3154 }
3487}3155}
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 {
3490 _ = self;3158 _ = self;
3491 _ = atom;3159 _ = atom;
3492 _ = new_block_size;3160 _ = new_block_size;
3493 _ = match;3161 _ = sect_id;
3494 // TODO check the new capacity, and if it crosses the size threshold into a big enough3162 // TODO check the new capacity, and if it crosses the size threshold into a big enough
3495 // capacity, insert a free list node for it.3163 // capacity, insert a free list node for it.
3496}3164}
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 {
3499 const sym = atom.getSymbol(self);3167 const sym = atom.getSymbol(self);
3500 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;3168 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;
3501 const need_realloc = !align_ok or new_atom_size > atom.capacity(self);3169 const need_realloc = !align_ok or new_atom_size > atom.capacity(self);
3502 if (!need_realloc) return sym.n_value;3170 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);
3504}3172}
35053173
3506fn allocateSymbol(self: *MachO) !u32 {3174fn allocateSymbol(self: *MachO) !u32 {
...@@ -3671,10 +3339,11 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv...@@ -3671,10 +3339,11 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
3671}3339}
36723340
3673pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Module.Decl.Index) !u32 {3341pub 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);
3675 defer code_buffer.deinit();3345 defer code_buffer.deinit();
36763346
3677 const gpa = self.base.allocator;
3678 const module = self.base.options.module.?;3347 const module = self.base.options.module.?;
3679 const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index);3348 const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index);
3680 if (!gop.found_existing) {3349 if (!gop.found_existing) {
...@@ -3725,25 +3394,25 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu...@@ -3725,25 +3394,25 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
3725 atom.code.clearRetainingCapacity();3394 atom.code.clearRetainingCapacity();
3726 try atom.code.appendSlice(gpa, code);3395 try atom.code.appendSlice(gpa, code);
37273396
3728 const match = try self.getMatchingSectionAtom(3397 const sect_id = try self.getOutputSectionAtom(
3729 atom,3398 atom,
3730 decl_name,3399 decl_name,
3731 typed_value.ty,3400 typed_value.ty,
3732 typed_value.val,3401 typed_value.val,
3733 required_alignment,3402 required_alignment,
3734 );3403 );
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
3737 log.debug("allocated atom for {?s} at 0x{x}", .{ name, addr });3406 log.debug("allocated atom for {?s} at 0x{x}", .{ name, addr });
3738 log.debug(" (required alignment 0x{x})", .{required_alignment});3407 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
3742 const symbol = atom.getSymbolPtr(self);3411 const symbol = atom.getSymbolPtr(self);
3743 symbol.* = .{3412 symbol.* = .{
3744 .n_strx = name_str_index,3413 .n_strx = name_str_index,
3745 .n_type = macho.N_SECT,3414 .n_type = macho.N_SECT,
3746 .n_sect = self.getSectionOrdinal(match),3415 .n_sect = sect_id + 1,
3747 .n_desc = 0,3416 .n_desc = 0,
3748 .n_value = addr,3417 .n_value = addr,
3749 };3418 };
...@@ -3894,44 +3563,40 @@ fn needsPointerRebase(ty: Type, val: Value, mod: *Module) bool {...@@ -3894,44 +3563,40 @@ fn needsPointerRebase(ty: Type, val: Value, mod: *Module) bool {
3894 }3563 }
3895}3564}
38963565
3897fn getMatchingSectionAtom(3566fn getOutputSectionAtom(
3898 self: *MachO,3567 self: *MachO,
3899 atom: *Atom,3568 atom: *Atom,
3900 name: []const u8,3569 name: []const u8,
3901 ty: Type,3570 ty: Type,
3902 val: Value,3571 val: Value,
3903 alignment: u32,3572 alignment: u32,
3904) !MatchingSection {3573) !u8 {
3905 const code = atom.code.items;3574 const code = atom.code.items;
3906 const mod = self.base.options.module.?;3575 const mod = self.base.options.module.?;
3907 const align_log_2 = math.log2(alignment);3576 const align_log_2 = math.log2(alignment);
3908 const zig_ty = ty.zigTypeTag();3577 const zig_ty = ty.zigTypeTag();
3909 const mode = self.base.options.optimize_mode;3578 const mode = self.base.options.optimize_mode;
3910 const match: MatchingSection = blk: {3579 const sect_id: u8 = blk: {
3911 // TODO finish and audit this function3580 // TODO finish and audit this function
3912 if (val.isUndefDeep()) {3581 if (val.isUndefDeep()) {
3913 if (mode == .ReleaseFast or mode == .ReleaseSmall) {3582 if (mode == .ReleaseFast or mode == .ReleaseSmall) {
3914 break :blk MatchingSection{3583 break :blk (try self.getOutputSection(.{
3915 .seg = self.data_segment_cmd_index.?,3584 .segname = makeStaticString("__DATA"),
3916 .sect = self.bss_section_index.?,3585 .sectname = makeStaticString("__bss"),
3917 };3586 .size = code.len,
3587 .@"align" = align_log_2,
3588 })).?;
3918 } else {3589 } else {
3919 break :blk MatchingSection{3590 break :blk self.data_section_index.?;
3920 .seg = self.data_segment_cmd_index.?,
3921 .sect = self.data_section_index.?,
3922 };
3923 }3591 }
3924 }3592 }
39253593
3926 if (val.castTag(.variable)) |_| {3594 if (val.castTag(.variable)) |_| {
3927 break :blk MatchingSection{3595 break :blk self.data_section_index.?;
3928 .seg = self.data_segment_cmd_index.?,
3929 .sect = self.data_section_index.?,
3930 };
3931 }3596 }
39323597
3933 if (needsPointerRebase(ty, val, mod)) {3598 if (needsPointerRebase(ty, val, mod)) {
3934 break :blk (try self.getMatchingSection(.{3599 break :blk (try self.getOutputSection(.{
3935 .segname = makeStaticString("__DATA_CONST"),3600 .segname = makeStaticString("__DATA_CONST"),
3936 .sectname = makeStaticString("__const"),3601 .sectname = makeStaticString("__const"),
3937 .size = code.len,3602 .size = code.len,
...@@ -3941,10 +3606,7 @@ fn getMatchingSectionAtom(...@@ -3941,10 +3606,7 @@ fn getMatchingSectionAtom(
39413606
3942 switch (zig_ty) {3607 switch (zig_ty) {
3943 .Fn => {3608 .Fn => {
3944 break :blk MatchingSection{3609 break :blk self.text_section_index.?;
3945 .seg = self.text_segment_cmd_index.?,
3946 .sect = self.text_section_index.?,
3947 };
3948 },3610 },
3949 .Array => {3611 .Array => {
3950 if (val.tag() == .bytes) {3612 if (val.tag() == .bytes) {
...@@ -3953,7 +3615,7 @@ fn getMatchingSectionAtom(...@@ -3953,7 +3615,7 @@ fn getMatchingSectionAtom(
3953 .const_slice_u8_sentinel_0,3615 .const_slice_u8_sentinel_0,
3954 .manyptr_const_u8_sentinel_0,3616 .manyptr_const_u8_sentinel_0,
3955 => {3617 => {
3956 break :blk (try self.getMatchingSection(.{3618 break :blk (try self.getOutputSection(.{
3957 .segname = makeStaticString("__TEXT"),3619 .segname = makeStaticString("__TEXT"),
3958 .sectname = makeStaticString("__cstring"),3620 .sectname = makeStaticString("__cstring"),
3959 .flags = macho.S_CSTRING_LITERALS,3621 .flags = macho.S_CSTRING_LITERALS,
...@@ -3967,22 +3629,21 @@ fn getMatchingSectionAtom(...@@ -3967,22 +3629,21 @@ fn getMatchingSectionAtom(
3967 },3629 },
3968 else => {},3630 else => {},
3969 }3631 }
3970 break :blk (try self.getMatchingSection(.{3632 break :blk (try self.getOutputSection(.{
3971 .segname = makeStaticString("__TEXT"),3633 .segname = makeStaticString("__TEXT"),
3972 .sectname = makeStaticString("__const"),3634 .sectname = makeStaticString("__const"),
3973 .size = code.len,3635 .size = code.len,
3974 .@"align" = align_log_2,3636 .@"align" = align_log_2,
3975 })).?;3637 })).?;
3976 };3638 };
3977 const sect = self.getSection(match);3639 const header = self.sections.items(.header)[sect_id];
3978 log.debug(" allocating atom '{s}' in '{s},{s}' ({d},{d})", .{3640 log.debug(" allocating atom '{s}' in '{s},{s}', ord({d})", .{
3979 name,3641 name,
3980 sect.segName(),3642 header.segName(),
3981 sect.sectName(),3643 header.sectName(),
3982 match.seg,3644 sect_id,
3983 match.sect,
3984 });3645 });
3985 return match;3646 return sect_id;
3986}3647}
39873648
3988fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !u64 {3649fn 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...@@ -3996,7 +3657,7 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !u64
39963657
3997 const decl_ptr = self.decls.getPtr(decl_index).?;3658 const decl_ptr = self.decls.getPtr(decl_index).?;
3998 if (decl_ptr.* == null) {3659 if (decl_ptr.* == null) {
3999 decl_ptr.* = try self.getMatchingSectionAtom(3660 decl_ptr.* = try self.getOutputSectionAtom(
4000 &decl.link.macho,3661 &decl.link.macho,
4001 sym_name,3662 sym_name,
4002 decl.ty,3663 decl.ty,
...@@ -4045,7 +3706,7 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !u64...@@ -4045,7 +3706,7 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !u64
4045 symbol.* = .{3706 symbol.* = .{
4046 .n_strx = name_str_index,3707 .n_strx = name_str_index,
4047 .n_type = macho.N_SECT,3708 .n_type = macho.N_SECT,
4048 .n_sect = self.getSectionOrdinal(match),3709 .n_sect = match + 1,
4049 .n_desc = 0,3710 .n_desc = 0,
4050 .n_value = addr,3711 .n_value = addr,
4051 };3712 };
...@@ -4134,10 +3795,7 @@ pub fn updateDeclExports(...@@ -4134,10 +3795,7 @@ pub fn updateDeclExports(
4134 sym.* = .{3795 sym.* = .{
4135 .n_strx = try self.strtab.insert(gpa, exp_name),3796 .n_strx = try self.strtab.insert(gpa, exp_name),
4136 .n_type = macho.N_SECT | macho.N_EXT,3797 .n_type = macho.N_SECT | macho.N_EXT,
4137 .n_sect = self.getSectionOrdinal(.{3798 .n_sect = self.text_section_index.? + 1, // TODO what if we export a variable?
4138 .seg = self.text_segment_cmd_index.?,
4139 .sect = self.text_section_index.?, // TODO what if we export a variable?
4140 }),
4141 .n_desc = 0,3799 .n_desc = 0,
4142 .n_value = decl_sym.n_value,3800 .n_value = decl_sym.n_value,
4143 };3801 };
...@@ -4208,10 +3866,10 @@ pub fn deleteExport(self: *MachO, exp: Export) void {...@@ -4208,10 +3866,10 @@ pub fn deleteExport(self: *MachO, exp: Export) void {
4208fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {3866fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {
4209 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;3867 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;
4210 for (unnamed_consts.items) |atom| {3868 for (unnamed_consts.items) |atom| {
4211 self.freeAtom(atom, .{3869 // TODO
4212 .seg = self.text_segment_cmd_index.?,3870 // const sect_id = atom.getSymbol(self).n_sect;
4213 .sect = self.text_const_section_index.?,3871 const sect_id = self.getSectionByName("__TEXT", "__const").?;
4214 }, true);3872 self.freeAtom(atom, sect_id, true);
4215 self.locals_free_list.append(self.base.allocator, atom.sym_index) catch {};3873 self.locals_free_list.append(self.base.allocator, atom.sym_index) catch {};
4216 self.locals.items[atom.sym_index].n_type = 0;3874 self.locals.items[atom.sym_index].n_type = 0;
4217 _ = self.atom_by_index_table.remove(atom.sym_index);3875 _ = 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...@@ -4294,6 +3952,7 @@ pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: Fil
4294}3952}
42953953
4296fn populateMissingMetadata(self: *MachO) !void {3954fn populateMissingMetadata(self: *MachO) !void {
3955 const gpa = self.base.allocator;
4297 const cpu_arch = self.base.options.target.cpu.arch;3956 const cpu_arch = self.base.options.target.cpu.arch;
4298 const pagezero_vmsize = self.base.options.pagezero_size orelse default_pagezero_vmsize;3957 const pagezero_vmsize = self.base.options.pagezero_size orelse default_pagezero_vmsize;
4299 const aligned_pagezero_vmsize = mem.alignBackwardGeneric(u64, pagezero_vmsize, self.page_size);3958 const aligned_pagezero_vmsize = mem.alignBackwardGeneric(u64, pagezero_vmsize, self.page_size);
...@@ -4305,21 +3964,16 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4305,21 +3964,16 @@ fn populateMissingMetadata(self: *MachO) !void {
4305 log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_vmsize});3964 log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_vmsize});
4306 log.warn(" rounding down to 0x{x}", .{aligned_pagezero_vmsize});3965 log.warn(" rounding down to 0x{x}", .{aligned_pagezero_vmsize});
4307 }3966 }
4308 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);3967 self.pagezero_segment_cmd_index = @intCast(u8, self.segments.items.len);
4309 try self.load_commands.append(self.base.allocator, .{3968 try self.segments.append(gpa, .{
4310 .segment = .{3969 .segname = makeStaticString("__PAGEZERO"),
4311 .inner = .{3970 .vmsize = aligned_pagezero_vmsize,
4312 .segname = makeStaticString("__PAGEZERO"),3971 .cmdsize = @sizeOf(macho.segment_command_64),
4313 .vmsize = aligned_pagezero_vmsize,
4314 .cmdsize = @sizeOf(macho.segment_command_64),
4315 },
4316 },
4317 });3972 });
4318 self.load_commands_dirty = true;
4319 }3973 }
43203974
4321 if (self.text_segment_cmd_index == null) {3975 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);
4323 const needed_size = if (self.mode == .incremental) blk: {3977 const needed_size = if (self.mode == .incremental) blk: {
4324 const headerpad_size = @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size);3978 const headerpad_size = @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size);
4325 const program_code_size_hint = self.base.options.program_code_size_hint;3979 const program_code_size_hint = self.base.options.program_code_size_hint;
...@@ -4329,20 +3983,15 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4329,20 +3983,15 @@ fn populateMissingMetadata(self: *MachO) !void {
4329 log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size });3983 log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size });
4330 break :blk needed_size;3984 break :blk needed_size;
4331 } else 0;3985 } else 0;
4332 try self.load_commands.append(self.base.allocator, .{3986 try self.segments.append(gpa, .{
4333 .segment = .{3987 .segname = makeStaticString("__TEXT"),
4334 .inner = .{3988 .vmaddr = aligned_pagezero_vmsize,
4335 .segname = makeStaticString("__TEXT"),3989 .vmsize = needed_size,
4336 .vmaddr = aligned_pagezero_vmsize,3990 .filesize = needed_size,
4337 .vmsize = needed_size,3991 .maxprot = macho.PROT.READ | macho.PROT.EXEC,
4338 .filesize = needed_size,3992 .initprot = macho.PROT.READ | macho.PROT.EXEC,
4339 .maxprot = macho.PROT.READ | macho.PROT.EXEC,3993 .cmdsize = @sizeOf(macho.segment_command_64),
4340 .initprot = macho.PROT.READ | macho.PROT.EXEC,
4341 .cmdsize = @sizeOf(macho.segment_command_64),
4342 },
4343 },
4344 });3994 });
4345 self.load_commands_dirty = true;
4346 }3995 }
43473996
4348 if (self.text_section_index == null) {3997 if (self.text_section_index == null) {
...@@ -4353,7 +4002,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4353,7 +4002,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4353 };4002 };
4354 const needed_size = if (self.mode == .incremental) self.base.options.program_code_size_hint else 0;4003 const needed_size = if (self.mode == .incremental) self.base.options.program_code_size_hint else 0;
4355 self.text_section_index = try self.initSection(4004 self.text_section_index = try self.initSection(
4356 self.text_segment_cmd_index.?,4005 "__TEXT",
4357 "__text",4006 "__text",
4358 needed_size,4007 needed_size,
4359 alignment,4008 alignment,
...@@ -4376,7 +4025,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4376,7 +4025,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4376 };4025 };
4377 const needed_size = if (self.mode == .incremental) stub_size * self.base.options.symbol_count_hint else 0;4026 const needed_size = if (self.mode == .incremental) stub_size * self.base.options.symbol_count_hint else 0;
4378 self.stubs_section_index = try self.initSection(4027 self.stubs_section_index = try self.initSection(
4379 self.text_segment_cmd_index.?,4028 "__TEXT",
4380 "__stubs",4029 "__stubs",
4381 needed_size,4030 needed_size,
4382 alignment,4031 alignment,
...@@ -4408,7 +4057,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4408,7 +4057,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4408 else4057 else
4409 0;4058 0;
4410 self.stub_helper_section_index = try self.initSection(4059 self.stub_helper_section_index = try self.initSection(
4411 self.text_segment_cmd_index.?,4060 "__TEXT",
4412 "__stub_helper",4061 "__stub_helper",
4413 needed_size,4062 needed_size,
4414 alignment,4063 alignment,
...@@ -4419,7 +4068,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4419,7 +4068,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4419 }4068 }
44204069
4421 if (self.data_const_segment_cmd_index == null) {4070 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);
4423 var vmaddr: u64 = 0;4072 var vmaddr: u64 = 0;
4424 var fileoff: u64 = 0;4073 var fileoff: u64 = 0;
4425 var needed_size: u64 = 0;4074 var needed_size: u64 = 0;
...@@ -4434,21 +4083,16 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4434,21 +4083,16 @@ fn populateMissingMetadata(self: *MachO) !void {
4434 fileoff + needed_size,4083 fileoff + needed_size,
4435 });4084 });
4436 }4085 }
4437 try self.load_commands.append(self.base.allocator, .{4086 try self.segments.append(gpa, .{
4438 .segment = .{4087 .segname = makeStaticString("__DATA_CONST"),
4439 .inner = .{4088 .vmaddr = vmaddr,
4440 .segname = makeStaticString("__DATA_CONST"),4089 .vmsize = needed_size,
4441 .vmaddr = vmaddr,4090 .fileoff = fileoff,
4442 .vmsize = needed_size,4091 .filesize = needed_size,
4443 .fileoff = fileoff,4092 .maxprot = macho.PROT.READ | macho.PROT.WRITE,
4444 .filesize = needed_size,4093 .initprot = macho.PROT.READ | macho.PROT.WRITE,
4445 .maxprot = macho.PROT.READ | macho.PROT.WRITE,4094 .cmdsize = @sizeOf(macho.segment_command_64),
4446 .initprot = macho.PROT.READ | macho.PROT.WRITE,
4447 .cmdsize = @sizeOf(macho.segment_command_64),
4448 },
4449 },
4450 });4095 });
4451 self.load_commands_dirty = true;
4452 }4096 }
44534097
4454 if (self.got_section_index == null) {4098 if (self.got_section_index == null) {
...@@ -4458,7 +4102,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4458,7 +4102,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4458 0;4102 0;
4459 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)4103 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4460 self.got_section_index = try self.initSection(4104 self.got_section_index = try self.initSection(
4461 self.data_const_segment_cmd_index.?,4105 "__DATA_CONST",
4462 "__got",4106 "__got",
4463 needed_size,4107 needed_size,
4464 alignment,4108 alignment,
...@@ -4469,7 +4113,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4469,7 +4113,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4469 }4113 }
44704114
4471 if (self.data_segment_cmd_index == null) {4115 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);
4473 var vmaddr: u64 = 0;4117 var vmaddr: u64 = 0;
4474 var fileoff: u64 = 0;4118 var fileoff: u64 = 0;
4475 var needed_size: u64 = 0;4119 var needed_size: u64 = 0;
...@@ -4484,21 +4128,16 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4484,21 +4128,16 @@ fn populateMissingMetadata(self: *MachO) !void {
4484 fileoff + needed_size,4128 fileoff + needed_size,
4485 });4129 });
4486 }4130 }
4487 try self.load_commands.append(self.base.allocator, .{4131 try self.segments.append(gpa, .{
4488 .segment = .{4132 .segname = makeStaticString("__DATA"),
4489 .inner = .{4133 .vmaddr = vmaddr,
4490 .segname = makeStaticString("__DATA"),4134 .vmsize = needed_size,
4491 .vmaddr = vmaddr,4135 .fileoff = fileoff,
4492 .vmsize = needed_size,4136 .filesize = needed_size,
4493 .fileoff = fileoff,4137 .maxprot = macho.PROT.READ | macho.PROT.WRITE,
4494 .filesize = needed_size,4138 .initprot = macho.PROT.READ | macho.PROT.WRITE,
4495 .maxprot = macho.PROT.READ | macho.PROT.WRITE,4139 .cmdsize = @sizeOf(macho.segment_command_64),
4496 .initprot = macho.PROT.READ | macho.PROT.WRITE,
4497 .cmdsize = @sizeOf(macho.segment_command_64),
4498 },
4499 },
4500 });4140 });
4501 self.load_commands_dirty = true;
4502 }4141 }
45034142
4504 if (self.la_symbol_ptr_section_index == null) {4143 if (self.la_symbol_ptr_section_index == null) {
...@@ -4508,7 +4147,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4508,7 +4147,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4508 0;4147 0;
4509 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)4148 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4510 self.la_symbol_ptr_section_index = try self.initSection(4149 self.la_symbol_ptr_section_index = try self.initSection(
4511 self.data_segment_cmd_index.?,4150 "__DATA",
4512 "__la_symbol_ptr",4151 "__la_symbol_ptr",
4513 needed_size,4152 needed_size,
4514 alignment,4153 alignment,
...@@ -4525,7 +4164,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4525,7 +4164,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4525 0;4164 0;
4526 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)4165 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4527 self.data_section_index = try self.initSection(4166 self.data_section_index = try self.initSection(
4528 self.data_segment_cmd_index.?,4167 "__DATA",
4529 "__data",4168 "__data",
4530 needed_size,4169 needed_size,
4531 alignment,4170 alignment,
...@@ -4533,76 +4172,8 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4533,76 +4172,8 @@ fn populateMissingMetadata(self: *MachO) !void {
4533 );4172 );
4534 }4173 }
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
4604 if (self.linkedit_segment_cmd_index == null) {4175 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);
4606 var vmaddr: u64 = 0;4177 var vmaddr: u64 = 0;
4607 var fileoff: u64 = 0;4178 var fileoff: u64 = 0;
4608 if (self.mode == .incremental) {4179 if (self.mode == .incremental) {
...@@ -4611,249 +4182,113 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4611,249 +4182,113 @@ fn populateMissingMetadata(self: *MachO) !void {
4611 fileoff = base.fileoff;4182 fileoff = base.fileoff;
4612 log.debug("found __LINKEDIT segment free space at 0x{x}", .{fileoff});4183 log.debug("found __LINKEDIT segment free space at 0x{x}", .{fileoff});
4613 }4184 }
4614 try self.load_commands.append(self.base.allocator, .{4185 try self.segments.append(gpa, .{
4615 .segment = .{4186 .segname = makeStaticString("__LINKEDIT"),
4616 .inner = .{4187 .vmaddr = vmaddr,
4617 .segname = makeStaticString("__LINKEDIT"),4188 .fileoff = fileoff,
4618 .vmaddr = vmaddr,4189 .maxprot = macho.PROT.READ,
4619 .fileoff = fileoff,4190 .initprot = macho.PROT.READ,
4620 .maxprot = macho.PROT.READ,4191 .cmdsize = @sizeOf(macho.segment_command_64),
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 },
4646 });4192 });
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;
4690 }4193 }
4194}
46914195
4692 if (self.dylinker_cmd_index == null) {4196inline fn calcInstallNameLen(cmd_size: u64, name: []const u8, assume_max_path_len: bool) u64 {
4693 self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len);4197 const name_len = if (assume_max_path_len) std.os.PATH_MAX else std.mem.len(name) + 1;
4694 const cmdsize = @intCast(u32, mem.alignForwardGeneric(4198 return mem.alignForwardGeneric(u64, cmd_size + name_len, @alignOf(u64));
4695 u64,4199}
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 }
47224200
4723 if (self.dylib_id_cmd_index == null and self.base.options.output_mode == .Lib) {4201fn calcLCsSize(self: *MachO, assume_max_path_len: bool) !u32 {
4724 self.dylib_id_cmd_index = @intCast(u16, self.load_commands.items.len);4202 const gpa = self.base.allocator;
4725 const install_name = self.base.options.install_name orelse self.base.options.emit.?.sub_path;4203 var sizeofcmds: u64 = 0;
4726 const current_version = self.base.options.version orelse4204 for (self.segments.items) |seg| {
4727 std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 };4205 sizeofcmds += seg.nsects * @sizeOf(macho.section_64) + @sizeOf(macho.segment_command_64);
4728 const compat_version = self.base.options.compatibility_version orelse4206 }
4729 std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 };4207
4730 var dylib_cmd = try macho.createLoadDylibCommand(4208 // LC_DYLD_INFO_ONLY
4731 self.base.allocator,4209 sizeofcmds += @sizeOf(macho.dyld_info_command);
4732 .ID_DYLIB,4210 // LC_FUNCTION_STARTS
4733 install_name,4211 if (self.text_section_index != null) {
4734 2,4212 sizeofcmds += @sizeOf(macho.linkedit_data_command);
4735 current_version.major << 16 | current_version.minor << 8 | current_version.patch,4213 }
4736 compat_version.major << 16 | compat_version.minor << 8 | compat_version.patch,4214 // LC_DATA_IN_CODE
4737 );4215 sizeofcmds += @sizeOf(macho.linkedit_data_command);
4738 errdefer dylib_cmd.deinit(self.base.allocator);4216 // LC_SYMTAB
4739 try self.load_commands.append(self.base.allocator, .{ .dylib = dylib_cmd });4217 sizeofcmds += @sizeOf(macho.symtab_command);
4740 self.load_commands_dirty = true;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 };
4741 }4240 }
47424241 // LC_RPATH
4743 if (self.source_version_cmd_index == null) {4242 {
4744 self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len);4243 var it = RpathIterator.init(gpa, self.base.options.rpath_list);
4745 try self.load_commands.append(self.base.allocator, .{4244 defer it.deinit();
4746 .source_version = .{4245 while (try it.next()) |rpath| {
4747 .cmdsize = @sizeOf(macho.source_version_command),4246 sizeofcmds += calcInstallNameLen(
4748 .version = 0x0,4247 @sizeOf(macho.rpath_command),
4749 },4248 rpath,
4750 });4249 assume_max_path_len,
4751 self.load_commands_dirty = true;4250 );
4251 }
4752 }4252 }
47534253 // LC_SOURCE_VERSION
4754 if (self.build_version_cmd_index == null) {4254 sizeofcmds += @sizeOf(macho.source_version_command);
4755 self.build_version_cmd_index = @intCast(u16, self.load_commands.items.len);4255 // LC_BUILD_VERSION
4756 const cmdsize = @intCast(u32, mem.alignForwardGeneric(4256 sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
4757 u64,4257 // LC_UUID
4758 @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version),4258 sizeofcmds += @sizeOf(macho.uuid_command);
4759 @sizeOf(u64),4259 // LC_LOAD_DYLIB
4760 ));4260 for (self.referenced_dylibs.keys()) |id| {
4761 const platform_version = blk: {4261 const dylib = self.dylibs.items[id];
4762 const ver = self.base.options.target.os.version_range.semver.min;4262 const dylib_id = dylib.id orelse unreachable;
4763 const platform_version = ver.major << 16 | ver.minor << 8;4263 sizeofcmds += calcInstallNameLen(
4764 break :blk platform_version;4264 @sizeOf(macho.dylib_command),
4765 };4265 dylib_id.name,
4766 const sdk_version = if (self.base.options.native_darwin_sdk) |sdk| blk: {4266 assume_max_path_len,
4767 const ver = sdk.version;4267 );
4768 const sdk_version = ver.major << 16 | ver.minor << 8;4268 }
4769 break :blk sdk_version;4269 // LC_CODE_SIGNATURE
4770 } else platform_version;4270 {
4771 const is_simulator_abi = self.base.options.target.abi == .simulator;4271 const target = self.base.options.target;
4772 var cmd = macho.emptyGenericCommandWithData(macho.build_version_command{4272 const requires_codesig = blk: {
4773 .cmdsize = cmdsize,4273 if (self.base.options.entitlements) |_| break :blk true;
4774 .platform = switch (self.base.options.target.os.tag) {4274 if (target.cpu.arch == .aarch64 and (target.os.tag == .macos or target.abi == .simulator))
4775 .macos => .MACOS,4275 break :blk true;
4776 .ios => if (is_simulator_abi) macho.PLATFORM.IOSSIMULATOR else macho.PLATFORM.IOS,4276 break :blk false;
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,
4801 };4277 };
4802 std.crypto.random.bytes(&uuid_cmd.uuid);4278 if (requires_codesig) {
4803 try self.load_commands.append(self.base.allocator, .{ .uuid = uuid_cmd });4279 sizeofcmds += @sizeOf(macho.linkedit_data_command);
4804 self.load_commands_dirty = true;4280 }
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;
4818 }4281 }
48194282
4820 if (self.data_in_code_cmd_index == null) {4283 return @intCast(u32, sizeofcmds);
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 }
4832}4284}
48334285
4834fn calcMinHeaderpad(self: *MachO) u64 {4286fn calcMinHeaderPad(self: *MachO) !u64 {
4835 var sizeofcmds: u32 = 0;4287 var padding: u32 = (try self.calcLCsSize(false)) + (self.base.options.headerpad_size orelse 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);
4842 log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)});4288 log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)});
48434289
4844 if (self.base.options.headerpad_max_install_names) {4290 if (self.base.options.headerpad_max_install_names) {
4845 var min_headerpad_size: u32 = 0;4291 var min_headerpad_size: u32 = try self.calcLCsSize(true);
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 };
4857 log.debug("headerpad_max_install_names minimum headerpad size 0x{x}", .{4292 log.debug("headerpad_max_install_names minimum headerpad size 0x{x}", .{
4858 min_headerpad_size + @sizeOf(macho.mach_header_64),4293 min_headerpad_size + @sizeOf(macho.mach_header_64),
4859 });4294 });
...@@ -4868,32 +4303,32 @@ fn calcMinHeaderpad(self: *MachO) u64 {...@@ -4868,32 +4303,32 @@ fn calcMinHeaderpad(self: *MachO) u64 {
4868fn allocateSegments(self: *MachO) !void {4303fn allocateSegments(self: *MachO) !void {
4869 try self.allocateSegment(self.text_segment_cmd_index, &.{4304 try self.allocateSegment(self.text_segment_cmd_index, &.{
4870 self.pagezero_segment_cmd_index,4305 self.pagezero_segment_cmd_index,
4871 }, self.calcMinHeaderpad());4306 }, try self.calcMinHeaderPad());
48724307
4873 if (self.text_segment_cmd_index) |index| blk: {4308 if (self.text_segment_cmd_index) |index| blk: {
4874 const seg = &self.load_commands.items[index].segment;4309 const indexes = self.getSectionIndexes(index);
4875 if (seg.sections.items.len == 0) break :blk;4310 if (indexes.start == indexes.end) break :blk;
4311 const seg = self.segments.items[index];
48764312
4877 // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments.4313 // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments.
4878 var min_alignment: u32 = 0;4314 var min_alignment: u32 = 0;
4879 for (seg.sections.items) |sect| {4315 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
4880 const alignment = try math.powi(u32, 2, sect.@"align");4316 const alignment = try math.powi(u32, 2, header.@"align");
4881 min_alignment = math.max(min_alignment, alignment);4317 min_alignment = math.max(min_alignment, alignment);
4882 }4318 }
48834319
4884 assert(min_alignment > 0);4320 assert(min_alignment > 0);
4885 const last_sect_idx = seg.sections.items.len - 1;4321 const last_header = self.sections.items(.header)[indexes.end - 1];
4886 const last_sect = seg.sections.items[last_sect_idx];
4887 const shift: u32 = shift: {4322 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;
4889 const factor = @divTrunc(diff, min_alignment);4324 const factor = @divTrunc(diff, min_alignment);
4890 break :shift @intCast(u32, factor * min_alignment);4325 break :shift @intCast(u32, factor * min_alignment);
4891 };4326 };
48924327
4893 if (shift > 0) {4328 if (shift > 0) {
4894 for (seg.sections.items) |*sect| {4329 for (self.sections.items(.header)[indexes.start..indexes.end]) |*header| {
4895 sect.offset += shift;4330 header.offset += shift;
4896 sect.addr += shift;4331 header.addr += shift;
4897 }4332 }
4898 }4333 }
4899 }4334 }
...@@ -4917,42 +4352,40 @@ fn allocateSegments(self: *MachO) !void {...@@ -4917,42 +4352,40 @@ fn allocateSegments(self: *MachO) !void {
4917 }, 0);4352 }, 0);
4918}4353}
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 {
4921 const index = maybe_index orelse return;4356 const index = maybe_index orelse return;
4922 const seg = &self.load_commands.items[index].segment;4357 const seg = &self.segments.items[index];
49234358
4924 const base = self.getSegmentAllocBase(indices);4359 const base = self.getSegmentAllocBase(indices);
4925 seg.inner.vmaddr = base.vmaddr;4360 seg.vmaddr = base.vmaddr;
4926 seg.inner.fileoff = base.fileoff;4361 seg.fileoff = base.fileoff;
4927 seg.inner.filesize = init_size;4362 seg.filesize = init_size;
4928 seg.inner.vmsize = init_size;4363 seg.vmsize = init_size;
49294364
4930 // Allocate the sections according to their alignment at the beginning of the segment.4365 // Allocate the sections according to their alignment at the beginning of the segment.
4366 const indexes = self.getSectionIndexes(index);
4931 var start = init_size;4367 var start = init_size;
4932 for (seg.sections.items) |*sect| {4368 const slice = self.sections.slice();
4933 const is_zerofill = sect.flags == macho.S_ZEROFILL or sect.flags == macho.S_THREAD_LOCAL_ZEROFILL;4369 for (slice.items(.header)[indexes.start..indexes.end]) |*header| {
4934 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;4370 const alignment = try math.powi(u32, 2, header.@"align");
4935 const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1;
4936 const alignment = try math.powi(u32, 2, sect.@"align");
4937 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);4371 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);
49384372
4939 // TODO handle zerofill sections in stage24373 header.offset = if (header.isZerofill())
4940 sect.offset = if (is_zerofill and (use_stage1 or use_llvm))
4941 04374 0
4942 else4375 else
4943 @intCast(u32, seg.inner.fileoff + start_aligned);4376 @intCast(u32, seg.fileoff + start_aligned);
4944 sect.addr = seg.inner.vmaddr + 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))) {4381 if (!header.isZerofill()) {
4949 seg.inner.filesize = start;4382 seg.filesize = start;
4950 }4383 }
4951 seg.inner.vmsize = start;4384 seg.vmsize = start;
4952 }4385 }
49534386
4954 seg.inner.filesize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size);4387 seg.filesize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
4955 seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.vmsize, self.page_size);4388 seg.vmsize = mem.alignForwardGeneric(u64, seg.vmsize, self.page_size);
4956}4389}
49574390
4958const InitSectionOpts = struct {4391const InitSectionOpts = struct {
...@@ -4963,184 +4396,270 @@ const InitSectionOpts = struct {...@@ -4963,184 +4396,270 @@ const InitSectionOpts = struct {
49634396
4964fn initSection(4397fn initSection(
4965 self: *MachO,4398 self: *MachO,
4966 segment_id: u16,4399 segname: []const u8,
4967 sectname: []const u8,4400 sectname: []const u8,
4968 size: u64,4401 size: u64,
4969 alignment: u32,4402 alignment: u32,
4970 opts: InitSectionOpts,4403 opts: InitSectionOpts,
4971) !u16 {4404) !u8 {
4972 const seg = &self.load_commands.items[segment_id].segment;4405 const segment_id = self.getSegmentByName(segname).?;
4973 var sect = macho.section_64{4406 const seg = &self.segments.items[segment_id];
4407 const index = try self.insertSection(segment_id, .{
4974 .sectname = makeStaticString(sectname),4408 .sectname = makeStaticString(sectname),
4975 .segname = seg.inner.segname,4409 .segname = seg.segname,
4976 .size = if (self.mode == .incremental) @intCast(u32, size) else 0,
4977 .@"align" = alignment,
4978 .flags = opts.flags,4410 .flags = opts.flags,
4979 .reserved1 = opts.reserved1,4411 .reserved1 = opts.reserved1,
4980 .reserved2 = opts.reserved2,4412 .reserved2 = opts.reserved2,
4981 };4413 });
4414 seg.cmdsize += @sizeOf(macho.section_64);
4415 seg.nsects += 1;
49824416
4983 if (self.mode == .incremental) {4417 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;
4984 const alignment_pow_2 = try math.powi(u32, 2, alignment);4429 const alignment_pow_2 = try math.powi(u32, 2, alignment);
4985 const padding: ?u32 = if (segment_id == self.text_segment_cmd_index.?)4430 // TODO better prealloc for __text section
4986 @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size)4431 // const padding: u64 = if (index == 0) try self.calcMinHeaderPad() else 0;
4987 else4432 const padding: u64 = if (index == 0) 0x1000 else 0;
4988 null;4433 const off = mem.alignForwardGeneric(u64, padding + prev_end_off, alignment_pow_2);
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 });
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;4440 // TODO Will this break if we are inserting section that is not the last section
5000 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;4441 // in a segment?
5001 const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1;4442 const max_size = self.allocatedSize(segment_id, off);
50024443
5003 // TODO handle zerofill in stage24444 if (size > max_size) {
5004 if (!(is_zerofill and (use_stage1 or use_llvm))) {4445 try self.growSection(index, @intCast(u32, size));
5005 sect.offset = @intCast(u32, off);
5006 }4446 }
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,
5007 }4475 }
4476}
50084477
5009 const index = @intCast(u16, seg.sections.items.len);4478fn insertSection(self: *MachO, segment_index: u8, header: macho.section_64) !u8 {
5010 try seg.sections.append(self.base.allocator, sect);4479 const precedence = getSectionPrecedence(header);
5011 seg.inner.cmdsize += @sizeOf(macho.section_64);4480 const indexes = self.getSectionIndexes(segment_index);
5012 seg.inner.nsects += 1;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{4507fn updateSectionOrdinals(self: *MachO, start: u8) void {
5015 .seg = segment_id,4508 const tracy = trace(@src());
5016 .sect = index,4509 defer tracy.end();
5017 };
5018 _ = try self.section_ordinals.getOrPut(self.base.allocator, match);
5019 try self.atom_free_lists.putNoClobber(self.base.allocator, match, .{});
50204510
5021 self.load_commands_dirty = true;4511 const slice = self.sections.slice();
5022 self.sections_order_dirty = true;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 }
5025}4532}
50264533
5027fn findFreeSpace(self: MachO, segment_id: u16, alignment: u64, start: ?u32) u64 {4534fn shiftLocalsByOffset(self: *MachO, sect_id: u8, offset: i64) !void {
5028 const seg = self.load_commands.items[segment_id].segment;4535 var atom = self.sections.items(.last_atom)[sect_id] orelse return;
5029 if (seg.sections.items.len == 0) {4536
5030 return if (start) |v| v else seg.inner.fileoff;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;
5031 }4552 }
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);
5035}4553}
50364554
5037fn growSegment(self: *MachO, seg_id: u16, new_size: u64) !void {4555fn growSegment(self: *MachO, segment_index: u8, new_size: u64) !void {
5038 const seg = &self.load_commands.items[seg_id].segment;4556 const segment = &self.segments.items[segment_index];
5039 const new_seg_size = mem.alignForwardGeneric(u64, new_size, self.page_size);4557 const new_segment_size = mem.alignForwardGeneric(u64, new_size, self.page_size);
5040 assert(new_seg_size > seg.inner.filesize);4558 assert(new_segment_size > segment.filesize);
5041 const offset_amt = new_seg_size - seg.inner.filesize;4559 const offset_amt = new_segment_size - segment.filesize;
5042 log.debug("growing segment {s} from 0x{x} to 0x{x}", .{4560 log.debug("growing segment {s} from 0x{x} to 0x{x}", .{
5043 seg.inner.segname,4561 segment.segname,
5044 seg.inner.filesize,4562 segment.filesize,
5045 new_seg_size,4563 new_segment_size,
5046 });4564 });
5047 seg.inner.filesize = new_seg_size;4565 segment.filesize = new_segment_size;
5048 seg.inner.vmsize = new_seg_size;4566 segment.vmsize = new_segment_size;
50494567
5050 log.debug(" (new segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{4568 log.debug(" (new segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
5051 seg.inner.fileoff,4569 segment.fileoff,
5052 seg.inner.fileoff + seg.inner.filesize,4570 segment.fileoff + segment.filesize,
5053 seg.inner.vmaddr,4571 segment.vmaddr,
5054 seg.inner.vmaddr + seg.inner.vmsize,4572 segment.vmaddr + segment.vmsize,
5055 });4573 });
50564574
5057 var next: usize = seg_id + 1;4575 var next: u8 = segment_index + 1;
5058 while (next < self.linkedit_segment_cmd_index.? + 1) : (next += 1) {4576 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
5061 try MachO.copyRangeAllOverlappingAlloc(4579 try MachO.copyRangeAllOverlappingAlloc(
5062 self.base.allocator,4580 self.base.allocator,
5063 self.base.file.?,4581 self.base.file.?,
5064 next_seg.inner.fileoff,4582 next_segment.fileoff,
5065 next_seg.inner.fileoff + offset_amt,4583 next_segment.fileoff + offset_amt,
5066 math.cast(usize, next_seg.inner.filesize) orelse return error.Overflow,4584 math.cast(usize, next_segment.filesize) orelse return error.Overflow,
5067 );4585 );
50684586
5069 next_seg.inner.fileoff += offset_amt;4587 next_segment.fileoff += offset_amt;
5070 next_seg.inner.vmaddr += offset_amt;4588 next_segment.vmaddr += offset_amt;
50714589
5072 log.debug(" (new {s} segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{4590 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,4591 next_segment.segname,
5074 next_seg.inner.fileoff,4592 next_segment.fileoff,
5075 next_seg.inner.fileoff + next_seg.inner.filesize,4593 next_segment.fileoff + next_segment.filesize,
5076 next_seg.inner.vmaddr,4594 next_segment.vmaddr,
5077 next_seg.inner.vmaddr + next_seg.inner.vmsize,4595 next_segment.vmaddr + next_segment.vmsize,
5078 });4596 });
50794597
5080 for (next_seg.sections.items) |*moved_sect, moved_sect_id| {4598 const indexes = self.getSectionIndexes(next);
5081 moved_sect.offset += @intCast(u32, offset_amt);4599 for (self.sections.items(.header)[indexes.start..indexes.end]) |*header, i| {
5082 moved_sect.addr += offset_amt;4600 header.offset += @intCast(u32, offset_amt);
50834601 header.addr += offset_amt;
5084 log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{4602
5085 moved_sect.segName(),4603 log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
5086 moved_sect.sectName(),4604 header.segName(),
5087 moved_sect.offset,4605 header.sectName(),
5088 moved_sect.offset + moved_sect.size,4606 header.offset,
5089 moved_sect.addr,4607 header.offset + header.size,
5090 moved_sect.addr + moved_sect.size,4608 header.addr,
4609 header.addr + header.size,
5091 });4610 });
50924611
5093 try self.shiftLocalsByOffset(.{4612 try self.shiftLocalsByOffset(@intCast(u8, i + indexes.start), @intCast(i64, offset_amt));
5094 .seg = @intCast(u16, next),
5095 .sect = @intCast(u16, moved_sect_id),
5096 }, @intCast(i64, offset_amt));
5097 }4613 }
5098 }4614 }
5099}4615}
51004616
5101fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {4617fn growSection(self: *MachO, sect_id: u8, new_size: u32) !void {
5102 const tracy = trace(@src());4618 const tracy = trace(@src());
5103 defer tracy.end();4619 defer tracy.end();
51044620
5105 const seg = &self.load_commands.items[match.seg].segment;4621 const section = self.sections.get(sect_id);
5106 const sect = &seg.sections.items[match.sect];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");4626 const alignment = try math.powi(u32, 2, header.@"align");
5109 const max_size = self.allocatedSize(match.seg, sect.offset);4627 const max_size = self.allocatedSize(segment_index, header.offset);
5110 const ideal_size = padToIdeal(new_size);4628 const ideal_size = padToIdeal(new_size);
5111 const needed_size = mem.alignForwardGeneric(u32, ideal_size, alignment);4629 const needed_size = mem.alignForwardGeneric(u32, ideal_size, alignment);
51124630
5113 if (needed_size > max_size) blk: {4631 if (needed_size > max_size) blk: {
5114 log.debug(" (need to grow! needed 0x{x}, max 0x{x})", .{ needed_size, max_size });4632 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) {
5117 // Last section, just grow segments4636 // 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);
5119 break :blk;4638 break :blk;
5120 }4639 }
51214640
5122 // Need to move all sections below in file and address spaces.4641 // Need to move all sections below in file and address spaces.
5123 const offset_amt = offset: {4642 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);
5125 break :offset mem.alignForwardGeneric(u64, needed_size - max_size, max_alignment);4644 break :offset mem.alignForwardGeneric(u64, needed_size - max_size, max_alignment);
5126 };4645 };
51274646
5128 // Before we commit to this, check if the segment needs to grow too.4647 // Before we commit to this, check if the segment needs to grow too.
5129 // We assume that each section header is growing linearly with the increasing4648 // We assume that each section header is growing linearly with the increasing
5130 // file offset / virtual memory address space.4649 // file offset / virtual memory address space.
5131 const last_sect = seg.sections.items[seg.sections.items.len - 1];4650 const last_sect_header = self.sections.items(.header)[indexes.end - 1];
5132 const last_sect_off = last_sect.offset + last_sect.size;4651 const last_sect_off = last_sect_header.offset + last_sect_header.size;
5133 const seg_off = seg.inner.fileoff + seg.inner.filesize;4652 const seg_off = segment.fileoff + segment.filesize;
51344653
5135 if (last_sect_off + offset_amt > seg_off) {4654 if (last_sect_off + offset_amt > seg_off) {
5136 // Need to grow segment first.4655 // Need to grow segment first.
5137 const spill_size = (last_sect_off + offset_amt) - seg_off;4656 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);
5139 }4658 }
51404659
5141 // We have enough space to expand within the segment, so move all sections by4660 // We have enough space to expand within the segment, so move all sections by
5142 // the required amount and update their header offsets.4661 // 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];
5144 const total_size = last_sect_off - next_sect.offset;4663 const total_size = last_sect_off - next_sect.offset;
51454664
5146 try MachO.copyRangeAllOverlappingAlloc(4665 try MachO.copyRangeAllOverlappingAlloc(
...@@ -5151,9 +4670,7 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {...@@ -5151,9 +4670,7 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {
5151 math.cast(usize, total_size) orelse return error.Overflow,4670 math.cast(usize, total_size) orelse return error.Overflow,
5152 );4671 );
51534672
5154 var next = match.sect + 1;4673 for (self.sections.items(.header)[sect_id + 1 .. indexes.end]) |*moved_sect, i| {
5155 while (next < seg.sections.items.len) : (next += 1) {
5156 const moved_sect = &seg.sections.items[next];
5157 moved_sect.offset += @intCast(u32, offset_amt);4674 moved_sect.offset += @intCast(u32, offset_amt);
5158 moved_sect.addr += offset_amt;4675 moved_sect.addr += offset_amt;
51594676
...@@ -5166,49 +4683,45 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {...@@ -5166,49 +4683,45 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {
5166 moved_sect.addr + moved_sect.size,4683 moved_sect.addr + moved_sect.size,
5167 });4684 });
51684685
5169 try self.shiftLocalsByOffset(.{4686 try self.shiftLocalsByOffset(@intCast(u8, sect_id + 1 + i), @intCast(i64, offset_amt));
5170 .seg = match.seg,
5171 .sect = next,
5172 }, @intCast(i64, offset_amt));
5173 }4687 }
5174 }4688 }
5175}4689}
51764690
5177fn allocatedSize(self: MachO, segment_id: u16, start: u64) u64 {4691fn allocatedSize(self: MachO, segment_id: u8, start: u64) u64 {
5178 const seg = self.load_commands.items[segment_id].segment;4692 const segment = self.segments.items[segment_id];
5179 assert(start >= seg.inner.fileoff);4693 const indexes = self.getSectionIndexes(segment_id);
5180 var min_pos: u64 = seg.inner.fileoff + seg.inner.filesize;4694 assert(start >= segment.fileoff);
4695 var min_pos: u64 = segment.fileoff + segment.filesize;
5181 if (start > min_pos) return 0;4696 if (start > min_pos) return 0;
5182 for (seg.sections.items) |section| {4697 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
5183 if (section.offset <= start) continue;4698 if (header.offset <= start) continue;
5184 if (section.offset < min_pos) min_pos = section.offset;4699 if (header.offset < min_pos) min_pos = header.offset;
5185 }4700 }
5186 return min_pos - start;4701 return min_pos - start;
5187}4702}
51884703
5189fn getSectionMaxAlignment(self: *MachO, segment_id: u16, start_sect_id: u16) !u32 {4704fn getSectionMaxAlignment(self: *MachO, start: u8, end: u8) !u32 {
5190 const seg = self.load_commands.items[segment_id].segment;
5191 var max_alignment: u32 = 1;4705 var max_alignment: u32 = 1;
5192 var next = start_sect_id;4706 const slice = self.sections.slice();
5193 while (next < seg.sections.items.len) : (next += 1) {4707 for (slice.items(.header)[start..end]) |header| {
5194 const sect = seg.sections.items[next];4708 const alignment = try math.powi(u32, 2, header.@"align");
5195 const alignment = try math.powi(u32, 2, sect.@"align");
5196 max_alignment = math.max(max_alignment, alignment);4709 max_alignment = math.max(max_alignment, alignment);
5197 }4710 }
5198 return max_alignment;4711 return max_alignment;
5199}4712}
52004713
5201fn allocateAtomCommon(self: *MachO, atom: *Atom, match: MatchingSection) !void {4714fn allocateAtomCommon(self: *MachO, atom: *Atom, sect_id: u8) !void {
5202 const sym = atom.getSymbolPtr(self);4715 const sym = atom.getSymbolPtr(self);
5203 if (self.mode == .incremental) {4716 if (self.mode == .incremental) {
5204 const size = atom.size;4717 const size = atom.size;
5205 const alignment = try math.powi(u32, 2, atom.alignment);4718 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);
5207 const sym_name = atom.getName(self);4720 const sym_name = atom.getName(self);
5208 log.debug("allocated {s} atom at 0x{x}", .{ sym_name, vaddr });4721 log.debug("allocated {s} atom at 0x{x}", .{ sym_name, vaddr });
5209 sym.n_value = vaddr;4722 sym.n_value = vaddr;
5210 } else try self.addAtomToSection(atom, match);4723 } else try self.addAtomToSection(atom, sect_id);
5211 sym.n_sect = self.getSectionOrdinal(match);4724 sym.n_sect = sect_id + 1;
5212}4725}
52134726
5214fn allocateAtom(4727fn allocateAtom(
...@@ -5216,15 +4729,15 @@ fn allocateAtom(...@@ -5216,15 +4729,15 @@ fn allocateAtom(
5216 atom: *Atom,4729 atom: *Atom,
5217 new_atom_size: u64,4730 new_atom_size: u64,
5218 alignment: u64,4731 alignment: u64,
5219 match: MatchingSection,4732 sect_id: u8,
5220) !u64 {4733) !u64 {
5221 const tracy = trace(@src());4734 const tracy = trace(@src());
5222 defer tracy.end();4735 defer tracy.end();
52234736
5224 const sect = self.getSectionPtr(match);4737 const header = &self.sections.items(.header)[sect_id];
5225 var free_list = self.atom_free_lists.get(match).?;4738 const free_list = &self.sections.items(.free_list)[sect_id];
5226 const needs_padding = match.seg == self.text_segment_cmd_index.? and match.sect == self.text_section_index.?;4739 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];
5227 const new_atom_ideal_capacity = if (needs_padding) padToIdeal(new_atom_size) else new_atom_size;4740 const new_atom_ideal_capacity = if (header.isCode()) padToIdeal(new_atom_size) else new_atom_size;
52284741
5229 // We use these to indicate our intention to update metadata, placing the new atom,4742 // We use these to indicate our intention to update metadata, placing the new atom,
5230 // and possibly removing a free list node.4743 // and possibly removing a free list node.
...@@ -5244,7 +4757,7 @@ fn allocateAtom(...@@ -5244,7 +4757,7 @@ fn allocateAtom(
5244 // Is it enough that we could fit this new atom?4757 // Is it enough that we could fit this new atom?
5245 const sym = big_atom.getSymbol(self);4758 const sym = big_atom.getSymbol(self);
5246 const capacity = big_atom.capacity(self);4759 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;
5248 const ideal_capacity_end_vaddr = math.add(u64, sym.n_value, ideal_capacity) catch ideal_capacity;4761 const ideal_capacity_end_vaddr = math.add(u64, sym.n_value, ideal_capacity) catch ideal_capacity;
5249 const capacity_end_vaddr = sym.n_value + capacity;4762 const capacity_end_vaddr = sym.n_value + capacity;
5250 const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity;4763 const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity;
...@@ -5272,30 +4785,28 @@ fn allocateAtom(...@@ -5272,30 +4785,28 @@ fn allocateAtom(
5272 free_list_removal = i;4785 free_list_removal = i;
5273 }4786 }
5274 break :blk new_start_vaddr;4787 break :blk new_start_vaddr;
5275 } else if (self.atoms.get(match)) |last| {4788 } else if (maybe_last_atom.*) |last| {
5276 const last_symbol = last.getSymbol(self);4789 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;
5278 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;4791 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;
5279 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);4792 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
5280 atom_placement = last;4793 atom_placement = last;
5281 break :blk new_start_vaddr;4794 break :blk new_start_vaddr;
5282 } else {4795 } else {
5283 break :blk mem.alignForwardGeneric(u64, sect.addr, alignment);4796 break :blk mem.alignForwardGeneric(u64, header.addr, alignment);
5284 }4797 }
5285 };4798 };
52864799
5287 const expand_section = atom_placement == null or atom_placement.?.next == null;4800 const expand_section = atom_placement == null or atom_placement.?.next == null;
5288 if (expand_section) {4801 if (expand_section) {
5289 const needed_size = @intCast(u32, (vaddr + new_atom_size) - sect.addr);4802 const needed_size = @intCast(u32, (vaddr + new_atom_size) - header.addr);
5290 try self.growSection(match, needed_size);4803 try self.growSection(sect_id, needed_size);
5291 _ = try self.atoms.put(self.base.allocator, match, atom);4804 maybe_last_atom.* = atom;
5292 sect.size = needed_size;4805 header.size = needed_size;
5293 self.load_commands_dirty = true;
5294 }4806 }
5295 const align_pow = @intCast(u32, math.log2(alignment));4807 const align_pow = @intCast(u32, math.log2(alignment));
5296 if (sect.@"align" < align_pow) {4808 if (header.@"align" < align_pow) {
5297 sect.@"align" = align_pow;4809 header.@"align" = align_pow;
5298 self.load_commands_dirty = true;
5299 }4810 }
5300 atom.size = new_atom_size;4811 atom.size = new_atom_size;
5301 atom.alignment = align_pow;4812 atom.alignment = align_pow;
...@@ -5322,20 +4833,19 @@ fn allocateAtom(...@@ -5322,20 +4833,19 @@ fn allocateAtom(
5322 return vaddr;4833 return vaddr;
5323}4834}
53244835
5325pub fn addAtomToSection(self: *MachO, atom: *Atom, match: MatchingSection) !void {4836pub fn addAtomToSection(self: *MachO, atom: *Atom, sect_id: u8) !void {
5326 if (self.atoms.getPtr(match)) |last| {4837 var section = self.sections.get(sect_id);
5327 last.*.next = atom;4838 if (section.header.size > 0) {
5328 atom.prev = last.*;4839 section.last_atom.?.next = atom;
5329 last.* = atom;4840 atom.prev = section.last_atom.?;
5330 } else {
5331 try self.atoms.putNoClobber(self.base.allocator, match, atom);
5332 }4841 }
5333 const sect = self.getSectionPtr(match);4842 section.last_atom = atom;
5334 const atom_alignment = try math.powi(u32, 2, atom.alignment);4843 const atom_alignment = try math.powi(u32, 2, atom.alignment);
5335 const aligned_end_addr = mem.alignForwardGeneric(u64, sect.size, atom_alignment);4844 const aligned_end_addr = mem.alignForwardGeneric(u64, section.header.size, atom_alignment);
5336 const padding = aligned_end_addr - sect.size;4845 const padding = aligned_end_addr - section.header.size;
5337 sect.size += padding + atom.size;4846 section.header.size += padding + atom.size;
5338 sect.@"align" = @maximum(sect.@"align", atom.alignment);4847 section.header.@"align" = @maximum(section.header.@"align", atom.alignment);
4848 self.sections.set(sect_id, section);
5339}4849}
53404850
5341pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {4851pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {
...@@ -5368,208 +4878,61 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {...@@ -5368,208 +4878,61 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {
5368 return sym_index;4878 return sym_index;
5369}4879}
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 } {
5372 for (indices) |maybe_prev_id| {4882 for (indices) |maybe_prev_id| {
5373 const prev_id = maybe_prev_id orelse continue;4883 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];
5375 return .{4885 return .{
5376 .vmaddr = prev.inner.vmaddr + prev.inner.vmsize,4886 .vmaddr = prev.vmaddr + prev.vmsize,
5377 .fileoff = prev.inner.fileoff + prev.inner.filesize,4887 .fileoff = prev.fileoff + prev.filesize,
5378 };4888 };
5379 }4889 }
5380 return .{ .vmaddr = 0, .fileoff = 0 };4890 return .{ .vmaddr = 0, .fileoff = 0 };
5381}4891}
53824892
5383fn pruneAndSortSectionsInSegment(self: *MachO, maybe_seg_id: *?u16, indices: []*?u16) !void {4893fn writeSegmentHeaders(self: *MachO, ncmds: *u32, writer: anytype) !void {
5384 const seg_id = maybe_seg_id.* orelse return;4894 for (self.segments.items) |seg, i| {
53854895 const indexes = self.getSectionIndexes(@intCast(u8, i));
5386 var mapping = std.AutoArrayHashMap(u16, ?u16).init(self.base.allocator);4896 var out_seg = seg;
5387 defer mapping.deinit();4897 out_seg.cmdsize = @sizeOf(macho.segment_command_64);
53884898 out_seg.nsects = 0;
5389 const seg = &self.load_commands.items[seg_id].segment;4899
5390 var sections = seg.sections.toOwnedSlice(self.base.allocator);4900 // Update section headers count; any section with size of 0 is excluded
5391 defer self.base.allocator.free(sections);4901 // since it doesn't have any data in the final binary file.
5392 try seg.sections.ensureTotalCapacity(self.base.allocator, sections.len);4902 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
53934903 if (header.size == 0) continue;
5394 for (indices) |maybe_index| {4904 out_seg.cmdsize += @sizeOf(macho.section_64);
5395 const old_idx = maybe_index.* orelse continue;4905 out_seg.nsects += 1;
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.*);
5405 }4906 }
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 {4908 if (out_seg.nsects == 0 and
5438 try self.pruneAndSortSectionsInSegment(&self.text_segment_cmd_index, &.{4909 (mem.eql(u8, out_seg.segName(), "__DATA_CONST") or
5439 &self.text_section_index,4910 mem.eql(u8, out_seg.segName(), "__DATA"))) continue;
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 });
54774911
5478 // Create new section ordinals.4912 try writer.writeStruct(out_seg);
5479 self.section_ordinals.clearRetainingCapacity();4913 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
5480 if (self.text_segment_cmd_index) |seg_id| {4914 if (header.size == 0) continue;
5481 const seg = self.load_commands.items[seg_id].segment;4915 try writer.writeStruct(header);
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);
5508 }4916 }
4917
4918 ncmds.* += 1;
5509 }4919 }
5510 self.sections_order_dirty = false;
5511}4920}
55124921
5513fn updateSectionOrdinals(self: *MachO) !void {4922fn writeLinkeditSegmentData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
5514 if (!self.sections_order_dirty) return;4923 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
55154924 seg.filesize = 0;
5516 const tracy = trace(@src());4925 seg.vmsize = 0;
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 }
55524926
5553 // FIXME Jakub4927 try self.writeDyldInfoData(ncmds, lc_writer);
5554 // TODO no need for duping work here; simply walk the atom graph4928 try self.writeFunctionStarts(ncmds, lc_writer);
5555 for (self.locals.items) |*sym| {4929 try self.writeDataInCode(ncmds, lc_writer);
5556 if (sym.undf()) continue;4930 try self.writeSymtabs(ncmds, lc_writer);
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 }
55674931
5568 self.section_ordinals.deinit(gpa);4932 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
5569 self.section_ordinals = ordinals;
5570}4933}
55714934
5572fn writeDyldInfoData(self: *MachO) !void {4935fn writeDyldInfoData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
5573 const tracy = trace(@src());4936 const tracy = trace(@src());
5574 defer tracy.end();4937 defer tracy.end();
55754938
...@@ -5582,89 +4945,86 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -5582,89 +4945,86 @@ fn writeDyldInfoData(self: *MachO) !void {
5582 var lazy_bind_pointers = std.ArrayList(bind.Pointer).init(gpa);4945 var lazy_bind_pointers = std.ArrayList(bind.Pointer).init(gpa);
5583 defer lazy_bind_pointers.deinit();4946 defer lazy_bind_pointers.deinit();
55844947
5585 {4948 const slice = self.sections.slice();
5586 var it = self.atoms.iterator();4949 for (slice.items(.last_atom)) |last_atom, sect_id| {
5587 while (it.next()) |entry| {4950 var atom = last_atom orelse continue;
5588 const match = entry.key_ptr.*;4951 const segment_index = slice.items(.segment_index)[sect_id];
5589 var atom: *Atom = entry.value_ptr.*;4952 const header = slice.items(.header)[sect_id];
55904953
5591 if (self.text_segment_cmd_index) |seg| {4954 if (mem.eql(u8, header.segName(), "__TEXT")) continue; // __TEXT is non-writable
5592 if (match.seg == seg) continue; // __TEXT is non-writable
5593 }
55944955
5595 const seg = self.getSegment(match);4956 log.debug("dyld info for {s},{s}", .{ header.segName(), header.sectName() });
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 }
56114957
5612 for (atom.bindings.items) |binding| {4958 const seg = self.segments.items[segment_index];
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 }
56374959
5638 for (atom.lazy_bindings.items) |binding| {4960 while (true) {
5639 const bind_sym = self.getSymbol(binding.target);4961 log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, atom.getName(self) });
5640 const bind_sym_name = self.getSymbolName(binding.target);4962 const sym = atom.getSymbol(self);
5641 const dylib_ordinal = @divTrunc(4963 const base_offset = sym.n_value - seg.vmaddr;
5642 @bitCast(i16, bind_sym.n_desc),4964
5643 macho.N_SYMBOL_RESOLVER,4965 for (atom.rebases.items) |offset| {
5644 );4966 log.debug(" | rebase at {x}", .{base_offset + offset});
5645 var flags: u4 = 0;4967 try rebase_pointers.append(.{
5646 log.debug(" | lazy bind at {x} import('{s}') ord({d})", .{4968 .offset = base_offset + offset,
5647 binding.offset + base_offset,4969 .segment_id = segment_index,
5648 bind_sym_name,4970 });
5649 dylib_ordinal,4971 }
5650 });4972
5651 if (bind_sym.weakRef()) {4973 for (atom.bindings.items) |binding| {
5652 log.debug(" | marking as weak ref ", .{});4974 const bind_sym = self.getSymbol(binding.target);
5653 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);4975 const bind_sym_name = self.getSymbolName(binding.target);
5654 }4976 const dylib_ordinal = @divTrunc(
5655 try lazy_bind_pointers.append(.{4977 @bitCast(i16, bind_sym.n_desc),
5656 .offset = binding.offset + base_offset,4978 macho.N_SYMBOL_RESOLVER,
5657 .segment_id = match.seg,4979 );
5658 .dylib_ordinal = dylib_ordinal,4980 var flags: u4 = 0;
5659 .name = bind_sym_name,4981 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
5660 .bind_flags = flags,4982 binding.offset + base_offset,
5661 });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);
5662 }4989 }
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| {4999 for (atom.lazy_bindings.items) |binding| {
5665 atom = prev;5000 const bind_sym = self.getSymbol(binding.target);
5666 } else break;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 });
5667 }5023 }
5024
5025 if (atom.prev) |prev| {
5026 atom = prev;
5027 } else break;
5668 }5028 }
5669 }5029 }
56705030
...@@ -5675,8 +5035,8 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -5675,8 +5035,8 @@ fn writeDyldInfoData(self: *MachO) !void {
5675 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.5035 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
5676 log.debug("generating export trie", .{});5036 log.debug("generating export trie", .{});
56775037
5678 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;5038 const text_segment = self.segments.items[self.text_segment_cmd_index.?];
5679 const base_address = text_segment.inner.vmaddr;5039 const base_address = text_segment.vmaddr;
56805040
5681 if (self.base.options.output_mode == .Exe) {5041 if (self.base.options.output_mode == .Exe) {
5682 for (&[_]SymbolWithLoc{5042 for (&[_]SymbolWithLoc{
...@@ -5714,103 +5074,91 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -5714,103 +5074,91 @@ fn writeDyldInfoData(self: *MachO) !void {
5714 try trie.finalize(gpa);5074 try trie.finalize(gpa);
5715 }5075 }
57165076
5717 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;5077 const link_seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
5718 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].dyld_info_only;5078 const rebase_off = mem.alignForwardGeneric(u64, link_seg.fileoff, @alignOf(u64));
57195079 assert(rebase_off == link_seg.fileoff);
5720 const rebase_off = mem.alignForwardGeneric(u64, seg.inner.fileoff, @alignOf(u64));
5721 const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items);5080 const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items);
5722 dyld_info.rebase_off = @intCast(u32, rebase_off);5081 log.debug("writing rebase info from 0x{x} to 0x{x}", .{ rebase_off, rebase_off + rebase_size });
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 });
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));
5730 const bind_size = try bind.bindInfoSize(bind_pointers.items);5084 const bind_size = try bind.bindInfoSize(bind_pointers.items);
5731 dyld_info.bind_off = @intCast(u32, bind_off);5085 log.debug("writing bind info from 0x{x} to 0x{x}", .{ bind_off, bind_off + bind_size });
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 });
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));
5739 const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items);5088 const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items);
5740 dyld_info.lazy_bind_off = @intCast(u32, lazy_bind_off);5089 log.debug("writing lazy bind info from 0x{x} to 0x{x}", .{ lazy_bind_off, lazy_bind_off + lazy_bind_size });
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 });
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));
5748 const export_size = trie.size;5092 const export_size = trie.size;
5749 dyld_info.export_off = @intCast(u32, export_off);5093 log.debug("writing export trie from 0x{x} to 0x{x}", .{ export_off, export_off + export_size });
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 });
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;5098 var buffer = try gpa.alloc(u8, math.cast(usize, needed_size) orelse return error.Overflow);
5759 var buffer = try gpa.alloc(u8, needed_size);
5760 defer gpa.free(buffer);5099 defer gpa.free(buffer);
5761 mem.set(u8, buffer, 0);5100 mem.set(u8, buffer, 0);
57625101
5763 var stream = std.io.fixedBufferStream(buffer);5102 var stream = std.io.fixedBufferStream(buffer);
5764 const writer = stream.writer();5103 const writer = stream.writer();
57655104
5766 const base_off = dyld_info.rebase_off;
5767 try bind.writeRebaseInfo(rebase_pointers.items, writer);5105 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
5770 try bind.writeBindInfo(bind_pointers.items, writer);5108 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
5773 try bind.writeLazyBindInfo(lazy_bind_pointers.items, writer);5111 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
5776 _ = try trie.write(writer);5114 _ = try trie.write(writer);
57775115
5778 log.debug("writing dyld info from 0x{x} to 0x{x}", .{5116 log.debug("writing dyld info from 0x{x} to 0x{x}", .{
5779 dyld_info.rebase_off,5117 rebase_off,
5780 dyld_info.rebase_off + needed_size,5118 rebase_off + needed_size,
5781 });5119 });
57825120
5783 try self.base.file.?.pwriteAll(buffer, dyld_info.rebase_off);5121 try self.base.file.?.pwriteAll(buffer, rebase_off);
5784 try self.populateLazyBindOffsetsInStubHelper(5122 const start = math.cast(usize, lazy_bind_off - rebase_off) orelse return error.Overflow;
5785 buffer[dyld_info.lazy_bind_off - base_off ..][0..dyld_info.lazy_bind_size],5123 const end = start + (math.cast(usize, lazy_bind_size) orelse return error.Overflow);
5786 );5124 try self.populateLazyBindOffsetsInStubHelper(buffer[start..end]);
57875125
5788 self.load_commands_dirty = true;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;
5789}5141}
57905142
5791fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {5143fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
5792 const gpa = self.base.allocator;5144 const gpa = self.base.allocator;
5793 const text_segment_cmd_index = self.text_segment_cmd_index orelse return;5145
5794 const stub_helper_section_index = self.stub_helper_section_index orelse return;5146 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;
5799 if (self.stub_helper_preamble_atom == null) return;5147 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
5802 var table = std.AutoHashMap(i64, *Atom).init(gpa);5153 var table = std.AutoHashMap(i64, *Atom).init(gpa);
5803 defer table.deinit();5154 defer table.deinit();
58045155
5805 {5156 {
5806 var stub_atom = last_atom;5157 var stub_atom = last_atom;
5807 var laptr_atom = self.atoms.get(.{5158 var laptr_atom = self.sections.items(.last_atom)[self.la_symbol_ptr_section_index.?].?;
5808 .seg = self.data_segment_cmd_index.?,
5809 .sect = self.la_symbol_ptr_section_index.?,
5810 }).?;
5811 const base_addr = blk: {5159 const base_addr = blk: {
5812 const seg = self.load_commands.items[self.data_segment_cmd_index.?].segment;5160 const seg = self.segments.items[self.data_segment_cmd_index.?];
5813 break :blk seg.inner.vmaddr;5161 break :blk seg.vmaddr;
5814 };5162 };
58155163
5816 while (true) {5164 while (true) {
...@@ -5871,10 +5219,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -5871,10 +5219,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
5871 }5219 }
5872 }5220 }
58735221
5874 const sect = self.getSection(.{5222 const header = self.sections.items(.header)[stub_helper_section_index];
5875 .seg = text_segment_cmd_index,
5876 .sect = stub_helper_section_index,
5877 });
5878 const stub_offset: u4 = switch (self.base.options.target.cpu.arch) {5223 const stub_offset: u4 = switch (self.base.options.target.cpu.arch) {
5879 .x86_64 => 1,5224 .x86_64 => 1,
5880 .aarch64 => 2 * @sizeOf(u32),5225 .aarch64 => 2 * @sizeOf(u32),
...@@ -5886,7 +5231,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -5886,7 +5231,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
5886 while (offsets.popOrNull()) |bind_offset| {5231 while (offsets.popOrNull()) |bind_offset| {
5887 const atom = table.get(bind_offset.sym_offset).?;5232 const atom = table.get(bind_offset.sym_offset).?;
5888 const sym = atom.getSymbol(self);5233 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;
5890 mem.writeIntLittle(u32, &buf, bind_offset.offset);5235 mem.writeIntLittle(u32, &buf, bind_offset.offset);
5891 log.debug("writing lazy bind offset in stub helper of 0x{x} for symbol {s} at offset 0x{x}", .{5236 log.debug("writing lazy bind offset in stub helper of 0x{x} for symbol {s} at offset 0x{x}", .{
5892 bind_offset.offset,5237 bind_offset.offset,
...@@ -5899,14 +5244,14 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -5899,14 +5244,14 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
58995244
5900const asc_u64 = std.sort.asc(u64);5245const asc_u64 = std.sort.asc(u64);
59015246
5902fn writeFunctionStarts(self: *MachO) !void {5247fn writeFunctionStarts(self: *MachO, ncmds: *u32, lc_writer: anytype) !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
5907 const tracy = trace(@src());5248 const tracy = trace(@src());
5908 defer tracy.end();5249 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
5910 const gpa = self.base.allocator;5255 const gpa = self.base.allocator;
59115256
5912 // We need to sort by address first5257 // We need to sort by address first
...@@ -5918,8 +5263,8 @@ fn writeFunctionStarts(self: *MachO) !void {...@@ -5918,8 +5263,8 @@ fn writeFunctionStarts(self: *MachO) !void {
5918 const sym = self.getSymbol(global);5263 const sym = self.getSymbol(global);
5919 if (sym.undf()) continue;5264 if (sym.undf()) continue;
5920 if (sym.n_desc == N_DESC_GCED) continue;5265 if (sym.n_desc == N_DESC_GCED) continue;
5921 const match = self.getMatchingSectionFromOrdinal(sym.n_sect);5266 const sect_id = sym.n_sect - 1;
5922 if (match.seg != text_seg_index or match.sect != text_sect_index) continue;5267 if (sect_id != text_sect_index) continue;
59235268
5924 addresses.appendAssumeCapacity(sym.n_value);5269 addresses.appendAssumeCapacity(sym.n_value);
5925 }5270 }
...@@ -5932,7 +5277,7 @@ fn writeFunctionStarts(self: *MachO) !void {...@@ -5932,7 +5277,7 @@ fn writeFunctionStarts(self: *MachO) !void {
59325277
5933 var last_off: u32 = 0;5278 var last_off: u32 = 0;
5934 for (addresses.items) |addr| {5279 for (addresses.items) |addr| {
5935 const offset = @intCast(u32, addr - text_seg.inner.vmaddr);5280 const offset = @intCast(u32, addr - text_seg.vmaddr);
5936 const diff = offset - last_off;5281 const diff = offset - last_off;
59375282
5938 if (diff == 0) continue;5283 if (diff == 0) continue;
...@@ -5951,22 +5296,22 @@ fn writeFunctionStarts(self: *MachO) !void {...@@ -5951,22 +5296,22 @@ fn writeFunctionStarts(self: *MachO) !void {
5951 try std.leb.writeULEB128(buffer.writer(), offset);5296 try std.leb.writeULEB128(buffer.writer(), offset);
5952 }5297 }
59535298
5954 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;5299 const link_seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
5955 const fn_cmd = &self.load_commands.items[self.function_starts_cmd_index.?].linkedit_data;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));5304 log.debug("writing function starts info from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
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;
59625305
5963 log.debug("writing function starts info from 0x{x} to 0x{x}", .{5306 try self.base.file.?.pwriteAll(buffer.items, offset);
5964 fn_cmd.dataoff,
5965 fn_cmd.dataoff + fn_cmd.datasize,
5966 });
59675307
5968 try self.base.file.?.pwriteAll(buffer.items, fn_cmd.dataoff);5308 try lc_writer.writeStruct(macho.linkedit_data_command{
5969 self.load_commands_dirty = true;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;
5970}5315}
59715316
5972fn filterDataInCode(5317fn filterDataInCode(
...@@ -5988,17 +5333,15 @@ fn filterDataInCode(...@@ -5988,17 +5333,15 @@ fn filterDataInCode(
5988 return dices[start..end];5333 return dices[start..end];
5989}5334}
59905335
5991fn writeDataInCode(self: *MachO) !void {5336fn writeDataInCode(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
5992 const tracy = trace(@src());5337 const tracy = trace(@src());
5993 defer tracy.end();5338 defer tracy.end();
59945339
5995 var out_dice = std.ArrayList(macho.data_in_code_entry).init(self.base.allocator);5340 var out_dice = std.ArrayList(macho.data_in_code_entry).init(self.base.allocator);
5996 defer out_dice.deinit();5341 defer out_dice.deinit();
59975342
5998 const text_sect = self.getSection(.{5343 const text_sect_id = self.text_section_index orelse return;
5999 .seg = self.text_segment_cmd_index orelse return,5344 const text_sect_header = self.sections.items(.header)[text_sect_id];
6000 .sect = self.text_section_index orelse return,
6001 });
60025345
6003 for (self.objects.items) |object| {5346 for (self.objects.items) |object| {
6004 const dice = object.parseDataInCode() orelse continue;5347 const dice = object.parseDataInCode() orelse continue;
...@@ -6008,15 +5351,15 @@ fn writeDataInCode(self: *MachO) !void {...@@ -6008,15 +5351,15 @@ fn writeDataInCode(self: *MachO) !void {
6008 const sym = atom.getSymbol(self);5351 const sym = atom.getSymbol(self);
6009 if (sym.n_desc == N_DESC_GCED) continue;5352 if (sym.n_desc == N_DESC_GCED) continue;
60105353
6011 const match = self.getMatchingSectionFromOrdinal(sym.n_sect);5354 const sect_id = sym.n_sect - 1;
6012 if (match.seg != self.text_segment_cmd_index.? and match.sect != self.text_section_index.?) {5355 if (sect_id != self.text_section_index.?) {
6013 continue;5356 continue;
6014 }5357 }
60155358
6016 const source_sym = object.getSourceSymbol(atom.sym_index) orelse continue;5359 const source_sym = object.getSourceSymbol(atom.sym_index) orelse continue;
6017 const source_addr = math.cast(u32, source_sym.n_value) orelse return error.Overflow;5360 const source_addr = math.cast(u32, source_sym.n_value) orelse return error.Overflow;
6018 const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size);5361 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) orelse5362 const base = math.cast(u32, sym.n_value - text_sect_header.addr + text_sect_header.offset) orelse
6020 return error.Overflow;5363 return error.Overflow;
60215364
6022 for (filtered_dice) |single| {5365 for (filtered_dice) |single| {
...@@ -6030,33 +5373,63 @@ fn writeDataInCode(self: *MachO) !void {...@@ -6030,33 +5373,63 @@ fn writeDataInCode(self: *MachO) !void {
6030 }5373 }
6031 }5374 }
60325375
6033 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;5376 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
6034 const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].linkedit_data;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));5381 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
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;
60415382
6042 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{5383 try self.base.file.?.pwriteAll(mem.sliceAsBytes(out_dice.items), offset);
6043 dice_cmd.dataoff,5384 try lc_writer.writeStruct(macho.linkedit_data_command{
6044 dice_cmd.dataoff + dice_cmd.datasize,5385 .cmd = .DATA_IN_CODE,
5386 .cmdsize = @sizeOf(macho.linkedit_data_command),
5387 .dataoff = @intCast(u32, offset),
5388 .datasize = @intCast(u32, needed_size),
6045 });5389 });
60465390 ncmds.* += 1;
6047 try self.base.file.?.pwriteAll(mem.sliceAsBytes(out_dice.items), dice_cmd.dataoff);
6048 self.load_commands_dirty = true;
6049}5391}
60505392
6051fn writeSymtab(self: *MachO) !void {5393fn writeSymtabs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
6052 const tracy = trace(@src());5394 var symtab_cmd = macho.symtab_command{
6053 defer tracy.end();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 {
6055 const gpa = self.base.allocator;5432 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
6061 var locals = std.ArrayList(macho.nlist_64).init(gpa);5434 var locals = std.ArrayList(macho.nlist_64).init(gpa);
6062 defer locals.deinit();5435 defer locals.deinit();
...@@ -6101,8 +5474,8 @@ fn writeSymtab(self: *MachO) !void {...@@ -6101,8 +5474,8 @@ fn writeSymtab(self: *MachO) !void {
61015474
6102 var imports = std.ArrayList(macho.nlist_64).init(gpa);5475 var imports = std.ArrayList(macho.nlist_64).init(gpa);
6103 defer imports.deinit();5476 defer imports.deinit();
5477
6104 var imports_table = std.AutoHashMap(SymbolWithLoc, u32).init(gpa);5478 var imports_table = std.AutoHashMap(SymbolWithLoc, u32).init(gpa);
6105 defer imports_table.deinit();
61065479
6107 for (self.globals.values()) |global| {5480 for (self.globals.values()) |global| {
6108 const sym = self.getSymbol(global);5481 const sym = self.getSymbol(global);
...@@ -6115,56 +5488,84 @@ fn writeSymtab(self: *MachO) !void {...@@ -6115,56 +5488,84 @@ fn writeSymtab(self: *MachO) !void {
6115 try imports_table.putNoClobber(global, new_index);5488 try imports_table.putNoClobber(global, new_index);
6116 }5489 }
61175490
6118 const nlocals = locals.items.len;5491 const nlocals = @intCast(u32, locals.items.len);
6119 const nexports = exports.items.len;5492 const nexports = @intCast(u32, exports.items.len);
6120 const nimports = imports.items.len;5493 const nimports = @intCast(u32, imports.items.len);
6121 symtab.nsyms = @intCast(u32, nlocals + nexports + nimports);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
6123 var buffer = std.ArrayList(u8).init(gpa);5505 var buffer = std.ArrayList(u8).init(gpa);
6124 defer buffer.deinit();5506 defer buffer.deinit();
6125 try buffer.ensureTotalCapacityPrecise(symtab.nsyms * @sizeOf(macho.nlist_64));5507 try buffer.ensureTotalCapacityPrecise(needed_size);
6126 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(locals.items));5508 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(locals.items));
6127 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(exports.items));5509 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(exports.items));
6128 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(imports.items));5510 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 });5512 log.debug("writing symtab from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
6131 try self.base.file.?.pwriteAll(buffer.items, symtab.symoff);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.5534 try self.base.file.?.pwriteAll(self.strtab.buffer.items, offset);
6136 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].dysymtab;5535
6137 dysymtab.nlocalsym = @intCast(u32, nlocals);5536 lc.stroff = @intCast(u32, offset);
6138 dysymtab.iextdefsym = dysymtab.nlocalsym;5537 lc.strsize = @intCast(u32, needed_size);
6139 dysymtab.nextdefsym = @intCast(u32, nexports);5538}
6140 dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym;5539
6141 dysymtab.nundefsym = @intCast(u32, nimports);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;
6143 const nstubs = @intCast(u32, self.stubs_table.count());5549 const nstubs = @intCast(u32, self.stubs_table.count());
6144 const ngot_entries = @intCast(u32, self.got_entries_table.count());5550 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));5555 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
6147 dysymtab.indirectsymoff = @intCast(u32, indirectsymoff);5556 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
6148 dysymtab.nindirectsyms = nstubs * 2 + ngot_entries;5557 const needed_size = nindirectsyms * @sizeOf(u32);
61495558 seg.filesize = offset + needed_size - seg.fileoff;
6150 seg.inner.filesize = dysymtab.indirectsymoff + dysymtab.nindirectsyms * @sizeOf(u32) - seg.inner.fileoff;
61515559
6152 log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{5560 log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
6153 dysymtab.indirectsymoff,
6154 dysymtab.indirectsymoff + dysymtab.nindirectsyms * @sizeOf(u32),
6155 });
61565561
6157 var buf = std.ArrayList(u8).init(gpa);5562 var buf = std.ArrayList(u8).init(gpa);
6158 defer buf.deinit();5563 defer buf.deinit();
6159 try buf.ensureTotalCapacity(dysymtab.nindirectsyms * @sizeOf(u32));5564 try buf.ensureTotalCapacity(needed_size);
6160 const writer = buf.writer();5565 const writer = buf.writer();
61615566
6162 if (self.text_segment_cmd_index) |text_segment_cmd_index| blk: {5567 if (self.stubs_section_index) |sect_id| {
6163 const stubs_section_index = self.stubs_section_index orelse break :blk;5568 const stubs = &self.sections.items(.header)[sect_id];
6164 const stubs = self.getSectionPtr(.{
6165 .seg = text_segment_cmd_index,
6166 .sect = stubs_section_index,
6167 });
6168 stubs.reserved1 = 0;5569 stubs.reserved1 = 0;
6169 for (self.stubs.items) |entry| {5570 for (self.stubs.items) |entry| {
6170 if (entry.sym_index == 0) continue;5571 if (entry.sym_index == 0) continue;
...@@ -6172,16 +5573,12 @@ fn writeSymtab(self: *MachO) !void {...@@ -6172,16 +5573,12 @@ fn writeSymtab(self: *MachO) !void {
6172 if (atom_sym.n_desc == N_DESC_GCED) continue;5573 if (atom_sym.n_desc == N_DESC_GCED) continue;
6173 const target_sym = self.getSymbol(entry.target);5574 const target_sym = self.getSymbol(entry.target);
6174 assert(target_sym.undf());5575 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).?);
6176 }5577 }
6177 }5578 }
61785579
6179 if (self.data_const_segment_cmd_index) |data_const_segment_cmd_index| blk: {5580 if (self.got_section_index) |sect_id| {
6180 const got_section_index = self.got_section_index orelse break :blk;5581 const got = &self.sections.items(.header)[sect_id];
6181 const got = self.getSectionPtr(.{
6182 .seg = data_const_segment_cmd_index,
6183 .sect = got_section_index,
6184 });
6185 got.reserved1 = nstubs;5582 got.reserved1 = nstubs;
6186 for (self.got_entries.items) |entry| {5583 for (self.got_entries.items) |entry| {
6187 if (entry.sym_index == 0) continue;5584 if (entry.sym_index == 0) continue;
...@@ -6189,19 +5586,15 @@ fn writeSymtab(self: *MachO) !void {...@@ -6189,19 +5586,15 @@ fn writeSymtab(self: *MachO) !void {
6189 if (atom_sym.n_desc == N_DESC_GCED) continue;5586 if (atom_sym.n_desc == N_DESC_GCED) continue;
6190 const target_sym = self.getSymbol(entry.target);5587 const target_sym = self.getSymbol(entry.target);
6191 if (target_sym.undf()) {5588 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).?);
6193 } else {5590 } else {
6194 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);5591 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
6195 }5592 }
6196 }5593 }
6197 }5594 }
61985595
6199 if (self.data_segment_cmd_index) |data_segment_cmd_index| blk: {5596 if (self.la_symbol_ptr_section_index) |sect_id| {
6200 const la_symbol_ptr_section_index = self.la_symbol_ptr_section_index orelse break :blk;5597 const la_symbol_ptr = &self.sections.items(.header)[sect_id];
6201 const la_symbol_ptr = self.getSectionPtr(.{
6202 .seg = data_segment_cmd_index,
6203 .sect = la_symbol_ptr_section_index,
6204 });
6205 la_symbol_ptr.reserved1 = nstubs + ngot_entries;5598 la_symbol_ptr.reserved1 = nstubs + ngot_entries;
6206 for (self.stubs.items) |entry| {5599 for (self.stubs.items) |entry| {
6207 if (entry.sym_index == 0) continue;5600 if (entry.sym_index == 0) continue;
...@@ -6209,131 +5602,76 @@ fn writeSymtab(self: *MachO) !void {...@@ -6209,131 +5602,76 @@ fn writeSymtab(self: *MachO) !void {
6209 if (atom_sym.n_desc == N_DESC_GCED) continue;5602 if (atom_sym.n_desc == N_DESC_GCED) continue;
6210 const target_sym = self.getSymbol(entry.target);5603 const target_sym = self.getSymbol(entry.target);
6211 assert(target_sym.undf());5604 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).?);
6213 }5606 }
6214 }5607 }
62155608
6216 assert(buf.items.len == dysymtab.nindirectsyms * @sizeOf(u32));5609 assert(buf.items.len == needed_size);
62175610 try self.base.file.?.pwriteAll(buf.items, offset);
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();
62455611
6246 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;5612 lc.nlocalsym = ctx.nlocalsym;
6247 seg.inner.filesize = 0;5613 lc.iextdefsym = iextdefsym;
62485614 lc.nextdefsym = ctx.nextdefsym;
6249 try self.writeDyldInfoData();5615 lc.iundefsym = iundefsym;
6250 try self.writeFunctionStarts();5616 lc.nundefsym = ctx.nundefsym;
6251 try self.writeDataInCode();5617 lc.indirectsymoff = @intCast(u32, offset);
6252 try self.writeSymtab();5618 lc.nindirectsyms = nindirectsyms;
6253 try self.writeStrtab();
6254
6255 seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size);
6256}5619}
62575620
6258fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {5621fn writeCodeSignaturePadding(
6259 const tracy = trace(@src());5622 self: *MachO,
6260 defer tracy.end();5623 code_sig: *CodeSignature,
62615624 ncmds: *u32,
6262 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;5625 lc_writer: anytype,
6263 const cs_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].linkedit_data;5626) !u32 {
5627 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
6264 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file5628 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file
6265 // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L2715629 // 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);5630 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, 16);
6267 const datasize = code_sig.estimateSize(dataoff);5631 const needed_size = code_sig.estimateSize(offset);
6268 cs_cmd.dataoff = @intCast(u32, dataoff);5632 seg.filesize = offset + needed_size - seg.fileoff;
6269 cs_cmd.datasize = @intCast(u32, code_sig.estimateSize(dataoff));5633 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
62705634 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
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 });
6275 // Pad out the space. We need to do this to calculate valid hashes for everything in the file5635 // Pad out the space. We need to do this to calculate valid hashes for everything in the file
6276 // except for code signature data.5636 // except for code signature data.
6277 try self.base.file.?.pwriteAll(&[_]u8{0}, dataoff + datasize - 1);5637 try self.base.file.?.pwriteAll(&[_]u8{0}, offset + needed_size - 1);
6278 self.load_commands_dirty = true;
6279}
62805638
6281fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature) !void {5639 try lc_writer.writeStruct(macho.linkedit_data_command{
6282 const tracy = trace(@src());5640 .cmd = .CODE_SIGNATURE,
6283 defer tracy.end();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;5650fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature, offset: u32) !void {
6286 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;5651 const seg = self.segments.items[self.text_segment_cmd_index.?];
62875652
6288 var buffer = std.ArrayList(u8).init(self.base.allocator);5653 var buffer = std.ArrayList(u8).init(self.base.allocator);
6289 defer buffer.deinit();5654 defer buffer.deinit();
6290 try buffer.ensureTotalCapacityPrecise(code_sig.size());5655 try buffer.ensureTotalCapacityPrecise(code_sig.size());
6291 try code_sig.writeAdhocSignature(self.base.allocator, .{5656 try code_sig.writeAdhocSignature(self.base.allocator, .{
6292 .file = self.base.file.?,5657 .file = self.base.file.?,
6293 .exec_seg_base = seg.inner.fileoff,5658 .exec_seg_base = seg.fileoff,
6294 .exec_seg_limit = seg.inner.filesize,5659 .exec_seg_limit = seg.filesize,
6295 .code_sig_cmd = code_sig_cmd,5660 .file_size = offset,
6296 .output_mode = self.base.options.output_mode,5661 .output_mode = self.base.options.output_mode,
6297 }, buffer.writer());5662 }, buffer.writer());
6298 assert(buffer.items.len == code_sig.size());5663 assert(buffer.items.len == code_sig.size());
62995664
6300 log.debug("writing code signature from 0x{x} to 0x{x}", .{5665 log.debug("writing code signature from 0x{x} to 0x{x}", .{
6301 code_sig_cmd.dataoff,5666 offset,
6302 code_sig_cmd.dataoff + buffer.items.len,5667 offset + buffer.items.len,
6303 });5668 });
63045669
6305 try self.base.file.?.pwriteAll(buffer.items, code_sig_cmd.dataoff);5670 try self.base.file.?.pwriteAll(buffer.items, offset);
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;
6333}5671}
63345672
6335/// Writes Mach-O file header.5673/// Writes Mach-O file header.
6336fn writeHeader(self: *MachO) !void {5674fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {
6337 var header: macho.mach_header_64 = .{};5675 var header: macho.mach_header_64 = .{};
6338 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;5676 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;
63395677
...@@ -6361,18 +5699,12 @@ fn writeHeader(self: *MachO) !void {...@@ -6361,18 +5699,12 @@ fn writeHeader(self: *MachO) !void {
6361 else => unreachable,5699 else => unreachable,
6362 }5700 }
63635701
6364 if (self.tlv_section_index) |_| {5702 if (self.getSectionByName("__DATA", "__thread_vars")) |_| {
6365 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;5703 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
6366 }5704 }
63675705
6368 header.ncmds = 0;5706 header.ncmds = ncmds;
6369 header.sizeofcmds = 0;5707 header.sizeofcmds = sizeofcmds;
6370
6371 for (self.load_commands.items) |cmd| {
6372 if (cmd.cmd() == .NONE) continue;
6373 header.sizeofcmds += cmd.cmdsize();
6374 header.ncmds += 1;
6375 }
63765708
6377 log.debug("writing Mach-O header {}", .{header});5709 log.debug("writing Mach-O header {}", .{header});
63785710
...@@ -6392,33 +5724,27 @@ pub fn makeStaticString(bytes: []const u8) [16]u8 {...@@ -6392,33 +5724,27 @@ pub fn makeStaticString(bytes: []const u8) [16]u8 {
6392 return buf;5724 return buf;
6393}5725}
63945726
6395pub fn getSectionOrdinal(self: *MachO, match: MatchingSection) u8 {5727fn getSegmentByName(self: MachO, segname: []const u8) ?u8 {
6396 return @intCast(u8, self.section_ordinals.getIndex(match).?) + 1;5728 for (self.segments.items) |seg, i| {
6397}5729 if (mem.eql(u8, segname, seg.segName())) return @intCast(u8, i);
63985730 } else return null;
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).*;
6412}5731}
64135732
6414pub fn getSectionPtr(self: *MachO, match: MatchingSection) *macho.section_64 {5733pub fn getSectionByName(self: MachO, segname: []const u8, sectname: []const u8) ?u8 {
6415 const seg = self.getSegmentPtr(match);5734 // TODO investigate caching with a hashmap
6416 assert(match.sect < seg.sections.items.len);5735 for (self.sections.items(.header)) |header, i| {
6417 return &seg.sections.items[match.sect];5736 if (mem.eql(u8, header.segName(), segname) and mem.eql(u8, header.sectName(), sectname))
5737 return @intCast(u8, i);
5738 } else return null;
6418}5739}
64195740
6420pub fn getSection(self: *MachO, match: MatchingSection) macho.section_64 {5741pub fn getSectionIndexes(self: MachO, segment_index: u8) struct { start: u8, end: u8 } {
6421 return self.getSectionPtr(match).*;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 };
6422}5748}
64235749
6424pub fn symbolIsTemp(self: *MachO, sym_with_loc: SymbolWithLoc) bool {5750pub 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:...@@ -6512,72 +5838,6 @@ pub fn findFirst(comptime T: type, haystack: []const T, start: usize, predicate:
6512 return i;5838 return i;
6513}5839}
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
6581pub fn generateSymbolStabs(5841pub fn generateSymbolStabs(
6582 self: *MachO,5842 self: *MachO,
6583 object: Object,5843 object: Object,
...@@ -6585,14 +5845,15 @@ pub fn generateSymbolStabs(...@@ -6585,14 +5845,15 @@ pub fn generateSymbolStabs(
6585) !void {5845) !void {
6586 assert(!self.base.options.strip);5846 assert(!self.base.options.strip);
65875847
6588 const gpa = self.base.allocator;
6589
6590 log.debug("parsing debug info in '{s}'", .{object.name});5848 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
6594 // We assume there is only one CU.5855 // 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) {
6596 error.MissingDebugInfo => {5857 error.MissingDebugInfo => {
6597 // TODO audit cases with missing debug info and audit our dwarf.zig module.5858 // TODO audit cases with missing debug info and audit our dwarf.zig module.
6598 log.debug("invalid or missing debug info in {s}; skipping", .{object.name});5859 log.debug("invalid or missing debug info in {s}; skipping", .{object.name});
...@@ -6600,8 +5861,8 @@ pub fn generateSymbolStabs(...@@ -6600,8 +5861,8 @@ pub fn generateSymbolStabs(
6600 },5861 },
6601 else => |e| return e,5862 else => |e| return e,
6602 };5863 };
6603 const tu_name = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT.name);5864 const tu_name = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.name);
6604 const tu_comp_dir = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT.comp_dir);5865 const tu_comp_dir = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.comp_dir);
66055866
6606 // Open scope5867 // Open scope
6607 try locals.ensureUnusedCapacity(3);5868 try locals.ensureUnusedCapacity(3);
...@@ -6664,7 +5925,7 @@ pub fn generateSymbolStabs(...@@ -6664,7 +5925,7 @@ pub fn generateSymbolStabs(
6664fn generateSymbolStabsForSymbol(5925fn generateSymbolStabsForSymbol(
6665 self: *MachO,5926 self: *MachO,
6666 sym_loc: SymbolWithLoc,5927 sym_loc: SymbolWithLoc,
6667 debug_info: DebugInfo,5928 debug_info: dwarf.DwarfInfo,
6668 buf: *[4]macho.nlist_64,5929 buf: *[4]macho.nlist_64,
6669) ![]const macho.nlist_64 {5930) ![]const macho.nlist_64 {
6670 const gpa = self.base.allocator;5931 const gpa = self.base.allocator;
...@@ -6679,7 +5940,7 @@ fn generateSymbolStabsForSymbol(...@@ -6679,7 +5940,7 @@ fn generateSymbolStabsForSymbol(
6679 const source_sym = object.getSourceSymbol(sym_loc.sym_index) orelse return buf[0..0];5940 const source_sym = object.getSourceSymbol(sym_loc.sym_index) orelse return buf[0..0];
6680 const size: ?u64 = size: {5941 const size: ?u64 = size: {
6681 if (source_sym.tentative()) break :size null;5942 if (source_sym.tentative()) break :size null;
6682 for (debug_info.inner.func_list.items) |func| {5943 for (debug_info.func_list.items) |func| {
6683 if (func.pc_range) |range| {5944 if (func.pc_range) |range| {
6684 if (source_sym.n_value >= range.start and source_sym.n_value < range.end) {5945 if (source_sym.n_value >= range.start and source_sym.n_value < range.end) {
6685 break :size range.end - range.start;5946 break :size range.end - range.start;
...@@ -6731,259 +5992,272 @@ fn generateSymbolStabsForSymbol(...@@ -6731,259 +5992,272 @@ fn generateSymbolStabsForSymbol(
6731 }5992 }
6732}5993}
67335994
6734fn snapshotState(self: *MachO) !void {5995// fn snapshotState(self: *MachO) !void {
6735 const emit = self.base.options.emit orelse {5996// const emit = self.base.options.emit orelse {
6736 log.debug("no emit directory found; skipping snapshot...", .{});5997// log.debug("no emit directory found; skipping snapshot...", .{});
6737 return;5998// return;
6738 };5999// };
67396000
6740 const Snapshot = struct {6001// const Snapshot = struct {
6741 const Node = struct {6002// const Node = struct {
6742 const Tag = enum {6003// const Tag = enum {
6743 section_start,6004// section_start,
6744 section_end,6005// section_end,
6745 atom_start,6006// atom_start,
6746 atom_end,6007// atom_end,
6747 relocation,6008// relocation,
67486009
6749 pub fn jsonStringify(6010// pub fn jsonStringify(
6750 tag: Tag,6011// tag: Tag,
6751 options: std.json.StringifyOptions,6012// options: std.json.StringifyOptions,
6752 out_stream: anytype,6013// out_stream: anytype,
6753 ) !void {6014// ) !void {
6754 _ = options;6015// _ = options;
6755 switch (tag) {6016// switch (tag) {
6756 .section_start => try out_stream.writeAll("\"section_start\""),6017// .section_start => try out_stream.writeAll("\"section_start\""),
6757 .section_end => try out_stream.writeAll("\"section_end\""),6018// .section_end => try out_stream.writeAll("\"section_end\""),
6758 .atom_start => try out_stream.writeAll("\"atom_start\""),6019// .atom_start => try out_stream.writeAll("\"atom_start\""),
6759 .atom_end => try out_stream.writeAll("\"atom_end\""),6020// .atom_end => try out_stream.writeAll("\"atom_end\""),
6760 .relocation => try out_stream.writeAll("\"relocation\""),6021// .relocation => try out_stream.writeAll("\"relocation\""),
6761 }6022// }
6762 }6023// }
6763 };6024// };
6764 const Payload = struct {6025// const Payload = struct {
6765 name: []const u8 = "",6026// name: []const u8 = "",
6766 aliases: [][]const u8 = &[0][]const u8{},6027// aliases: [][]const u8 = &[0][]const u8{},
6767 is_global: bool = false,6028// is_global: bool = false,
6768 target: u64 = 0,6029// target: u64 = 0,
6769 };6030// };
6770 address: u64,6031// address: u64,
6771 tag: Tag,6032// tag: Tag,
6772 payload: Payload,6033// payload: Payload,
6773 };6034// };
6774 timestamp: i128,6035// timestamp: i128,
6775 nodes: []Node,6036// nodes: []Node,
6776 };6037// };
67776038
6778 var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);6039// var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
6779 defer arena_allocator.deinit();6040// defer arena_allocator.deinit();
6780 const arena = arena_allocator.allocator();6041// const arena = arena_allocator.allocator();
67816042
6782 const out_file = try emit.directory.handle.createFile("snapshots.json", .{6043// const out_file = try emit.directory.handle.createFile("snapshots.json", .{
6783 .truncate = false,6044// .truncate = false,
6784 .read = true,6045// .read = true,
6785 });6046// });
6786 defer out_file.close();6047// defer out_file.close();
67876048
6788 if (out_file.seekFromEnd(-1)) {6049// if (out_file.seekFromEnd(-1)) {
6789 try out_file.writer().writeByte(',');6050// try out_file.writer().writeByte(',');
6790 } else |err| switch (err) {6051// } else |err| switch (err) {
6791 error.Unseekable => try out_file.writer().writeByte('['),6052// error.Unseekable => try out_file.writer().writeByte('['),
6792 else => |e| return e,6053// else => |e| return e,
6793 }6054// }
6794 const writer = out_file.writer();6055// const writer = out_file.writer();
67956056
6796 var snapshot = Snapshot{6057// var snapshot = Snapshot{
6797 .timestamp = std.time.nanoTimestamp(),6058// .timestamp = std.time.nanoTimestamp(),
6798 .nodes = undefined,6059// .nodes = undefined,
6799 };6060// };
6800 var nodes = std.ArrayList(Snapshot.Node).init(arena);6061// var nodes = std.ArrayList(Snapshot.Node).init(arena);
68016062
6802 for (self.section_ordinals.keys()) |key| {6063// for (self.section_ordinals.keys()) |key| {
6803 const sect = self.getSection(key);6064// const sect = self.getSection(key);
6804 const sect_name = try std.fmt.allocPrint(arena, "{s},{s}", .{ sect.segName(), sect.sectName() });6065// const sect_name = try std.fmt.allocPrint(arena, "{s},{s}", .{ sect.segName(), sect.sectName() });
6805 try nodes.append(.{6066// try nodes.append(.{
6806 .address = sect.addr,6067// .address = sect.addr,
6807 .tag = .section_start,6068// .tag = .section_start,
6808 .payload = .{ .name = sect_name },6069// .payload = .{ .name = sect_name },
6809 });6070// });
68106071
6811 const is_tlv = sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;6072// const is_tlv = sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;
68126073
6813 var atom: *Atom = self.atoms.get(key) orelse {6074// var atom: *Atom = self.atoms.get(key) orelse {
6814 try nodes.append(.{6075// try nodes.append(.{
6815 .address = sect.addr + sect.size,6076// .address = sect.addr + sect.size,
6816 .tag = .section_end,6077// .tag = .section_end,
6817 .payload = .{},6078// .payload = .{},
6818 });6079// });
6819 continue;6080// continue;
6820 };6081// };
68216082
6822 while (atom.prev) |prev| {6083// while (atom.prev) |prev| {
6823 atom = prev;6084// atom = prev;
6824 }6085// }
68256086
6826 while (true) {6087// while (true) {
6827 const atom_sym = atom.getSymbol(self);6088// const atom_sym = atom.getSymbol(self);
6828 var node = Snapshot.Node{6089// var node = Snapshot.Node{
6829 .address = atom_sym.n_value,6090// .address = atom_sym.n_value,
6830 .tag = .atom_start,6091// .tag = .atom_start,
6831 .payload = .{6092// .payload = .{
6832 .name = atom.getName(self),6093// .name = atom.getName(self),
6833 .is_global = self.globals.contains(atom.getName(self)),6094// .is_global = self.globals.contains(atom.getName(self)),
6834 },6095// },
6835 };6096// };
68366097
6837 var aliases = std.ArrayList([]const u8).init(arena);6098// var aliases = std.ArrayList([]const u8).init(arena);
6838 for (atom.contained.items) |sym_off| {6099// for (atom.contained.items) |sym_off| {
6839 if (sym_off.offset == 0) {6100// if (sym_off.offset == 0) {
6840 try aliases.append(self.getSymbolName(.{6101// try aliases.append(self.getSymbolName(.{
6841 .sym_index = sym_off.sym_index,6102// .sym_index = sym_off.sym_index,
6842 .file = atom.file,6103// .file = atom.file,
6843 }));6104// }));
6844 }6105// }
6845 }6106// }
6846 node.payload.aliases = aliases.toOwnedSlice();6107// node.payload.aliases = aliases.toOwnedSlice();
6847 try nodes.append(node);6108// try nodes.append(node);
68486109
6849 var relocs = try std.ArrayList(Snapshot.Node).initCapacity(arena, atom.relocs.items.len);6110// var relocs = try std.ArrayList(Snapshot.Node).initCapacity(arena, atom.relocs.items.len);
6850 for (atom.relocs.items) |rel| {6111// for (atom.relocs.items) |rel| {
6851 const source_addr = blk: {6112// const source_addr = blk: {
6852 const source_sym = atom.getSymbol(self);6113// const source_sym = atom.getSymbol(self);
6853 break :blk source_sym.n_value + rel.offset;6114// break :blk source_sym.n_value + rel.offset;
6854 };6115// };
6855 const target_addr = blk: {6116// const target_addr = blk: {
6856 const target_atom = rel.getTargetAtom(self) orelse {6117// const target_atom = rel.getTargetAtom(self) orelse {
6857 // If there is no atom for target, we still need to check for special, atom-less6118// // If there is no atom for target, we still need to check for special, atom-less
6858 // symbols such as `___dso_handle`.6119// // symbols such as `___dso_handle`.
6859 const target_name = self.getSymbolName(rel.target);6120// const target_name = self.getSymbolName(rel.target);
6860 if (self.globals.contains(target_name)) {6121// if (self.globals.contains(target_name)) {
6861 const atomless_sym = self.getSymbol(rel.target);6122// const atomless_sym = self.getSymbol(rel.target);
6862 break :blk atomless_sym.n_value;6123// break :blk atomless_sym.n_value;
6863 }6124// }
6864 break :blk 0;6125// break :blk 0;
6865 };6126// };
6866 const target_sym = if (target_atom.isSymbolContained(rel.target, self))6127// const target_sym = if (target_atom.isSymbolContained(rel.target, self))
6867 self.getSymbol(rel.target)6128// self.getSymbol(rel.target)
6868 else6129// else
6869 target_atom.getSymbol(self);6130// target_atom.getSymbol(self);
6870 const base_address: u64 = if (is_tlv) base_address: {6131// const base_address: u64 = if (is_tlv) base_address: {
6871 const sect_id: u16 = sect_id: {6132// const sect_id: u16 = sect_id: {
6872 if (self.tlv_data_section_index) |i| {6133// if (self.tlv_data_section_index) |i| {
6873 break :sect_id i;6134// break :sect_id i;
6874 } else if (self.tlv_bss_section_index) |i| {6135// } else if (self.tlv_bss_section_index) |i| {
6875 break :sect_id i;6136// break :sect_id i;
6876 } else unreachable;6137// } else unreachable;
6877 };6138// };
6878 break :base_address self.getSection(.{6139// break :base_address self.getSection(.{
6879 .seg = self.data_segment_cmd_index.?,6140// .seg = self.data_segment_cmd_index.?,
6880 .sect = sect_id,6141// .sect = sect_id,
6881 }).addr;6142// }).addr;
6882 } else 0;6143// } else 0;
6883 break :blk target_sym.n_value - base_address;6144// break :blk target_sym.n_value - base_address;
6884 };6145// };
68856146
6886 relocs.appendAssumeCapacity(.{6147// relocs.appendAssumeCapacity(.{
6887 .address = source_addr,6148// .address = source_addr,
6888 .tag = .relocation,6149// .tag = .relocation,
6889 .payload = .{ .target = target_addr },6150// .payload = .{ .target = target_addr },
6890 });6151// });
6891 }6152// }
68926153
6893 if (atom.contained.items.len == 0) {6154// if (atom.contained.items.len == 0) {
6894 try nodes.appendSlice(relocs.items);6155// try nodes.appendSlice(relocs.items);
6895 } else {6156// } else {
6896 // Need to reverse iteration order of relocs since by default for relocatable sources6157// // 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, for6158// // they come in reverse. For linking, this doesn't matter in any way, however, for
6898 // arranging the memoryline for displaying it does.6159// // arranging the memoryline for displaying it does.
6899 std.mem.reverse(Snapshot.Node, relocs.items);6160// std.mem.reverse(Snapshot.Node, relocs.items);
69006161
6901 var next_i: usize = 0;6162// var next_i: usize = 0;
6902 var last_rel: usize = 0;6163// var last_rel: usize = 0;
6903 while (next_i < atom.contained.items.len) : (next_i += 1) {6164// while (next_i < atom.contained.items.len) : (next_i += 1) {
6904 const loc = SymbolWithLoc{6165// const loc = SymbolWithLoc{
6905 .sym_index = atom.contained.items[next_i].sym_index,6166// .sym_index = atom.contained.items[next_i].sym_index,
6906 .file = atom.file,6167// .file = atom.file,
6907 };6168// };
6908 const cont_sym = self.getSymbol(loc);6169// const cont_sym = self.getSymbol(loc);
6909 const cont_sym_name = self.getSymbolName(loc);6170// const cont_sym_name = self.getSymbolName(loc);
6910 var contained_node = Snapshot.Node{6171// var contained_node = Snapshot.Node{
6911 .address = cont_sym.n_value,6172// .address = cont_sym.n_value,
6912 .tag = .atom_start,6173// .tag = .atom_start,
6913 .payload = .{6174// .payload = .{
6914 .name = cont_sym_name,6175// .name = cont_sym_name,
6915 .is_global = self.globals.contains(cont_sym_name),6176// .is_global = self.globals.contains(cont_sym_name),
6916 },6177// },
6917 };6178// };
69186179
6919 // Accumulate aliases6180// // Accumulate aliases
6920 var inner_aliases = std.ArrayList([]const u8).init(arena);6181// var inner_aliases = std.ArrayList([]const u8).init(arena);
6921 while (true) {6182// while (true) {
6922 if (next_i + 1 >= atom.contained.items.len) break;6183// if (next_i + 1 >= atom.contained.items.len) break;
6923 const next_sym_loc = SymbolWithLoc{6184// const next_sym_loc = SymbolWithLoc{
6924 .sym_index = atom.contained.items[next_i + 1].sym_index,6185// .sym_index = atom.contained.items[next_i + 1].sym_index,
6925 .file = atom.file,6186// .file = atom.file,
6926 };6187// };
6927 const next_sym = self.getSymbol(next_sym_loc);6188// const next_sym = self.getSymbol(next_sym_loc);
6928 if (next_sym.n_value != cont_sym.n_value) break;6189// if (next_sym.n_value != cont_sym.n_value) break;
6929 const next_sym_name = self.getSymbolName(next_sym_loc);6190// const next_sym_name = self.getSymbolName(next_sym_loc);
6930 if (self.globals.contains(next_sym_name)) {6191// if (self.globals.contains(next_sym_name)) {
6931 try inner_aliases.append(contained_node.payload.name);6192// try inner_aliases.append(contained_node.payload.name);
6932 contained_node.payload.name = next_sym_name;6193// contained_node.payload.name = next_sym_name;
6933 contained_node.payload.is_global = true;6194// contained_node.payload.is_global = true;
6934 } else try inner_aliases.append(next_sym_name);6195// } else try inner_aliases.append(next_sym_name);
6935 next_i += 1;6196// next_i += 1;
6936 }6197// }
69376198
6938 const cont_size = if (next_i + 1 < atom.contained.items.len)6199// const cont_size = if (next_i + 1 < atom.contained.items.len)
6939 self.getSymbol(.{6200// self.getSymbol(.{
6940 .sym_index = atom.contained.items[next_i + 1].sym_index,6201// .sym_index = atom.contained.items[next_i + 1].sym_index,
6941 .file = atom.file,6202// .file = atom.file,
6942 }).n_value - cont_sym.n_value6203// }).n_value - cont_sym.n_value
6943 else6204// else
6944 atom_sym.n_value + atom.size - cont_sym.n_value;6205// atom_sym.n_value + atom.size - cont_sym.n_value;
69456206
6946 contained_node.payload.aliases = inner_aliases.toOwnedSlice();6207// contained_node.payload.aliases = inner_aliases.toOwnedSlice();
6947 try nodes.append(contained_node);6208// try nodes.append(contained_node);
69486209
6949 for (relocs.items[last_rel..]) |rel| {6210// for (relocs.items[last_rel..]) |rel| {
6950 if (rel.address >= cont_sym.n_value + cont_size) {6211// if (rel.address >= cont_sym.n_value + cont_size) {
6951 break;6212// break;
6952 }6213// }
6953 try nodes.append(rel);6214// try nodes.append(rel);
6954 last_rel += 1;6215// last_rel += 1;
6955 }6216// }
69566217
6957 try nodes.append(.{6218// try nodes.append(.{
6958 .address = cont_sym.n_value + cont_size,6219// .address = cont_sym.n_value + cont_size,
6959 .tag = .atom_end,6220// .tag = .atom_end,
6960 .payload = .{},6221// .payload = .{},
6961 });6222// });
6962 }6223// }
6963 }6224// }
69646225
6965 try nodes.append(.{6226// try nodes.append(.{
6966 .address = atom_sym.n_value + atom.size,6227// .address = atom_sym.n_value + atom.size,
6967 .tag = .atom_end,6228// .tag = .atom_end,
6968 .payload = .{},6229// .payload = .{},
6969 });6230// });
69706231
6971 if (atom.next) |next| {6232// if (atom.next) |next| {
6972 atom = next;6233// atom = next;
6973 } else break;6234// } else break;
6974 }6235// }
69756236
6976 try nodes.append(.{6237// try nodes.append(.{
6977 .address = sect.addr + sect.size,6238// .address = sect.addr + sect.size,
6978 .tag = .section_end,6239// .tag = .section_end,
6979 .payload = .{},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,
6980 });6259 });
6981 }6260 }
6982
6983 snapshot.nodes = nodes.toOwnedSlice();
6984
6985 try std.json.stringify(snapshot, .{}, writer);
6986 try writer.writeByte(']');
6987}6261}
69886262
6989fn logSymAttributes(sym: macho.nlist_64, buf: *[9]u8) []const u8 {6263fn logSymAttributes(sym: macho.nlist_64, buf: *[9]u8) []const u8 {
...@@ -7104,26 +6378,19 @@ fn logSymtab(self: *MachO) void {...@@ -7104,26 +6378,19 @@ fn logSymtab(self: *MachO) void {
7104 }6378 }
7105}6379}
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
7114fn logAtoms(self: *MachO) void {6381fn logAtoms(self: *MachO) void {
7115 log.debug("atoms:", .{});6382 log.debug("atoms:", .{});
7116 var it = self.atoms.iterator();6383
7117 while (it.next()) |entry| {6384 const slice = self.sections.slice();
7118 const match = entry.key_ptr.*;6385 for (slice.items(.last_atom)) |last, i| {
7119 var atom = entry.value_ptr.*;6386 var atom = last orelse continue;
6387 const header = slice.items(.header)[i];
71206388
7121 while (atom.prev) |prev| {6389 while (atom.prev) |prev| {
7122 atom = prev;6390 atom = prev;
7123 }6391 }
71246392
7125 const sect = self.getSection(match);6393 log.debug("{s},{s}", .{ header.segName(), header.sectName() });
7126 log.debug("{s},{s}", .{ sect.segName(), sect.sectName() });
71276394
7128 while (true) {6395 while (true) {
7129 self.logAtom(atom);6396 self.logAtom(atom);
src/link/MachO/Archive.zig+33-25
...@@ -6,19 +6,14 @@ const fs = std.fs;...@@ -6,19 +6,14 @@ const fs = std.fs;
6const log = std.log.scoped(.link);6const log = std.log.scoped(.link);
7const macho = std.macho;7const macho = std.macho;
8const mem = std.mem;8const mem = std.mem;
9const fat = @import("fat.zig");
109
11const Allocator = mem.Allocator;10const Allocator = mem.Allocator;
12const Object = @import("Object.zig");11const Object = @import("Object.zig");
1312
14file: fs.File,13file: fs.File,
14fat_offset: u64,
15name: []const u8,15name: []const u8,
1616header: ar_hdr = undefined,
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,
2217
23/// Parsed table of contents.18/// Parsed table of contents.
24/// Each symbol name points to a list of all definition19/// Each symbol name points to a list of all definition
...@@ -103,11 +98,7 @@ pub fn deinit(self: *Archive, allocator: Allocator) void {...@@ -103,11 +98,7 @@ pub fn deinit(self: *Archive, allocator: Allocator) void {
103 allocator.free(self.name);98 allocator.free(self.name);
104}99}
105100
106pub fn parse(self: *Archive, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) !void {101pub fn parse(self: *Archive, allocator: Allocator, reader: anytype) !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
111 const magic = try reader.readBytesNoEof(SARMAG);102 const magic = try reader.readBytesNoEof(SARMAG);
112 if (!mem.eql(u8, &magic, ARMAG)) {103 if (!mem.eql(u8, &magic, ARMAG)) {
113 log.debug("invalid magic: expected '{s}', found '{s}'", .{ ARMAG, magic });104 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...@@ -115,21 +106,23 @@ pub fn parse(self: *Archive, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch
115 }106 }
116107
117 self.header = try reader.readStruct(ar_hdr);108 self.header = try reader.readStruct(ar_hdr);
118 if (!mem.eql(u8, &self.header.?.ar_fmag, ARFMAG)) {109 if (!mem.eql(u8, &self.header.ar_fmag, ARFMAG)) {
119 log.debug("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, self.header.?.ar_fmag });110 log.debug("invalid header delimiter: expected '{s}', found '{s}'", .{
111 ARFMAG,
112 self.header.ar_fmag,
113 });
120 return error.NotArchive;114 return error.NotArchive;
121 }115 }
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);
124 log.debug("parsing archive '{s}' at '{s}'", .{ embedded_name, self.name });119 log.debug("parsing archive '{s}' at '{s}'", .{ embedded_name, self.name });
125 defer allocator.free(embedded_name);120 defer allocator.free(embedded_name);
126121
127 try self.parseTableOfContents(allocator, reader);122 try self.parseTableOfContents(allocator, reader);
128 try reader.context.seekTo(0);
129}123}
130124
131fn parseName(allocator: Allocator, header: ar_hdr, reader: anytype) ![]u8 {125fn parseName(allocator: Allocator, name_or_length: ar_hdr.NameOrLength, reader: anytype) ![]u8 {
132 const name_or_length = try header.nameOrLength();
133 var name: []u8 = undefined;126 var name: []u8 = undefined;
134 switch (name_or_length) {127 switch (name_or_length) {
135 .Name => |n| {128 .Name => |n| {
...@@ -187,9 +180,14 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) !...@@ -187,9 +180,14 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) !
187 }180 }
188}181}
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 {
191 const reader = self.file.reader();189 const reader = self.file.reader();
192 try reader.context.seekTo(offset + self.library_offset);190 try reader.context.seekTo(self.fat_offset + offset);
193191
194 const object_header = try reader.readStruct(ar_hdr);192 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...@@ -198,7 +196,8 @@ pub fn parseObject(self: Archive, allocator: Allocator, cpu_arch: std.Target.Cpu
198 return error.MalformedArchive;196 return error.MalformedArchive;
199 }197 }
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);
202 defer allocator.free(object_name);201 defer allocator.free(object_name);
203202
204 log.debug("extracting object '{s}' from archive '{s}'", .{ object_name, self.name });203 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...@@ -209,15 +208,24 @@ pub fn parseObject(self: Archive, allocator: Allocator, cpu_arch: std.Target.Cpu
209 break :name try std.fmt.allocPrint(allocator, "{s}({s})", .{ path, object_name });208 break :name try std.fmt.allocPrint(allocator, "{s}({s})", .{ path, object_name });
210 };209 };
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
212 var object = Object{222 var object = Object{
213 .file = try fs.cwd().openFile(self.name, .{}),
214 .name = name,223 .name = name,
215 .file_offset = @intCast(u32, try reader.context.getPos()),224 .mtime = try self.header.date(),
216 .mtime = try self.header.?.date(),225 .contents = contents,
217 };226 };
218227
219 try object.parse(allocator, cpu_arch);228 try object.parse(allocator, cpu_arch);
220 try reader.context.seekTo(0);
221229
222 return object;230 return object;
223}231}
src/link/MachO/Atom.zig+21-19
...@@ -246,7 +246,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:...@@ -246,7 +246,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
246 else => {246 else => {
247 log.err("unexpected relocation type after ARM64_RELOC_ADDEND", .{});247 log.err("unexpected relocation type after ARM64_RELOC_ADDEND", .{});
248 log.err(" expected ARM64_RELOC_PAGE21 or ARM64_RELOC_PAGEOFF12", .{});248 log.err(" expected ARM64_RELOC_PAGE21 or ARM64_RELOC_PAGEOFF12", .{});
249 log.err(" found {}", .{next});249 log.err(" found {s}", .{@tagName(next)});
250 return error.UnexpectedRelocationType;250 return error.UnexpectedRelocationType;
251 },251 },
252 }252 }
...@@ -285,7 +285,9 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:...@@ -285,7 +285,9 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
285 else => {285 else => {
286 log.err("unexpected relocation type after ARM64_RELOC_ADDEND", .{});286 log.err("unexpected relocation type after ARM64_RELOC_ADDEND", .{});
287 log.err(" expected ARM64_RELOC_UNSIGNED", .{});287 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 });
289 return error.UnexpectedRelocationType;291 return error.UnexpectedRelocationType;
290 },292 },
291 },293 },
...@@ -294,7 +296,9 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:...@@ -294,7 +296,9 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
294 else => {296 else => {
295 log.err("unexpected relocation type after X86_64_RELOC_ADDEND", .{});297 log.err("unexpected relocation type after X86_64_RELOC_ADDEND", .{});
296 log.err(" expected X86_64_RELOC_UNSIGNED", .{});298 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 });
298 return error.UnexpectedRelocationType;302 return error.UnexpectedRelocationType;
299 },303 },
300 },304 },
...@@ -309,13 +313,13 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:...@@ -309,13 +313,13 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
309 const sect_id = @intCast(u16, rel.r_symbolnum - 1);313 const sect_id = @intCast(u16, rel.r_symbolnum - 1);
310 const sym_index = object.sections_as_symbols.get(sect_id) orelse blk: {314 const sym_index = object.sections_as_symbols.get(sect_id) orelse blk: {
311 const sect = object.getSourceSection(sect_id);315 const sect = object.getSourceSection(sect_id);
312 const match = (try context.macho_file.getMatchingSection(sect)) orelse316 const match = (try context.macho_file.getOutputSection(sect)) orelse
313 unreachable;317 unreachable;
314 const sym_index = @intCast(u32, object.symtab.items.len);318 const sym_index = @intCast(u32, object.symtab.items.len);
315 try object.symtab.append(gpa, .{319 try object.symtab.append(gpa, .{
316 .n_strx = 0,320 .n_strx = 0,
317 .n_type = macho.N_SECT,321 .n_type = macho.N_SECT,
318 .n_sect = context.macho_file.getSectionOrdinal(match),322 .n_sect = match + 1,
319 .n_desc = 0,323 .n_desc = 0,
320 .n_value = sect.addr,324 .n_value = sect.addr,
321 });325 });
...@@ -459,9 +463,10 @@ fn addPtrBindingOrRebase(...@@ -459,9 +463,10 @@ fn addPtrBindingOrRebase(
459 });463 });
460 } else {464 } else {
461 const source_sym = self.getSymbol(context.macho_file);465 const source_sym = self.getSymbol(context.macho_file);
462 const match = context.macho_file.getMatchingSectionFromOrdinal(source_sym.n_sect);466 const section = context.macho_file.sections.get(source_sym.n_sect - 1);
463 const sect = context.macho_file.getSection(match);467 const header = section.header;
464 const sect_type = sect.type_();468 const segment_index = section.segment_index;
469 const sect_type = header.@"type"();
465470
466 const should_rebase = rebase: {471 const should_rebase = rebase: {
467 if (rel.r_length != 3) break :rebase false;472 if (rel.r_length != 3) break :rebase false;
...@@ -470,12 +475,12 @@ fn addPtrBindingOrRebase(...@@ -470,12 +475,12 @@ fn addPtrBindingOrRebase(
470 // that the segment is writable should be enough here.475 // that the segment is writable should be enough here.
471 const is_right_segment = blk: {476 const is_right_segment = blk: {
472 if (context.macho_file.data_segment_cmd_index) |idx| {477 if (context.macho_file.data_segment_cmd_index) |idx| {
473 if (match.seg == idx) {478 if (segment_index == idx) {
474 break :blk true;479 break :blk true;
475 }480 }
476 }481 }
477 if (context.macho_file.data_const_segment_cmd_index) |idx| {482 if (context.macho_file.data_const_segment_cmd_index) |idx| {
478 if (match.seg == idx) {483 if (segment_index == idx) {
479 break :blk true;484 break :blk true;
480 }485 }
481 }486 }
...@@ -524,6 +529,7 @@ fn addStub(target: MachO.SymbolWithLoc, context: RelocContext) !void {...@@ -524,6 +529,7 @@ fn addStub(target: MachO.SymbolWithLoc, context: RelocContext) !void {
524 if (context.macho_file.stubs_table.contains(target)) return;529 if (context.macho_file.stubs_table.contains(target)) return;
525530
526 const stub_index = try context.macho_file.allocateStubEntry(target);531 const stub_index = try context.macho_file.allocateStubEntry(target);
532
527 const stub_helper_atom = try context.macho_file.createStubHelperAtom();533 const stub_helper_atom = try context.macho_file.createStubHelperAtom();
528 const laptr_atom = try context.macho_file.createLazyPointerAtom(stub_helper_atom.sym_index, target);534 const laptr_atom = try context.macho_file.createLazyPointerAtom(stub_helper_atom.sym_index, target);
529 const stub_atom = try context.macho_file.createStubAtom(laptr_atom.sym_index);535 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 {...@@ -565,9 +571,8 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
565 };571 };
566 const is_tlv = is_tlv: {572 const is_tlv = is_tlv: {
567 const source_sym = self.getSymbol(macho_file);573 const source_sym = self.getSymbol(macho_file);
568 const match = macho_file.getMatchingSectionFromOrdinal(source_sym.n_sect);574 const header = macho_file.sections.items(.header)[source_sym.n_sect - 1];
569 const sect = macho_file.getSection(match);575 break :is_tlv header.@"type"() == macho.S_THREAD_LOCAL_VARIABLES;
570 break :is_tlv sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;
571 };576 };
572 const target_addr = blk: {577 const target_addr = blk: {
573 const target_atom = rel.getTargetAtom(macho_file) orelse {578 const target_atom = rel.getTargetAtom(macho_file) orelse {
...@@ -597,9 +602,9 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -597,9 +602,9 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
597 // * wrt to __thread_data if defined, then602 // * wrt to __thread_data if defined, then
598 // * wrt to __thread_bss603 // * wrt to __thread_bss
599 const sect_id: u16 = sect_id: {604 const sect_id: u16 = sect_id: {
600 if (macho_file.tlv_data_section_index) |i| {605 if (macho_file.getSectionByName("__DATA", "__thread_data")) |i| {
601 break :sect_id i;606 break :sect_id i;
602 } else if (macho_file.tlv_bss_section_index) |i| {607 } else if (macho_file.getSectionByName("__DATA", "__thread_bss")) |i| {
603 break :sect_id i;608 break :sect_id i;
604 } else {609 } else {
605 log.err("threadlocal variables present but no initializer sections found", .{});610 log.err("threadlocal variables present but no initializer sections found", .{});
...@@ -608,10 +613,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -608,10 +613,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
608 return error.FailedToResolveRelocationTarget;613 return error.FailedToResolveRelocationTarget;
609 }614 }
610 };615 };
611 break :base_address macho_file.getSection(.{616 break :base_address macho_file.sections.items(.header)[sect_id].addr;
612 .seg = macho_file.data_segment_cmd_index.?,
613 .sect = sect_id,
614 }).addr;
615 } else 0;617 } else 0;
616 break :blk target_sym.n_value - base_address;618 break :blk target_sym.n_value - base_address;
617 };619 };
src/link/MachO/CodeSignature.zig+7-5
...@@ -252,7 +252,7 @@ pub const WriteOpts = struct {...@@ -252,7 +252,7 @@ pub const WriteOpts = struct {
252 file: fs.File,252 file: fs.File,
253 exec_seg_base: u64,253 exec_seg_base: u64,
254 exec_seg_limit: u64,254 exec_seg_limit: u64,
255 code_sig_cmd: macho.linkedit_data_command,255 file_size: u32,
256 output_mode: std.builtin.OutputMode,256 output_mode: std.builtin.OutputMode,
257};257};
258258
...@@ -274,10 +274,9 @@ pub fn writeAdhocSignature(...@@ -274,10 +274,9 @@ pub fn writeAdhocSignature(
274 self.code_directory.inner.execSegBase = opts.exec_seg_base;274 self.code_directory.inner.execSegBase = opts.exec_seg_base;
275 self.code_directory.inner.execSegLimit = opts.exec_seg_limit;275 self.code_directory.inner.execSegLimit = opts.exec_seg_limit;
276 self.code_directory.inner.execSegFlags = if (opts.output_mode == .Exe) macho.CS_EXECSEG_MAIN_BINARY else 0;276 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;277 self.code_directory.inner.codeLimit = opts.file_size;
278 self.code_directory.inner.codeLimit = 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
282 var buffer = try allocator.alloc(u8, self.page_size);281 var buffer = try allocator.alloc(u8, self.page_size);
283 defer allocator.free(buffer);282 defer allocator.free(buffer);
...@@ -289,7 +288,10 @@ pub fn writeAdhocSignature(...@@ -289,7 +288,10 @@ pub fn writeAdhocSignature(
289 var i: usize = 0;288 var i: usize = 0;
290 while (i < total_pages) : (i += 1) {289 while (i < total_pages) : (i += 1) {
291 const fstart = i * self.page_size;290 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;
293 const len = try opts.file.preadAll(buffer, fstart);295 const len = try opts.file.preadAll(buffer, fstart);
294 assert(fsize <= len);296 assert(fsize <= len);
295297
src/link/MachO/DebugSymbols.zig+199-331
...@@ -25,35 +25,18 @@ base: *MachO,...@@ -25,35 +25,18 @@ base: *MachO,
25dwarf: Dwarf,25dwarf: Dwarf,
26file: fs.File,26file: fs.File,
2727
28/// Table of all load commands28segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
29load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},29sections: std.ArrayListUnmanaged(macho.section_64) = .{},
30/// __PAGEZERO segment30
31pagezero_segment_cmd_index: ?u16 = null,31linkedit_segment_cmd_index: ?u8 = null,
32/// __TEXT segment32dwarf_segment_cmd_index: ?u8 = null,
33text_segment_cmd_index: ?u16 = null,33
34/// __DATA_CONST segment34debug_info_section_index: ?u8 = null,
35data_const_segment_cmd_index: ?u16 = null,35debug_abbrev_section_index: ?u8 = null,
36/// __DATA segment36debug_str_section_index: ?u8 = null,
37data_segment_cmd_index: ?u16 = null,37debug_aranges_section_index: ?u8 = null,
38/// __LINKEDIT segment38debug_line_section_index: ?u8 = null,
39linkedit_segment_cmd_index: ?u16 = null,39
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,
57debug_string_table_dirty: bool = false,40debug_string_table_dirty: bool = false,
58debug_abbrev_section_dirty: bool = false,41debug_abbrev_section_dirty: bool = false,
59debug_aranges_section_dirty: bool = false,42debug_aranges_section_dirty: bool = false,
...@@ -78,98 +61,44 @@ pub const Reloc = struct {...@@ -78,98 +61,44 @@ pub const Reloc = struct {
78/// You must call this function *after* `MachO.populateMissingMetadata()`61/// You must call this function *after* `MachO.populateMissingMetadata()`
79/// has been called to get a viable debug symbols output.62/// has been called to get a viable debug symbols output.
80pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void {63pub 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
137 if (self.linkedit_segment_cmd_index == null) {64 if (self.linkedit_segment_cmd_index == null) {
138 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);65 self.linkedit_segment_cmd_index = @intCast(u8, self.segments.items.len);
139 const base_cmd = self.base.load_commands.items[self.base.linkedit_segment_cmd_index.?].segment;66 log.debug("found __LINKEDIT segment free space 0x{x} to 0x{x}", .{
140 var cmd = try self.copySegmentCommand(allocator, base_cmd);67 self.base.page_size,
68 self.base.page_size * 2,
69 });
141 // TODO this needs reworking70 // TODO this needs reworking
142 cmd.inner.vmsize = self.base.page_size;71 try self.segments.append(allocator, .{
143 cmd.inner.fileoff = self.base.page_size;72 .segname = makeStaticString("__LINKEDIT"),
144 cmd.inner.filesize = self.base.page_size;73 .vmaddr = self.base.page_size,
145 try self.load_commands.append(allocator, .{ .segment = cmd });74 .vmsize = self.base.page_size,
146 self.load_commands_dirty = true;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 });
147 }81 }
14882
149 if (self.dwarf_segment_cmd_index == null) {83 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.?];
153 const ideal_size: u16 = 200 + 128 + 160 + 250;87 const ideal_size: u16 = 200 + 128 + 160 + 250;
154 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.base.page_size);88 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.base.page_size);
155 const fileoff = linkedit.inner.fileoff + linkedit.inner.filesize;89 const fileoff = linkedit.fileoff + linkedit.filesize;
156 const vmaddr = linkedit.inner.vmaddr + linkedit.inner.vmsize;90 const vmaddr = linkedit.vmaddr + linkedit.vmsize;
15791
158 log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ fileoff, fileoff + needed_size });92 log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ fileoff, fileoff + needed_size });
15993
160 try self.load_commands.append(allocator, .{94 try self.segments.append(allocator, .{
161 .segment = .{95 .segname = makeStaticString("__DWARF"),
162 .inner = .{96 .vmaddr = vmaddr,
163 .segname = makeStaticString("__DWARF"),97 .vmsize = needed_size,
164 .vmaddr = vmaddr,98 .fileoff = fileoff,
165 .vmsize = needed_size,99 .filesize = needed_size,
166 .fileoff = fileoff,100 .cmdsize = @sizeOf(macho.segment_command_64),
167 .filesize = needed_size,
168 .cmdsize = @sizeOf(macho.segment_command_64),
169 },
170 },
171 });101 });
172 self.load_commands_dirty = true;
173 }102 }
174103
175 if (self.debug_str_section_index == null) {104 if (self.debug_str_section_index == null) {
...@@ -203,18 +132,18 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void...@@ -203,18 +132,18 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
203 }132 }
204}133}
205134
206fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignment: u16) !u16 {135fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignment: u16) !u8 {
207 const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;136 const segment = &self.segments.items[self.dwarf_segment_cmd_index.?];
208 var sect = macho.section_64{137 var sect = macho.section_64{
209 .sectname = makeStaticString(sectname),138 .sectname = makeStaticString(sectname),
210 .segname = seg.inner.segname,139 .segname = segment.segname,
211 .size = @intCast(u32, size),140 .size = @intCast(u32, size),
212 .@"align" = alignment,141 .@"align" = alignment,
213 };142 };
214 const alignment_pow_2 = try math.powi(u32, 2, alignment);143 const alignment_pow_2 = try math.powi(u32, 2, alignment);
215 const off = self.findFreeSpace(size, alignment_pow_2);144 const off = self.findFreeSpace(size, alignment_pow_2);
216145
217 assert(off + size <= seg.inner.fileoff + seg.inner.filesize); // TODO expand146 assert(off + size <= segment.fileoff + segment.filesize); // TODO expand
218147
219 log.debug("found {s},{s} section free space 0x{x} to 0x{x}", .{148 log.debug("found {s},{s} section free space 0x{x} to 0x{x}", .{
220 sect.segName(),149 sect.segName(),
...@@ -223,31 +152,20 @@ fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignme...@@ -223,31 +152,20 @@ fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignme
223 off + size,152 off + size,
224 });153 });
225154
226 sect.addr = seg.inner.vmaddr + off - seg.inner.fileoff;155 sect.addr = segment.vmaddr + off - segment.fileoff;
227 sect.offset = @intCast(u32, off);156 sect.offset = @intCast(u32, off);
228157
229 const index = @intCast(u16, seg.sections.items.len);158 const index = @intCast(u8, self.sections.items.len);
230 try seg.sections.append(self.base.base.allocator, sect);159 try self.sections.append(self.base.base.allocator, sect);
231 seg.inner.cmdsize += @sizeOf(macho.section_64);160 segment.cmdsize += @sizeOf(macho.section_64);
232 seg.inner.nsects += 1;161 segment.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;
243162
244 return index;163 return index;
245}164}
246165
247fn detectAllocCollision(self: *DebugSymbols, start: u64, size: u64) ?u64 {166fn detectAllocCollision(self: *DebugSymbols, start: u64, size: u64) ?u64 {
248 const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
249 const end = start + padToIdeal(size);167 const end = start + padToIdeal(size);
250 for (seg.sections.items) |section| {168 for (self.sections.items) |section| {
251 const increased_size = padToIdeal(section.size);169 const increased_size = padToIdeal(section.size);
252 const test_end = section.offset + increased_size;170 const test_end = section.offset + increased_size;
253 if (end > section.offset and start < test_end) {171 if (end > section.offset and start < test_end) {
...@@ -258,8 +176,8 @@ fn detectAllocCollision(self: *DebugSymbols, start: u64, size: u64) ?u64 {...@@ -258,8 +176,8 @@ fn detectAllocCollision(self: *DebugSymbols, start: u64, size: u64) ?u64 {
258}176}
259177
260pub fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64) u64 {178pub fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64) u64 {
261 const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;179 const segment = self.segments.items[self.dwarf_segment_cmd_index.?];
262 var offset: u64 = seg.inner.fileoff;180 var offset: u64 = segment.fileoff;
263 while (self.detectAllocCollision(offset, object_size)) |item_end| {181 while (self.detectAllocCollision(offset, object_size)) |item_end| {
264 offset = mem.alignForwardGeneric(u64, item_end, min_alignment);182 offset = mem.alignForwardGeneric(u64, item_end, min_alignment);
265 }183 }
...@@ -296,8 +214,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti...@@ -296,8 +214,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
296 break :blk got_entry.getName(self.base);214 break :blk got_entry.getName(self.base);
297 },215 },
298 };216 };
299 const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;217 const sect = &self.sections.items[self.debug_info_section_index.?];
300 const sect = &seg.sections.items[self.debug_info_section_index.?];
301 const file_offset = sect.offset + reloc.offset;218 const file_offset = sect.offset + reloc.offset;
302 log.debug("resolving relocation: {d}@{x} ('{s}') at offset {x}", .{219 log.debug("resolving relocation: {d}@{x} ('{s}') at offset {x}", .{
303 reloc.target,220 reloc.target,
...@@ -311,15 +228,13 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti...@@ -311,15 +228,13 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
311228
312 if (self.debug_abbrev_section_dirty) {229 if (self.debug_abbrev_section_dirty) {
313 try self.dwarf.writeDbgAbbrev(&self.base.base);230 try self.dwarf.writeDbgAbbrev(&self.base.base);
314 self.load_commands_dirty = true;
315 self.debug_abbrev_section_dirty = false;231 self.debug_abbrev_section_dirty = false;
316 }232 }
317233
318 if (self.debug_info_header_dirty) {234 if (self.debug_info_header_dirty) {
319 // Currently only one compilation unit is supported, so the address range is simply235 // Currently only one compilation unit is supported, so the address range is simply
320 // identical to the main program header virtual address and memory size.236 // 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;237 const text_section = self.base.sections.items(.header)[self.base.text_section_index.?];
322 const text_section = text_segment.sections.items[self.text_section_index.?];
323 const low_pc = text_section.addr;238 const low_pc = text_section.addr;
324 const high_pc = text_section.addr + text_section.size;239 const high_pc = text_section.addr + text_section.size;
325 try self.dwarf.writeDbgInfoHeader(&self.base.base, module, low_pc, high_pc);240 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...@@ -329,10 +244,8 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
329 if (self.debug_aranges_section_dirty) {244 if (self.debug_aranges_section_dirty) {
330 // Currently only one compilation unit is supported, so the address range is simply245 // Currently only one compilation unit is supported, so the address range is simply
331 // identical to the main program header virtual address and memory size.246 // 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;247 const text_section = self.base.sections.items(.header)[self.base.text_section_index.?];
333 const text_section = text_segment.sections.items[self.text_section_index.?];
334 try self.dwarf.writeDbgAranges(&self.base.base, text_section.addr, text_section.size);248 try self.dwarf.writeDbgAranges(&self.base.base, text_section.addr, text_section.size);
335 self.load_commands_dirty = true;
336 self.debug_aranges_section_dirty = false;249 self.debug_aranges_section_dirty = false;
337 }250 }
338251
...@@ -342,8 +255,8 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti...@@ -342,8 +255,8 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
342 }255 }
343256
344 {257 {
345 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;258 const dwarf_segment = &self.segments.items[self.dwarf_segment_cmd_index.?];
346 const debug_strtab_sect = &dwarf_segment.sections.items[self.debug_str_section_index.?];259 const debug_strtab_sect = &self.sections.items[self.debug_str_section_index.?];
347 if (self.debug_string_table_dirty or self.dwarf.strtab.items.len != debug_strtab_sect.size) {260 if (self.debug_string_table_dirty or self.dwarf.strtab.items.len != debug_strtab_sect.size) {
348 const allocated_size = self.allocatedSize(debug_strtab_sect.offset);261 const allocated_size = self.allocatedSize(debug_strtab_sect.offset);
349 const needed_size = self.dwarf.strtab.items.len;262 const needed_size = self.dwarf.strtab.items.len;
...@@ -351,7 +264,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti...@@ -351,7 +264,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
351 if (needed_size > allocated_size) {264 if (needed_size > allocated_size) {
352 debug_strtab_sect.size = 0; // free the space265 debug_strtab_sect.size = 0; // free the space
353 const new_offset = self.findFreeSpace(needed_size, 1);266 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;
355 debug_strtab_sect.offset = @intCast(u32, new_offset);268 debug_strtab_sect.offset = @intCast(u32, new_offset);
356 }269 }
357 debug_strtab_sect.size = @intCast(u32, needed_size);270 debug_strtab_sect.size = @intCast(u32, needed_size);
...@@ -362,28 +275,40 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti...@@ -362,28 +275,40 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
362 });275 });
363276
364 try self.file.pwriteAll(self.dwarf.strtab.items, debug_strtab_sect.offset);277 try self.file.pwriteAll(self.dwarf.strtab.items, debug_strtab_sect.offset);
365 self.load_commands_dirty = true;
366 self.debug_string_table_dirty = false;278 self.debug_string_table_dirty = false;
367 }279 }
368 }280 }
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);
370 self.updateDwarfSegment();288 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
377 assert(!self.debug_abbrev_section_dirty);304 assert(!self.debug_abbrev_section_dirty);
378 assert(!self.debug_aranges_section_dirty);305 assert(!self.debug_aranges_section_dirty);
379 assert(!self.debug_string_table_dirty);306 assert(!self.debug_string_table_dirty);
380}307}
381308
382pub fn deinit(self: *DebugSymbols, allocator: Allocator) void {309pub fn deinit(self: *DebugSymbols, allocator: Allocator) void {
383 for (self.load_commands.items) |*lc| {310 self.segments.deinit(allocator);
384 lc.deinit(allocator);311 self.sections.deinit(allocator);
385 }
386 self.load_commands.deinit(allocator);
387 self.dwarf.deinit();312 self.dwarf.deinit();
388 self.strtab.deinit(allocator);313 self.strtab.deinit(allocator);
389 self.relocs.deinit(allocator);314 self.relocs.deinit(allocator);
...@@ -402,59 +327,20 @@ pub fn swapRemoveRelocs(self: *DebugSymbols, target: u32) void {...@@ -402,59 +327,20 @@ pub fn swapRemoveRelocs(self: *DebugSymbols, target: u32) void {
402 }327 }
403}328}
404329
405fn copySegmentCommand(330fn updateDwarfSegment(self: *DebugSymbols) void {
406 self: *DebugSymbols,331 const linkedit = self.segments.items[self.linkedit_segment_cmd_index.?];
407 allocator: Allocator,332 const dwarf_segment = &self.segments.items[self.dwarf_segment_cmd_index.?];
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 }
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;
448 }339 }
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
456 var max_offset: u64 = 0;341 var max_offset: u64 = 0;
457 for (dwarf_segment.sections.items) |sect| {342 for (self.sections.items) |*sect| {
343 sect.addr += diff;
458 log.debug(" {s},{s} - 0x{x}-0x{x} - 0x{x}-0x{x}", .{344 log.debug(" {s},{s} - 0x{x}-0x{x} - 0x{x}-0x{x}", .{
459 sect.segName(),345 sect.segName(),
460 sect.sectName(),346 sect.sectName(),
...@@ -468,42 +354,59 @@ fn updateDwarfSegment(self: *DebugSymbols) void {...@@ -468,42 +354,59 @@ fn updateDwarfSegment(self: *DebugSymbols) void {
468 }354 }
469 }355 }
470356
471 const file_size = max_offset - dwarf_segment.inner.fileoff;357 const file_size = max_offset - dwarf_segment.fileoff;
472 log.debug("__DWARF size 0x{x}", .{file_size});358 log.debug("__DWARF size 0x{x}", .{file_size});
473359
474 if (file_size != dwarf_segment.inner.filesize) {360 if (file_size != dwarf_segment.filesize) {
475 dwarf_segment.inner.filesize = file_size;361 dwarf_segment.filesize = file_size;
476 if (dwarf_segment.inner.vmsize < dwarf_segment.inner.filesize) {362 dwarf_segment.vmsize = mem.alignForwardGeneric(u64, dwarf_segment.filesize, self.base.page_size);
477 dwarf_segment.inner.vmsize = mem.alignForwardGeneric(u64, dwarf_segment.inner.filesize, self.base.page_size);
478 }
479 self.load_commands_dirty = true;
480 }363 }
481}364}
482365
483/// Writes all load commands and section headers.366fn writeSegmentHeaders(self: *DebugSymbols, ncmds: *u32, writer: anytype) !void {
484fn writeLoadCommands(self: *DebugSymbols, allocator: Allocator) !void {367 // Write segment/section headers from the binary file first.
485 if (!self.load_commands_dirty) return;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;385 if (out_seg.nsects == 0 and
488 for (self.load_commands.items) |lc| {386 (mem.eql(u8, out_seg.segName(), "__DATA_CONST") or
489 sizeofcmds += lc.cmdsize();387 mem.eql(u8, out_seg.segName(), "__DATA"))) continue;
490 }
491388
492 var buffer = try allocator.alloc(u8, sizeofcmds);389 try writer.writeStruct(out_seg);
493 defer allocator.free(buffer);390 for (self.base.sections.items(.header)[indexes.start..indexes.end]) |header| {
494 var fib = std.io.fixedBufferStream(buffer);391 if (header.size == 0) continue;
495 const writer = fib.writer();392 var out_header = header;
496 for (self.load_commands.items) |lc| {393 out_header.offset = 0;
497 try lc.write(writer);394 try writer.writeStruct(out_header);
498 }395 }
499396
500 const off = @sizeOf(macho.mach_header_64);397 ncmds.* += 1;
501 log.debug("writing {} load commands from 0x{x} to 0x{x}", .{ self.load_commands.items.len, off, off + sizeofcmds });398 }
502 try self.file.pwriteAll(buffer, off);399 // Next, commit DSYM's __LINKEDIT and __DWARF segments headers.
503 self.load_commands_dirty = false;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 }
504}407}
505408
506fn writeHeader(self: *DebugSymbols) !void {409fn writeHeader(self: *DebugSymbols, ncmds: u32, sizeofcmds: u32) !void {
507 var header: macho.mach_header_64 = .{};410 var header: macho.mach_header_64 = .{};
508 header.filetype = macho.MH_DSYM;411 header.filetype = macho.MH_DSYM;
509412
...@@ -519,12 +422,8 @@ fn writeHeader(self: *DebugSymbols) !void {...@@ -519,12 +422,8 @@ fn writeHeader(self: *DebugSymbols) !void {
519 else => return error.UnsupportedCpuArchitecture,422 else => return error.UnsupportedCpuArchitecture,
520 }423 }
521424
522 header.ncmds = @intCast(u32, self.load_commands.items.len);425 header.ncmds = ncmds;
523 header.sizeofcmds = 0;426 header.sizeofcmds = sizeofcmds;
524
525 for (self.load_commands.items) |cmd| {
526 header.sizeofcmds += cmd.cmdsize();
527 }
528427
529 log.debug("writing Mach-O header {}", .{header});428 log.debug("writing Mach-O header {}", .{header});
530429
...@@ -532,79 +431,46 @@ fn writeHeader(self: *DebugSymbols) !void {...@@ -532,79 +431,46 @@ fn writeHeader(self: *DebugSymbols) !void {
532}431}
533432
534pub fn allocatedSize(self: *DebugSymbols, start: u64) u64 {433pub fn allocatedSize(self: *DebugSymbols, start: u64) u64 {
535 const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;434 const seg = self.segments.items[self.dwarf_segment_cmd_index.?];
536 assert(start >= seg.inner.fileoff);435 assert(start >= seg.fileoff);
537 var min_pos: u64 = std.math.maxInt(u64);436 var min_pos: u64 = std.math.maxInt(u64);
538 for (seg.sections.items) |section| {437 for (self.sections.items) |section| {
539 if (section.offset <= start) continue;438 if (section.offset <= start) continue;
540 if (section.offset < min_pos) min_pos = section.offset;439 if (section.offset < min_pos) min_pos = section.offset;
541 }440 }
542 return min_pos - start;441 return min_pos - start;
543}442}
544443
545fn updateVirtualMemoryMapping(self: *DebugSymbols) !void {444fn writeLinkeditSegmentData(self: *DebugSymbols, ncmds: *u32, lc_writer: anytype) !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 {
588 const tracy = trace(@src());445 const tracy = trace(@src());
589 defer tracy.end();446 defer tracy.end();
590447
591 try self.writeSymbolTable();448 const source_vmaddr = self.base.segments.items[self.base.linkedit_segment_cmd_index.?].vmaddr;
592 try self.writeStringTable();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;452 var symtab_cmd = macho.symtab_command{
595 const aligned_size = mem.alignForwardGeneric(u64, seg.inner.filesize, self.base.page_size);453 .cmdsize = @sizeOf(macho.symtab_command),
596 seg.inner.filesize = aligned_size;454 .symoff = 0,
597 seg.inner.vmsize = aligned_size;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;
598}467}
599468
600fn writeSymbolTable(self: *DebugSymbols) !void {469fn writeSymtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
601 const tracy = trace(@src());470 const tracy = trace(@src());
602 defer tracy.end();471 defer tracy.end();
603472
604 const gpa = self.base.base.allocator;473 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
609 var locals = std.ArrayList(macho.nlist_64).init(gpa);475 var locals = std.ArrayList(macho.nlist_64).init(gpa);
610 defer locals.deinit();476 defer locals.deinit();
...@@ -634,34 +500,32 @@ fn writeSymbolTable(self: *DebugSymbols) !void {...@@ -634,34 +500,32 @@ fn writeSymbolTable(self: *DebugSymbols) !void {
634500
635 const nlocals = locals.items.len;501 const nlocals = locals.items.len;
636 const nexports = exports.items.len;502 const nexports = exports.items.len;
637 const locals_off = symtab.symoff;503 const nsyms = nlocals + nexports;
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);
641504
642 symtab.nsyms = @intCast(u32, nlocals + nexports);505 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
643 const needed_size = (nlocals + nexports) * @sizeOf(macho.nlist_64);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) {
646 const aligned_size = mem.alignForwardGeneric(u64, needed_size, self.base.page_size);510 const aligned_size = mem.alignForwardGeneric(u64, needed_size, self.base.page_size);
647 const diff = @intCast(u32, aligned_size - seg.inner.filesize);511 const diff = @intCast(u32, aligned_size - seg.filesize);
648 const dwarf_seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;512 const dwarf_seg = &self.segments.items[self.dwarf_segment_cmd_index.?];
649 seg.inner.filesize = aligned_size;513 seg.filesize = aligned_size;
650514
651 try MachO.copyRangeAllOverlappingAlloc(515 try MachO.copyRangeAllOverlappingAlloc(
652 self.base.base.allocator,516 self.base.base.allocator,
653 self.file,517 self.file,
654 dwarf_seg.inner.fileoff,518 dwarf_seg.fileoff,
655 dwarf_seg.inner.fileoff + diff,519 dwarf_seg.fileoff + diff,
656 math.cast(usize, dwarf_seg.inner.filesize) orelse return error.Overflow,520 math.cast(usize, dwarf_seg.filesize) orelse return error.Overflow,
657 );521 );
658522
659 const old_seg_fileoff = dwarf_seg.inner.fileoff;523 const old_seg_fileoff = dwarf_seg.fileoff;
660 dwarf_seg.inner.fileoff += diff;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| {
665 const old_offset = sect.offset;529 const old_offset = sect.offset;
666 sect.offset += diff;530 sect.offset += diff;
667531
...@@ -674,47 +538,53 @@ fn writeSymbolTable(self: *DebugSymbols) !void {...@@ -674,47 +538,53 @@ fn writeSymbolTable(self: *DebugSymbols) !void {
674 }538 }
675 }539 }
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
677 log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off });549 log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off });
678 try self.file.pwriteAll(mem.sliceAsBytes(locals.items), locals_off);550 try self.file.pwriteAll(mem.sliceAsBytes(locals.items), locals_off);
679551
680 log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off });552 log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off });
681 try self.file.pwriteAll(mem.sliceAsBytes(exports.items), exports_off);553 try self.file.pwriteAll(mem.sliceAsBytes(exports.items), exports_off);
682
683 self.load_commands_dirty = true;
684}554}
685555
686fn writeStringTable(self: *DebugSymbols) !void {556fn writeStrtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
687 const tracy = trace(@src());557 const tracy = trace(@src());
688 defer tracy.end();558 defer tracy.end();
689559
690 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;560 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
691 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;561 const symtab_size = @intCast(u32, lc.nsyms * @sizeOf(macho.nlist_64));
692 const symtab_size = @intCast(u32, symtab.nsyms * @sizeOf(macho.nlist_64));562 const offset = mem.alignForwardGeneric(u64, lc.symoff + symtab_size, @alignOf(u64));
693 symtab.stroff = symtab.symoff + symtab_size;563 lc.stroff = @intCast(u32, offset);
694564
695 const needed_size = mem.alignForwardGeneric(u64, self.strtab.buffer.items.len, @alignOf(u64));565 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) {568 if (symtab_size + needed_size > seg.filesize) {
699 const aligned_size = mem.alignForwardGeneric(u64, symtab_size + needed_size, self.base.page_size);569 const aligned_size = mem.alignForwardGeneric(u64, offset + needed_size, self.base.page_size);
700 const diff = @intCast(u32, aligned_size - seg.inner.filesize);570 const diff = @intCast(u32, aligned_size - seg.filesize);
701 const dwarf_seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;571 const dwarf_seg = &self.segments.items[self.dwarf_segment_cmd_index.?];
702 seg.inner.filesize = aligned_size;572 seg.filesize = aligned_size;
703573
704 try MachO.copyRangeAllOverlappingAlloc(574 try MachO.copyRangeAllOverlappingAlloc(
705 self.base.base.allocator,575 self.base.base.allocator,
706 self.file,576 self.file,
707 dwarf_seg.inner.fileoff,577 dwarf_seg.fileoff,
708 dwarf_seg.inner.fileoff + diff,578 dwarf_seg.fileoff + diff,
709 math.cast(usize, dwarf_seg.inner.filesize) orelse return error.Overflow,579 math.cast(usize, dwarf_seg.filesize) orelse return error.Overflow,
710 );580 );
711581
712 const old_seg_fileoff = dwarf_seg.inner.fileoff;582 const old_seg_fileoff = dwarf_seg.fileoff;
713 dwarf_seg.inner.fileoff += diff;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| {
718 const old_offset = sect.offset;588 const old_offset = sect.offset;
719 sect.offset += diff;589 sect.offset += diff;
720590
...@@ -727,9 +597,7 @@ fn writeStringTable(self: *DebugSymbols) !void {...@@ -727,9 +597,7 @@ fn writeStringTable(self: *DebugSymbols) !void {
727 }597 }
728 }598 }
729599
730 log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });600 log.debug("writing string table from 0x{x} to 0x{x}", .{ lc.stroff, lc.stroff + lc.strsize });
731
732 try self.file.pwriteAll(self.strtab.buffer.items, symtab.stroff);
733601
734 self.load_commands_dirty = true;602 try self.file.pwriteAll(self.strtab.buffer.items, lc.stroff);
735}603}
src/link/MachO/Dylib.zig+53-106
...@@ -13,23 +13,9 @@ const fat = @import("fat.zig");...@@ -13,23 +13,9 @@ const fat = @import("fat.zig");
13const Allocator = mem.Allocator;13const Allocator = mem.Allocator;
14const CrossTarget = std.zig.CrossTarget;14const CrossTarget = std.zig.CrossTarget;
15const LibStub = @import("../tapi.zig").LibStub;15const LibStub = @import("../tapi.zig").LibStub;
16const LoadCommandIterator = macho.LoadCommandIterator;
16const MachO = @import("../MachO.zig");17const 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
33id: ?Id = null,19id: ?Id = null,
34weak: bool = false,20weak: bool = false,
3521
...@@ -53,16 +39,12 @@ pub const Id = struct {...@@ -53,16 +39,12 @@ pub const Id = struct {
53 };39 };
54 }40 }
5541
56 pub fn fromLoadCommand(allocator: Allocator, lc: macho.GenericCommandWithData(macho.dylib_command)) !Id {42 pub fn fromLoadCommand(allocator: Allocator, lc: macho.dylib_command, name: []const u8) !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
61 return Id{43 return Id{
62 .name = name,44 .name = try allocator.dupe(u8, name),
63 .timestamp = dylib.timestamp,45 .timestamp = lc.dylib.timestamp,
64 .current_version = dylib.current_version,46 .current_version = lc.dylib.current_version,
65 .compatibility_version = dylib.compatibility_version,47 .compatibility_version = lc.dylib.compatibility_version,
66 };48 };
67 }49 }
6850
...@@ -126,125 +108,89 @@ pub const Id = struct {...@@ -126,125 +108,89 @@ pub const Id = struct {
126};108};
127109
128pub fn deinit(self: *Dylib, allocator: Allocator) void {110pub 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
134 for (self.symbols.keys()) |key| {111 for (self.symbols.keys()) |key| {
135 allocator.free(key);112 allocator.free(key);
136 }113 }
137 self.symbols.deinit(allocator);114 self.symbols.deinit(allocator);
138
139 allocator.free(self.name);
140
141 if (self.id) |*id| {115 if (self.id) |*id| {
142 id.deinit(allocator);116 id.deinit(allocator);
143 }117 }
144}118}
145119
146pub fn parse(120pub fn parseFromBinary(
147 self: *Dylib,121 self: *Dylib,
148 allocator: Allocator,122 allocator: Allocator,
149 cpu_arch: std.Target.Cpu.Arch,123 cpu_arch: std.Target.Cpu.Arch,
150 dylib_id: u16,124 dylib_id: u16,
151 dependent_libs: anytype,125 dependent_libs: anytype,
126 name: []const u8,
127 data: []align(@alignOf(u64)) const u8,
152) !void {128) !void {
153 log.debug("parsing shared library '{s}'", .{self.name});129 var stream = std.io.fixedBufferStream(data);
154130 const reader = stream.reader();
155 self.library_offset = try fat.getLibraryOffset(self.file.reader(), cpu_arch);
156131
157 try self.file.seekTo(self.library_offset);132 log.debug("parsing shared library '{s}'", .{name});
158133
159 var reader = self.file.reader();134 const header = try reader.readStruct(macho.mach_header_64);
160 self.header = try reader.readStruct(macho.mach_header_64);
161135
162 if (self.header.?.filetype != macho.MH_DYLIB) {136 if (header.filetype != macho.MH_DYLIB) {
163 log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{ macho.MH_DYLIB, self.header.?.filetype });137 log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{ macho.MH_DYLIB, header.filetype });
164 return error.NotDylib;138 return error.NotDylib;
165 }139 }
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
169 if (this_arch != cpu_arch) {143 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 });
171 return error.MismatchedCpuArchitecture;148 return error.MismatchedCpuArchitecture;
172 }149 }
173150
174 try self.readLoadCommands(allocator, reader, dylib_id, dependent_libs);151 const should_lookup_reexports = header.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0;
175 try self.parseId(allocator);152 var it = LoadCommandIterator{
176 try self.parseSymbols(allocator);153 .ncmds = header.ncmds,
177}154 .buffer = data[@sizeOf(macho.mach_header_64)..][0..header.sizeofcmds],
178155 };
179fn readLoadCommands(156 while (it.next()) |cmd| {
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);
193 switch (cmd.cmd()) {157 switch (cmd.cmd()) {
194 .SYMTAB => {158 .SYMTAB => {
195 self.symtab_cmd_index = i;159 const symtab_cmd = cmd.cast(macho.symtab_command).?;
196 },160 const symtab = @ptrCast(
197 .DYSYMTAB => {161 [*]const macho.nlist_64,
198 self.dysymtab_cmd_index = i;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 }
199 },173 },
200 .ID_DYLIB => {174 .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 );
202 },180 },
203 .REEXPORT_DYLIB => {181 .REEXPORT_DYLIB => {
204 if (should_lookup_reexports) {182 if (should_lookup_reexports) {
205 // Parse install_name to dependent dylib.183 // 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 );
207 try dependent_libs.writeItem(.{ .id = id, .parent = dylib_id });189 try dependent_libs.writeItem(.{ .id = id, .parent = dylib_id });
208 }190 }
209 },191 },
210 else => {192 else => {},
211 log.debug("Unknown load command detected: 0x{x}.", .{@enumToInt(cmd.cmd())});
212 },
213 }193 }
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, {});
248 }194 }
249}195}
250196
...@@ -356,10 +302,11 @@ pub fn parseFromStub(...@@ -356,10 +302,11 @@ pub fn parseFromStub(
356 lib_stub: LibStub,302 lib_stub: LibStub,
357 dylib_id: u16,303 dylib_id: u16,
358 dependent_libs: anytype,304 dependent_libs: anytype,
305 name: []const u8,
359) !void {306) !void {
360 if (lib_stub.inner.len == 0) return error.EmptyStubFile;307 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
364 const umbrella_lib = lib_stub.inner[0];311 const umbrella_lib = lib_stub.inner[0];
365312
src/link/MachO/Object.zig+151-176
...@@ -3,6 +3,7 @@ const Object = @This();...@@ -3,6 +3,7 @@ const Object = @This();
3const std = @import("std");3const std = @import("std");
4const build_options = @import("build_options");4const build_options = @import("build_options");
5const assert = std.debug.assert;5const assert = std.debug.assert;
6const dwarf = std.dwarf;
6const fs = std.fs;7const fs = std.fs;
7const io = std.io;8const io = std.io;
8const log = std.log.scoped(.link);9const log = std.log.scoped(.link);
...@@ -14,43 +15,20 @@ const trace = @import("../../tracy.zig").trace;...@@ -14,43 +15,20 @@ const trace = @import("../../tracy.zig").trace;
1415
15const Allocator = mem.Allocator;16const Allocator = mem.Allocator;
16const Atom = @import("Atom.zig");17const Atom = @import("Atom.zig");
18const LoadCommandIterator = macho.LoadCommandIterator;
17const MachO = @import("../MachO.zig");19const MachO = @import("../MachO.zig");
18const MatchingSection = MachO.MatchingSection;
19const SymbolWithLoc = MachO.SymbolWithLoc;20const SymbolWithLoc = MachO.SymbolWithLoc;
2021
21file: fs.File,
22name: []const u8,22name: []const u8,
23mtime: u64,23mtime: u64,
2424contents: []align(@alignOf(u64)) const u8,
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,
3125
32header: macho.mach_header_64 = undefined,26header: macho.mach_header_64 = undefined,
3327in_symtab: []const macho.nlist_64 = undefined,
34load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},28in_strtab: []const u8 = undefined,
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,
5029
51symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},30symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
52strtab: []const u8 = &.{},31sections: std.ArrayListUnmanaged(macho.section_64) = .{},
53data_in_code_entries: []const macho.data_in_code_entry = &.{},
5432
55sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{},33sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{},
5634
...@@ -61,12 +39,8 @@ managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},...@@ -61,12 +39,8 @@ managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
61atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{},39atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{},
6240
63pub fn deinit(self: *Object, gpa: Allocator) void {41pub 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);
69 self.symtab.deinit(gpa);42 self.symtab.deinit(gpa);
43 self.sections.deinit(gpa);
70 self.sections_as_symbols.deinit(gpa);44 self.sections_as_symbols.deinit(gpa);
71 self.atom_by_index_table.deinit(gpa);45 self.atom_by_index_table.deinit(gpa);
7246
...@@ -77,22 +51,15 @@ pub fn deinit(self: *Object, gpa: Allocator) void {...@@ -77,22 +51,15 @@ pub fn deinit(self: *Object, gpa: Allocator) void {
77 self.managed_atoms.deinit(gpa);51 self.managed_atoms.deinit(gpa);
7852
79 gpa.free(self.name);53 gpa.free(self.name);
54 gpa.free(self.contents);
80}55}
8156
82pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) !void {57pub 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
87 var stream = std.io.fixedBufferStream(self.contents);58 var stream = std.io.fixedBufferStream(self.contents);
88 const reader = stream.reader();59 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
95 self.header = try reader.readStruct(macho.mach_header_64);61 self.header = try reader.readStruct(macho.mach_header_64);
62
96 if (self.header.filetype != macho.MH_OBJECT) {63 if (self.header.filetype != macho.MH_OBJECT) {
97 log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{64 log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{
98 macho.MH_OBJECT,65 macho.MH_OBJECT,
...@@ -110,92 +77,54 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)...@@ -110,92 +77,54 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
110 },77 },
111 };78 };
112 if (this_arch != cpu_arch) {79 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 });
114 return error.MismatchedCpuArchitecture;84 return error.MismatchedCpuArchitecture;
115 }85 }
11686
117 try self.load_commands.ensureUnusedCapacity(allocator, self.header.ncmds);87 var it = LoadCommandIterator{
11888 .ncmds = self.header.ncmds,
119 var i: u16 = 0;89 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
120 while (i < self.header.ncmds) : (i += 1) {90 };
121 var cmd = try macho.LoadCommand.read(allocator, reader);91 while (it.next()) |cmd| {
122 switch (cmd.cmd()) {92 switch (cmd.cmd()) {
123 .SEGMENT_64 => {93 .SEGMENT_64 => {
124 self.segment_cmd_index = i;94 const segment = cmd.cast(macho.segment_command_64).?;
125 var seg = cmd.segment;95 try self.sections.ensureUnusedCapacity(allocator, segment.nsects);
126 for (seg.sections.items) |*sect, j| {96 for (cmd.getSections()) |sect| {
127 const index = @intCast(u16, j);97 self.sections.appendAssumeCapacity(sect);
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 }
154 }98 }
155
156 seg.inner.fileoff += file_offset;
157 },99 },
158 .SYMTAB => {100 .SYMTAB => {
159 self.symtab_cmd_index = i;101 const symtab = cmd.cast(macho.symtab_command).?;
160 cmd.symtab.symoff += file_offset;102 self.in_symtab = @ptrCast(
161 cmd.symtab.stroff += file_offset;103 [*]const macho.nlist_64,
162 },104 @alignCast(@alignOf(macho.nlist_64), &self.contents[symtab.symoff]),
163 .DYSYMTAB => {105 )[0..symtab.nsyms];
164 self.dysymtab_cmd_index = i;106 self.in_strtab = self.contents[symtab.stroff..][0..symtab.strsize];
165 },107 try self.symtab.appendSlice(allocator, self.in_symtab);
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())});
175 },108 },
109 else => {},
176 }110 }
177 self.load_commands.appendAssumeCapacity(cmd);
178 }111 }
179
180 try self.parseSymtab(allocator);
181}112}
182113
183const Context = struct {114const Context = struct {
184 symtab: []const macho.nlist_64,115 object: *const Object,
185 strtab: []const u8,
186};116};
187117
188const SymbolAtIndex = struct {118const SymbolAtIndex = struct {
189 index: u32,119 index: u32,
190120
191 fn getSymbol(self: SymbolAtIndex, ctx: Context) macho.nlist_64 {121 fn getSymbol(self: SymbolAtIndex, ctx: Context) macho.nlist_64 {
192 return ctx.symtab[self.index];122 return ctx.object.getSourceSymbol(self.index).?;
193 }123 }
194124
195 fn getSymbolName(self: SymbolAtIndex, ctx: Context) []const u8 {125 fn getSymbolName(self: SymbolAtIndex, ctx: Context) []const u8 {
196 const sym = self.getSymbol(ctx);126 const sym = self.getSymbol(ctx);
197 assert(sym.n_strx < ctx.strtab.len);127 return ctx.object.getString(sym.n_strx);
198 return mem.sliceTo(@ptrCast([*:0]const u8, ctx.strtab.ptr + sym.n_strx), 0);
199 }128 }
200129
201 /// Returns whether lhs is less than rhs by allocated address in object file.130 /// Returns whether lhs is less than rhs by allocated address in object file.
...@@ -285,6 +214,23 @@ fn filterRelocs(...@@ -285,6 +214,23 @@ fn filterRelocs(
285 return relocs[start..end];214 return relocs[start..end];
286}215}
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
288/// Splits object into atoms assuming one-shot linking mode.234/// Splits object into atoms assuming one-shot linking mode.
289pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32) !void {235pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32) !void {
290 assert(macho_file.mode == .one_shot);236 assert(macho_file.mode == .one_shot);
...@@ -293,7 +239,6 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)...@@ -293,7 +239,6 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
293 defer tracy.end();239 defer tracy.end();
294240
295 const gpa = macho_file.base.allocator;241 const gpa = macho_file.base.allocator;
296 const seg = self.load_commands.items[self.segment_cmd_index.?].segment;
297242
298 log.debug("splitting object({d}, {s}) into atoms: one-shot mode", .{ object_id, self.name });243 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)...@@ -302,13 +247,12 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
302 // the GO compiler does not necessarily respect that therefore we sort immediately by type247 // the GO compiler does not necessarily respect that therefore we sort immediately by type
303 // and address within.248 // and address within.
304 const context = Context{249 const context = Context{
305 .symtab = self.getSourceSymtab(),250 .object = self,
306 .strtab = self.strtab,
307 };251 };
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);
309 defer sorted_all_syms.deinit();253 defer sorted_all_syms.deinit();
310254
311 for (context.symtab) |_, index| {255 for (self.in_symtab) |_, index| {
312 sorted_all_syms.appendAssumeCapacity(.{ .index = @intCast(u32, index) });256 sorted_all_syms.appendAssumeCapacity(.{ .index = @intCast(u32, index) });
313 }257 }
314258
...@@ -320,53 +264,48 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)...@@ -320,53 +264,48 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
320264
321 // Well, shit, sometimes compilers skip the dysymtab load command altogether, meaning we265 // Well, shit, sometimes compilers skip the dysymtab load command altogether, meaning we
322 // have to infer the start of undef section in the symtab ourselves.266 // have to infer the start of undef section in the symtab ourselves.
323 const iundefsym = if (self.dysymtab_cmd_index) |cmd_index| blk: {267 const iundefsym = blk: {
324 const dysymtab = self.load_commands.items[cmd_index].dysymtab;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 };
325 break :blk dysymtab.iundefsym;276 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;
333 };277 };
334278
335 // We only care about defined symbols, so filter every other out.279 // We only care about defined symbols, so filter every other out.
336 const sorted_syms = sorted_all_syms.items[0..iundefsym];280 const sorted_syms = sorted_all_syms.items[0..iundefsym];
337 const subsections_via_symbols = self.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;281 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| {
340 const sect_id = @intCast(u8, id);284 const sect_id = @intCast(u8, id);
341 log.debug("splitting section '{s},{s}' into atoms", .{ sect.segName(), sect.sectName() });285 log.debug("splitting section '{s},{s}' into atoms", .{ sect.segName(), sect.sectName() });
342286
343 // Get matching segment/section in the final artifact.287 // 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 {
345 log.debug(" unhandled section", .{});289 log.debug(" unhandled section", .{});
346 continue;290 continue;
347 };291 };
348292
349 log.debug(" output sect({d}, '{s},{s}')", .{293 log.debug(" output sect({d}, '{s},{s}')", .{
350 macho_file.getSectionOrdinal(match),294 match + 1,
351 macho_file.getSection(match).segName(),295 macho_file.sections.items(.header)[match].segName(),
352 macho_file.getSection(match).sectName(),296 macho_file.sections.items(.header)[match].sectName(),
353 });297 });
354298
355 const cpu_arch = macho_file.base.options.target.cpu.arch;299 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
361 // Read section's code301 // 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
364 // Read section's list of relocations304 // Read section's list of relocations
365 const raw_relocs = self.contents[sect.reloff..][0 .. sect.nreloc * @sizeOf(macho.relocation_info)];305 const relocs = @ptrCast(
366 const relocs = mem.bytesAsSlice(306 [*]const macho.relocation_info,
367 macho.relocation_info,307 @alignCast(@alignOf(macho.relocation_info), &self.contents[sect.reloff]),
368 @alignCast(@alignOf(macho.relocation_info), raw_relocs),308 )[0..sect.nreloc];
369 );
370309
371 // Symbols within this section only.310 // Symbols within this section only.
372 const filtered_syms = filterSymbolsByAddress(311 const filtered_syms = filterSymbolsByAddress(
...@@ -387,7 +326,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)...@@ -387,7 +326,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
387 try self.symtab.append(gpa, .{326 try self.symtab.append(gpa, .{
388 .n_strx = 0,327 .n_strx = 0,
389 .n_type = macho.N_SECT,328 .n_type = macho.N_SECT,
390 .n_sect = macho_file.getSectionOrdinal(match),329 .n_sect = match + 1,
391 .n_desc = 0,330 .n_desc = 0,
392 .n_value = sect.addr,331 .n_value = sect.addr,
393 });332 });
...@@ -476,7 +415,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)...@@ -476,7 +415,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
476 try self.symtab.append(gpa, .{415 try self.symtab.append(gpa, .{
477 .n_strx = 0,416 .n_strx = 0,
478 .n_type = macho.N_SECT,417 .n_type = macho.N_SECT,
479 .n_sect = macho_file.getSectionOrdinal(match),418 .n_sect = match + 1,
480 .n_desc = 0,419 .n_desc = 0,
481 .n_value = addr,420 .n_value = addr,
482 });421 });
...@@ -501,7 +440,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)...@@ -501,7 +440,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
501 try self.symtab.append(gpa, .{440 try self.symtab.append(gpa, .{
502 .n_strx = 0,441 .n_strx = 0,
503 .n_type = macho.N_SECT,442 .n_type = macho.N_SECT,
504 .n_sect = macho_file.getSectionOrdinal(match),443 .n_sect = match + 1,
505 .n_desc = 0,444 .n_desc = 0,
506 .n_value = sect.addr,445 .n_value = sect.addr,
507 });446 });
...@@ -535,21 +474,21 @@ fn createAtomFromSubsection(...@@ -535,21 +474,21 @@ fn createAtomFromSubsection(
535 code: ?[]const u8,474 code: ?[]const u8,
536 relocs: []const macho.relocation_info,475 relocs: []const macho.relocation_info,
537 indexes: []const SymbolAtIndex,476 indexes: []const SymbolAtIndex,
538 match: MatchingSection,477 match: u8,
539 sect: macho.section_64,478 sect: macho.section_64,
540) !*Atom {479) !*Atom {
541 const gpa = macho_file.base.allocator;480 const gpa = macho_file.base.allocator;
542 const sym = self.symtab.items[sym_index];481 const sym = self.symtab.items[sym_index];
543 const atom = try MachO.createEmptyAtom(gpa, sym_index, size, alignment);482 const atom = try MachO.createEmptyAtom(gpa, sym_index, size, alignment);
544 atom.file = object_id;483 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
547 log.debug("creating ATOM(%{d}, '{s}') in sect({d}, '{s},{s}') in object({d})", .{486 log.debug("creating ATOM(%{d}, '{s}') in sect({d}, '{s},{s}') in object({d})", .{
548 sym_index,487 sym_index,
549 self.getString(sym.n_strx),488 self.getString(sym.n_strx),
550 macho_file.getSectionOrdinal(match),489 match + 1,
551 macho_file.getSection(match).segName(),490 macho_file.sections.items(.header)[match].segName(),
552 macho_file.getSection(match).sectName(),491 macho_file.sections.items(.header)[match].sectName(),
553 object_id,492 object_id,
554 });493 });
555494
...@@ -577,7 +516,7 @@ fn createAtomFromSubsection(...@@ -577,7 +516,7 @@ fn createAtomFromSubsection(
577 try atom.contained.ensureTotalCapacity(gpa, indexes.len);516 try atom.contained.ensureTotalCapacity(gpa, indexes.len);
578 for (indexes) |inner_sym_index| {517 for (indexes) |inner_sym_index| {
579 const inner_sym = &self.symtab.items[inner_sym_index.index];518 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;
581 atom.contained.appendAssumeCapacity(.{520 atom.contained.appendAssumeCapacity(.{
582 .sym_index = inner_sym_index.index,521 .sym_index = inner_sym_index.index,
583 .offset = inner_sym.n_value - sym.n_value,522 .offset = inner_sym.n_value - sym.n_value,
...@@ -589,48 +528,84 @@ fn createAtomFromSubsection(...@@ -589,48 +528,84 @@ fn createAtomFromSubsection(
589 return atom;528 return atom;
590}529}
591530
592fn parseSymtab(self: *Object, allocator: Allocator) !void {531pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 {
593 const index = self.symtab_cmd_index orelse return;532 if (index >= self.in_symtab.len) return null;
594 const symtab = self.load_commands.items[index].symtab;533 return self.in_symtab[index];
595 try self.symtab.appendSlice(allocator, self.getSourceSymtab());
596 self.strtab = self.contents[symtab.stroff..][0..symtab.strsize];
597}534}
598535
599pub fn getSourceSymtab(self: Object) []const macho.nlist_64 {536pub fn getSourceSection(self: Object, index: u16) macho.section_64 {
600 const index = self.symtab_cmd_index orelse return &[0]macho.nlist_64{};537 assert(index < self.sections.items.len);
601 const symtab = self.load_commands.items[index].symtab;538 return self.sections.items[index];
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 );
608}539}
609540
610pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 {541pub fn parseDataInCode(self: Object) ?[]const macho.data_in_code_entry {
611 const symtab = self.getSourceSymtab();542 var it = LoadCommandIterator{
612 if (index >= symtab.len) return null;543 .ncmds = self.header.ncmds,
613 return symtab[index];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;
614}559}
615560
616pub fn getSourceSection(self: Object, index: u16) macho.section_64 {561fn parseDysymtab(self: Object) ?macho.dysymtab_command {
617 const seg = self.load_commands.items[self.segment_cmd_index.?].segment;562 var it = LoadCommandIterator{
618 assert(index < seg.sections.items.len);563 .ncmds = self.header.ncmds,
619 return seg.sections.items[index];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;
620}574}
621575
622pub fn parseDataInCode(self: Object) ?[]const macho.data_in_code_entry {576pub fn parseDwarfInfo(self: Object) error{Overflow}!dwarf.DwarfInfo {
623 const index = self.data_in_code_cmd_index orelse return null;577 var di = dwarf.DwarfInfo{
624 const data_in_code = self.load_commands.items[index].linkedit_data;578 .endian = .Little,
625 const raw_dice = self.contents[data_in_code.dataoff..][0..data_in_code.datasize];579 .debug_info = &[0]u8{},
626 return mem.bytesAsSlice(580 .debug_abbrev = &[0]u8{},
627 macho.data_in_code_entry,581 .debug_str = &[0]u8{},
628 @alignCast(@alignOf(macho.data_in_code_entry), raw_dice),582 .debug_line = &[0]u8{},
629 );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;
630}606}
631607
632pub fn getSectionContents(self: Object, index: u16) error{Overflow}![]const u8 {608pub fn getSectionContents(self: Object, sect: macho.section_64) error{Overflow}![]const u8 {
633 const sect = self.getSourceSection(index);
634 const size = math.cast(usize, sect.size) orelse return error.Overflow;609 const size = math.cast(usize, sect.size) orelse return error.Overflow;
635 log.debug("getting {s},{s} data at 0x{x} - 0x{x}", .{610 log.debug("getting {s},{s} data at 0x{x} - 0x{x}", .{
636 sect.segName(),611 sect.segName(),
...@@ -642,8 +617,8 @@ pub fn getSectionContents(self: Object, index: u16) error{Overflow}![]const u8 {...@@ -642,8 +617,8 @@ pub fn getSectionContents(self: Object, index: u16) error{Overflow}![]const u8 {
642}617}
643618
644pub fn getString(self: Object, off: u32) []const u8 {619pub fn getString(self: Object, off: u32) []const u8 {
645 assert(off < self.strtab.len);620 assert(off < self.in_strtab.len);
646 return mem.sliceTo(@ptrCast([*:0]const u8, self.strtab.ptr + off), 0);621 return mem.sliceTo(@ptrCast([*:0]const u8, self.in_strtab.ptr + off), 0);
647}622}
648623
649pub fn getAtomForSymbol(self: Object, sym_index: u32) ?*Atom {624pub fn getAtomForSymbol(self: Object, sym_index: u32) ?*Atom {
src/link/MachO/dead_strip.zig+26-24
...@@ -8,7 +8,6 @@ const mem = std.mem;...@@ -8,7 +8,6 @@ const mem = std.mem;
8const Allocator = mem.Allocator;8const Allocator = mem.Allocator;
9const Atom = @import("Atom.zig");9const Atom = @import("Atom.zig");
10const MachO = @import("../MachO.zig");10const MachO = @import("../MachO.zig");
11const MatchingSection = MachO.MatchingSection;
1211
13pub fn gcAtoms(macho_file: *MachO) !void {12pub fn gcAtoms(macho_file: *MachO) !void {
14 const gpa = macho_file.base.allocator;13 const gpa = macho_file.base.allocator;
...@@ -25,12 +24,12 @@ pub fn gcAtoms(macho_file: *MachO) !void {...@@ -25,12 +24,12 @@ pub fn gcAtoms(macho_file: *MachO) !void {
25 try prune(arena, alive, macho_file);24 try prune(arena, alive, macho_file);
26}25}
2726
28fn removeAtomFromSection(atom: *Atom, match: MatchingSection, macho_file: *MachO) void {27fn removeAtomFromSection(atom: *Atom, match: u8, macho_file: *MachO) void {
29 const sect = macho_file.getSectionPtr(match);28 var section = macho_file.sections.get(match);
3029
31 // If we want to enable GC for incremental codepath, we need to take into30 // If we want to enable GC for incremental codepath, we need to take into
32 // account any padding that might have been left here.31 // account any padding that might have been left here.
33 sect.size -= atom.size;32 section.header.size -= atom.size;
3433
35 if (atom.prev) |prev| {34 if (atom.prev) |prev| {
36 prev.next = atom.next;35 prev.next = atom.next;
...@@ -38,15 +37,16 @@ fn removeAtomFromSection(atom: *Atom, match: MatchingSection, macho_file: *MachO...@@ -38,15 +37,16 @@ fn removeAtomFromSection(atom: *Atom, match: MatchingSection, macho_file: *MachO
38 if (atom.next) |next| {37 if (atom.next) |next| {
39 next.prev = atom.prev;38 next.prev = atom.prev;
40 } else {39 } else {
41 const last = macho_file.atoms.getPtr(match).?;
42 if (atom.prev) |prev| {40 if (atom.prev) |prev| {
43 last.* = prev;41 section.last_atom = prev;
44 } else {42 } else {
45 // The section will be GCed in the next step.43 // The section will be GCed in the next step.
46 last.* = undefined;44 section.last_atom = null;
47 sect.size = 0;45 section.header.size = 0;
48 }46 }
49 }47 }
48
49 macho_file.sections.set(match, section);
50}50}
5151
52fn collectRoots(roots: *std.AutoHashMap(*Atom, void), macho_file: *MachO) !void {52fn 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...@@ -93,7 +93,7 @@ fn collectRoots(roots: *std.AutoHashMap(*Atom, void), macho_file: *MachO) !void
93 const is_gc_root = blk: {93 const is_gc_root = blk: {
94 if (source_sect.isDontDeadStrip()) break :blk true;94 if (source_sect.isDontDeadStrip()) break :blk true;
95 if (mem.eql(u8, "__StaticInit", source_sect.sectName())) break :blk true;95 if (mem.eql(u8, "__StaticInit", source_sect.sectName())) break :blk true;
96 switch (source_sect.type_()) {96 switch (source_sect.@"type"()) {
97 macho.S_MOD_INIT_FUNC_POINTERS,97 macho.S_MOD_INIT_FUNC_POINTERS,
98 macho.S_MOD_TERM_FUNC_POINTERS,98 macho.S_MOD_TERM_FUNC_POINTERS,
99 => break :blk true,99 => break :blk true,
...@@ -173,19 +173,19 @@ fn mark(...@@ -173,19 +173,19 @@ fn mark(
173fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *MachO) !void {173fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *MachO) !void {
174 // Any section that ends up here will be updated, that is,174 // Any section that ends up here will be updated, that is,
175 // its size and alignment recalculated.175 // 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);
177 var loop: bool = true;177 var loop: bool = true;
178 while (loop) {178 while (loop) {
179 loop = false;179 loop = false;
180180
181 for (macho_file.objects.items) |object| {181 for (macho_file.objects.items) |object| {
182 for (object.getSourceSymtab()) |_, source_index| {182 for (object.in_symtab) |_, source_index| {
183 const atom = object.getAtomForSymbol(@intCast(u32, source_index)) orelse continue;183 const atom = object.getAtomForSymbol(@intCast(u32, source_index)) orelse continue;
184 if (alive.contains(atom)) continue;184 if (alive.contains(atom)) continue;
185185
186 const global = atom.getSymbolWithLoc();186 const global = atom.getSymbolWithLoc();
187 const sym = atom.getSymbolPtr(macho_file);187 const sym = atom.getSymbolPtr(macho_file);
188 const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);188 const match = sym.n_sect - 1;
189189
190 if (sym.n_desc == MachO.N_DESC_GCED) continue;190 if (sym.n_desc == MachO.N_DESC_GCED) continue;
191 if (!sym.ext() and !refersDead(atom, macho_file)) continue;191 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...@@ -232,7 +232,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
232232
233 // TODO tombstone233 // TODO tombstone
234 const atom = entry.getAtom(macho_file);234 const atom = entry.getAtom(macho_file);
235 const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);235 const match = sym.n_sect - 1;
236 removeAtomFromSection(atom, match, macho_file);236 removeAtomFromSection(atom, match, macho_file);
237 _ = try gc_sections.put(match, {});237 _ = try gc_sections.put(match, {});
238 _ = macho_file.got_entries_table.remove(entry.target);238 _ = macho_file.got_entries_table.remove(entry.target);
...@@ -244,7 +244,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac...@@ -244,7 +244,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
244244
245 // TODO tombstone245 // TODO tombstone
246 const atom = entry.getAtom(macho_file);246 const atom = entry.getAtom(macho_file);
247 const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);247 const match = sym.n_sect - 1;
248 removeAtomFromSection(atom, match, macho_file);248 removeAtomFromSection(atom, match, macho_file);
249 _ = try gc_sections.put(match, {});249 _ = try gc_sections.put(match, {});
250 _ = macho_file.stubs_table.remove(entry.target);250 _ = macho_file.stubs_table.remove(entry.target);
...@@ -256,7 +256,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac...@@ -256,7 +256,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
256256
257 // TODO tombstone257 // TODO tombstone
258 const atom = entry.getAtom(macho_file);258 const atom = entry.getAtom(macho_file);
259 const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);259 const match = sym.n_sect - 1;
260 removeAtomFromSection(atom, match, macho_file);260 removeAtomFromSection(atom, match, macho_file);
261 _ = try gc_sections.put(match, {});261 _ = try gc_sections.put(match, {});
262 _ = macho_file.tlv_ptr_entries_table.remove(entry.target);262 _ = 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...@@ -265,13 +265,13 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
265 var gc_sections_it = gc_sections.iterator();265 var gc_sections_it = gc_sections.iterator();
266 while (gc_sections_it.next()) |entry| {266 while (gc_sections_it.next()) |entry| {
267 const match = entry.key_ptr.*;267 const match = entry.key_ptr.*;
268 const sect = macho_file.getSectionPtr(match);268 var section = macho_file.sections.get(match);
269 if (sect.size == 0) continue; // Pruning happens automatically in next step.269 if (section.header.size == 0) continue; // Pruning happens automatically in next step.
270270
271 sect.@"align" = 0;271 section.header.@"align" = 0;
272 sect.size = 0;272 section.header.size = 0;
273273
274 var atom = macho_file.atoms.get(match).?;274 var atom = section.last_atom.?;
275275
276 while (atom.prev) |prev| {276 while (atom.prev) |prev| {
277 atom = prev;277 atom = prev;
...@@ -279,14 +279,16 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac...@@ -279,14 +279,16 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
279279
280 while (true) {280 while (true) {
281 const atom_alignment = try math.powi(u32, 2, atom.alignment);281 const atom_alignment = try math.powi(u32, 2, atom.alignment);
282 const aligned_end_addr = mem.alignForwardGeneric(u64, sect.size, atom_alignment);282 const aligned_end_addr = mem.alignForwardGeneric(u64, section.header.size, atom_alignment);
283 const padding = aligned_end_addr - sect.size;283 const padding = aligned_end_addr - section.header.size;
284 sect.size += padding + atom.size;284 section.header.size += padding + atom.size;
285 sect.@"align" = @maximum(sect.@"align", atom.alignment);285 section.header.@"align" = @maximum(section.header.@"align", atom.alignment);
286286
287 if (atom.next) |next| {287 if (atom.next) |next| {
288 atom = next;288 atom = next;
289 } else break;289 } else break;
290 }290 }
291
292 macho_file.sections.set(match, section);
291 }293 }
292}294}
src/link/MachO/fat.zig+3-1
...@@ -46,7 +46,9 @@ pub fn getLibraryOffset(reader: anytype, cpu_arch: std.Target.Cpu.Arch) !u64 {...@@ -46,7 +46,9 @@ pub fn getLibraryOffset(reader: anytype, cpu_arch: std.Target.Cpu.Arch) !u64 {
46 return fat_arch.offset;46 return fat_arch.offset;
47 }47 }
48 } else {48 } 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 });
50 return error.MismatchedCpuArchitecture;52 return error.MismatchedCpuArchitecture;
51 }53 }
52}54}
test/behavior/bitcast.zig-16
...@@ -90,22 +90,6 @@ test "nested bitcast" {...@@ -90,22 +90,6 @@ test "nested bitcast" {
90 comptime try S.foo(42);90 comptime try S.foo(42);
91}91}
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
109// issue #3010: compiler segfault93// issue #3010: compiler segfault
110test "bitcast literal [4]u8 param to u32" {94test "bitcast literal [4]u8 param to u32" {
111 const ip = @bitCast(u32, [_]u8{ 255, 255, 255, 255 });95 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;...@@ -6,6 +6,8 @@ const expectEqual = std.testing.expectEqual;
6const native_endian = builtin.cpu.arch.endian();6const native_endian = builtin.cpu.arch.endian();
77
8test "correct size of packed structs" {8test "correct size of packed structs" {
9 // Stage2 has different packed struct semantics.
10 if (builtin.zig_backend != .stage1) return error.SkipZigTest;
9 const T1 = packed struct { one: u8, three: [3]u8 };11 const T1 = packed struct { one: u8, three: [3]u8 };
1012
11 try expectEqual(4, @sizeOf(T1));13 try expectEqual(4, @sizeOf(T1));
...@@ -118,18 +120,6 @@ test "flags in packed structs" {...@@ -118,18 +120,6 @@ test "flags in packed structs" {
118 try expectEqual(32, @bitSizeOf(Flags3));120 try expectEqual(32, @bitSizeOf(Flags3));
119}121}
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
133test "consistent size of packed structs" {123test "consistent size of packed structs" {
134 if (builtin.zig_backend == .stage1) return error.SkipZigTest;124 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
135125
...@@ -145,23 +135,15 @@ test "consistent size of packed structs" {...@@ -145,23 +135,15 @@ test "consistent size of packed structs" {
145 try expectEqual(register_size_bits, @bitSizeOf(TxData2));135 try expectEqual(register_size_bits, @bitSizeOf(TxData2));
146 try expectEqual(register_size_bytes, @sizeOf(TxData2));136 try expectEqual(register_size_bytes, @sizeOf(TxData2));
147137
148 const TxData3 = packed struct { a: u32, b: [3]u8 };
149 const TxData4 = packed struct { a: u32, b: u24 };138 const TxData4 = packed struct { a: u32, b: u24 };
150 const TxData5 = packed struct { a: [3]u8, b: u32 };
151 const TxData6 = packed struct { a: u24, b: u32 };139 const TxData6 = packed struct { a: u24, b: u32 };
152140
153 const expectedBitSize = 56;141 const expectedBitSize = 56;
154 const expectedByteSize = @sizeOf(u56);142 const expectedByteSize = @sizeOf(u56);
155143
156 try expectEqual(expectedBitSize, @bitSizeOf(TxData3));
157 try expectEqual(expectedByteSize, @sizeOf(TxData3));
158
159 try expectEqual(expectedBitSize, @bitSizeOf(TxData4));144 try expectEqual(expectedBitSize, @bitSizeOf(TxData4));
160 try expectEqual(expectedByteSize, @sizeOf(TxData4));145 try expectEqual(expectedByteSize, @sizeOf(TxData4));
161146
162 try expectEqual(expectedBitSize, @bitSizeOf(TxData5));
163 try expectEqual(expectedByteSize, @sizeOf(TxData5));
164
165 try expectEqual(expectedBitSize, @bitSizeOf(TxData6));147 try expectEqual(expectedBitSize, @bitSizeOf(TxData6));
166 try expectEqual(expectedByteSize, @sizeOf(TxData6));148 try expectEqual(expectedByteSize, @sizeOf(TxData6));
167}149}
...@@ -234,12 +216,6 @@ test "correct sizeOf and offsets in packed structs" {...@@ -234,12 +216,6 @@ test "correct sizeOf and offsets in packed structs" {
234 try expectEqual(@as(u7, 0b1111010), s2.y);216 try expectEqual(@as(u7, 0b1111010), s2.y);
235 try expectEqual(@as(u24, 0xd5c71f), s2.z);217 try expectEqual(@as(u24, 0xd5c71f), s2.z);
236 }218 }
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));
243}219}
244220
245test "nested packed structs" {221test "nested packed structs" {
test/behavior/sizeof_and_typeof.zig+2
...@@ -105,6 +105,8 @@ test "@offsetOf" {...@@ -105,6 +105,8 @@ test "@offsetOf" {
105}105}
106106
107test "@offsetOf packed struct, array length not power of 2 or multiple of native pointer width in bytes" {107test "@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;
108 const p3a_len = 3;110 const p3a_len = 3;
109 const P3 = packed struct {111 const P3 = packed struct {
110 a: [p3a_len]u8,112 a: [p3a_len]u8,
test/behavior/struct.zig+2-4
...@@ -704,10 +704,8 @@ const FooArray24Bits = packed struct {...@@ -704,10 +704,8 @@ const FooArray24Bits = packed struct {
704};704};
705705
706test "aligned array of packed struct" {706test "aligned array of packed struct" {
707 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO707 // Stage2 has different packed struct semantics.
708 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO708 if (builtin.zig_backend != .stage1) return error.SkipZigTest;
709 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
710 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
711709
712 comptime {710 comptime {
713 try expect(@sizeOf(FooStructAligned) == 2);711 try expect(@sizeOf(FooStructAligned) == 2);
test/behavior/type.zig+40-17
...@@ -247,26 +247,17 @@ fn add(a: i32, b: i32) i32 {...@@ -247,26 +247,17 @@ fn add(a: i32, b: i32) i32 {
247}247}
248248
249test "Type.ErrorSet" {249test "Type.ErrorSet" {
250 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
251 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
252
250 try testing.expect(@Type(.{ .ErrorSet = null }) == anyerror);253 try testing.expect(@Type(.{ .ErrorSet = null }) == anyerror);
251254
252 // error sets don't compare equal so just check if they compile255 // error sets don't compare equal so just check if they compile
253 _ = @Type(@typeInfo(error{}));256 inline for (.{ error{}, error{A}, error{ A, B, C } }) |T| {
254 _ = @Type(@typeInfo(error{A}));257 const info = @typeInfo(T);
255 _ = @Type(@typeInfo(error{ A, B, C }));258 const T2 = @Type(info);
256 _ = @Type(.{259 try testing.expect(T == T2);
257 .ErrorSet = &[_]Type.Error{260 }
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 });
270}261}
271262
272test "Type.Struct" {263test "Type.Struct" {
...@@ -517,3 +508,35 @@ test "Type.Union from regular enum" {...@@ -517,3 +508,35 @@ test "Type.Union from regular enum" {
517 _ = T;508 _ = T;
518 _ = @typeInfo(T).Union;509 _ = @typeInfo(T).Union;
519}510}
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 {...@@ -9,4 +9,4 @@ export fn entry() void {
9// target=native9// target=native
10//10//
11// :3:24: error: cannot @bitCast to 'tmp.entry.E'11// :3:24: error: cannot @bitCast to 'tmp.entry.E'
12// :3:24: note: use @intToEnum for type coercion12// :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 {...@@ -31,5 +31,5 @@ export fn entry() void {
31// backend=stage231// backend=stage2
32// target=native32// target=native
33//33//
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'
35// :1:13: note: enum declared here35// :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 {...@@ -7,5 +7,5 @@ export fn entry() foo {
7// backend=stage27// backend=stage2
8// target=native8// target=native
9//9//
10// :1:1: error: variable of type 'type' must be const or comptime10// :1:5: error: variable of type 'type' must be const or comptime
11// :1:1: note: types are not available at runtime11// :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 {...@@ -8,5 +8,5 @@ export fn entry() void {
8// backend=stage28// backend=stage2
9// target=native9// target=native
10//10//
11// :1:1: error: variable of type 'comptime_int' must be const or comptime11// :1:5: 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 type12// :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;...@@ -4,4 +4,4 @@ usingnamespace void;
4// backend=stage24// backend=stage2
5// target=native5// target=native
6//6//
7// :1:1: error: type void has no namespace7// :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");...@@ -2,17 +2,18 @@ const std = @import("std");
22
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
4 _ = stack_trace;4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "sentinel mismatch")) {5 if (std.mem.eql(u8, message, "sentinel mismatch: expected 0, found 4")) {
6 std.process.exit(0);6 std.process.exit(0);
7 }7 }
8 std.process.exit(1);8 std.process.exit(1);
9}9}
10
10pub fn main() !void {11pub fn main() !void {
11 var buf: [4]u8 = undefined;12 var buf: [4]u8 = .{ 1, 2, 3, 4 };
12 const slice = buf[0..3 :0];13 const slice = buf[0..3 :0];
13 _ = slice;14 _ = slice;
14 return error.TestFailed;15 return error.TestFailed;
15}16}
16// run17// run
17// backend=stage118// backend=llvm
18// target=native19// target=native