authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-08-14 11:59:59+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-08-16 10:29:59+02:00
log31500242f0c82b8d3b5e1d7a683a8a6f4c803bb2
tree83d4706de1f7910d1bf022f5f7a8d5b3434e522f
parente25015bdce1e6ff4ab594a34fc7d1f0cd296dc25

link: rework export API

The old API fundamentally did not make sense, because the frontend does not track exports incrementally: it only knows the set of referenced exports at the end of each incremental update. Attempting to represent this as an incremental add/remove-style API was just a lie. It was also buggy; `deleteExport` was called from the main thread, leading to races in incremental compilation if `deleteExport` was actually implemented! We also exposed data in an unnecessarily complicated way which did not benefit most linkers. The only one which actually cared about knowing all exports for a given nav/uav upfront was the C linker (which here I have written to reconstruct that data), and even it can probably be refactored at some point to avoid that. So, replace the export API with a simpler one, where all the frontend does is tell the linker about every `Zcu.Export.Index` which is alive, just before calling `flush`. In theory this information could actually be passed into `flush`, but using a separate function made for a simpler implementation in practice. Resolves: https://github.com/ziglang/zig/issues/23616

16 files changed, 533 insertions(+), 767 deletions(-)

src/Sema.zig-2
...@@ -5674,7 +5674,6 @@ fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void...@@ -5674,7 +5674,6 @@ fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
5674 .opts = options,5674 .opts = options,
5675 .src = src,5675 .src = src,
5676 .exported = target,5676 .exported = target,
5677 .status = .in_progress,
5678 });5677 });
5679}5678}
56805679
...@@ -5722,7 +5721,6 @@ pub fn analyzeExportSelfNav(...@@ -5722,7 +5721,6 @@ pub fn analyzeExportSelfNav(
5722 .opts = .{ .name = name },5721 .opts = .{ .name = name },
5723 .src = src,5722 .src = src,
5724 .exported = .{ .nav = export_nav },5723 .exported = .{ .nav = export_nav },
5725 .status = .in_progress,
5726 });5724 });
5727}5725}
57285726
src/Zcu.zig+1-33
...@@ -757,14 +757,6 @@ pub const Export = struct {...@@ -757,14 +757,6 @@ pub const Export = struct {
757 opts: Options,757 opts: Options,
758 src: LazySrcLoc,758 src: LazySrcLoc,
759 exported: Exported,759 exported: Exported,
760 status: enum {
761 in_progress,
762 failed,
763 /// Indicates that the failure was due to a temporary issue, such as an I/O error
764 /// when writing to the output file. Retrying the export may succeed.
765 failed_retryable,
766 complete,
767 },
768760
769 pub const Options = struct {761 pub const Options = struct {
770 name: InternPool.NullTerminatedString,762 name: InternPool.NullTerminatedString,
...@@ -3789,13 +3781,8 @@ pub fn resetUnit(zcu: *Zcu, unit: AnalUnit) void {...@@ -3789,13 +3781,8 @@ pub fn resetUnit(zcu: *Zcu, unit: AnalUnit) void {
3789 }3781 }
3790 break :exports;3782 break :exports;
3791 };3783 };
3792 for (zcu.all_exports.items[base..][0..len], base..) |exp, exp_index_usize| {3784 for (base..base + len) |exp_index_usize| {
3793 const exp_index: Export.Index = @fromBackingInt(@intCast(exp_index_usize));3785 const exp_index: Export.Index = @fromBackingInt(@intCast(exp_index_usize));
3794 if (zcu.llvm_object) |llvm_object| {
3795 _ = llvm_object; // TODO: delete exports from LLVM
3796 } else if (zcu.comp.bin_file) |lf| {
3797 lf.deleteExport(exp.exported, exp.opts.name);
3798 }
3799 if (zcu.failed_exports.fetchSwapRemove(exp_index)) |failed_kv| {3786 if (zcu.failed_exports.fetchSwapRemove(exp_index)) |failed_kv| {
3800 failed_kv.value.destroy(gpa);3787 failed_kv.value.destroy(gpa);
3801 }3788 }
...@@ -3966,25 +3953,6 @@ pub fn getTarget(zcu: *const Zcu) *const Target {...@@ -3966,25 +3953,6 @@ pub fn getTarget(zcu: *const Zcu) *const Target {
3966 return &zcu.root_mod.resolved_target.result;3953 return &zcu.root_mod.resolved_target.result;
3967}3954}
39683955
3969pub fn handleUpdateExports(
3970 zcu: *Zcu,
3971 export_indices: []const Export.Index,
3972 result: link.Error!void,
3973) (Allocator.Error || Io.Cancelable)!void {
3974 const gpa = zcu.gpa;
3975 result catch |err| switch (err) {
3976 else => |e| return e,
3977 error.AlreadyReported => {
3978 const export_idx = export_indices[0];
3979 const new_export = export_idx.ptr(zcu);
3980 new_export.status = .failed_retryable;
3981 try zcu.failed_exports.ensureUnusedCapacity(gpa, 1);
3982 const msg = try ErrorMsg.create(gpa, new_export.src, "unable to export: {s}", .{@errorName(err)});
3983 zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, msg);
3984 },
3985 };
3986}
3987
3988pub fn addGlobalAssembly(zcu: *Zcu, unit: AnalUnit, source: []const u8) !void {3956pub fn addGlobalAssembly(zcu: *Zcu, unit: AnalUnit, source: []const u8) !void {
3989 const gpa = zcu.gpa;3957 const gpa = zcu.gpa;
3990 const gop = try zcu.global_assembly.getOrPut(gpa, unit);3958 const gop = try zcu.global_assembly.getOrPut(gpa, unit);
src/Zcu/PerThread.zig+49-146
...@@ -3554,7 +3554,7 @@ fn lockAndClearFileCompileError(pt: Zcu.PerThread, file_index: Zcu.File.Index, f...@@ -3554,7 +3554,7 @@ fn lockAndClearFileCompileError(pt: Zcu.PerThread, file_index: Zcu.File.Index, f
3554/// Called from `Compilation.update`, after everything is done, just before3554/// Called from `Compilation.update`, after everything is done, just before
3555/// reporting compile errors. In this function we emit exported symbol collision3555/// reporting compile errors. In this function we emit exported symbol collision
3556/// errors and communicate exported symbols to the linker backend.3556/// errors and communicate exported symbols to the linker backend.
3557pub fn processExports(pt: Zcu.PerThread) !void {3557pub fn processExports(pt: Zcu.PerThread) (Allocator.Error || Io.Cancelable)!void {
3558 const zcu = pt.zcu;3558 const zcu = pt.zcu;
3559 const gpa = zcu.gpa;3559 const gpa = zcu.gpa;
35603560
...@@ -3563,169 +3563,72 @@ pub fn processExports(pt: Zcu.PerThread) !void {...@@ -3563,169 +3563,72 @@ pub fn processExports(pt: Zcu.PerThread) !void {
3563 return;3563 return;
3564 }3564 }
35653565
3566 // First, construct a mapping of every exported value and Nav to the indices of all its different exports.3566 var alive_exports: std.ArrayList(Zcu.Export.Index) = .empty;
3567 var nav_exports: std.array_hash_map.Auto(InternPool.Nav.Index, std.ArrayList(Zcu.Export.Index)) = .empty;3567 defer alive_exports.deinit(gpa);
3568 var uav_exports: std.array_hash_map.Auto(InternPool.Index, std.ArrayList(Zcu.Export.Index)) = .empty;
3569 defer {
3570 for (nav_exports.values()) |*exports| {
3571 exports.deinit(gpa);
3572 }
3573 nav_exports.deinit(gpa);
3574 for (uav_exports.values()) |*exports| {
3575 exports.deinit(gpa);
3576 }
3577 uav_exports.deinit(gpa);
3578 }
3579
3580 // We note as a heuristic:
3581 // * It is rare to export a value.
3582 // * It is rare for one Nav to be exported multiple times.
3583 // So, this ensureTotalCapacity serves as a reasonable (albeit very approximate) optimization.
3584 try nav_exports.ensureTotalCapacity(gpa, zcu.single_exports.count() + zcu.multi_exports.count());
35853568
3586 const unit_references = try zcu.resolveReferences();3569 const unit_references = try zcu.resolveReferences();
35873570
3571 try alive_exports.ensureUnusedCapacity(gpa, zcu.single_exports.count());
3588 for (zcu.single_exports.keys(), zcu.single_exports.values()) |exporter, export_idx| {3572 for (zcu.single_exports.keys(), zcu.single_exports.values()) |exporter, export_idx| {
3589 const exp = export_idx.ptr(zcu);3573 if (!unit_references.contains(exporter)) continue;
3590 if (!unit_references.contains(exporter)) {3574 alive_exports.appendAssumeCapacity(export_idx);
3591 // This export might already have been sent to the linker on a previous update, in which case we need to delete it.
3592 // The linker export API should be modified to eliminate this call. #23616
3593 if (zcu.comp.bin_file) |lf| {
3594 if (zcu.llvm_object == null) {
3595 lf.deleteExport(exp.exported, exp.opts.name);
3596 }
3597 }
3598 continue;
3599 }
3600 const value_ptr, const found_existing = switch (exp.exported) {
3601 .nav => |nav| gop: {
3602 const gop = try nav_exports.getOrPut(gpa, nav);
3603 break :gop .{ gop.value_ptr, gop.found_existing };
3604 },
3605 .uav => |uav| gop: {
3606 const gop = try uav_exports.getOrPut(gpa, uav);
3607 break :gop .{ gop.value_ptr, gop.found_existing };
3608 },
3609 };
3610 if (!found_existing) value_ptr.* = .empty;
3611 try value_ptr.append(gpa, export_idx);
3612 }3575 }
36133576
3614 for (zcu.multi_exports.keys(), zcu.multi_exports.values()) |exporter, info| {3577 for (zcu.multi_exports.keys(), zcu.multi_exports.values()) |exporter, info| {
3615 const exports = zcu.all_exports.items[info.index..][0..info.len];3578 if (!unit_references.contains(exporter)) continue;
3616 if (!unit_references.contains(exporter)) {3579 try alive_exports.ensureUnusedCapacity(gpa, info.len);
3617 // This export might already have been sent to the linker on a previous update, in which case we need to delete it.3580 for (0..info.len) |off| {
3618 // The linker export API should be modified to eliminate this loop. #236163581 const export_idx: Zcu.Export.Index = @fromBackingInt(@intCast(info.index + off));
3619 if (zcu.comp.bin_file) |lf| {3582 alive_exports.appendAssumeCapacity(export_idx);
3620 if (zcu.llvm_object == null) {
3621 for (exports) |exp| {
3622 lf.deleteExport(exp.exported, exp.opts.name);
3623 }
3624 }
3625 }
3626 continue;
3627 }3583 }
3628 for (exports, info.index..) |exp, export_idx| {
3629 const value_ptr, const found_existing = switch (exp.exported) {
3630 .nav => |nav| gop: {
3631 const gop = try nav_exports.getOrPut(gpa, nav);
3632 break :gop .{ gop.value_ptr, gop.found_existing };
3633 },
3634 .uav => |uav| gop: {
3635 const gop = try uav_exports.getOrPut(gpa, uav);
3636 break :gop .{ gop.value_ptr, gop.found_existing };
3637 },
3638 };
3639 if (!found_existing) value_ptr.* = .empty;
3640 try value_ptr.append(gpa, @fromBackingInt(@intCast(export_idx)));
3641 }
3642 }
3643
3644 // If there are compile errors, we won't call `updateExports`. Not only would it be redundant
3645 // work, but the linker may not have seen an exported `Nav` due to a compile error, so linker
3646 // implementations would have to handle that case. This early return avoids that.
3647 const skip_linker_work = zcu.comp.anyErrors();
3648
3649 // Map symbol names to `Export` for name collision detection.
3650 var symbol_exports: SymbolExports = .{};
3651 defer symbol_exports.deinit(gpa);
3652
3653 for (nav_exports.keys(), nav_exports.values()) |exported_nav, exports_list| {
3654 const exported: Zcu.Exported = .{ .nav = exported_nav };
3655 try pt.processExportsInner(&symbol_exports, exported, exports_list.items, skip_linker_work);
3656 }
3657
3658 for (uav_exports.keys(), uav_exports.values()) |exported_uav, exports_list| {
3659 const exported: Zcu.Exported = .{ .uav = exported_uav };
3660 try pt.processExportsInner(&symbol_exports, exported, exports_list.items, skip_linker_work);
3661 }3584 }
3662}
36633585
3664const SymbolExports = std.array_hash_map.Auto(InternPool.NullTerminatedString, Zcu.Export.Index);3586 // Detect export name collisions
3587 {
3588 var exports_by_name: std.array_hash_map.Auto(
3589 InternPool.NullTerminatedString,
3590 Zcu.Export.Index,
3591 ) = .empty;
3592 defer exports_by_name.deinit(gpa);
36653593
3666fn processExportsInner(3594 try exports_by_name.ensureUnusedCapacity(gpa, alive_exports.items.len);
3667 pt: Zcu.PerThread,
3668 symbol_exports: *SymbolExports,
3669 exported: Zcu.Exported,
3670 export_indices: []const Zcu.Export.Index,
3671 skip_linker_work: bool,
3672) error{ OutOfMemory, Canceled }!void {
3673 const zcu = pt.zcu;
3674 const gpa = zcu.gpa;
3675 const ip = &zcu.intern_pool;
36763595
3677 for (export_indices) |export_idx| {3596 for (alive_exports.items) |export_index| {
3678 const new_export = export_idx.ptr(zcu);3597 const exp = export_index.ptr(zcu);
3679 const gop = try symbol_exports.getOrPut(gpa, new_export.opts.name);3598 const gop = exports_by_name.getOrPutAssumeCapacity(exp.opts.name);
3680 if (gop.found_existing) {3599 if (gop.found_existing) {
3681 new_export.status = .failed_retryable;3600 const existing_exp = gop.value_ptr.*.ptr(zcu);
3682 try zcu.failed_exports.ensureUnusedCapacity(gpa, 1);3601 try zcu.failed_exports.ensureUnusedCapacity(gpa, 1);
3683 const msg = try Zcu.ErrorMsg.create(gpa, new_export.src, "exported symbol collision: {f}", .{3602 const msg = try Zcu.ErrorMsg.create(
3684 new_export.opts.name.fmt(ip),3603 gpa,
3685 });3604 exp.src,
3686 errdefer msg.destroy(gpa);3605 "exported symbol collision: {f}",
3687 const other_export = gop.value_ptr.ptr(zcu);3606 .{exp.opts.name.fmt(&zcu.intern_pool)},
3688 try zcu.errNote(other_export.src, msg, "other symbol here", .{});3607 );
3689 zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, msg);3608 errdefer msg.destroy(gpa);
3690 new_export.status = .failed;3609 try zcu.errNote(existing_exp.src, msg, "other symbol here", .{});
3691 } else {3610 zcu.failed_exports.putAssumeCapacityNoClobber(export_index, msg);
3692 gop.value_ptr.* = export_idx;3611 } else {
3693 }3612 gop.value_ptr.* = export_index;
3694 }
3695
3696 switch (exported) {
3697 .nav => |nav_index| if (failed: {
3698 const nav = ip.getNav(nav_index);
3699 if (zcu.failed_codegen.contains(nav_index)) break :failed true;
3700 if (nav.analysis != null) {
3701 const unit: AnalUnit = .wrap(.{ .nav_val = nav_index });
3702 if (zcu.failed_analysis.contains(unit)) break :failed true;
3703 if (zcu.transitive_failed_analysis.contains(unit)) break :failed true;
3704 }
3705 const val: Value = switch ((nav.resolved orelse break :failed true).value) {
3706 .none => break :failed true,
3707 else => |val| .fromInterned(val),
3708 };
3709 // If the value is a function, we also need to check if that function succeeded analysis.
3710 if (val.typeOf(zcu).zigTypeTag(zcu) == .@"fn") {
3711 const func_unit = AnalUnit.wrap(.{ .func = val.toIntern() });
3712 if (zcu.failed_analysis.contains(func_unit)) break :failed true;
3713 if (zcu.transitive_failed_analysis.contains(func_unit)) break :failed true;
3714 }3613 }
3715 break :failed false;3614 }
3716 }) {
3717 // This `Nav` is failed, so was never sent to codegen. There should be a compile error.
3718 assert(skip_linker_work);
3719 },
3720 .uav => {},
3721 }3615 }
37223616
3723 if (skip_linker_work) return;3617 // If there are compile errors, we won't call `updateExports`. Not only would it be redundant
3618 // work, but the linker may not have seen an exported `Nav` due to a compile error, so linker
3619 // implementations would have to handle that case. This early return avoids that.
3620 if (zcu.comp.anyErrors()) return;
37243621
3725 if (zcu.llvm_object) |llvm_object| {3622 if (zcu.llvm_object) |llvm_object| {
3726 try zcu.handleUpdateExports(export_indices, llvm_object.updateExports(exported, export_indices));3623 llvm_object.updateExports(alive_exports.items) catch |err| switch (err) {
3624 else => |e| return e,
3625 error.AlreadyReported => {},
3626 };
3727 } else if (zcu.comp.bin_file) |lf| {3627 } else if (zcu.comp.bin_file) |lf| {
3728 try zcu.handleUpdateExports(export_indices, lf.updateExports(pt, exported, export_indices));3628 lf.updateExports(pt, alive_exports.items) catch |err| switch (err) {
3629 else => |e| return e,
3630 error.AlreadyReported => {},
3631 };
3729 }3632 }
3730}3633}
37313634
src/codegen/llvm.zig+104-109
...@@ -1441,43 +1441,46 @@ pub const Object = struct {...@@ -1441,43 +1441,46 @@ pub const Object = struct {
14411441
1442 pub fn updateExports(1442 pub fn updateExports(
1443 o: *Object,1443 o: *Object,
1444 exported: Zcu.Exported,
1445 export_indices: []const Zcu.Export.Index,1444 export_indices: []const Zcu.Export.Index,
1446 ) link.Error!void {1445 ) link.Error!void {
1447 const zcu = o.zcu;1446 const zcu = o.zcu;
1448 const ip = &zcu.intern_pool;1447 const ip = &zcu.intern_pool;
1449 const ty: Type, const llvm_ptr: Builder.Constant = switch (exported) {1448 for (export_indices) |export_index| {
1450 .nav => |nav| exp: {1449 const ty: Type, const llvm_ptr: Builder.Constant = switch (export_index.ptr(zcu).exported) {
1451 const nav_ty: Type = .fromInterned(ip.getNav(nav).resolved.?.type);1450 .nav => |nav| exp: {
1452 const nav_ref = try o.lowerNavRef(nav);1451 const nav_ty: Type = .fromInterned(ip.getNav(nav).resolved.?.type);
1453 break :exp .{ nav_ty, nav_ref };1452 const nav_ref = try o.lowerNavRef(nav);
1454 },1453 break :exp .{ nav_ty, nav_ref };
1455 .uav => |uav| exp: {1454 },
1456 const uav_ty = Value.fromInterned(uav).typeOf(zcu);1455 .uav => |uav| exp: {
1457 const uav_ref = try o.lowerUavRef(1456 const uav_ty = Value.fromInterned(uav).typeOf(zcu);
1458 uav,1457 const uav_ref = try o.lowerUavRef(
1459 uav_ty.abiAlignment(zcu).toLlvm(),1458 uav,
1460 target_util.defaultAddressSpace(zcu.getTarget(), .global_constant),1459 uav_ty.abiAlignment(zcu).toLlvm(),
1461 );1460 target_util.defaultAddressSpace(zcu.getTarget(), .global_constant),
1462 break :exp .{ uav_ty, uav_ref };1461 );
1463 },1462 break :exp .{ uav_ty, uav_ref };
1464 };1463 },
1465 switch (llvm_ptr.unwrap()) {1464 };
1466 .global => |global| return o.updateExportedGlobal(global, ty, export_indices),1465 switch (llvm_ptr.unwrap()) {
1467 .constant => @panic("LLVM TODO: export zero-bit value"),1466 .global => |global| try o.addGlobalExport(global, ty, export_index),
1467 .constant => @panic("LLVM TODO: export zero-bit value"),
1468 }
1468 }1469 }
1469 }1470 }
14701471
1471 fn updateExportedGlobal(1472 fn addGlobalExport(
1472 o: *Object,1473 o: *Object,
1473 llvm_global: Builder.Global.Index,1474 llvm_global: Builder.Global.Index,
1474 ty: Type,1475 ty: Type,
1475 export_indices: []const Zcu.Export.Index,1476 export_index: Zcu.Export.Index,
1476 ) link.Error!void {1477 ) link.Error!void {
1477 const zcu = o.zcu;1478 const zcu = o.zcu;
1478 const comp = zcu.comp;1479 const comp = zcu.comp;
1479 const ip = &zcu.intern_pool;1480 const ip = &zcu.intern_pool;
14801481
1482 const exp = export_index.ptr(zcu);
1483
1481 // If we're on COFF and linking with LLD, the linker cares about our exports to determine the subsystem in use.1484 // If we're on COFF and linking with LLD, the linker cares about our exports to determine the subsystem in use.
1482 coff_export_flags: {1485 coff_export_flags: {
1483 const lf = comp.bin_file orelse break :coff_export_flags;1486 const lf = comp.bin_file orelse break :coff_export_flags;
...@@ -1488,23 +1491,20 @@ pub const Object = struct {...@@ -1488,23 +1491,20 @@ pub const Object = struct {
1488 };1491 };
1489 if (ty.zigTypeTag(zcu) != .@"fn") break :coff_export_flags;1492 if (ty.zigTypeTag(zcu) != .@"fn") break :coff_export_flags;
1490 const flags = &coff.lld_export_flags;1493 const flags = &coff.lld_export_flags;
1491 for (export_indices) |export_index| {1494 if (exp.opts.name.eqlSlice("main", ip)) flags.c_main = true;
1492 const name = export_index.ptr(zcu).opts.name;1495 if (exp.opts.name.eqlSlice("WinMain", ip)) flags.winmain = true;
1493 if (name.eqlSlice("main", ip)) flags.c_main = true;1496 if (exp.opts.name.eqlSlice("wWinMain", ip)) flags.wwinmain = true;
1494 if (name.eqlSlice("WinMain", ip)) flags.winmain = true;1497 if (exp.opts.name.eqlSlice("WinMainCRTStartup", ip)) flags.winmain_crt_startup = true;
1495 if (name.eqlSlice("wWinMain", ip)) flags.wwinmain = true;1498 if (exp.opts.name.eqlSlice("wWinMainCRTStartup", ip)) flags.wwinmain_crt_startup = true;
1496 if (name.eqlSlice("WinMainCRTStartup", ip)) flags.winmain_crt_startup = true;1499 if (exp.opts.name.eqlSlice("DllMainCRTStartup", ip)) flags.dllmain_crt_startup = true;
1497 if (name.eqlSlice("wWinMainCRTStartup", ip)) flags.wwinmain_crt_startup = true;1500 if (exp.opts.name.eqlSlice("_DllMainCRTStartup", ip)) flags.dllmain_crt_startup = true;
1498 if (name.eqlSlice("DllMainCRTStartup", ip)) flags.dllmain_crt_startup = true;
1499 if (name.eqlSlice("_DllMainCRTStartup", ip)) flags.dllmain_crt_startup = true;
1500 }
1501 }1501 }
15021502
1503 // If the first export specifies a linksection, set the exported variable's section to that1503 // If the export specifies a linksection, set the exported variable's section to that one.
1504 // one. This is kind of a hack because `std.lang.ExportOptions.section` doesn't actually1504 // This is kind of a hack because `std.lang.ExportOptions.section` doesn't actually make
1505 // make much sense: the linksection should be associated with the declaration itself rather1505 // much sense: the linksection should be associated with the declaration itself rather than
1506 // than some particular symbol it is exported as!1506 // some particular symbol it is exported as!
1507 if (export_indices[0].ptr(zcu).opts.section.toSlice(ip)) |section_slice| {1507 if (exp.opts.section.toSlice(ip)) |section_slice| {
1508 const variable = &llvm_global.ptrConst(&o.builder).kind.variable;1508 const variable = &llvm_global.ptrConst(&o.builder).kind.variable;
1509 variable.setSection(try o.builder.string(section_slice), &o.builder);1509 variable.setSection(try o.builder.string(section_slice), &o.builder);
1510 }1510 }
...@@ -1519,29 +1519,54 @@ pub const Object = struct {...@@ -1519,29 +1519,54 @@ pub const Object = struct {
1519 // TODO: we currently do not delete old exports. To do that we'll need to track which1519 // TODO: we currently do not delete old exports. To do that we'll need to track which
1520 // globals actually *are* exports.1520 // globals actually *are* exports.
15211521
1522 for (export_indices, 0..) |export_idx, export_i| {1522 const exp_name = try o.builder.strtabString(exp.opts.name.toSlice(ip));
1523 const exp = export_idx.ptr(zcu);
1524 const exp_name = try o.builder.strtabString(exp.opts.name.toSlice(ip));
15251523
1526 // Our goal is to make an alias with the name `exp_name`, but if that name is already1524 // Our goal is to make an alias with the name `exp_name`, but if that name is already
1527 // taken by some existing global, we need to figure out what to do with that existing1525 // taken by some existing global, we need to figure out what to do with that existing
1528 // global.1526 // global.
1529 //1527 //
1530 // The name, aliasee, and type will be set within this block. Other properties of the1528 // The name, aliasee, and type will be set within this block. Other properties of the
1531 // alias will be set below.1529 // alias will be set below.
1532 const alias_global: Builder.Global.Index = global: {1530 const alias_global: Builder.Global.Index = global: {
15331531
1534 // WORKAROUND (see https://github.com/llvm/llvm-project/issues/213504, https://github.com/llvm/llvm-project/issues/214835)1532 // WORKAROUND (see https://github.com/llvm/llvm-project/issues/213504, https://github.com/llvm/llvm-project/issues/214835)
1535 // For NVPTX, LLVM throws "NVPTX aliasee must be a non-kernel function definition" if we try to alias a kernel1533 // For NVPTX, LLVM throws "NVPTX aliasee must be a non-kernel function definition" if we try to alias a kernel
1536 // On AMDGCN, LLVM does not generate an alias for the kernel descriptor symbol on associated functions1534 // On AMDGCN, LLVM does not generate an alias for the kernel descriptor symbol on associated functions
1537 // To solve these, we rename the global1535 // To solve these, we rename the global
1538 if (workaround_alias_bugs and export_i == 0) {1536 if (workaround_alias_bugs) {
1539 try llvm_global.rename(exp_name, &o.builder);1537 try llvm_global.rename(exp_name, &o.builder);
1540 break :global llvm_global;1538 break :global llvm_global;
1541 }1539 }
15421540
1543 const existing_global = o.builder.getGlobal(exp_name) orelse {1541 const existing_global = o.builder.getGlobal(exp_name) orelse {
1544 // There is no existing global with this name, so make a new alias.1542 // There is no existing global with this name, so make a new alias.
1543 const alias = try o.builder.addAlias(
1544 exp_name,
1545 llvm_global_ty,
1546 llvm_global.ptrConst(&o.builder).addr_space,
1547 llvm_global.toConst(),
1548 );
1549 break :global alias.ptrConst(&o.builder).global;
1550 };
1551 // There is an existing global with this name, so we can't just create an alias. We
1552 // need to figure out what to do with the existing global instead.
1553 switch (existing_global.ptrConst(&o.builder).kind) {
1554 .alias => |alias| {
1555 // We can just repurpose the existing alias.
1556 alias.setAliasee(llvm_global.toConst(), &o.builder);
1557 alias.ptrConst(&o.builder).global.ptr(&o.builder).type = llvm_global.typeOf(&o.builder);
1558 alias.ptrConst(&o.builder).global.ptr(&o.builder).addr_space = llvm_global.ptrConst(&o.builder).addr_space;
1559 break :global existing_global;
1560 },
1561 .variable, .function => {
1562 // This must be an extern, which is no good to us---we need an alias. The
1563 // extern should refer to the value we're exporting, so replace it with the
1564 // exported value. That will free up the name for us to create a new alias.
1565 // We need to make a new global which is an alias. Replace this existing one
1566 // with the target global, making the name available and fixing references
1567 // to this global to point to the target.
1568 try existing_global.replace(llvm_global, &o.builder);
1569 // The name is now free, so create an alias.
1545 const alias = try o.builder.addAlias(1570 const alias = try o.builder.addAlias(
1546 exp_name,1571 exp_name,
1547 llvm_global_ty,1572 llvm_global_ty,
...@@ -1549,58 +1574,28 @@ pub const Object = struct {...@@ -1549,58 +1574,28 @@ pub const Object = struct {
1549 llvm_global.toConst(),1574 llvm_global.toConst(),
1550 );1575 );
1551 break :global alias.ptrConst(&o.builder).global;1576 break :global alias.ptrConst(&o.builder).global;
1552 };1577 },
1553 // There is an existing global with this name, so we can't just create an alias. We1578 .replaced => unreachable, // a replaced global would have lost the name `exp_name`
1554 // need to figure out what to do with the existing global instead.1579 }
1555 switch (existing_global.ptrConst(&o.builder).kind) {1580 };
1556 .alias => |alias| {
1557 // We can just repurpose the existing alias.
1558 alias.setAliasee(llvm_global.toConst(), &o.builder);
1559 alias.ptrConst(&o.builder).global.ptr(&o.builder).type = llvm_global.typeOf(&o.builder);
1560 alias.ptrConst(&o.builder).global.ptr(&o.builder).addr_space = llvm_global.ptrConst(&o.builder).addr_space;
1561 break :global existing_global;
1562 },
1563 .variable, .function => {
1564 // This must be an extern, which is no good to us---we need an alias. The
1565 // extern should refer to the value we're exporting, so replace it with the
1566 // exported value. That will free up the name for us to create a new alias.
1567 // We need to make a new global which is an alias. Replace this existing one
1568 // with the target global, making the name available and fixing references
1569 // to this global to point to the target.
1570 try existing_global.replace(llvm_global, &o.builder);
1571 // The name is now free, so create an alias.
1572 const alias = try o.builder.addAlias(
1573 exp_name,
1574 llvm_global_ty,
1575 llvm_global.ptrConst(&o.builder).addr_space,
1576 llvm_global.toConst(),
1577 );
1578 break :global alias.ptrConst(&o.builder).global;
1579 },
1580 .replaced => unreachable, // a replaced global would have lost the name `exp_name`
1581 }
1582 };
1583
1584 // Now for a bit of setup which
1585
1586 // We need the alias to *not* be `unnamed_addr` to ensure that the alias address equals
1587 // the address of the original global.
1588 alias_global.setUnnamedAddr(.default, &o.builder);
15891581
1590 if (comp.config.dll_export_fns and exp.opts.visibility != .hidden)1582 // We need the alias to *not* be `unnamed_addr` to ensure that the alias address equals
1591 alias_global.setDllStorageClass(.dllexport, &o.builder);1583 // the address of the original global.
1592 alias_global.setLinkage(switch (exp.opts.linkage) {1584 alias_global.setUnnamedAddr(.default, &o.builder);
1593 .internal => if (o.builder.strip) .private else .internal, // we still did useful work in replacing an existing symbol if there was one1585
1594 .strong => .external,1586 if (comp.config.dll_export_fns and exp.opts.visibility != .hidden)
1595 .weak => .weak_odr,1587 alias_global.setDllStorageClass(.dllexport, &o.builder);
1596 .link_once => .linkonce_odr,1588 alias_global.setLinkage(switch (exp.opts.linkage) {
1597 }, &o.builder);1589 .internal => if (o.builder.strip) .private else .internal, // we still did useful work in replacing an existing symbol if there was one
1598 alias_global.setVisibility(switch (exp.opts.visibility) {1590 .strong => .external,
1599 .default => .default,1591 .weak => .weak_odr,
1600 .hidden => .hidden,1592 .link_once => .linkonce_odr,
1601 .protected => .protected,1593 }, &o.builder);
1602 }, &o.builder);1594 alias_global.setVisibility(switch (exp.opts.visibility) {
1603 }1595 .default => .default,
1596 .hidden => .hidden,
1597 .protected => .protected,
1598 }, &o.builder);
1604 }1599 }
16051600
1606 pub fn updateContainerType(o: *Object, pt: Zcu.PerThread, ty: InternPool.Index, success: bool) Allocator.Error!void {1601 pub fn updateContainerType(o: *Object, pt: Zcu.PerThread, ty: InternPool.Index, success: bool) Allocator.Error!void {
src/link.zig+7-30
...@@ -788,7 +788,6 @@ pub const File = struct {...@@ -788,7 +788,6 @@ pub const File = struct {
788 }788 }
789 }789 }
790790
791 /// May be called before or after updateExports for any given Nav.
792 /// Asserts that the ZCU is not using the LLVM backend.791 /// Asserts that the ZCU is not using the LLVM backend.
793 fn updateNav(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Error!void {792 fn updateNav(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Error!void {
794 assert(base.comp.zcu.?.llvm_object == null);793 assert(base.comp.zcu.?.llvm_object == null);
...@@ -831,7 +830,6 @@ pub const File = struct {...@@ -831,7 +830,6 @@ pub const File = struct {
831 }830 }
832 }831 }
833832
834 /// May be called before or after updateExports for any given Decl.
835 /// The active tag of `mir` is determined by the backend used for the module this function is in.833 /// The active tag of `mir` is determined by the backend used for the module this function is in.
836 /// Never called when LLVM is codegenning the ZCU.834 /// Never called when LLVM is codegenning the ZCU.
837 fn updateFunc(835 fn updateFunc(
...@@ -971,15 +969,16 @@ pub const File = struct {...@@ -971,15 +969,16 @@ pub const File = struct {
971 }969 }
972 }970 }
973971
974 /// This is called for every exported thing. `exports` is almost always972 /// This is called once per update, before `flush`.
975 /// a list of size 1, meaning that `exported` is exported once. However, it is possible973 ///
976 /// to export the same thing with multiple different symbol names (aliases).974 /// `export_indices` contains the index of every export from the ZCU which should be performed
977 /// May be called before or after updateDecl for any given Decl.975 /// on this update. "Removal" of exports is signaled implicitly by the export being in this
976 /// slice on one update but not the next.
977 ///
978 /// Never called when LLVM is codegenning the ZCU.978 /// Never called when LLVM is codegenning the ZCU.
979 pub fn updateExports(979 pub fn updateExports(
980 base: *File,980 base: *File,
981 pt: Zcu.PerThread,981 pt: Zcu.PerThread,
982 exported: Zcu.Exported,
983 export_indices: []const Zcu.Export.Index,982 export_indices: []const Zcu.Export.Index,
984 ) Error!void {983 ) Error!void {
985 assert(base.comp.zcu.?.llvm_object == null);984 assert(base.comp.zcu.?.llvm_object == null);
...@@ -992,7 +991,7 @@ pub const File = struct {...@@ -992,7 +991,7 @@ pub const File = struct {
992 .plan9 => unreachable,991 .plan9 => unreachable,
993 inline else => |tag| {992 inline else => |tag| {
994 dev.check(tag.devFeature());993 dev.check(tag.devFeature());
995 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateExports(pt, exported, export_indices);994 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateExports(pt, export_indices);
996 },995 },
997 }996 }
998 }997 }
...@@ -1071,28 +1070,6 @@ pub const File = struct {...@@ -1071,28 +1070,6 @@ pub const File = struct {
1071 }1070 }
1072 }1071 }
10731072
1074 /// Never called when LLVM is codegenning the ZCU.
1075 pub fn deleteExport(
1076 base: *File,
1077 exported: Zcu.Exported,
1078 name: InternPool.NullTerminatedString,
1079 ) void {
1080 assert(base.comp.zcu.?.llvm_object == null);
1081
1082 switch (base.tag) {
1083 .lld => unreachable,
1084 .plan9 => unreachable,
1085
1086 .spirv,
1087 => {},
1088
1089 inline else => |tag| {
1090 dev.check(tag.devFeature());
1091 return @as(*tag.Type(), @fieldParentPtr("base", base)).deleteExport(exported, name);
1092 },
1093 }
1094 }
1095
1096 pub const DumpResult = enum {1073 pub const DumpResult = enum {
1097 unimplemented,1074 unimplemented,
1098 needs_extensions,1075 needs_extensions,
src/link/C.zig+44-40
...@@ -1228,58 +1228,62 @@ const Flush = struct {...@@ -1228,58 +1228,62 @@ const Flush = struct {
1228pub fn updateExports(1228pub fn updateExports(
1229 c: *C,1229 c: *C,
1230 pt: Zcu.PerThread,1230 pt: Zcu.PerThread,
1231 exported: Zcu.Exported,
1232 export_indices: []const Zcu.Export.Index,1231 export_indices: []const Zcu.Export.Index,
1233) Allocator.Error!void {1232) Allocator.Error!void {
1234 const zcu = pt.zcu;1233 const zcu = pt.zcu;
1235 const gpa = zcu.gpa;1234 const gpa = zcu.gpa;
12361235
1236 c.exported_navs.clearRetainingCapacity();
1237 c.exported_uavs.clearRetainingCapacity();
1238
1237 var arena: std.heap.ArenaAllocator = .init(gpa);1239 var arena: std.heap.ArenaAllocator = .init(gpa);
1238 defer arena.deinit();1240 defer arena.deinit();
12391241
1240 var dg: codegen.DeclGen = .{1242 var by_exported: std.array_hash_map.Auto(Zcu.Exported, std.ArrayList(Zcu.Export.Index)) = .empty;
1241 .gpa = gpa,1243 try by_exported.ensureUnusedCapacity(arena.allocator(), export_indices.len);
1242 .arena = arena.allocator(),1244
1243 .pt = pt,1245 for (export_indices) |exp_index| {
1244 .mod = zcu.root_mod,1246 const exported = exp_index.ptr(zcu).exported;
1245 .owner_nav = .none,1247 const gop = by_exported.getOrPutAssumeCapacity(exported);
1246 .is_naked_fn = false,1248 if (!gop.found_existing) {
1247 .expected_block = null,1249 gop.value_ptr.* = .empty;
1248 .ctype_deps = .empty,1250 }
1249 .uavs = .empty,1251 try gop.value_ptr.append(arena.allocator(), exp_index);
1250 };
1251 defer {
1252 assert(dg.uavs.count() == 0);
1253 dg.ctype_deps.deinit(gpa);
1254 }1252 }
12551253
1256 const code: String = code: {1254 for (by_exported.keys(), by_exported.values()) |exported, *exports_of_this| {
1257 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);1255 var dg: codegen.DeclGen = .{
1258 defer c.string_bytes = aw.toArrayList();1256 .gpa = gpa,
1259 const start = aw.written().len;1257 .arena = arena.allocator(),
1260 codegen.genExports(&dg, &aw.writer, exported, export_indices) catch |err| switch (err) {1258 .pt = pt,
1261 error.WriteFailed => return error.OutOfMemory,1259 .mod = zcu.root_mod,
1262 error.OutOfMemory => |e| return e,1260 .owner_nav = .none,
1261 .is_naked_fn = false,
1262 .expected_block = null,
1263 .ctype_deps = .empty,
1264 .uavs = .empty,
1263 };1265 };
1264 break :code .{1266 defer {
1265 .start = @intCast(start),1267 assert(dg.uavs.count() == 0);
1266 .len = @intCast(aw.written().len - start),1268 dg.ctype_deps.deinit(gpa);
1269 }
1270 const code: String = code: {
1271 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);
1272 defer c.string_bytes = aw.toArrayList();
1273 const start = aw.written().len;
1274 codegen.genExports(&dg, &aw.writer, exported, exports_of_this.items) catch |err| switch (err) {
1275 error.WriteFailed => return error.OutOfMemory,
1276 error.OutOfMemory => |e| return e,
1277 };
1278 break :code .{
1279 .start = @intCast(start),
1280 .len = @intCast(aw.written().len - start),
1281 };
1267 };1282 };
1268 };1283 switch (exported) {
1269 switch (exported) {1284 .nav => |nav| try c.exported_navs.put(gpa, nav, code),
1270 .nav => |nav| try c.exported_navs.put(gpa, nav, code),1285 .uav => |uav| try c.exported_uavs.put(gpa, uav, code),
1271 .uav => |uav| try c.exported_uavs.put(gpa, uav, code),1286 }
1272 }
1273}
1274
1275pub fn deleteExport(
1276 self: *C,
1277 exported: Zcu.Exported,
1278 _: InternPool.NullTerminatedString,
1279) void {
1280 switch (exported) {
1281 .nav => |nav| _ = self.exported_navs.swapRemove(nav),
1282 .uav => |uav| _ = self.exported_uavs.swapRemove(uav),
1283 }1287 }
1284}1288}
12851289
src/link/Coff.zig+134-147
...@@ -5893,10 +5893,6 @@ pub fn flush(...@@ -5893,10 +5893,6 @@ pub fn flush(
58935893
5894 const comp = coff.base.comp;5894 const comp = coff.base.comp;
58955895
5896 // TODO: When https://github.com/ziglang/zig/issues/23617 is in,
5897 // this should be set after updateExports instead
5898 coff.exports_complete = true;
5899
5900 while (try coff.resolve(tid)) {}5896 while (try coff.resolve(tid)) {}
5901 while (try coff.idle(tid)) {}5897 while (try coff.idle(tid)) {}
59025898
...@@ -7374,30 +7370,38 @@ fn virtualSlide(coff: *Coff, start_section_index: usize, start_rva: u32) !void {...@@ -7374,30 +7370,38 @@ fn virtualSlide(coff: *Coff, start_section_index: usize, start_rva: u32) !void {
7374pub fn updateExports(7370pub fn updateExports(
7375 coff: *Coff,7371 coff: *Coff,
7376 pt: Zcu.PerThread,7372 pt: Zcu.PerThread,
7377 exported: Zcu.Exported,
7378 export_indices: []const Zcu.Export.Index,7373 export_indices: []const Zcu.Export.Index,
7379) link.Error!void {7374) link.Error!void {
7375 // TODO: delete old exports from first/second linker member table
7376 // TODO: delete old exports from symbol table inside section
7380 const diags = &coff.base.comp.link_diags;7377 const diags = &coff.base.comp.link_diags;
7381 return coff.updateExportsInner(pt, exported, export_indices) catch |err| switch (err) {7378 var alias_syms: std.array_hash_map.Auto(Symbol.Index, Symbol.Index) = .empty;
7382 error.MappedFileIo => return diags.fail(7379 defer alias_syms.deinit(coff.base.comp.gpa);
7383 "failed to write output file: {t}",7380 for (export_indices) |export_index| {
7384 .{coff.mf.io_err.?},7381 coff.updateExportInner(pt, export_index, &alias_syms) catch |err| switch (err) {
7385 ),7382 error.MappedFileIo => return diags.fail(
7386 else => |e| return e,7383 "failed to write output file: {t}",
7387 };7384 .{coff.mf.io_err.?},
7385 ),
7386 else => |e| return e,
7387 };
7388 }
7389 coff.exports_complete = true;
7388}7390}
7389fn updateExportsInner(7391fn updateExportInner(
7390 coff: *Coff,7392 coff: *Coff,
7391 pt: Zcu.PerThread,7393 pt: Zcu.PerThread,
7392 exported: Zcu.Exported,7394 export_index: Zcu.Export.Index,
7393 export_indices: []const Zcu.Export.Index,7395 alias_syms: *std.array_hash_map.Auto(Symbol.Index, Symbol.Index),
7394) !void {7396) !void {
7395 const zcu = pt.zcu;7397 const zcu = pt.zcu;
7396 const gpa = zcu.gpa;7398 const gpa = zcu.gpa;
7397 const ip = &zcu.intern_pool;7399 const ip = &zcu.intern_pool;
73987400
7399 try coff.symbols.ensureUnusedCapacity(gpa, export_indices.len);7401 const exp = export_index.ptr(zcu);
7400 const exported_si: Symbol.Index = switch (exported) {7402
7403 try coff.symbols.ensureUnusedCapacity(gpa, 1);
7404 const exported_si: Symbol.Index = switch (exp.exported) {
7401 .nav => |nav| try coff.navSymbol(zcu, nav),7405 .nav => |nav| try coff.navSymbol(zcu, nav),
7402 .uav => |uav| @fromBackingInt(@intCast(@backingInt(try coff.lowerUav(7406 .uav => |uav| @fromBackingInt(@intCast(@backingInt(try coff.lowerUav(
7403 pt,7407 pt,
...@@ -7405,7 +7409,7 @@ fn updateExportsInner(...@@ -7405,7 +7409,7 @@ fn updateExportsInner(
7405 Type.fromInterned(ip.typeOf(uav)).abiAlignment(zcu),7409 Type.fromInterned(ip.typeOf(uav)).abiAlignment(zcu),
7406 )))),7410 )))),
7407 };7411 };
7408 switch (exported) {7412 switch (exp.exported) {
7409 .nav => |nav| log.debug("updateExports({f}) = {d}", .{ ip.getNav(nav).fqn.fmt(ip), exported_si }),7413 .nav => |nav| log.debug("updateExports({f}) = {d}", .{ ip.getNav(nav).fqn.fmt(ip), exported_si }),
7410 .uav => |uav| log.debug("updateExports(@as({f}, {f})) = {d}", .{7414 .uav => |uav| log.debug("updateExports(@as({f}, {f})) = {d}", .{
7411 Type.fromInterned(ip.typeOf(uav)).fmt(pt),7415 Type.fromInterned(ip.typeOf(uav)).fmt(pt),
...@@ -7419,154 +7423,137 @@ fn updateExportsInner(...@@ -7419,154 +7423,137 @@ fn updateExportsInner(
7419 const machine = coff.targetLoad(&coff.headerPtr().machine);7423 const machine = coff.targetLoad(&coff.headerPtr().machine);
7420 const exported_ni = exported_si.node(coff);7424 const exported_ni = exported_si.node(coff);
7421 const exported_sym = exported_si.get(coff);7425 const exported_sym = exported_si.get(coff);
7422 var prev_alias_si = exported_si;
74237426
7424 for (export_indices) |export_index| {7427 const @"export" = export_index.ptr(zcu);
7425 const @"export" = export_index.ptr(zcu);7428 const name = @"export".opts.name.toSlice(ip);
7426 const name = @"export".opts.name.toSlice(ip);7429
74277430 // TODO: add an errMsg if this conflicts with an existing symbol
7428 // TODO: add an errMsg if this conflicts with an existing symbol7431 const export_si = try coff.globalSymbol(.{ .name = name });
7429 const export_si = try coff.globalSymbol(.{ .name = name });7432 const export_sym = export_si.get(coff);
7430 const export_sym = export_si.get(coff);7433 export_sym.ni = exported_ni;
7431 export_sym.ni = exported_ni;7434 export_sym.rva = exported_sym.rva;
7432 export_sym.rva = exported_sym.rva;7435 export_sym.section_number = exported_sym.section_number;
7433 export_sym.section_number = exported_sym.section_number;7436 if (@"export".opts.linkage == .weak and !coff.isImage()) {
7434 if (@"export".opts.linkage == .weak and !coff.isImage()) {7437 // exported_si needs to be ahead of export_si in the symbol table,
7435 // exported_si needs to be ahead of export_si in the symbol table,7438 // so that its sti is known when creating the weak external aux entry
7436 // so that its sti is known when creating the weak external aux entry7439 try coff.pendingSymbolTableEntry(exported_si);
7437 try coff.pendingSymbolTableEntry(exported_si);7440 export_sym.flags.weak_external_strat = .alias;
7438 export_sym.flags.weak_external_strat = .alias;7441 export_sym.setValue(.{ .weak_alias_si = exported_si });
7439 export_sym.setValue(.{ .weak_alias_si = exported_si });7442 }
7440 }7443 defer export_si.applyTargetRelocs(coff, .none) catch unreachable;
7441 defer export_si.applyTargetRelocs(coff, .none) catch unreachable;
7442
7443 // The last symbol in the alias list holds the size
7444 const prev_alias_sym = prev_alias_si.get(coff);
7445 switch (prev_alias_sym.flags.extra_tag) {
7446 .size => export_sym.setExtra(.{ .size = prev_alias_sym.extra.size }),
7447 // This export should have been deleted
7448 .next_alias_si => assert(prev_alias_sym.extra.next_alias_si == export_si),
7449 else => unreachable,
7450 }
7451
7452 prev_alias_sym.setExtra(.{ .next_alias_si = export_si });
7453 prev_alias_si = export_si;
74547444
7455 if (!coff.isImage()) continue;7445 const prev_alias_si: Symbol.Index = si: {
7446 const gop = try alias_syms.getOrPut(gpa, exported_si);
7447 const prev_alias_si = if (gop.found_existing) gop.value_ptr.* else exported_si;
7448 gop.value_ptr.* = export_si;
7449 break :si prev_alias_si;
7450 };
74567451
7457 const entries_ctx = ExportTable.Adapter{ .coff = coff };7452 // The last symbol in the alias list holds the size
7458 const gop = try coff.export_table.entries.getOrPutAdapted(7453 const prev_alias_sym = prev_alias_si.get(coff);
7459 gpa,7454 switch (prev_alias_sym.flags.extra_tag) {
7460 name,7455 .size => export_sym.setExtra(.{ .size = prev_alias_sym.extra.size }),
7461 entries_ctx,7456 // This export should have been deleted
7462 );7457 .next_alias_si => assert(prev_alias_sym.extra.next_alias_si == export_si),
7458 else => unreachable,
7459 }
74637460
7464 if (!gop.found_existing) {7461 prev_alias_sym.setExtra(.{ .next_alias_si = export_si });
7465 errdefer _ = coff.export_table.entries.pop();
74667462
7467 const export_count = coff.export_table.entries.count();7463 if (!coff.isImage()) return;
7468 if (export_count > std.math.maxInt(@FieldType(std.coff.ExportDirectoryTable, "number_of_entries")))
7469 return coff.base.comp.link_diags.fail("exceeded maximum number of exports", .{});
74707464
7471 const name_index: u32 = @intCast(coff.export_table.name_table_ni.location(&coff.mf).resolve(&coff.mf)[1]);7465 const entries_ctx = ExportTable.Adapter{ .coff = coff };
7472 const new_name_table_size = name_index + name.len + 1;7466 const gop = try coff.export_table.entries.getOrPutAdapted(
7473 if (new_name_table_size > std.math.maxInt(@FieldType(ExportTable.Entry, "name_index")))7467 gpa,
7474 return coff.base.comp.link_diags.fail("exports name table limit reached", .{});7468 name,
7469 entries_ctx,
7470 );
74757471
7476 try coff.export_table.name_table_ni.resize(&coff.mf, gpa, new_name_table_size);7472 if (!gop.found_existing) {
7473 errdefer _ = coff.export_table.entries.pop();
74777474
7478 const name_table_slice = coff.export_table.name_table_ni.slice(&coff.mf);7475 const export_count = coff.export_table.entries.count();
7479 @memcpy(name_table_slice[name_index..][0 .. name.len + 1], name[0 .. name.len + 1]);7476 if (export_count > std.math.maxInt(@FieldType(std.coff.ExportDirectoryTable, "number_of_entries")))
74807477 return coff.base.comp.link_diags.fail("exceeded maximum number of exports", .{});
7481 // If the new name sorts after the current tail of the sorted list, we don't need to re-sort
7482 {
7483 const ordinal_table_slice = coff.exportOrdinalTableSlice();
7484 if (ordinal_table_slice.len > 0 and !coff.export_table.pending_sort) {
7485 const tail_index: ExportTable.Ordinal =
7486 @fromBackingInt(@intCast(ordinal_table_slice[ordinal_table_slice.len - 1].unbiased_ordinal));
7487 const tail_entry = tail_index.get(coff);
7488 const tail_name = name_table_slice[tail_entry.name_index..][0..tail_entry.name_len];
7489 coff.export_table.pending_sort = std.mem.lessThan(u8, name, tail_name);
7490 }
7491 }
74927478
7493 const edt = coff.exportDirectoryTable();7479 const name_index: u32 = @intCast(coff.export_table.name_table_ni.location(&coff.mf).resolve(&coff.mf)[1]);
7494 coff.targetStore(&edt.number_of_names, @intCast(export_count));7480 const new_name_table_size = name_index + name.len + 1;
7495 edt.number_of_entries = edt.number_of_names;7481 if (new_name_table_size > std.math.maxInt(@FieldType(ExportTable.Entry, "name_index")))
7482 return coff.base.comp.link_diags.fail("exports name table limit reached", .{});
74967483
7497 // TODO: These should all be resized ahead of time to fit all exports7484 try coff.export_table.name_table_ni.resize(&coff.mf, gpa, new_name_table_size);
7498 // after https://github.com/ziglang/zig/issues/23616
7499 try coff.export_table.export_address_table_si.node(coff).resize(
7500 &coff.mf,
7501 gpa,
7502 export_count * @sizeOf(std.coff.ExportAddressTableEntry),
7503 );
75047485
7505 try coff.export_table.name_pointer_table_ni.resize(7486 const name_table_slice = coff.export_table.name_table_ni.slice(&coff.mf);
7506 &coff.mf,7487 @memcpy(name_table_slice[name_index..][0 .. name.len + 1], name[0 .. name.len + 1]);
7507 gpa,
7508 export_count * @sizeOf(std.coff.ExportNamePointerTableEntry),
7509 );
75107488
7511 try coff.export_table.ordinal_table_ni.resize(7489 // If the new name sorts after the current tail of the sorted list, we don't need to re-sort
7512 &coff.mf,7490 {
7513 gpa,7491 const ordinal_table_slice = coff.exportOrdinalTableSlice();
7514 export_count * @sizeOf(std.coff.ExportOrdinalTableEntry),7492 if (ordinal_table_slice.len > 0 and !coff.export_table.pending_sort) {
7515 );7493 const tail_index: ExportTable.Ordinal =
7494 @fromBackingInt(@intCast(ordinal_table_slice[ordinal_table_slice.len - 1].unbiased_ordinal));
7495 const tail_entry = tail_index.get(coff);
7496 const tail_name = name_table_slice[tail_entry.name_index..][0..tail_entry.name_len];
7497 coff.export_table.pending_sort = std.mem.lessThan(u8, name, tail_name);
7498 }
7499 }
75167500
7517 coff.targetStore(7501 const edt = coff.exportDirectoryTable();
7518 &coff.exportNamePointerTableSlice()[gop.index].name_rva,7502 coff.targetStore(&edt.number_of_names, @intCast(export_count));
7519 @intCast(coff.computeNodeRva(coff.export_table.name_table_ni) + name_index),7503 edt.number_of_entries = edt.number_of_names;
7520 );
7521 coff.targetStore(
7522 &coff.exportOrdinalTableSlice()[gop.index].unbiased_ordinal,
7523 @intCast(gop.index),
7524 );
75257504
7526 gop.value_ptr.* = .{7505 // TODO: These should all be resized ahead of time to fit all exports
7527 .si = export_si,7506 // after https://github.com/ziglang/zig/issues/23616
7528 .name_index = @intCast(name_index),7507 try coff.export_table.export_address_table_si.node(coff).resize(
7529 .name_len = @intCast(name.len),7508 &coff.mf,
7530 .export_address_table_ri = @fromBackingInt(@intCast(coff.relocs.items.len)),7509 gpa,
7531 };7510 export_count * @sizeOf(std.coff.ExportAddressTableEntry),
7511 );
75327512
7533 try coff.addReloc(7513 try coff.export_table.name_pointer_table_ni.resize(
7534 coff.export_table.export_address_table_si,7514 &coff.mf,
7535 @intCast(@sizeOf(std.coff.ExportAddressTableEntry) * gop.index),7515 gpa,
7536 export_si,7516 export_count * @sizeOf(std.coff.ExportNamePointerTableEntry),
7537 .{ .known = 0 },7517 );
7538 switch (machine) {
7539 else => |tag| @panic(@tagName(tag)),
7540 .AMD64 => .{ .AMD64 = .ADDR32NB },
7541 .I386 => .{ .I386 = .DIR32NB },
7542 },
7543 );
7544 } else {
7545 gop.value_ptr.si = export_si;
7546 const reloc = gop.value_ptr.*.export_address_table_ri.get(coff);
7547 reloc.target = export_si;
7548 }
7549 }
7550}
75517518
7552pub fn deleteExport(7519 try coff.export_table.ordinal_table_ni.resize(
7553 coff: *Coff,7520 &coff.mf,
7554 exported: Zcu.Exported,7521 gpa,
7555 name: InternPool.NullTerminatedString,7522 export_count * @sizeOf(std.coff.ExportOrdinalTableEntry),
7556) void {7523 );
7557 const zcu = coff.base.comp.zcu.?;
7558 const ip = &zcu.intern_pool;
75597524
7560 const exported_si: Symbol.Index = switch (exported) {7525 coff.targetStore(
7561 .nav => |nav| coff.navs.get(nav).?,7526 &coff.exportNamePointerTableSlice()[gop.index].name_rva,
7562 .uav => |uav| coff.uavs.get(uav).?,7527 @intCast(coff.computeNodeRva(coff.export_table.name_table_ni) + name_index),
7563 };7528 );
7529 coff.targetStore(
7530 &coff.exportOrdinalTableSlice()[gop.index].unbiased_ordinal,
7531 @intCast(gop.index),
7532 );
75647533
7565 const name_slice = name.toSlice(ip);7534 gop.value_ptr.* = .{
7566 log.debug("deleteExport({s}, {d})", .{ name_slice, exported_si });7535 .si = export_si,
7536 .name_index = @intCast(name_index),
7537 .name_len = @intCast(name.len),
7538 .export_address_table_ri = @fromBackingInt(@intCast(coff.relocs.items.len)),
7539 };
75677540
7568 // TODO: Delete from first / second linker member table7541 try coff.addReloc(
7569 // TODO: Delete from symbol table inside section7542 coff.export_table.export_address_table_si,
7543 @intCast(@sizeOf(std.coff.ExportAddressTableEntry) * gop.index),
7544 export_si,
7545 .{ .known = 0 },
7546 switch (machine) {
7547 else => |tag| @panic(@tagName(tag)),
7548 .AMD64 => .{ .AMD64 = .ADDR32NB },
7549 .I386 => .{ .I386 = .DIR32NB },
7550 },
7551 );
7552 } else {
7553 gop.value_ptr.si = export_si;
7554 const reloc = gop.value_ptr.*.export_address_table_ri.get(coff);
7555 reloc.target = export_si;
7556 }
7570}7557}
75717558
7572fn dumpStderr(coff: *Coff, tid: Zcu.PerThread.Id) !void {7559fn dumpStderr(coff: *Coff, tid: Zcu.PerThread.Id) !void {
src/link/Elf.zig+1-10
...@@ -1685,24 +1685,15 @@ pub fn updateContainerType(...@@ -1685,24 +1685,15 @@ pub fn updateContainerType(
1685pub fn updateExports(1685pub fn updateExports(
1686 self: *Elf,1686 self: *Elf,
1687 pt: Zcu.PerThread,1687 pt: Zcu.PerThread,
1688 exported: Zcu.Exported,
1689 export_indices: []const Zcu.Export.Index,1688 export_indices: []const Zcu.Export.Index,
1690) link.Error!void {1689) link.Error!void {
1691 return self.zigObjectPtr().?.updateExports(self, pt, exported, export_indices);1690 return self.zigObjectPtr().?.updateExports(self, pt, export_indices);
1692}1691}
16931692
1694pub fn updateLineNumber(self: *Elf, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) link.Error!void {1693pub fn updateLineNumber(self: *Elf, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) link.Error!void {
1695 return self.zigObjectPtr().?.updateLineNumber(pt, ti_id);1694 return self.zigObjectPtr().?.updateLineNumber(pt, ti_id);
1696}1695}
16971696
1698pub fn deleteExport(
1699 self: *Elf,
1700 exported: Zcu.Exported,
1701 name: InternPool.NullTerminatedString,
1702) void {
1703 return self.zigObjectPtr().?.deleteExport(self, exported, name);
1704}
1705
1706fn checkDuplicates(self: *Elf) !void {1697fn checkDuplicates(self: *Elf) !void {
1707 const gpa = self.base.comp.gpa;1698 const gpa = self.base.comp.gpa;
17081699
src/link/Elf/ZigObject.zig+44-57
...@@ -1857,7 +1857,6 @@ pub fn updateExports(...@@ -1857,7 +1857,6 @@ pub fn updateExports(
1857 self: *ZigObject,1857 self: *ZigObject,
1858 elf_file: *Elf,1858 elf_file: *Elf,
1859 pt: Zcu.PerThread,1859 pt: Zcu.PerThread,
1860 exported: Zcu.Exported,
1861 export_indices: []const Zcu.Export.Index,1860 export_indices: []const Zcu.Export.Index,
1862) link.Error!void {1861) link.Error!void {
1863 const tracy = trace(@src());1862 const tracy = trace(@src());
...@@ -1865,27 +1864,49 @@ pub fn updateExports(...@@ -1865,27 +1864,49 @@ pub fn updateExports(
18651864
1866 const zcu = pt.zcu;1865 const zcu = pt.zcu;
1867 const gpa = elf_file.base.comp.gpa;1866 const gpa = elf_file.base.comp.gpa;
1868 const metadata = switch (exported) {
1869 .nav => |nav| blk: {
1870 _ = try self.getOrCreateMetadataForNav(zcu, nav);
1871 break :blk self.navs.getPtr(nav).?;
1872 },
1873 .uav => |uav| self.uavs.getPtr(uav) orelse blk: {
1874 _ = try self.lowerUav(elf_file, pt, uav, .none);
1875 break :blk self.uavs.getPtr(uav).?;
1876 },
1877 };
1878 const sym_index = metadata.symbol_index;
1879 const esym_index = self.symbol(sym_index).esym_index;
1880 const esym = self.symtab.items(.elf_sym)[esym_index];
1881 const esym_shndx = self.symtab.items(.shndx)[esym_index];
18821867
1883 for (export_indices) |export_idx| {1868 // Delete all existing exports first
1884 const exp = export_idx.ptr(zcu);1869 for (self.navs.values()) |*metadata| {
1870 for (metadata.exports.items) |sym_index| {
1871 const esym_index = self.symbol(sym_index).esym_index;
1872 const esym = &self.symtab.items(.elf_sym)[esym_index];
1873 _ = self.globals_lookup.remove(esym.st_name);
1874 esym.* = Elf.null_sym;
1875 self.symtab.items(.shndx)[esym_index] = elf.SHN_UNDEF;
1876 }
1877 metadata.exports.clearRetainingCapacity();
1878 }
1879 for (self.uavs.values()) |*metadata| {
1880 for (metadata.exports.items) |sym_index| {
1881 const esym_index = self.symbol(sym_index).esym_index;
1882 const esym = &self.symtab.items(.elf_sym)[esym_index];
1883 _ = self.globals_lookup.remove(esym.st_name);
1884 esym.* = Elf.null_sym;
1885 self.symtab.items(.shndx)[esym_index] = elf.SHN_UNDEF;
1886 }
1887 metadata.exports.clearRetainingCapacity();
1888 }
1889
1890 for (export_indices) |export_index| {
1891 const exp = export_index.ptr(zcu);
1892 const metadata = switch (exp.exported) {
1893 .nav => |nav| blk: {
1894 _ = try self.getOrCreateMetadataForNav(zcu, nav);
1895 break :blk self.navs.getPtr(nav).?;
1896 },
1897 .uav => |uav| self.uavs.getPtr(uav) orelse blk: {
1898 _ = try self.lowerUav(elf_file, pt, uav, .none);
1899 break :blk self.uavs.getPtr(uav).?;
1900 },
1901 };
1902 const sym_index = metadata.symbol_index;
1903 const esym_index = self.symbol(sym_index).esym_index;
1904 const esym = self.symtab.items(.elf_sym)[esym_index];
1905 const esym_shndx = self.symtab.items(.shndx)[esym_index];
1885 if (exp.opts.section.unwrap()) |section_name| {1906 if (exp.opts.section.unwrap()) |section_name| {
1886 if (!section_name.eqlSlice(".text", &zcu.intern_pool)) {1907 if (!section_name.eqlSlice(".text", &zcu.intern_pool)) {
1887 try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1);1908 try zcu.failed_exports.ensureUnusedCapacity(gpa, 1);
1888 zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, try Zcu.ErrorMsg.create(1909 zcu.failed_exports.putAssumeCapacityNoClobber(export_index, try Zcu.ErrorMsg.create(
1889 gpa,1910 gpa,
1890 exp.src,1911 exp.src,
1891 "Unimplemented: ExportOptions.section",1912 "Unimplemented: ExportOptions.section",
...@@ -1899,8 +1920,8 @@ pub fn updateExports(...@@ -1899,8 +1920,8 @@ pub fn updateExports(
1899 .strong => elf.STB_GLOBAL,1920 .strong => elf.STB_GLOBAL,
1900 .weak => elf.STB_WEAK,1921 .weak => elf.STB_WEAK,
1901 .link_once => {1922 .link_once => {
1902 try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1);1923 try zcu.failed_exports.ensureUnusedCapacity(gpa, 1);
1903 zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, try Zcu.ErrorMsg.create(1924 zcu.failed_exports.putAssumeCapacityNoClobber(export_index, try Zcu.ErrorMsg.create(
1904 gpa,1925 gpa,
1905 exp.src,1926 exp.src,
1906 "Unimplemented: GlobalLinkage.LinkOnce",1927 "Unimplemented: GlobalLinkage.LinkOnce",
...@@ -1912,13 +1933,8 @@ pub fn updateExports(...@@ -1912,13 +1933,8 @@ pub fn updateExports(
1912 const stt_bits: u8 = @as(u4, @truncate(esym.st_info));1933 const stt_bits: u8 = @as(u4, @truncate(esym.st_info));
1913 const exp_name = exp.opts.name.toSlice(&zcu.intern_pool);1934 const exp_name = exp.opts.name.toSlice(&zcu.intern_pool);
1914 const name_off = try self.strtab.insert(gpa, exp_name);1935 const name_off = try self.strtab.insert(gpa, exp_name);
1915 const global_sym_index = if (metadata.@"export"(self, exp_name)) |exp_index|1936 const global_sym_index = try self.getGlobalSymbol(elf_file, exp_name, null);
1916 exp_index.*1937 try metadata.exports.append(gpa, global_sym_index);
1917 else blk: {
1918 const global_sym_index = try self.getGlobalSymbol(elf_file, exp_name, null);
1919 try metadata.exports.append(gpa, global_sym_index);
1920 break :blk global_sym_index;
1921 };
19221938
1923 const value = self.symbol(sym_index).value;1939 const value = self.symbol(sym_index).value;
1924 const global_sym = self.symbol(global_sym_index);1940 const global_sym = self.symbol(global_sym_index);
...@@ -1947,27 +1963,6 @@ pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.T...@@ -1947,27 +1963,6 @@ pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.T
1947 }1963 }
1948}1964}
19491965
1950pub fn deleteExport(
1951 self: *ZigObject,
1952 elf_file: *Elf,
1953 exported: Zcu.Exported,
1954 name: InternPool.NullTerminatedString,
1955) void {
1956 const metadata = switch (exported) {
1957 .nav => |nav| self.navs.getPtr(nav),
1958 .uav => |uav| self.uavs.getPtr(uav),
1959 } orelse return;
1960 const zcu = elf_file.base.comp.zcu.?;
1961 const exp_name = name.toSlice(&zcu.intern_pool);
1962 const sym_index = metadata.@"export"(self, exp_name) orelse return;
1963 log.debug("deleting export '{s}'", .{exp_name});
1964 const esym_index = self.symbol(sym_index.*).esym_index;
1965 const esym = &self.symtab.items(.elf_sym)[esym_index];
1966 _ = self.globals_lookup.remove(esym.st_name);
1967 esym.* = Elf.null_sym;
1968 self.symtab.items(.shndx)[esym_index] = elf.SHN_UNDEF;
1969}
1970
1971pub fn getGlobalSymbol(self: *ZigObject, elf_file: *Elf, name: []const u8, lib_name: ?[]const u8) !u32 {1966pub fn getGlobalSymbol(self: *ZigObject, elf_file: *Elf, name: []const u8, lib_name: ?[]const u8) !u32 {
1972 _ = lib_name;1967 _ = lib_name;
1973 const gpa = elf_file.base.comp.gpa;1968 const gpa = elf_file.base.comp.gpa;
...@@ -2362,14 +2357,6 @@ const AvMetadata = struct {...@@ -2362,14 +2357,6 @@ const AvMetadata = struct {
2362 exports: std.ArrayList(Symbol.Index) = .empty,2357 exports: std.ArrayList(Symbol.Index) = .empty,
2363 /// Set to true if the AV has been initialized and allocated.2358 /// Set to true if the AV has been initialized and allocated.
2364 allocated: bool = false,2359 allocated: bool = false,
2365
2366 fn @"export"(m: AvMetadata, zig_object: *ZigObject, name: []const u8) ?*u32 {
2367 for (m.exports.items) |*exp| {
2368 const exp_name = zig_object.getString(zig_object.symbol(exp.*).name_offset);
2369 if (mem.eql(u8, name, exp_name)) return exp;
2370 }
2371 return null;
2372 }
2373};2360};
23742361
2375fn checkNavAllocated(pt: Zcu.PerThread, index: InternPool.Nav.Index, meta: AvMetadata) void {2362fn checkNavAllocated(pt: Zcu.PerThread, index: InternPool.Nav.Index, meta: AvMetadata) void {
src/link/Elf2.zig+48-54
...@@ -8287,33 +8287,35 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad...@@ -8287,33 +8287,35 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad
8287pub fn updateExports(8287pub fn updateExports(
8288 elf: *Elf,8288 elf: *Elf,
8289 pt: Zcu.PerThread,8289 pt: Zcu.PerThread,
8290 exported: Zcu.Exported,
8291 export_indices: []const Zcu.Export.Index,8290 export_indices: []const Zcu.Export.Index,
8292) link.Error!void {8291) link.Error!void {
8293 const diags = &elf.base.comp.link_diags;8292 const diags = &elf.base.comp.link_diags;
8294 return elf.updateExportsInner(pt, exported, export_indices) catch |err| switch (err) {8293 for (export_indices) |export_index| {
8295 else => |e| return e,8294 elf.updateExportInner(pt, export_index) catch |err| switch (err) {
8296 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),8295 else => |e| return e,
8297 };8296 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
8297 };
8298 }
8298}8299}
8299fn updateExportsInner(8300fn updateExportInner(
8300 elf: *Elf,8301 elf: *Elf,
8301 pt: Zcu.PerThread,8302 pt: Zcu.PerThread,
8302 exported: Zcu.Exported,8303 export_index: Zcu.Export.Index,
8303 export_indices: []const Zcu.Export.Index,
8304) Error!void {8304) Error!void {
8305 const zcu = pt.zcu;8305 const zcu = pt.zcu;
8306 const ip = &zcu.intern_pool;8306 const ip = &zcu.intern_pool;
83078307
8308 switch (exported) {8308 const @"export" = export_index.ptr(zcu);
8309
8310 switch (@"export".exported) {
8309 .nav => |nav| log.debug("updateExports({f})", .{ip.getNav(nav).fqn.fmt(ip)}),8311 .nav => |nav| log.debug("updateExports({f})", .{ip.getNav(nav).fqn.fmt(ip)}),
8310 .uav => |uav| log.debug("updateExports(@as({f}, {f}))", .{8312 .uav => |uav| log.debug("updateExports(@as({f}, {f}))", .{
8311 Type.fromInterned(ip.typeOf(uav)).fmt(pt),8313 Type.fromInterned(ip.typeOf(uav)).fmt(pt),
8312 Value.fromInterned(uav).fmtValue(pt),8314 Value.fromInterned(uav).fmtValue(pt),
8313 }),8315 }),
8314 }8316 }
8315 try elf.ensureUnusedSymbolCapacity(@intCast(export_indices.len), .maybe_global);8317 try elf.ensureUnusedSymbolCapacity(1, .maybe_global);
8316 const exported_lsi: Symbol.LocalIndex, const @"type": std.elf.STT = switch (exported) {8318 const exported_lsi: Symbol.LocalIndex, const @"type": std.elf.STT = switch (@"export".exported) {
8317 .nav => |nav| .{8319 .nav => |nav| .{
8318 (try elf.navMapIndex(zcu, nav)).symbol(elf),8320 (try elf.navMapIndex(zcu, nav)).symbol(elf),
8319 elf.navType(ip.getNav(nav).resolved.?),8321 elf.navType(ip.getNav(nav).resolved.?),
...@@ -8331,50 +8333,42 @@ fn updateExportsInner(...@@ -8331,50 +8333,42 @@ fn updateExportsInner(
8331 .fromSection(elf.targetLoad(&exported_sym.shndx)),8333 .fromSection(elf.targetLoad(&exported_sym.shndx)),
8332 },8334 },
8333 };8335 };
8334 for (export_indices) |export_index| {
8335 const @"export" = export_index.ptr(zcu);
8336 const name = @"export".opts.name.toSlice(ip);
8337 _ = elf.addGlobalSymbolAssumeCapacity(.{
8338 .node = .none,
8339 .name = try .string(elf, name),
8340 .value = value,
8341 .size = @intCast(size),
8342 .type = @"type",
8343 .bind = switch (@"export".opts.linkage) {
8344 .strong => .strong,
8345 .weak => .weak,
8346 .internal => return elf.base.comp.link_diags.fail("TODO(Elf2): '.internal' linkage", .{}),
8347 .link_once => return elf.base.comp.link_diags.fail("TODO(Elf2): '.link_once' linkage", .{}),
8348 },
8349 .visibility = switch (@"export".opts.visibility) {
8350 .default => .DEFAULT,
8351 .hidden => .HIDDEN,
8352 .protected => .PROTECTED,
8353 },
8354 .shndx = shndx,
8355 }) catch |err| switch (err) {
8356 error.MultipleDefinitions => {
8357 // HACK: because we currently don't/can't delete these exports, we would typically
8358 // get these errors on every non-initial incremental update. Hack around that by
8359 // only emitting this error if the symbol we're conflicting with comes from an input
8360 // section (as opposed to the ZCU).
8361 const conflicting_global = elf.globalByName(try elf.string(.strtab, name)).?;
8362 const conflicting_node = conflicting_global.symtab_index.ptr(elf).node;
8363 if (elf.getNode(conflicting_node) == .input_section) {
8364 return elf.base.comp.link_diags.fail(
8365 "multiple definitions of '{s}'",
8366 .{name},
8367 );
8368 }
8369 },
8370 };
8371 }
8372}
83738336
8374pub fn deleteExport(elf: *Elf, exported: Zcu.Exported, name: InternPool.NullTerminatedString) void {8337 const name = @"export".opts.name.toSlice(ip);
8375 _ = elf;8338 _ = elf.addGlobalSymbolAssumeCapacity(.{
8376 _ = exported;8339 .node = .none,
8377 _ = name;8340 .name = try .string(elf, name),
8341 .value = value,
8342 .size = @intCast(size),
8343 .type = @"type",
8344 .bind = switch (@"export".opts.linkage) {
8345 .strong => .strong,
8346 .weak => .weak,
8347 .internal => return elf.base.comp.link_diags.fail("TODO(Elf2): '.internal' linkage", .{}),
8348 .link_once => return elf.base.comp.link_diags.fail("TODO(Elf2): '.link_once' linkage", .{}),
8349 },
8350 .visibility = switch (@"export".opts.visibility) {
8351 .default => .DEFAULT,
8352 .hidden => .HIDDEN,
8353 .protected => .PROTECTED,
8354 },
8355 .shndx = shndx,
8356 }) catch |err| switch (err) {
8357 error.MultipleDefinitions => {
8358 // HACK: because we currently don't/can't delete these exports, we would typically
8359 // get these errors on every non-initial incremental update. Hack around that by
8360 // only emitting this error if the symbol we're conflicting with comes from an input
8361 // section (as opposed to the ZCU).
8362 const conflicting_global = elf.globalByName(try elf.string(.strtab, name)).?;
8363 const conflicting_node = conflicting_global.symtab_index.ptr(elf).node;
8364 if (elf.getNode(conflicting_node) == .input_section) {
8365 return elf.base.comp.link_diags.fail(
8366 "multiple definitions of '{s}'",
8367 .{name},
8368 );
8369 }
8370 },
8371 };
8378}8372}
83798373
8380fn dumpStderr(elf: *Elf, tid: Zcu.PerThread.Id) !void {8374fn dumpStderr(elf: *Elf, tid: Zcu.PerThread.Id) !void {
src/link/MachO.zig+1-10
...@@ -3102,18 +3102,9 @@ pub fn updateLineNumber(self: *MachO, pt: Zcu.PerThread, ti_id: InternPool.Track...@@ -3102,18 +3102,9 @@ pub fn updateLineNumber(self: *MachO, pt: Zcu.PerThread, ti_id: InternPool.Track
3102pub fn updateExports(3102pub fn updateExports(
3103 self: *MachO,3103 self: *MachO,
3104 pt: Zcu.PerThread,3104 pt: Zcu.PerThread,
3105 exported: Zcu.Exported,
3106 export_indices: []const Zcu.Export.Index,3105 export_indices: []const Zcu.Export.Index,
3107) link.Error!void {3106) link.Error!void {
3108 return self.getZigObject().?.updateExports(self, pt, exported, export_indices);3107 return self.getZigObject().?.updateExports(self, pt, export_indices);
3109}
3110
3111pub fn deleteExport(
3112 self: *MachO,
3113 exported: Zcu.Exported,
3114 name: InternPool.NullTerminatedString,
3115) void {
3116 return self.getZigObject().?.deleteExport(self, exported, name);
3117}3108}
31183109
3119pub fn freeNav(self: *MachO, nav: InternPool.Nav.Index) void {3110pub fn freeNav(self: *MachO, nav: InternPool.Nav.Index) void {
src/link/MachO/ZigObject.zig+54-62
...@@ -1238,7 +1238,6 @@ pub fn updateExports(...@@ -1238,7 +1238,6 @@ pub fn updateExports(
1238 self: *ZigObject,1238 self: *ZigObject,
1239 macho_file: *MachO,1239 macho_file: *MachO,
1240 pt: Zcu.PerThread,1240 pt: Zcu.PerThread,
1241 exported: Zcu.Exported,
1242 export_indices: []const Zcu.Export.Index,1241 export_indices: []const Zcu.Export.Index,
1243) link.Error!void {1242) link.Error!void {
1244 const tracy = trace(@src());1243 const tracy = trace(@src());
...@@ -1246,26 +1245,60 @@ pub fn updateExports(...@@ -1246,26 +1245,60 @@ pub fn updateExports(
12461245
1247 const zcu = pt.zcu;1246 const zcu = pt.zcu;
1248 const gpa = macho_file.base.comp.gpa;1247 const gpa = macho_file.base.comp.gpa;
1249 const metadata = switch (exported) {
1250 .nav => |nav| blk: {
1251 _ = try self.getOrCreateMetadataForNav(macho_file, nav);
1252 break :blk self.navs.getPtr(nav).?;
1253 },
1254 .uav => |uav| self.uavs.getPtr(uav) orelse blk: {
1255 _ = try self.lowerUav(macho_file, pt, uav, .none);
1256 break :blk self.uavs.getPtr(uav).?;
1257 },
1258 };
1259 const sym_index = metadata.symbol_index;
1260 const nlist_idx = self.symbols.items[sym_index].nlist_idx;
1261 const nlist = self.symtab.items(.nlist)[nlist_idx];
12621248
1263 for (export_indices) |export_idx| {1249 // Delete all existing exports first
1264 const exp = export_idx.ptr(zcu);1250 for (self.navs.values()) |*metadata| {
1251 for (metadata.exports.items) |nlist_index| {
1252 const nlist = &self.symtab.items(.nlist)[nlist_index];
1253 self.symtab.items(.size)[nlist_index] = 0;
1254 _ = self.globals_lookup.remove(nlist.n_strx);
1255 // TODO actually remove the export
1256 // const sym_index = macho_file.globals.get(nlist.n_strx).?;
1257 // const sym = &self.symbols.items[sym_index];
1258 // if (sym.file == self.index) {
1259 // sym.* = .{};
1260 // }
1261 nlist.* = MachO.null_sym;
1262 }
1263 metadata.exports.clearRetainingCapacity();
1264 }
1265 for (self.uavs.values()) |*metadata| {
1266 for (metadata.exports.items) |nlist_index| {
1267 const nlist = &self.symtab.items(.nlist)[nlist_index];
1268 self.symtab.items(.size)[nlist_index] = 0;
1269 _ = self.globals_lookup.remove(nlist.n_strx);
1270 // TODO actually remove the export
1271 // const sym_index = macho_file.globals.get(nlist.n_strx).?;
1272 // const sym = &self.symbols.items[sym_index];
1273 // if (sym.file == self.index) {
1274 // sym.* = .{};
1275 // }
1276 nlist.* = MachO.null_sym;
1277 }
1278 metadata.exports.clearRetainingCapacity();
1279 }
1280
1281 for (export_indices) |export_index| {
1282 const exp = export_index.ptr(zcu);
1283
1284 const metadata = switch (exp.exported) {
1285 .nav => |nav| blk: {
1286 _ = try self.getOrCreateMetadataForNav(macho_file, nav);
1287 break :blk self.navs.getPtr(nav).?;
1288 },
1289 .uav => |uav| self.uavs.getPtr(uav) orelse blk: {
1290 _ = try self.lowerUav(macho_file, pt, uav, .none);
1291 break :blk self.uavs.getPtr(uav).?;
1292 },
1293 };
1294 const sym_index = metadata.symbol_index;
1295 const nlist_idx = self.symbols.items[sym_index].nlist_idx;
1296 const nlist = self.symtab.items(.nlist)[nlist_idx];
1297
1265 if (exp.opts.section.unwrap()) |section_name| {1298 if (exp.opts.section.unwrap()) |section_name| {
1266 if (!section_name.eqlSlice("__text", &zcu.intern_pool)) {1299 if (!section_name.eqlSlice("__text", &zcu.intern_pool)) {
1267 try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1);1300 try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1);
1268 zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, try Zcu.ErrorMsg.create(1301 zcu.failed_exports.putAssumeCapacityNoClobber(export_index, try Zcu.ErrorMsg.create(
1269 gpa,1302 gpa,
1270 exp.src,1303 exp.src,
1271 "Unimplemented: ExportOptions.section",1304 "Unimplemented: ExportOptions.section",
...@@ -1275,7 +1308,7 @@ pub fn updateExports(...@@ -1275,7 +1308,7 @@ pub fn updateExports(
1275 }1308 }
1276 }1309 }
1277 if (exp.opts.linkage == .link_once) {1310 if (exp.opts.linkage == .link_once) {
1278 try zcu.failed_exports.putNoClobber(zcu.gpa, export_idx, try Zcu.ErrorMsg.create(1311 try zcu.failed_exports.putNoClobber(zcu.gpa, export_index, try Zcu.ErrorMsg.create(
1279 gpa,1312 gpa,
1280 exp.src,1313 exp.src,
1281 "Unimplemented: GlobalLinkage.link_once",1314 "Unimplemented: GlobalLinkage.link_once",
...@@ -1285,13 +1318,9 @@ pub fn updateExports(...@@ -1285,13 +1318,9 @@ pub fn updateExports(
1285 }1318 }
12861319
1287 const exp_name = exp.opts.name.toSlice(&zcu.intern_pool);1320 const exp_name = exp.opts.name.toSlice(&zcu.intern_pool);
1288 const global_nlist_index = if (metadata.@"export"(self, exp_name)) |exp_index|1321 const global_nlist_index = try self.getGlobalSymbol(macho_file, exp_name, null);
1289 exp_index.*1322 try metadata.exports.append(gpa, global_nlist_index);
1290 else blk: {1323
1291 const global_nlist_index = try self.getGlobalSymbol(macho_file, exp_name, null);
1292 try metadata.exports.append(gpa, global_nlist_index);
1293 break :blk global_nlist_index;
1294 };
1295 const global_nlist = &self.symtab.items(.nlist)[global_nlist_index];1324 const global_nlist = &self.symtab.items(.nlist)[global_nlist_index];
1296 const atom_index = self.symtab.items(.atom)[nlist_idx];1325 const atom_index = self.symtab.items(.atom)[nlist_idx];
1297 const global_sym = &self.symbols.items[global_nlist_index];1326 const global_sym = &self.symbols.items[global_nlist_index];
...@@ -1400,34 +1429,6 @@ pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.T...@@ -1400,34 +1429,6 @@ pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.T
1400 }1429 }
1401}1430}
14021431
1403pub fn deleteExport(
1404 self: *ZigObject,
1405 macho_file: *MachO,
1406 exported: Zcu.Exported,
1407 name: InternPool.NullTerminatedString,
1408) void {
1409 const zcu = macho_file.base.comp.zcu.?;
1410
1411 const metadata = switch (exported) {
1412 .nav => |nav| self.navs.getPtr(nav),
1413 .uav => |uav| self.uavs.getPtr(uav),
1414 } orelse return;
1415 const nlist_index = metadata.@"export"(self, name.toSlice(&zcu.intern_pool)) orelse return;
1416
1417 log.debug("deleting export '{f}'", .{name.fmt(&zcu.intern_pool)});
1418
1419 const nlist = &self.symtab.items(.nlist)[nlist_index.*];
1420 self.symtab.items(.size)[nlist_index.*] = 0;
1421 _ = self.globals_lookup.remove(nlist.n_strx);
1422 // TODO actually remove the export
1423 // const sym_index = macho_file.globals.get(nlist.n_strx).?;
1424 // const sym = &self.symbols.items[sym_index];
1425 // if (sym.file == self.index) {
1426 // sym.* = .{};
1427 // }
1428 nlist.* = MachO.null_sym;
1429}
1430
1431pub fn getGlobalSymbol(self: *ZigObject, macho_file: *MachO, name: []const u8, lib_name: ?[]const u8) !u32 {1432pub fn getGlobalSymbol(self: *ZigObject, macho_file: *MachO, name: []const u8, lib_name: ?[]const u8) !u32 {
1432 _ = lib_name;1433 _ = lib_name;
1433 const gpa = macho_file.base.comp.gpa;1434 const gpa = macho_file.base.comp.gpa;
...@@ -1722,15 +1723,6 @@ const AvMetadata = struct {...@@ -1722,15 +1723,6 @@ const AvMetadata = struct {
1722 symbol_index: Symbol.Index,1723 symbol_index: Symbol.Index,
1723 /// A list of all exports aliases of this Av.1724 /// A list of all exports aliases of this Av.
1724 exports: std.ArrayList(Symbol.Index) = .empty,1725 exports: std.ArrayList(Symbol.Index) = .empty,
1725
1726 fn @"export"(m: AvMetadata, zig_object: *ZigObject, name: []const u8) ?*u32 {
1727 for (m.exports.items) |*exp| {
1728 const nlist = zig_object.symtab.items(.nlist)[exp.*];
1729 const exp_name = zig_object.strtab.getAssumeExists(nlist.n_strx);
1730 if (mem.eql(u8, name, exp_name)) return exp;
1731 }
1732 return null;
1733 }
1734};1726};
17351727
1736const LazySymbolMetadata = struct {1728const LazySymbolMetadata = struct {
src/link/SpirV.zig+10-14
...@@ -219,24 +219,20 @@ pub fn updateNav(linker: *Linker, pt: Zcu.PerThread, nav: InternPool.Nav.Index)...@@ -219,24 +219,20 @@ pub fn updateNav(linker: *Linker, pt: Zcu.PerThread, nav: InternPool.Nav.Index)
219pub fn updateExports(219pub fn updateExports(
220 linker: *Linker,220 linker: *Linker,
221 pt: Zcu.PerThread,221 pt: Zcu.PerThread,
222 exported: Zcu.Exported,
223 export_indices: []const Zcu.Export.Index,222 export_indices: []const Zcu.Export.Index,
224) !void {223) link.Error!void {
225 const zcu = pt.zcu;224 const zcu = pt.zcu;
226 const ip = &zcu.intern_pool;225 const ip = &zcu.intern_pool;
227 const gpa = linker.base.comp.gpa;226 const gpa = linker.base.comp.gpa;
228 const nav_index = switch (exported) {227 for (export_indices) |exp_index| {
229 .nav => |nav| nav,228 const exp = exp_index.ptr(zcu);
230 .uav => |uav| {229 const nav_index = switch (exp.exported) {
231 _ = uav;230 .nav => |nav| nav,
232 @panic("TODO: implement Linker linker code for exporting a constant value");231 .uav => @panic("TODO: implement Linker linker code for exporting a constant value"),
233 },232 };
234 };233 const nav_ty = ip.getNav(nav_index).resolved.?.type;
235 const nav_ty = ip.getNav(nav_index).resolved.?.type;234 if (ip.isFunctionType(nav_ty)) {
236 if (ip.isFunctionType(nav_ty)) {235 const cc = Type.fromInterned(nav_ty).fnCallingConvention(zcu);
237 const cc = Type.fromInterned(nav_ty).fnCallingConvention(zcu);
238 for (export_indices) |export_idx| {
239 const exp = export_idx.ptr(zcu);
240 try linker.entry_points.append(gpa, .{236 try linker.entry_points.append(gpa, .{
241 .nav = nav_index,237 .nav = nav_index,
242 .name = exp.opts.name.toSlice(ip),238 .name = exp.opts.name.toSlice(ip),
src/link/Wasm.zig+33-50
...@@ -3,11 +3,11 @@...@@ -3,11 +3,11 @@
3//! performed without any knowledge of functions and globals provided by the3//! performed without any knowledge of functions and globals provided by the
4//! Zcu. If there is no Zcu, effectively all linking is done in `prelink`.4//! Zcu. If there is no Zcu, effectively all linking is done in `prelink`.
5//!5//!
6//! `updateFunc`, `updateNav`, `updateExports`, and `deleteExport` are handled6//! `updateFunc`, `updateNav`, and `updateExports` are handled by merely
7//! by merely tracking references to the relevant functions and globals. All7//! tracking references to the relevant functions and globals. All the linking
8//! the linking logic between objects and Zcu happens in `flush`. Many8//! logic between objects and Zcu happens in `flush`. Many components of the
9//! components of the final output are computed on-the-fly at this time rather9//! final output are computed on-the-fly at this time rather than being
10//! than being precomputed and stored separately.10//! precomputed and stored separately.
1111
12const Wasm = @This();12const Wasm = @This();
13const Archive = @import("Wasm/Archive.zig");13const Archive = @import("Wasm/Archive.zig");
...@@ -219,9 +219,9 @@ data_imports_len_prelink: u32 = 0,...@@ -219,9 +219,9 @@ data_imports_len_prelink: u32 = 0,
219/// objects.219/// objects.
220///220///
221/// During the Zcu phase, entries are not deleted from this table221/// During the Zcu phase, entries are not deleted from this table
222/// because doing so would be irreversible when a `deleteExport` call is222/// because doing so would be irreversible when an export is deleted.
223/// handled. However, entries are added during the Zcu phase when extern223/// However, entries are added during the Zcu phase when extern functions
224/// functions are passed to `updateNav`.224/// are passed to `updateNav`.
225///225///
226/// `flush` gets a copy of this table, and then Zcu exports are applied to226/// `flush` gets a copy of this table, and then Zcu exports are applied to
227/// remove elements from the table, and the remainder are either undefined227/// remove elements from the table, and the remainder are either undefined
...@@ -232,9 +232,9 @@ function_imports: std.array_hash_map.Auto(String, FunctionImportId) = .empty,...@@ -232,9 +232,9 @@ function_imports: std.array_hash_map.Auto(String, FunctionImportId) = .empty,
232/// objects.232/// objects.
233///233///
234/// During the Zcu phase, entries are not deleted from this table234/// During the Zcu phase, entries are not deleted from this table
235/// because doing so would be irreversible when a `deleteExport` call is235/// because doing so would be irreversible when an export is deleted.
236/// handled. However, entries are added during the Zcu phase when extern236/// However, entries are added during the Zcu phase when extern functions
237/// functions are passed to `updateNav`.237/// are passed to `updateNav`.
238///238///
239/// `flush` gets a copy of this table, and then Zcu exports are applied to239/// `flush` gets a copy of this table, and then Zcu exports are applied to
240/// remove elements from the table, and the remainder are either undefined240/// remove elements from the table, and the remainder are either undefined
...@@ -3744,62 +3744,45 @@ pub fn updateLineNumber(wasm: *Wasm, pt: Zcu.PerThread, ti_id: InternPool.Tracke...@@ -3744,62 +3744,45 @@ pub fn updateLineNumber(wasm: *Wasm, pt: Zcu.PerThread, ti_id: InternPool.Tracke
3744 }3744 }
3745}3745}
37463746
3747pub fn deleteExport(
3748 wasm: *Wasm,
3749 exported: Zcu.Exported,
3750 name: InternPool.NullTerminatedString,
3751) void {
3752 const zcu = wasm.base.comp.zcu.?;
3753 const ip = &zcu.intern_pool;
3754 const name_slice = name.toSlice(ip);
3755 const export_name = wasm.getExistingString(name_slice).?;
3756 switch (exported) {
3757 .nav => |nav_index| {
3758 log.debug("deleteExport '{s}' nav={d}", .{ name_slice, @backingInt(nav_index) });
3759 assert(wasm.nav_exports.swapRemove(.{ .nav_index = nav_index, .name = export_name }));
3760 },
3761 .uav => |uav_index| assert(wasm.uav_exports.swapRemove(.{ .uav_index = uav_index, .name = export_name })),
3762 }
3763}
3764
3765pub fn updateExports(3747pub fn updateExports(
3766 wasm: *Wasm,3748 wasm: *Wasm,
3767 pt: Zcu.PerThread,3749 pt: Zcu.PerThread,
3768 exported: Zcu.Exported,
3769 export_indices: []const Zcu.Export.Index,3750 export_indices: []const Zcu.Export.Index,
3770) !void {3751) !void {
3771 const zcu = pt.zcu;3752 const zcu = pt.zcu;
3772 const gpa = zcu.gpa;3753 const gpa = zcu.gpa;
3773 const ip = &zcu.intern_pool;3754 const ip = &zcu.intern_pool;
3774 const is_obj = wasm.base.comp.config.output_mode == .Obj;3755 const is_obj = wasm.base.comp.config.output_mode == .Obj;
3775 switch (exported) {3756
3776 .nav => {}, // handled in updateNav3757 wasm.nav_exports.clearRetainingCapacity();
3777 .uav => |uav_index| { // export may be the only reference3758 wasm.uav_exports.clearRetainingCapacity();
3778 const zds: ZcuDataStarts = .init(wasm);3759
3779 if (is_obj) {
3780 const gop = try wasm.uavs_obj.getOrPut(gpa, uav_index);
3781 if (!gop.found_existing) gop.value_ptr.* = undefined;
3782 } else {
3783 const gop = try wasm.uavs_exe.getOrPut(gpa, uav_index);
3784 if (!gop.found_existing) gop.value_ptr.* = .{
3785 .code = undefined,
3786 .count = 0,
3787 };
3788 gop.value_ptr.count += 1;
3789 }
3790 try zds.finish(wasm, pt);
3791 },
3792 }
3793 for (export_indices) |export_idx| {3760 for (export_indices) |export_idx| {
3794 const exp = export_idx.ptr(zcu);3761 const exp = export_idx.ptr(zcu);
3795 const name_slice = exp.opts.name.toSlice(ip);3762 const name_slice = exp.opts.name.toSlice(ip);
3796 const name = try wasm.internString(name_slice);3763 const name = try wasm.internString(name_slice);
3797 switch (exported) {3764 switch (exp.exported) {
3798 .nav => |nav_index| {3765 .nav => |nav_index| {
3799 log.debug("updateExports '{s}' nav={d}", .{ name_slice, @backingInt(nav_index) });3766 log.debug("updateExports '{s}' nav={d}", .{ name_slice, @backingInt(nav_index) });
3800 try wasm.nav_exports.put(gpa, .{ .nav_index = nav_index, .name = name }, export_idx);3767 try wasm.nav_exports.put(gpa, .{ .nav_index = nav_index, .name = name }, export_idx);
3801 },3768 },
3802 .uav => |uav_index| try wasm.uav_exports.put(gpa, .{ .uav_index = uav_index, .name = name }, export_idx),3769 .uav => |uav_index| {
3770 // Lower the UAV, as the export may be the only reference.
3771 const zds: ZcuDataStarts = .init(wasm);
3772 if (is_obj) {
3773 const gop = try wasm.uavs_obj.getOrPut(gpa, uav_index);
3774 if (!gop.found_existing) gop.value_ptr.* = undefined;
3775 } else {
3776 const gop = try wasm.uavs_exe.getOrPut(gpa, uav_index);
3777 if (!gop.found_existing) gop.value_ptr.* = .{
3778 .code = undefined,
3779 .count = 0,
3780 };
3781 gop.value_ptr.count += 1;
3782 }
3783 try zds.finish(wasm, pt);
3784 try wasm.uav_exports.put(gpa, .{ .uav_index = uav_index, .name = name }, export_idx);
3785 },
3803 }3786 }
3804 }3787 }
3805}3788}
test/link/snapshots/static-lib.llvm.dmp+1-1
...@@ -8,8 +8,8 @@ xxxx 00000000 1 NULL() STATIC | this_is_a_long_name.fooWeak...@@ -8,8 +8,8 @@ xxxx 00000000 1 NULL() STATIC | this_is_a_long_name.fooWeak
8xxxx 00000000 2 NULL STATIC | this_is_a_long_name.foo_strong8xxxx 00000000 2 NULL STATIC | this_is_a_long_name.foo_strong
9xxxx 00000008 2 NULL STATIC | this_is_a_long_name.foo_array9xxxx 00000008 2 NULL STATIC | this_is_a_long_name.foo_array
10xxxx 00000000 2 NULL EXTERNAL | foo_strong10xxxx 00000000 2 NULL EXTERNAL | foo_strong
11xxxx 00000000 2 NULL EXTERNAL | foo_strong_alias
12xxxx 00000008 2 NULL EXTERNAL | foo_array11xxxx 00000008 2 NULL EXTERNAL | foo_array
13xxxx 00000000 UNDEF NULL WEAK_EXTERNAL | fooWeak12xxxx 00000000 UNDEF NULL WEAK_EXTERNAL | fooWeak
14 | Weak External [falls back to relative ordinal 000000+2 via SEARCH_ALIAS]13 | Weak External [falls back to relative ordinal 000000+2 via SEARCH_ALIAS]
15xxxx 00000000 1 NULL() EXTERNAL | .weak.fooWeak.default.foo_strong14xxxx 00000000 1 NULL() EXTERNAL | .weak.fooWeak.default.foo_strong
15xxxx 00000000 2 NULL EXTERNAL | foo_strong_alias
test/link/snapshots/static-lib.no-llvm.dmp+2-2
...@@ -6,7 +6,7 @@ xxxx 00000004 2 NULL EXTERNAL | foo2...@@ -6,7 +6,7 @@ xxxx 00000004 2 NULL EXTERNAL | foo2
6lib.lib(this_is_a_long_name.obj): COFF object6lib.lib(this_is_a_long_name.obj): COFF object
7xxxx 00000000 4 NULL() EXTERNAL | this_is_a_long_name.fooWeak7xxxx 00000000 4 NULL() EXTERNAL | this_is_a_long_name.fooWeak
8xxxx 00000000 2 NULL EXTERNAL | foo_strong8xxxx 00000000 2 NULL EXTERNAL | foo_strong
9xxxx 00000000 2 NULL EXTERNAL | foo_strong_alias
10xxxx 00000010 2 NULL EXTERNAL | foo_array9xxxx 00000010 2 NULL EXTERNAL | foo_array
11xxxx 00000000 UNDEF NULL() WEAK_EXTERNAL | fooWeak10xxxx 00000000 UNDEF NULL() WEAK_EXTERNAL | fooWeak
12 | Weak External [falls back to relative ordinal 000000-4 via SEARCH_ALIAS]11 | Weak External [falls back to relative ordinal 000000-3 via SEARCH_ALIAS]
12xxxx 00000000 2 NULL EXTERNAL | foo_strong_alias