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
56745674 .opts = options,
56755675 .src = src,
56765676 .exported = target,
5677 .status = .in_progress,
56785677 });
56795678}
56805679
......@@ -5722,7 +5721,6 @@ pub fn analyzeExportSelfNav(
57225721 .opts = .{ .name = name },
57235722 .src = src,
57245723 .exported = .{ .nav = export_nav },
5725 .status = .in_progress,
57265724 });
57275725}
57285726
src/Zcu.zig+1-33
......@@ -757,14 +757,6 @@ pub const Export = struct {
757757 opts: Options,
758758 src: LazySrcLoc,
759759 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
769761 pub const Options = struct {
770762 name: InternPool.NullTerminatedString,
......@@ -3789,13 +3781,8 @@ pub fn resetUnit(zcu: *Zcu, unit: AnalUnit) void {
37893781 }
37903782 break :exports;
37913783 };
3792 for (zcu.all_exports.items[base..][0..len], base..) |exp, exp_index_usize| {
3784 for (base..base + len) |exp_index_usize| {
37933785 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 }
37993786 if (zcu.failed_exports.fetchSwapRemove(exp_index)) |failed_kv| {
38003787 failed_kv.value.destroy(gpa);
38013788 }
......@@ -3966,25 +3953,6 @@ pub fn getTarget(zcu: *const Zcu) *const Target {
39663953 return &zcu.root_mod.resolved_target.result;
39673954}
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
39883956pub fn addGlobalAssembly(zcu: *Zcu, unit: AnalUnit, source: []const u8) !void {
39893957 const gpa = zcu.gpa;
39903958 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
35543554/// Called from `Compilation.update`, after everything is done, just before
35553555/// reporting compile errors. In this function we emit exported symbol collision
35563556/// 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 {
35583558 const zcu = pt.zcu;
35593559 const gpa = zcu.gpa;
35603560
......@@ -3563,169 +3563,72 @@ pub fn processExports(pt: Zcu.PerThread) !void {
35633563 return;
35643564 }
35653565
3566 // First, construct a mapping of every exported value and Nav to the indices of all its different exports.
3567 var nav_exports: std.array_hash_map.Auto(InternPool.Nav.Index, std.ArrayList(Zcu.Export.Index)) = .empty;
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());
3566 var alive_exports: std.ArrayList(Zcu.Export.Index) = .empty;
3567 defer alive_exports.deinit(gpa);
35853568
35863569 const unit_references = try zcu.resolveReferences();
35873570
3571 try alive_exports.ensureUnusedCapacity(gpa, zcu.single_exports.count());
35883572 for (zcu.single_exports.keys(), zcu.single_exports.values()) |exporter, export_idx| {
3589 const exp = export_idx.ptr(zcu);
3590 if (!unit_references.contains(exporter)) {
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);
3573 if (!unit_references.contains(exporter)) continue;
3574 alive_exports.appendAssumeCapacity(export_idx);
36123575 }
36133576
36143577 for (zcu.multi_exports.keys(), zcu.multi_exports.values()) |exporter, info| {
3615 const exports = zcu.all_exports.items[info.index..][0..info.len];
3616 if (!unit_references.contains(exporter)) {
3617 // This export might already have been sent to the linker on a previous update, in which case we need to delete it.
3618 // The linker export API should be modified to eliminate this loop. #23616
3619 if (zcu.comp.bin_file) |lf| {
3620 if (zcu.llvm_object == null) {
3621 for (exports) |exp| {
3622 lf.deleteExport(exp.exported, exp.opts.name);
3623 }
3624 }
3625 }
3626 continue;
3578 if (!unit_references.contains(exporter)) continue;
3579 try alive_exports.ensureUnusedCapacity(gpa, info.len);
3580 for (0..info.len) |off| {
3581 const export_idx: Zcu.Export.Index = @fromBackingInt(@intCast(info.index + off));
3582 alive_exports.appendAssumeCapacity(export_idx);
36273583 }
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);
36613584 }
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(
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;
3594 try exports_by_name.ensureUnusedCapacity(gpa, alive_exports.items.len);
36763595
3677 for (export_indices) |export_idx| {
3678 const new_export = export_idx.ptr(zcu);
3679 const gop = try symbol_exports.getOrPut(gpa, new_export.opts.name);
3680 if (gop.found_existing) {
3681 new_export.status = .failed_retryable;
3682 try zcu.failed_exports.ensureUnusedCapacity(gpa, 1);
3683 const msg = try Zcu.ErrorMsg.create(gpa, new_export.src, "exported symbol collision: {f}", .{
3684 new_export.opts.name.fmt(ip),
3685 });
3686 errdefer msg.destroy(gpa);
3687 const other_export = gop.value_ptr.ptr(zcu);
3688 try zcu.errNote(other_export.src, msg, "other symbol here", .{});
3689 zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, msg);
3690 new_export.status = .failed;
3691 } else {
3692 gop.value_ptr.* = export_idx;
3693 }
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;
3596 for (alive_exports.items) |export_index| {
3597 const exp = export_index.ptr(zcu);
3598 const gop = exports_by_name.getOrPutAssumeCapacity(exp.opts.name);
3599 if (gop.found_existing) {
3600 const existing_exp = gop.value_ptr.*.ptr(zcu);
3601 try zcu.failed_exports.ensureUnusedCapacity(gpa, 1);
3602 const msg = try Zcu.ErrorMsg.create(
3603 gpa,
3604 exp.src,
3605 "exported symbol collision: {f}",
3606 .{exp.opts.name.fmt(&zcu.intern_pool)},
3607 );
3608 errdefer msg.destroy(gpa);
3609 try zcu.errNote(existing_exp.src, msg, "other symbol here", .{});
3610 zcu.failed_exports.putAssumeCapacityNoClobber(export_index, msg);
3611 } else {
3612 gop.value_ptr.* = export_index;
37143613 }
3715 break :failed false;
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 => {},
3614 }
37213615 }
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
37253622 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 };
37273627 } 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 };
37293632 }
37303633}
37313634
src/codegen/llvm.zig+104-109
......@@ -1441,43 +1441,46 @@ pub const Object = struct {
14411441
14421442 pub fn updateExports(
14431443 o: *Object,
1444 exported: Zcu.Exported,
14451444 export_indices: []const Zcu.Export.Index,
14461445 ) link.Error!void {
14471446 const zcu = o.zcu;
14481447 const ip = &zcu.intern_pool;
1449 const ty: Type, const llvm_ptr: Builder.Constant = switch (exported) {
1450 .nav => |nav| exp: {
1451 const nav_ty: Type = .fromInterned(ip.getNav(nav).resolved.?.type);
1452 const nav_ref = try o.lowerNavRef(nav);
1453 break :exp .{ nav_ty, nav_ref };
1454 },
1455 .uav => |uav| exp: {
1456 const uav_ty = Value.fromInterned(uav).typeOf(zcu);
1457 const uav_ref = try o.lowerUavRef(
1458 uav,
1459 uav_ty.abiAlignment(zcu).toLlvm(),
1460 target_util.defaultAddressSpace(zcu.getTarget(), .global_constant),
1461 );
1462 break :exp .{ uav_ty, uav_ref };
1463 },
1464 };
1465 switch (llvm_ptr.unwrap()) {
1466 .global => |global| return o.updateExportedGlobal(global, ty, export_indices),
1467 .constant => @panic("LLVM TODO: export zero-bit value"),
1448 for (export_indices) |export_index| {
1449 const ty: Type, const llvm_ptr: Builder.Constant = switch (export_index.ptr(zcu).exported) {
1450 .nav => |nav| exp: {
1451 const nav_ty: Type = .fromInterned(ip.getNav(nav).resolved.?.type);
1452 const nav_ref = try o.lowerNavRef(nav);
1453 break :exp .{ nav_ty, nav_ref };
1454 },
1455 .uav => |uav| exp: {
1456 const uav_ty = Value.fromInterned(uav).typeOf(zcu);
1457 const uav_ref = try o.lowerUavRef(
1458 uav,
1459 uav_ty.abiAlignment(zcu).toLlvm(),
1460 target_util.defaultAddressSpace(zcu.getTarget(), .global_constant),
1461 );
1462 break :exp .{ uav_ty, uav_ref };
1463 },
1464 };
1465 switch (llvm_ptr.unwrap()) {
1466 .global => |global| try o.addGlobalExport(global, ty, export_index),
1467 .constant => @panic("LLVM TODO: export zero-bit value"),
1468 }
14681469 }
14691470 }
14701471
1471 fn updateExportedGlobal(
1472 fn addGlobalExport(
14721473 o: *Object,
14731474 llvm_global: Builder.Global.Index,
14741475 ty: Type,
1475 export_indices: []const Zcu.Export.Index,
1476 export_index: Zcu.Export.Index,
14761477 ) link.Error!void {
14771478 const zcu = o.zcu;
14781479 const comp = zcu.comp;
14791480 const ip = &zcu.intern_pool;
14801481
1482 const exp = export_index.ptr(zcu);
1483
14811484 // If we're on COFF and linking with LLD, the linker cares about our exports to determine the subsystem in use.
14821485 coff_export_flags: {
14831486 const lf = comp.bin_file orelse break :coff_export_flags;
......@@ -1488,23 +1491,20 @@ pub const Object = struct {
14881491 };
14891492 if (ty.zigTypeTag(zcu) != .@"fn") break :coff_export_flags;
14901493 const flags = &coff.lld_export_flags;
1491 for (export_indices) |export_index| {
1492 const name = export_index.ptr(zcu).opts.name;
1493 if (name.eqlSlice("main", ip)) flags.c_main = true;
1494 if (name.eqlSlice("WinMain", ip)) flags.winmain = true;
1495 if (name.eqlSlice("wWinMain", ip)) flags.wwinmain = true;
1496 if (name.eqlSlice("WinMainCRTStartup", ip)) flags.winmain_crt_startup = true;
1497 if (name.eqlSlice("wWinMainCRTStartup", ip)) flags.wwinmain_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 }
1494 if (exp.opts.name.eqlSlice("main", ip)) flags.c_main = true;
1495 if (exp.opts.name.eqlSlice("WinMain", ip)) flags.winmain = true;
1496 if (exp.opts.name.eqlSlice("wWinMain", ip)) flags.wwinmain = true;
1497 if (exp.opts.name.eqlSlice("WinMainCRTStartup", ip)) flags.winmain_crt_startup = true;
1498 if (exp.opts.name.eqlSlice("wWinMainCRTStartup", ip)) flags.wwinmain_crt_startup = true;
1499 if (exp.opts.name.eqlSlice("DllMainCRTStartup", ip)) flags.dllmain_crt_startup = true;
1500 if (exp.opts.name.eqlSlice("_DllMainCRTStartup", ip)) flags.dllmain_crt_startup = true;
15011501 }
15021502
1503 // If the first export specifies a linksection, set the exported variable's section to that
1504 // one. This is kind of a hack because `std.lang.ExportOptions.section` doesn't actually
1505 // make much sense: the linksection should be associated with the declaration itself rather
1506 // than some particular symbol it is exported as!
1507 if (export_indices[0].ptr(zcu).opts.section.toSlice(ip)) |section_slice| {
1503 // If the export specifies a linksection, set the exported variable's section to that one.
1504 // This is kind of a hack because `std.lang.ExportOptions.section` doesn't actually make
1505 // much sense: the linksection should be associated with the declaration itself rather than
1506 // some particular symbol it is exported as!
1507 if (exp.opts.section.toSlice(ip)) |section_slice| {
15081508 const variable = &llvm_global.ptrConst(&o.builder).kind.variable;
15091509 variable.setSection(try o.builder.string(section_slice), &o.builder);
15101510 }
......@@ -1519,29 +1519,54 @@ pub const Object = struct {
15191519 // TODO: we currently do not delete old exports. To do that we'll need to track which
15201520 // globals actually *are* exports.
15211521
1522 for (export_indices, 0..) |export_idx, export_i| {
1523 const exp = export_idx.ptr(zcu);
1524 const exp_name = try o.builder.strtabString(exp.opts.name.toSlice(ip));
1522 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 already
1527 // taken by some existing global, we need to figure out what to do with that existing
1528 // global.
1529 //
1530 // The name, aliasee, and type will be set within this block. Other properties of the
1531 // alias will be set below.
1532 const alias_global: Builder.Global.Index = global: {
1533
1534 // 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 kernel
1536 // On AMDGCN, LLVM does not generate an alias for the kernel descriptor symbol on associated functions
1537 // To solve these, we rename the global
1538 if (workaround_alias_bugs and export_i == 0) {
1539 try llvm_global.rename(exp_name, &o.builder);
1540 break :global llvm_global;
1541 }
1524 // Our goal is to make an alias with the name `exp_name`, but if that name is already
1525 // taken by some existing global, we need to figure out what to do with that existing
1526 // global.
1527 //
1528 // The name, aliasee, and type will be set within this block. Other properties of the
1529 // alias will be set below.
1530 const alias_global: Builder.Global.Index = global: {
1531
1532 // WORKAROUND (see https://github.com/llvm/llvm-project/issues/213504, https://github.com/llvm/llvm-project/issues/214835)
1533 // For NVPTX, LLVM throws "NVPTX aliasee must be a non-kernel function definition" if we try to alias a kernel
1534 // On AMDGCN, LLVM does not generate an alias for the kernel descriptor symbol on associated functions
1535 // To solve these, we rename the global
1536 if (workaround_alias_bugs) {
1537 try llvm_global.rename(exp_name, &o.builder);
1538 break :global llvm_global;
1539 }
15421540
1543 const existing_global = o.builder.getGlobal(exp_name) orelse {
1544 // There is no existing global with this name, so make a new alias.
1541 const existing_global = o.builder.getGlobal(exp_name) orelse {
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.
15451570 const alias = try o.builder.addAlias(
15461571 exp_name,
15471572 llvm_global_ty,
......@@ -1549,58 +1574,28 @@ pub const Object = struct {
15491574 llvm_global.toConst(),
15501575 );
15511576 break :global alias.ptrConst(&o.builder).global;
1552 };
1553 // There is an existing global with this name, so we can't just create an alias. We
1554 // need to figure out what to do with the existing global instead.
1555 switch (existing_global.ptrConst(&o.builder).kind) {
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);
1577 },
1578 .replaced => unreachable, // a replaced global would have lost the name `exp_name`
1579 }
1580 };
15891581
1590 if (comp.config.dll_export_fns and exp.opts.visibility != .hidden)
1591 alias_global.setDllStorageClass(.dllexport, &o.builder);
1592 alias_global.setLinkage(switch (exp.opts.linkage) {
1593 .internal => if (o.builder.strip) .private else .internal, // we still did useful work in replacing an existing symbol if there was one
1594 .strong => .external,
1595 .weak => .weak_odr,
1596 .link_once => .linkonce_odr,
1597 }, &o.builder);
1598 alias_global.setVisibility(switch (exp.opts.visibility) {
1599 .default => .default,
1600 .hidden => .hidden,
1601 .protected => .protected,
1602 }, &o.builder);
1603 }
1582 // We need the alias to *not* be `unnamed_addr` to ensure that the alias address equals
1583 // the address of the original global.
1584 alias_global.setUnnamedAddr(.default, &o.builder);
1585
1586 if (comp.config.dll_export_fns and exp.opts.visibility != .hidden)
1587 alias_global.setDllStorageClass(.dllexport, &o.builder);
1588 alias_global.setLinkage(switch (exp.opts.linkage) {
1589 .internal => if (o.builder.strip) .private else .internal, // we still did useful work in replacing an existing symbol if there was one
1590 .strong => .external,
1591 .weak => .weak_odr,
1592 .link_once => .linkonce_odr,
1593 }, &o.builder);
1594 alias_global.setVisibility(switch (exp.opts.visibility) {
1595 .default => .default,
1596 .hidden => .hidden,
1597 .protected => .protected,
1598 }, &o.builder);
16041599 }
16051600
16061601 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 {
788788 }
789789 }
790790
791 /// May be called before or after updateExports for any given Nav.
792791 /// Asserts that the ZCU is not using the LLVM backend.
793792 fn updateNav(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Error!void {
794793 assert(base.comp.zcu.?.llvm_object == null);
......@@ -831,7 +830,6 @@ pub const File = struct {
831830 }
832831 }
833832
834 /// May be called before or after updateExports for any given Decl.
835833 /// The active tag of `mir` is determined by the backend used for the module this function is in.
836834 /// Never called when LLVM is codegenning the ZCU.
837835 fn updateFunc(
......@@ -971,15 +969,16 @@ pub const File = struct {
971969 }
972970 }
973971
974 /// This is called for every exported thing. `exports` is almost always
975 /// a list of size 1, meaning that `exported` is exported once. However, it is possible
976 /// to export the same thing with multiple different symbol names (aliases).
977 /// May be called before or after updateDecl for any given Decl.
972 /// This is called once per update, before `flush`.
973 ///
974 /// `export_indices` contains the index of every export from the ZCU which should be performed
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 ///
978978 /// Never called when LLVM is codegenning the ZCU.
979979 pub fn updateExports(
980980 base: *File,
981981 pt: Zcu.PerThread,
982 exported: Zcu.Exported,
983982 export_indices: []const Zcu.Export.Index,
984983 ) Error!void {
985984 assert(base.comp.zcu.?.llvm_object == null);
......@@ -992,7 +991,7 @@ pub const File = struct {
992991 .plan9 => unreachable,
993992 inline else => |tag| {
994993 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);
996995 },
997996 }
998997 }
......@@ -1071,28 +1070,6 @@ pub const File = struct {
10711070 }
10721071 }
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
10961073 pub const DumpResult = enum {
10971074 unimplemented,
10981075 needs_extensions,
src/link/C.zig+44-40
......@@ -1228,58 +1228,62 @@ const Flush = struct {
12281228pub fn updateExports(
12291229 c: *C,
12301230 pt: Zcu.PerThread,
1231 exported: Zcu.Exported,
12321231 export_indices: []const Zcu.Export.Index,
12331232) Allocator.Error!void {
12341233 const zcu = pt.zcu;
12351234 const gpa = zcu.gpa;
12361235
1236 c.exported_navs.clearRetainingCapacity();
1237 c.exported_uavs.clearRetainingCapacity();
1238
12371239 var arena: std.heap.ArenaAllocator = .init(gpa);
12381240 defer arena.deinit();
12391241
1240 var dg: codegen.DeclGen = .{
1241 .gpa = gpa,
1242 .arena = arena.allocator(),
1243 .pt = pt,
1244 .mod = zcu.root_mod,
1245 .owner_nav = .none,
1246 .is_naked_fn = false,
1247 .expected_block = null,
1248 .ctype_deps = .empty,
1249 .uavs = .empty,
1250 };
1251 defer {
1252 assert(dg.uavs.count() == 0);
1253 dg.ctype_deps.deinit(gpa);
1242 var by_exported: std.array_hash_map.Auto(Zcu.Exported, std.ArrayList(Zcu.Export.Index)) = .empty;
1243 try by_exported.ensureUnusedCapacity(arena.allocator(), export_indices.len);
1244
1245 for (export_indices) |exp_index| {
1246 const exported = exp_index.ptr(zcu).exported;
1247 const gop = by_exported.getOrPutAssumeCapacity(exported);
1248 if (!gop.found_existing) {
1249 gop.value_ptr.* = .empty;
1250 }
1251 try gop.value_ptr.append(arena.allocator(), exp_index);
12541252 }
12551253
1256 const code: String = code: {
1257 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);
1258 defer c.string_bytes = aw.toArrayList();
1259 const start = aw.written().len;
1260 codegen.genExports(&dg, &aw.writer, exported, export_indices) catch |err| switch (err) {
1261 error.WriteFailed => return error.OutOfMemory,
1262 error.OutOfMemory => |e| return e,
1254 for (by_exported.keys(), by_exported.values()) |exported, *exports_of_this| {
1255 var dg: codegen.DeclGen = .{
1256 .gpa = gpa,
1257 .arena = arena.allocator(),
1258 .pt = pt,
1259 .mod = zcu.root_mod,
1260 .owner_nav = .none,
1261 .is_naked_fn = false,
1262 .expected_block = null,
1263 .ctype_deps = .empty,
1264 .uavs = .empty,
12631265 };
1264 break :code .{
1265 .start = @intCast(start),
1266 .len = @intCast(aw.written().len - start),
1266 defer {
1267 assert(dg.uavs.count() == 0);
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 };
12671282 };
1268 };
1269 switch (exported) {
1270 .nav => |nav| try c.exported_navs.put(gpa, nav, code),
1271 .uav => |uav| try c.exported_uavs.put(gpa, uav, code),
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 switch (exported) {
1284 .nav => |nav| try c.exported_navs.put(gpa, nav, code),
1285 .uav => |uav| try c.exported_uavs.put(gpa, uav, code),
1286 }
12831287 }
12841288}
12851289
src/link/Coff.zig+134-147
......@@ -5893,10 +5893,6 @@ pub fn flush(
58935893
58945894 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
59005896 while (try coff.resolve(tid)) {}
59015897 while (try coff.idle(tid)) {}
59025898
......@@ -7374,30 +7370,38 @@ fn virtualSlide(coff: *Coff, start_section_index: usize, start_rva: u32) !void {
73747370pub fn updateExports(
73757371 coff: *Coff,
73767372 pt: Zcu.PerThread,
7377 exported: Zcu.Exported,
73787373 export_indices: []const Zcu.Export.Index,
73797374) link.Error!void {
7375 // TODO: delete old exports from first/second linker member table
7376 // TODO: delete old exports from symbol table inside section
73807377 const diags = &coff.base.comp.link_diags;
7381 return coff.updateExportsInner(pt, exported, export_indices) catch |err| switch (err) {
7382 error.MappedFileIo => return diags.fail(
7383 "failed to write output file: {t}",
7384 .{coff.mf.io_err.?},
7385 ),
7386 else => |e| return e,
7387 };
7378 var alias_syms: std.array_hash_map.Auto(Symbol.Index, Symbol.Index) = .empty;
7379 defer alias_syms.deinit(coff.base.comp.gpa);
7380 for (export_indices) |export_index| {
7381 coff.updateExportInner(pt, export_index, &alias_syms) catch |err| switch (err) {
7382 error.MappedFileIo => return diags.fail(
7383 "failed to write output file: {t}",
7384 .{coff.mf.io_err.?},
7385 ),
7386 else => |e| return e,
7387 };
7388 }
7389 coff.exports_complete = true;
73887390}
7389fn updateExportsInner(
7391fn updateExportInner(
73907392 coff: *Coff,
73917393 pt: Zcu.PerThread,
7392 exported: Zcu.Exported,
7393 export_indices: []const Zcu.Export.Index,
7394 export_index: Zcu.Export.Index,
7395 alias_syms: *std.array_hash_map.Auto(Symbol.Index, Symbol.Index),
73947396) !void {
73957397 const zcu = pt.zcu;
73967398 const gpa = zcu.gpa;
73977399 const ip = &zcu.intern_pool;
73987400
7399 try coff.symbols.ensureUnusedCapacity(gpa, export_indices.len);
7400 const exported_si: Symbol.Index = switch (exported) {
7401 const exp = export_index.ptr(zcu);
7402
7403 try coff.symbols.ensureUnusedCapacity(gpa, 1);
7404 const exported_si: Symbol.Index = switch (exp.exported) {
74017405 .nav => |nav| try coff.navSymbol(zcu, nav),
74027406 .uav => |uav| @fromBackingInt(@intCast(@backingInt(try coff.lowerUav(
74037407 pt,
......@@ -7405,7 +7409,7 @@ fn updateExportsInner(
74057409 Type.fromInterned(ip.typeOf(uav)).abiAlignment(zcu),
74067410 )))),
74077411 };
7408 switch (exported) {
7412 switch (exp.exported) {
74097413 .nav => |nav| log.debug("updateExports({f}) = {d}", .{ ip.getNav(nav).fqn.fmt(ip), exported_si }),
74107414 .uav => |uav| log.debug("updateExports(@as({f}, {f})) = {d}", .{
74117415 Type.fromInterned(ip.typeOf(uav)).fmt(pt),
......@@ -7419,154 +7423,137 @@ fn updateExportsInner(
74197423 const machine = coff.targetLoad(&coff.headerPtr().machine);
74207424 const exported_ni = exported_si.node(coff);
74217425 const exported_sym = exported_si.get(coff);
7422 var prev_alias_si = exported_si;
74237426
7424 for (export_indices) |export_index| {
7425 const @"export" = export_index.ptr(zcu);
7426 const name = @"export".opts.name.toSlice(ip);
7427
7428 // TODO: add an errMsg if this conflicts with an existing symbol
7429 const export_si = try coff.globalSymbol(.{ .name = name });
7430 const export_sym = export_si.get(coff);
7431 export_sym.ni = exported_ni;
7432 export_sym.rva = exported_sym.rva;
7433 export_sym.section_number = exported_sym.section_number;
7434 if (@"export".opts.linkage == .weak and !coff.isImage()) {
7435 // exported_si needs to be ahead of export_si in the symbol table,
7436 // so that its sti is known when creating the weak external aux entry
7437 try coff.pendingSymbolTableEntry(exported_si);
7438 export_sym.flags.weak_external_strat = .alias;
7439 export_sym.setValue(.{ .weak_alias_si = exported_si });
7440 }
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;
7427 const @"export" = export_index.ptr(zcu);
7428 const name = @"export".opts.name.toSlice(ip);
7429
7430 // TODO: add an errMsg if this conflicts with an existing symbol
7431 const export_si = try coff.globalSymbol(.{ .name = name });
7432 const export_sym = export_si.get(coff);
7433 export_sym.ni = exported_ni;
7434 export_sym.rva = exported_sym.rva;
7435 export_sym.section_number = exported_sym.section_number;
7436 if (@"export".opts.linkage == .weak and !coff.isImage()) {
7437 // 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
7439 try coff.pendingSymbolTableEntry(exported_si);
7440 export_sym.flags.weak_external_strat = .alias;
7441 export_sym.setValue(.{ .weak_alias_si = exported_si });
7442 }
7443 defer export_si.applyTargetRelocs(coff, .none) catch unreachable;
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 };
7458 const gop = try coff.export_table.entries.getOrPutAdapted(
7459 gpa,
7460 name,
7461 entries_ctx,
7462 );
7452 // The last symbol in the alias list holds the size
7453 const prev_alias_sym = prev_alias_si.get(coff);
7454 switch (prev_alias_sym.flags.extra_tag) {
7455 .size => export_sym.setExtra(.{ .size = prev_alias_sym.extra.size }),
7456 // This export should have been deleted
7457 .next_alias_si => assert(prev_alias_sym.extra.next_alias_si == export_si),
7458 else => unreachable,
7459 }
74637460
7464 if (!gop.found_existing) {
7465 errdefer _ = coff.export_table.entries.pop();
7461 prev_alias_sym.setExtra(.{ .next_alias_si = export_si });
74667462
7467 const export_count = coff.export_table.entries.count();
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", .{});
7463 if (!coff.isImage()) return;
74707464
7471 const name_index: u32 = @intCast(coff.export_table.name_table_ni.location(&coff.mf).resolve(&coff.mf)[1]);
7472 const new_name_table_size = name_index + name.len + 1;
7473 if (new_name_table_size > std.math.maxInt(@FieldType(ExportTable.Entry, "name_index")))
7474 return coff.base.comp.link_diags.fail("exports name table limit reached", .{});
7465 const entries_ctx = ExportTable.Adapter{ .coff = coff };
7466 const gop = try coff.export_table.entries.getOrPutAdapted(
7467 gpa,
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);
7479 @memcpy(name_table_slice[name_index..][0 .. name.len + 1], name[0 .. name.len + 1]);
7480
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 }
7475 const export_count = coff.export_table.entries.count();
7476 if (export_count > std.math.maxInt(@FieldType(std.coff.ExportDirectoryTable, "number_of_entries")))
7477 return coff.base.comp.link_diags.fail("exceeded maximum number of exports", .{});
74927478
7493 const edt = coff.exportDirectoryTable();
7494 coff.targetStore(&edt.number_of_names, @intCast(export_count));
7495 edt.number_of_entries = edt.number_of_names;
7479 const name_index: u32 = @intCast(coff.export_table.name_table_ni.location(&coff.mf).resolve(&coff.mf)[1]);
7480 const new_name_table_size = name_index + name.len + 1;
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 exports
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 );
7484 try coff.export_table.name_table_ni.resize(&coff.mf, gpa, new_name_table_size);
75047485
7505 try coff.export_table.name_pointer_table_ni.resize(
7506 &coff.mf,
7507 gpa,
7508 export_count * @sizeOf(std.coff.ExportNamePointerTableEntry),
7509 );
7486 const name_table_slice = coff.export_table.name_table_ni.slice(&coff.mf);
7487 @memcpy(name_table_slice[name_index..][0 .. name.len + 1], name[0 .. name.len + 1]);
75107488
7511 try coff.export_table.ordinal_table_ni.resize(
7512 &coff.mf,
7513 gpa,
7514 export_count * @sizeOf(std.coff.ExportOrdinalTableEntry),
7515 );
7489 // If the new name sorts after the current tail of the sorted list, we don't need to re-sort
7490 {
7491 const ordinal_table_slice = coff.exportOrdinalTableSlice();
7492 if (ordinal_table_slice.len > 0 and !coff.export_table.pending_sort) {
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(
7518 &coff.exportNamePointerTableSlice()[gop.index].name_rva,
7519 @intCast(coff.computeNodeRva(coff.export_table.name_table_ni) + name_index),
7520 );
7521 coff.targetStore(
7522 &coff.exportOrdinalTableSlice()[gop.index].unbiased_ordinal,
7523 @intCast(gop.index),
7524 );
7501 const edt = coff.exportDirectoryTable();
7502 coff.targetStore(&edt.number_of_names, @intCast(export_count));
7503 edt.number_of_entries = edt.number_of_names;
75257504
7526 gop.value_ptr.* = .{
7527 .si = export_si,
7528 .name_index = @intCast(name_index),
7529 .name_len = @intCast(name.len),
7530 .export_address_table_ri = @fromBackingInt(@intCast(coff.relocs.items.len)),
7531 };
7505 // TODO: These should all be resized ahead of time to fit all exports
7506 // after https://github.com/ziglang/zig/issues/23616
7507 try coff.export_table.export_address_table_si.node(coff).resize(
7508 &coff.mf,
7509 gpa,
7510 export_count * @sizeOf(std.coff.ExportAddressTableEntry),
7511 );
75327512
7533 try coff.addReloc(
7534 coff.export_table.export_address_table_si,
7535 @intCast(@sizeOf(std.coff.ExportAddressTableEntry) * gop.index),
7536 export_si,
7537 .{ .known = 0 },
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}
7513 try coff.export_table.name_pointer_table_ni.resize(
7514 &coff.mf,
7515 gpa,
7516 export_count * @sizeOf(std.coff.ExportNamePointerTableEntry),
7517 );
75517518
7552pub fn deleteExport(
7553 coff: *Coff,
7554 exported: Zcu.Exported,
7555 name: InternPool.NullTerminatedString,
7556) void {
7557 const zcu = coff.base.comp.zcu.?;
7558 const ip = &zcu.intern_pool;
7519 try coff.export_table.ordinal_table_ni.resize(
7520 &coff.mf,
7521 gpa,
7522 export_count * @sizeOf(std.coff.ExportOrdinalTableEntry),
7523 );
75597524
7560 const exported_si: Symbol.Index = switch (exported) {
7561 .nav => |nav| coff.navs.get(nav).?,
7562 .uav => |uav| coff.uavs.get(uav).?,
7563 };
7525 coff.targetStore(
7526 &coff.exportNamePointerTableSlice()[gop.index].name_rva,
7527 @intCast(coff.computeNodeRva(coff.export_table.name_table_ni) + name_index),
7528 );
7529 coff.targetStore(
7530 &coff.exportOrdinalTableSlice()[gop.index].unbiased_ordinal,
7531 @intCast(gop.index),
7532 );
75647533
7565 const name_slice = name.toSlice(ip);
7566 log.debug("deleteExport({s}, {d})", .{ name_slice, exported_si });
7534 gop.value_ptr.* = .{
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 table
7569 // TODO: Delete from symbol table inside section
7541 try coff.addReloc(
7542 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 }
75707557}
75717558
75727559fn dumpStderr(coff: *Coff, tid: Zcu.PerThread.Id) !void {
src/link/Elf.zig+1-10
......@@ -1685,24 +1685,15 @@ pub fn updateContainerType(
16851685pub fn updateExports(
16861686 self: *Elf,
16871687 pt: Zcu.PerThread,
1688 exported: Zcu.Exported,
16891688 export_indices: []const Zcu.Export.Index,
16901689) link.Error!void {
1691 return self.zigObjectPtr().?.updateExports(self, pt, exported, export_indices);
1690 return self.zigObjectPtr().?.updateExports(self, pt, export_indices);
16921691}
16931692
16941693pub fn updateLineNumber(self: *Elf, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) link.Error!void {
16951694 return self.zigObjectPtr().?.updateLineNumber(pt, ti_id);
16961695}
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
17061697fn checkDuplicates(self: *Elf) !void {
17071698 const gpa = self.base.comp.gpa;
17081699
src/link/Elf/ZigObject.zig+44-57
......@@ -1857,7 +1857,6 @@ pub fn updateExports(
18571857 self: *ZigObject,
18581858 elf_file: *Elf,
18591859 pt: Zcu.PerThread,
1860 exported: Zcu.Exported,
18611860 export_indices: []const Zcu.Export.Index,
18621861) link.Error!void {
18631862 const tracy = trace(@src());
......@@ -1865,27 +1864,49 @@ pub fn updateExports(
18651864
18661865 const zcu = pt.zcu;
18671866 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| {
1884 const exp = export_idx.ptr(zcu);
1868 // Delete all existing exports first
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];
18851906 if (exp.opts.section.unwrap()) |section_name| {
18861907 if (!section_name.eqlSlice(".text", &zcu.intern_pool)) {
1887 try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1);
1888 zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, try Zcu.ErrorMsg.create(
1908 try zcu.failed_exports.ensureUnusedCapacity(gpa, 1);
1909 zcu.failed_exports.putAssumeCapacityNoClobber(export_index, try Zcu.ErrorMsg.create(
18891910 gpa,
18901911 exp.src,
18911912 "Unimplemented: ExportOptions.section",
......@@ -1899,8 +1920,8 @@ pub fn updateExports(
18991920 .strong => elf.STB_GLOBAL,
19001921 .weak => elf.STB_WEAK,
19011922 .link_once => {
1902 try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1);
1903 zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, try Zcu.ErrorMsg.create(
1923 try zcu.failed_exports.ensureUnusedCapacity(gpa, 1);
1924 zcu.failed_exports.putAssumeCapacityNoClobber(export_index, try Zcu.ErrorMsg.create(
19041925 gpa,
19051926 exp.src,
19061927 "Unimplemented: GlobalLinkage.LinkOnce",
......@@ -1912,13 +1933,8 @@ pub fn updateExports(
19121933 const stt_bits: u8 = @as(u4, @truncate(esym.st_info));
19131934 const exp_name = exp.opts.name.toSlice(&zcu.intern_pool);
19141935 const name_off = try self.strtab.insert(gpa, exp_name);
1915 const global_sym_index = if (metadata.@"export"(self, exp_name)) |exp_index|
1916 exp_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 };
1936 const global_sym_index = try self.getGlobalSymbol(elf_file, exp_name, null);
1937 try metadata.exports.append(gpa, global_sym_index);
19221938
19231939 const value = self.symbol(sym_index).value;
19241940 const global_sym = self.symbol(global_sym_index);
......@@ -1947,27 +1963,6 @@ pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.T
19471963 }
19481964}
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
19711966pub fn getGlobalSymbol(self: *ZigObject, elf_file: *Elf, name: []const u8, lib_name: ?[]const u8) !u32 {
19721967 _ = lib_name;
19731968 const gpa = elf_file.base.comp.gpa;
......@@ -2362,14 +2357,6 @@ const AvMetadata = struct {
23622357 exports: std.ArrayList(Symbol.Index) = .empty,
23632358 /// Set to true if the AV has been initialized and allocated.
23642359 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 }
23732360};
23742361
23752362fn 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
82878287pub fn updateExports(
82888288 elf: *Elf,
82898289 pt: Zcu.PerThread,
8290 exported: Zcu.Exported,
82918290 export_indices: []const Zcu.Export.Index,
82928291) link.Error!void {
82938292 const diags = &elf.base.comp.link_diags;
8294 return elf.updateExportsInner(pt, exported, export_indices) catch |err| switch (err) {
8295 else => |e| return e,
8296 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
8297 };
8293 for (export_indices) |export_index| {
8294 elf.updateExportInner(pt, export_index) catch |err| switch (err) {
8295 else => |e| return e,
8296 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
8297 };
8298 }
82988299}
8299fn updateExportsInner(
8300fn updateExportInner(
83008301 elf: *Elf,
83018302 pt: Zcu.PerThread,
8302 exported: Zcu.Exported,
8303 export_indices: []const Zcu.Export.Index,
8303 export_index: Zcu.Export.Index,
83048304) Error!void {
83058305 const zcu = pt.zcu;
83068306 const ip = &zcu.intern_pool;
83078307
8308 switch (exported) {
8308 const @"export" = export_index.ptr(zcu);
8309
8310 switch (@"export".exported) {
83098311 .nav => |nav| log.debug("updateExports({f})", .{ip.getNav(nav).fqn.fmt(ip)}),
83108312 .uav => |uav| log.debug("updateExports(@as({f}, {f}))", .{
83118313 Type.fromInterned(ip.typeOf(uav)).fmt(pt),
83128314 Value.fromInterned(uav).fmtValue(pt),
83138315 }),
83148316 }
8315 try elf.ensureUnusedSymbolCapacity(@intCast(export_indices.len), .maybe_global);
8316 const exported_lsi: Symbol.LocalIndex, const @"type": std.elf.STT = switch (exported) {
8317 try elf.ensureUnusedSymbolCapacity(1, .maybe_global);
8318 const exported_lsi: Symbol.LocalIndex, const @"type": std.elf.STT = switch (@"export".exported) {
83178319 .nav => |nav| .{
83188320 (try elf.navMapIndex(zcu, nav)).symbol(elf),
83198321 elf.navType(ip.getNav(nav).resolved.?),
......@@ -8331,50 +8333,42 @@ fn updateExportsInner(
83318333 .fromSection(elf.targetLoad(&exported_sym.shndx)),
83328334 },
83338335 };
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 {
8375 _ = elf;
8376 _ = exported;
8377 _ = name;
8337 const name = @"export".opts.name.toSlice(ip);
8338 _ = elf.addGlobalSymbolAssumeCapacity(.{
8339 .node = .none,
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 };
83788372}
83798373
83808374fn 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
31023102pub fn updateExports(
31033103 self: *MachO,
31043104 pt: Zcu.PerThread,
3105 exported: Zcu.Exported,
31063105 export_indices: []const Zcu.Export.Index,
31073106) link.Error!void {
3108 return self.getZigObject().?.updateExports(self, pt, exported, 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);
3107 return self.getZigObject().?.updateExports(self, pt, export_indices);
31173108}
31183109
31193110pub fn freeNav(self: *MachO, nav: InternPool.Nav.Index) void {
src/link/MachO/ZigObject.zig+54-62
......@@ -1238,7 +1238,6 @@ pub fn updateExports(
12381238 self: *ZigObject,
12391239 macho_file: *MachO,
12401240 pt: Zcu.PerThread,
1241 exported: Zcu.Exported,
12421241 export_indices: []const Zcu.Export.Index,
12431242) link.Error!void {
12441243 const tracy = trace(@src());
......@@ -1246,26 +1245,60 @@ pub fn updateExports(
12461245
12471246 const zcu = pt.zcu;
12481247 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| {
1264 const exp = export_idx.ptr(zcu);
1249 // Delete all existing exports first
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
12651298 if (exp.opts.section.unwrap()) |section_name| {
12661299 if (!section_name.eqlSlice("__text", &zcu.intern_pool)) {
12671300 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(
12691302 gpa,
12701303 exp.src,
12711304 "Unimplemented: ExportOptions.section",
......@@ -1275,7 +1308,7 @@ pub fn updateExports(
12751308 }
12761309 }
12771310 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(
12791312 gpa,
12801313 exp.src,
12811314 "Unimplemented: GlobalLinkage.link_once",
......@@ -1285,13 +1318,9 @@ pub fn updateExports(
12851318 }
12861319
12871320 const exp_name = exp.opts.name.toSlice(&zcu.intern_pool);
1288 const global_nlist_index = if (metadata.@"export"(self, exp_name)) |exp_index|
1289 exp_index.*
1290 else blk: {
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 };
1321 const global_nlist_index = try self.getGlobalSymbol(macho_file, exp_name, null);
1322 try metadata.exports.append(gpa, global_nlist_index);
1323
12951324 const global_nlist = &self.symtab.items(.nlist)[global_nlist_index];
12961325 const atom_index = self.symtab.items(.atom)[nlist_idx];
12971326 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
14001429 }
14011430}
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
14311432pub fn getGlobalSymbol(self: *ZigObject, macho_file: *MachO, name: []const u8, lib_name: ?[]const u8) !u32 {
14321433 _ = lib_name;
14331434 const gpa = macho_file.base.comp.gpa;
......@@ -1722,15 +1723,6 @@ const AvMetadata = struct {
17221723 symbol_index: Symbol.Index,
17231724 /// A list of all exports aliases of this Av.
17241725 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 }
17341726};
17351727
17361728const LazySymbolMetadata = struct {
src/link/SpirV.zig+10-14
......@@ -219,24 +219,20 @@ pub fn updateNav(linker: *Linker, pt: Zcu.PerThread, nav: InternPool.Nav.Index)
219219pub fn updateExports(
220220 linker: *Linker,
221221 pt: Zcu.PerThread,
222 exported: Zcu.Exported,
223222 export_indices: []const Zcu.Export.Index,
224) !void {
223) link.Error!void {
225224 const zcu = pt.zcu;
226225 const ip = &zcu.intern_pool;
227226 const gpa = linker.base.comp.gpa;
228 const nav_index = switch (exported) {
229 .nav => |nav| nav,
230 .uav => |uav| {
231 _ = uav;
232 @panic("TODO: implement Linker linker code for exporting a constant value");
233 },
234 };
235 const nav_ty = ip.getNav(nav_index).resolved.?.type;
236 if (ip.isFunctionType(nav_ty)) {
237 const cc = Type.fromInterned(nav_ty).fnCallingConvention(zcu);
238 for (export_indices) |export_idx| {
239 const exp = export_idx.ptr(zcu);
227 for (export_indices) |exp_index| {
228 const exp = exp_index.ptr(zcu);
229 const nav_index = switch (exp.exported) {
230 .nav => |nav| nav,
231 .uav => @panic("TODO: implement Linker linker code for exporting a constant value"),
232 };
233 const nav_ty = ip.getNav(nav_index).resolved.?.type;
234 if (ip.isFunctionType(nav_ty)) {
235 const cc = Type.fromInterned(nav_ty).fnCallingConvention(zcu);
240236 try linker.entry_points.append(gpa, .{
241237 .nav = nav_index,
242238 .name = exp.opts.name.toSlice(ip),
src/link/Wasm.zig+33-50
......@@ -3,11 +3,11 @@
33//! performed without any knowledge of functions and globals provided by the
44//! Zcu. If there is no Zcu, effectively all linking is done in `prelink`.
55//!
6//! `updateFunc`, `updateNav`, `updateExports`, and `deleteExport` are handled
7//! by merely tracking references to the relevant functions and globals. All
8//! the linking logic between objects and Zcu happens in `flush`. Many
9//! components of the final output are computed on-the-fly at this time rather
10//! than being precomputed and stored separately.
6//! `updateFunc`, `updateNav`, and `updateExports` are handled by merely
7//! tracking references to the relevant functions and globals. All the linking
8//! logic between objects and Zcu happens in `flush`. Many components of the
9//! final output are computed on-the-fly at this time rather than being
10//! precomputed and stored separately.
1111
1212const Wasm = @This();
1313const Archive = @import("Wasm/Archive.zig");
......@@ -219,9 +219,9 @@ data_imports_len_prelink: u32 = 0,
219219/// objects.
220220///
221221/// During the Zcu phase, entries are not deleted from this table
222/// because doing so would be irreversible when a `deleteExport` call is
223/// handled. However, entries are added during the Zcu phase when extern
224/// functions are passed to `updateNav`.
222/// because doing so would be irreversible when an export is deleted.
223/// However, entries are added during the Zcu phase when extern functions
224/// are passed to `updateNav`.
225225///
226226/// `flush` gets a copy of this table, and then Zcu exports are applied to
227227/// 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,
232232/// objects.
233233///
234234/// During the Zcu phase, entries are not deleted from this table
235/// because doing so would be irreversible when a `deleteExport` call is
236/// handled. However, entries are added during the Zcu phase when extern
237/// functions are passed to `updateNav`.
235/// because doing so would be irreversible when an export is deleted.
236/// However, entries are added during the Zcu phase when extern functions
237/// are passed to `updateNav`.
238238///
239239/// `flush` gets a copy of this table, and then Zcu exports are applied to
240240/// 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
37443744 }
37453745}
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
37653747pub fn updateExports(
37663748 wasm: *Wasm,
37673749 pt: Zcu.PerThread,
3768 exported: Zcu.Exported,
37693750 export_indices: []const Zcu.Export.Index,
37703751) !void {
37713752 const zcu = pt.zcu;
37723753 const gpa = zcu.gpa;
37733754 const ip = &zcu.intern_pool;
37743755 const is_obj = wasm.base.comp.config.output_mode == .Obj;
3775 switch (exported) {
3776 .nav => {}, // handled in updateNav
3777 .uav => |uav_index| { // export may be the only reference
3778 const zds: ZcuDataStarts = .init(wasm);
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 }
3756
3757 wasm.nav_exports.clearRetainingCapacity();
3758 wasm.uav_exports.clearRetainingCapacity();
3759
37933760 for (export_indices) |export_idx| {
37943761 const exp = export_idx.ptr(zcu);
37953762 const name_slice = exp.opts.name.toSlice(ip);
37963763 const name = try wasm.internString(name_slice);
3797 switch (exported) {
3764 switch (exp.exported) {
37983765 .nav => |nav_index| {
37993766 log.debug("updateExports '{s}' nav={d}", .{ name_slice, @backingInt(nav_index) });
38003767 try wasm.nav_exports.put(gpa, .{ .nav_index = nav_index, .name = name }, export_idx);
38013768 },
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 },
38033786 }
38043787 }
38053788}
test/link/snapshots/static-lib.llvm.dmp+1-1
......@@ -8,8 +8,8 @@ xxxx 00000000 1 NULL() STATIC | this_is_a_long_name.fooWeak
88xxxx 00000000 2 NULL STATIC | this_is_a_long_name.foo_strong
99xxxx 00000008 2 NULL STATIC | this_is_a_long_name.foo_array
1010xxxx 00000000 2 NULL EXTERNAL | foo_strong
11xxxx 00000000 2 NULL EXTERNAL | foo_strong_alias
1211xxxx 00000008 2 NULL EXTERNAL | foo_array
1312xxxx 00000000 UNDEF NULL WEAK_EXTERNAL | fooWeak
1413 | Weak External [falls back to relative ordinal 000000+2 via SEARCH_ALIAS]
1514xxxx 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
66lib.lib(this_is_a_long_name.obj): COFF object
77xxxx 00000000 4 NULL() EXTERNAL | this_is_a_long_name.fooWeak
88xxxx 00000000 2 NULL EXTERNAL | foo_strong
9xxxx 00000000 2 NULL EXTERNAL | foo_strong_alias
109xxxx 00000010 2 NULL EXTERNAL | foo_array
1110xxxx 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