| author | |
| committer | |
| log | 31500242f0c82b8d3b5e1d7a683a8a6f4c803bb2 |
| tree | 83d4706de1f7910d1bf022f5f7a8d5b3434e522f |
| parent | e25015bdce1e6ff4ab594a34fc7d1f0cd296dc25 |
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/2361616 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 | 5674 | .opts = options, |
| 5675 | 5675 | .src = src, |
| 5676 | 5676 | .exported = target, |
| 5677 | .status = .in_progress, | |
| 5678 | 5677 | }); |
| 5679 | 5678 | } |
| 5680 | 5679 | |
| ... | ... | @@ -5722,7 +5721,6 @@ pub fn analyzeExportSelfNav( |
| 5722 | 5721 | .opts = .{ .name = name }, |
| 5723 | 5722 | .src = src, |
| 5724 | 5723 | .exported = .{ .nav = export_nav }, |
| 5725 | .status = .in_progress, | |
| 5726 | 5724 | }); |
| 5727 | 5725 | } |
| 5728 | 5726 |
src/Zcu.zig+1-33| ... | ... | @@ -757,14 +757,6 @@ pub const Export = struct { |
| 757 | 757 | opts: Options, |
| 758 | 758 | src: LazySrcLoc, |
| 759 | 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 | }, | |
| 768 | 760 | |
| 769 | 761 | pub const Options = struct { |
| 770 | 762 | name: InternPool.NullTerminatedString, |
| ... | ... | @@ -3789,13 +3781,8 @@ pub fn resetUnit(zcu: *Zcu, unit: AnalUnit) void { |
| 3789 | 3781 | } |
| 3790 | 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 | 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 | 3786 | if (zcu.failed_exports.fetchSwapRemove(exp_index)) |failed_kv| { |
| 3800 | 3787 | failed_kv.value.destroy(gpa); |
| 3801 | 3788 | } |
| ... | ... | @@ -3966,25 +3953,6 @@ pub fn getTarget(zcu: *const Zcu) *const Target { |
| 3966 | 3953 | return &zcu.root_mod.resolved_target.result; |
| 3967 | 3954 | } |
| 3968 | 3955 | |
| 3969 | pub 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 | ||
| 3988 | 3956 | pub fn addGlobalAssembly(zcu: *Zcu, unit: AnalUnit, source: []const u8) !void { |
| 3989 | 3957 | const gpa = zcu.gpa; |
| 3990 | 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 | 3554 | /// Called from `Compilation.update`, after everything is done, just before |
| 3555 | 3555 | /// reporting compile errors. In this function we emit exported symbol collision |
| 3556 | 3556 | /// errors and communicate exported symbols to the linker backend. |
| 3557 | pub fn processExports(pt: Zcu.PerThread) !void { | |
| 3557 | pub fn processExports(pt: Zcu.PerThread) (Allocator.Error || Io.Cancelable)!void { | |
| 3558 | 3558 | const zcu = pt.zcu; |
| 3559 | 3559 | const gpa = zcu.gpa; |
| 3560 | 3560 | |
| ... | ... | @@ -3563,169 +3563,72 @@ pub fn processExports(pt: Zcu.PerThread) !void { |
| 3563 | 3563 | return; |
| 3564 | 3564 | } |
| 3565 | 3565 | |
| 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); | |
| 3585 | 3568 | |
| 3586 | 3569 | const unit_references = try zcu.resolveReferences(); |
| 3587 | 3570 | |
| 3571 | try alive_exports.ensureUnusedCapacity(gpa, zcu.single_exports.count()); | |
| 3588 | 3572 | 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); | |
| 3612 | 3575 | } |
| 3613 | 3576 | |
| 3614 | 3577 | 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); | |
| 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 | } | |
| 3663 | 3585 | |
| 3664 | const 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); | |
| 3665 | 3593 | |
| 3666 | fn 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); | |
| 3676 | 3595 | |
| 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; | |
| 3714 | 3613 | } |
| 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 | } | |
| 3721 | 3615 | } |
| 3722 | 3616 | |
| 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; | |
| 3724 | 3621 | |
| 3725 | 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 | 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 | } |
| 3731 | 3634 |
src/codegen/llvm.zig+104-109| ... | ... | @@ -1441,43 +1441,46 @@ pub const Object = struct { |
| 1441 | 1441 | |
| 1442 | 1442 | pub fn updateExports( |
| 1443 | 1443 | o: *Object, |
| 1444 | exported: Zcu.Exported, | |
| 1445 | 1444 | export_indices: []const Zcu.Export.Index, |
| 1446 | 1445 | ) link.Error!void { |
| 1447 | 1446 | const zcu = o.zcu; |
| 1448 | 1447 | 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 | } | |
| 1468 | 1469 | } |
| 1469 | 1470 | } |
| 1470 | 1471 | |
| 1471 | fn updateExportedGlobal( | |
| 1472 | fn addGlobalExport( | |
| 1472 | 1473 | o: *Object, |
| 1473 | 1474 | llvm_global: Builder.Global.Index, |
| 1474 | 1475 | ty: Type, |
| 1475 | export_indices: []const Zcu.Export.Index, | |
| 1476 | export_index: Zcu.Export.Index, | |
| 1476 | 1477 | ) link.Error!void { |
| 1477 | 1478 | const zcu = o.zcu; |
| 1478 | 1479 | const comp = zcu.comp; |
| 1479 | 1480 | const ip = &zcu.intern_pool; |
| 1480 | 1481 | |
| 1482 | const exp = export_index.ptr(zcu); | |
| 1483 | ||
| 1481 | 1484 | // If we're on COFF and linking with LLD, the linker cares about our exports to determine the subsystem in use. |
| 1482 | 1485 | coff_export_flags: { |
| 1483 | 1486 | const lf = comp.bin_file orelse break :coff_export_flags; |
| ... | ... | @@ -1488,23 +1491,20 @@ pub const Object = struct { |
| 1488 | 1491 | }; |
| 1489 | 1492 | if (ty.zigTypeTag(zcu) != .@"fn") break :coff_export_flags; |
| 1490 | 1493 | 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; | |
| 1501 | 1501 | } |
| 1502 | 1502 | |
| 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| { | |
| 1508 | 1508 | const variable = &llvm_global.ptrConst(&o.builder).kind.variable; |
| 1509 | 1509 | variable.setSection(try o.builder.string(section_slice), &o.builder); |
| 1510 | 1510 | } |
| ... | ... | @@ -1519,29 +1519,54 @@ pub const Object = struct { |
| 1519 | 1519 | // TODO: we currently do not delete old exports. To do that we'll need to track which |
| 1520 | 1520 | // globals actually *are* exports. |
| 1521 | 1521 | |
| 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)); | |
| 1525 | 1523 | |
| 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 | } | |
| 1542 | 1540 | |
| 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. | |
| 1545 | 1570 | const alias = try o.builder.addAlias( |
| 1546 | 1571 | exp_name, |
| 1547 | 1572 | llvm_global_ty, |
| ... | ... | @@ -1549,58 +1574,28 @@ pub const Object = struct { |
| 1549 | 1574 | llvm_global.toConst(), |
| 1550 | 1575 | ); |
| 1551 | 1576 | 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 | }; | |
| 1589 | 1581 | |
| 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); | |
| 1604 | 1599 | } |
| 1605 | 1600 | |
| 1606 | 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 | 788 | } |
| 789 | 789 | } |
| 790 | 790 | |
| 791 | /// May be called before or after updateExports for any given Nav. | |
| 792 | 791 | /// Asserts that the ZCU is not using the LLVM backend. |
| 793 | 792 | fn updateNav(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Error!void { |
| 794 | 793 | assert(base.comp.zcu.?.llvm_object == null); |
| ... | ... | @@ -831,7 +830,6 @@ pub const File = struct { |
| 831 | 830 | } |
| 832 | 831 | } |
| 833 | 832 | |
| 834 | /// May be called before or after updateExports for any given Decl. | |
| 835 | 833 | /// The active tag of `mir` is determined by the backend used for the module this function is in. |
| 836 | 834 | /// Never called when LLVM is codegenning the ZCU. |
| 837 | 835 | fn updateFunc( |
| ... | ... | @@ -971,15 +969,16 @@ pub const File = struct { |
| 971 | 969 | } |
| 972 | 970 | } |
| 973 | 971 | |
| 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 | /// | |
| 978 | 978 | /// Never called when LLVM is codegenning the ZCU. |
| 979 | 979 | pub fn updateExports( |
| 980 | 980 | base: *File, |
| 981 | 981 | pt: Zcu.PerThread, |
| 982 | exported: Zcu.Exported, | |
| 983 | 982 | export_indices: []const Zcu.Export.Index, |
| 984 | 983 | ) Error!void { |
| 985 | 984 | assert(base.comp.zcu.?.llvm_object == null); |
| ... | ... | @@ -992,7 +991,7 @@ pub const File = struct { |
| 992 | 991 | .plan9 => unreachable, |
| 993 | 992 | inline else => |tag| { |
| 994 | 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 | 1070 | } |
| 1072 | 1071 | } |
| 1073 | 1072 | |
| 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 | 1073 | pub const DumpResult = enum { |
| 1097 | 1074 | unimplemented, |
| 1098 | 1075 | needs_extensions, |
src/link/C.zig+44-40| ... | ... | @@ -1228,58 +1228,62 @@ const Flush = struct { |
| 1228 | 1228 | pub fn updateExports( |
| 1229 | 1229 | c: *C, |
| 1230 | 1230 | pt: Zcu.PerThread, |
| 1231 | exported: Zcu.Exported, | |
| 1232 | 1231 | export_indices: []const Zcu.Export.Index, |
| 1233 | 1232 | ) Allocator.Error!void { |
| 1234 | 1233 | const zcu = pt.zcu; |
| 1235 | 1234 | const gpa = zcu.gpa; |
| 1236 | 1235 | |
| 1236 | c.exported_navs.clearRetainingCapacity(); | |
| 1237 | c.exported_uavs.clearRetainingCapacity(); | |
| 1238 | ||
| 1237 | 1239 | var arena: std.heap.ArenaAllocator = .init(gpa); |
| 1238 | 1240 | defer arena.deinit(); |
| 1239 | 1241 | |
| 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); | |
| 1254 | 1252 | } |
| 1255 | 1253 | |
| 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, | |
| 1263 | 1265 | }; |
| 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 | }; | |
| 1267 | 1282 | }; |
| 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 | ||
| 1275 | pub 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 | } | |
| 1283 | 1287 | } |
| 1284 | 1288 | } |
| 1285 | 1289 |
src/link/Coff.zig+134-147| ... | ... | @@ -5893,10 +5893,6 @@ pub fn flush( |
| 5893 | 5893 | |
| 5894 | 5894 | const comp = coff.base.comp; |
| 5895 | 5895 | |
| 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 | 5896 | while (try coff.resolve(tid)) {} |
| 5901 | 5897 | while (try coff.idle(tid)) {} |
| 5902 | 5898 | |
| ... | ... | @@ -7374,30 +7370,38 @@ fn virtualSlide(coff: *Coff, start_section_index: usize, start_rva: u32) !void { |
| 7374 | 7370 | pub fn updateExports( |
| 7375 | 7371 | coff: *Coff, |
| 7376 | 7372 | pt: Zcu.PerThread, |
| 7377 | exported: Zcu.Exported, | |
| 7378 | 7373 | export_indices: []const Zcu.Export.Index, |
| 7379 | 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 | 7377 | 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; | |
| 7388 | 7390 | } |
| 7389 | fn updateExportsInner( | |
| 7391 | fn updateExportInner( | |
| 7390 | 7392 | coff: *Coff, |
| 7391 | 7393 | 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), | |
| 7394 | 7396 | ) !void { |
| 7395 | 7397 | const zcu = pt.zcu; |
| 7396 | 7398 | const gpa = zcu.gpa; |
| 7397 | 7399 | const ip = &zcu.intern_pool; |
| 7398 | 7400 | |
| 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) { | |
| 7401 | 7405 | .nav => |nav| try coff.navSymbol(zcu, nav), |
| 7402 | 7406 | .uav => |uav| @fromBackingInt(@intCast(@backingInt(try coff.lowerUav( |
| 7403 | 7407 | pt, |
| ... | ... | @@ -7405,7 +7409,7 @@ fn updateExportsInner( |
| 7405 | 7409 | Type.fromInterned(ip.typeOf(uav)).abiAlignment(zcu), |
| 7406 | 7410 | )))), |
| 7407 | 7411 | }; |
| 7408 | switch (exported) { | |
| 7412 | switch (exp.exported) { | |
| 7409 | 7413 | .nav => |nav| log.debug("updateExports({f}) = {d}", .{ ip.getNav(nav).fqn.fmt(ip), exported_si }), |
| 7410 | 7414 | .uav => |uav| log.debug("updateExports(@as({f}, {f})) = {d}", .{ |
| 7411 | 7415 | Type.fromInterned(ip.typeOf(uav)).fmt(pt), |
| ... | ... | @@ -7419,154 +7423,137 @@ fn updateExportsInner( |
| 7419 | 7423 | const machine = coff.targetLoad(&coff.headerPtr().machine); |
| 7420 | 7424 | const exported_ni = exported_si.node(coff); |
| 7421 | 7425 | const exported_sym = exported_si.get(coff); |
| 7422 | var prev_alias_si = exported_si; | |
| 7423 | 7426 | |
| 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; | |
| 7454 | 7444 | |
| 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 | }; | |
| 7456 | 7451 | |
| 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 | } | |
| 7463 | 7460 | |
| 7464 | if (!gop.found_existing) { | |
| 7465 | errdefer _ = coff.export_table.entries.pop(); | |
| 7461 | prev_alias_sym.setExtra(.{ .next_alias_si = export_si }); | |
| 7466 | 7462 | |
| 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; | |
| 7470 | 7464 | |
| 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 | ); | |
| 7475 | 7471 | |
| 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(); | |
| 7477 | 7474 | |
| 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", .{}); | |
| 7492 | 7478 | |
| 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", .{}); | |
| 7496 | 7483 | |
| 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); | |
| 7504 | 7485 | |
| 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]); | |
| 7510 | 7488 | |
| 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 | } | |
| 7516 | 7500 | |
| 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; | |
| 7525 | 7504 | |
| 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 | ); | |
| 7532 | 7512 | |
| 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 | ); | |
| 7551 | 7518 | |
| 7552 | pub 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 | ); | |
| 7559 | 7524 | |
| 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 | ); | |
| 7564 | 7533 | |
| 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 | }; | |
| 7567 | 7540 | |
| 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 | } | |
| 7570 | 7557 | } |
| 7571 | 7558 | |
| 7572 | 7559 | fn dumpStderr(coff: *Coff, tid: Zcu.PerThread.Id) !void { |
src/link/Elf.zig+1-10| ... | ... | @@ -1685,24 +1685,15 @@ pub fn updateContainerType( |
| 1685 | 1685 | pub fn updateExports( |
| 1686 | 1686 | self: *Elf, |
| 1687 | 1687 | pt: Zcu.PerThread, |
| 1688 | exported: Zcu.Exported, | |
| 1689 | 1688 | export_indices: []const Zcu.Export.Index, |
| 1690 | 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 | } |
| 1693 | 1692 | |
| 1694 | 1693 | pub fn updateLineNumber(self: *Elf, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) link.Error!void { |
| 1695 | 1694 | return self.zigObjectPtr().?.updateLineNumber(pt, ti_id); |
| 1696 | 1695 | } |
| 1697 | 1696 | |
| 1698 | pub 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 | ||
| 1706 | 1697 | fn checkDuplicates(self: *Elf) !void { |
| 1707 | 1698 | const gpa = self.base.comp.gpa; |
| 1708 | 1699 |
src/link/Elf/ZigObject.zig+44-57| ... | ... | @@ -1857,7 +1857,6 @@ pub fn updateExports( |
| 1857 | 1857 | self: *ZigObject, |
| 1858 | 1858 | elf_file: *Elf, |
| 1859 | 1859 | pt: Zcu.PerThread, |
| 1860 | exported: Zcu.Exported, | |
| 1861 | 1860 | export_indices: []const Zcu.Export.Index, |
| 1862 | 1861 | ) link.Error!void { |
| 1863 | 1862 | const tracy = trace(@src()); |
| ... | ... | @@ -1865,27 +1864,49 @@ pub fn updateExports( |
| 1865 | 1864 | |
| 1866 | 1865 | const zcu = pt.zcu; |
| 1867 | 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]; | |
| 1882 | 1867 | |
| 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]; | |
| 1885 | 1906 | if (exp.opts.section.unwrap()) |section_name| { |
| 1886 | 1907 | 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( | |
| 1889 | 1910 | gpa, |
| 1890 | 1911 | exp.src, |
| 1891 | 1912 | "Unimplemented: ExportOptions.section", |
| ... | ... | @@ -1899,8 +1920,8 @@ pub fn updateExports( |
| 1899 | 1920 | .strong => elf.STB_GLOBAL, |
| 1900 | 1921 | .weak => elf.STB_WEAK, |
| 1901 | 1922 | .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( | |
| 1904 | 1925 | gpa, |
| 1905 | 1926 | exp.src, |
| 1906 | 1927 | "Unimplemented: GlobalLinkage.LinkOnce", |
| ... | ... | @@ -1912,13 +1933,8 @@ pub fn updateExports( |
| 1912 | 1933 | const stt_bits: u8 = @as(u4, @truncate(esym.st_info)); |
| 1913 | 1934 | const exp_name = exp.opts.name.toSlice(&zcu.intern_pool); |
| 1914 | 1935 | 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); | |
| 1922 | 1938 | |
| 1923 | 1939 | const value = self.symbol(sym_index).value; |
| 1924 | 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 | 1963 | } |
| 1948 | 1964 | } |
| 1949 | 1965 | |
| 1950 | pub 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 | ||
| 1971 | 1966 | pub fn getGlobalSymbol(self: *ZigObject, elf_file: *Elf, name: []const u8, lib_name: ?[]const u8) !u32 { |
| 1972 | 1967 | _ = lib_name; |
| 1973 | 1968 | const gpa = elf_file.base.comp.gpa; |
| ... | ... | @@ -2362,14 +2357,6 @@ const AvMetadata = struct { |
| 2362 | 2357 | exports: std.ArrayList(Symbol.Index) = .empty, |
| 2363 | 2358 | /// Set to true if the AV has been initialized and allocated. |
| 2364 | 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 | }; |
| 2374 | 2361 | |
| 2375 | 2362 | fn 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 | 8287 | pub fn updateExports( |
| 8288 | 8288 | elf: *Elf, |
| 8289 | 8289 | pt: Zcu.PerThread, |
| 8290 | exported: Zcu.Exported, | |
| 8291 | 8290 | export_indices: []const Zcu.Export.Index, |
| 8292 | 8291 | ) link.Error!void { |
| 8293 | 8292 | 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 | } | |
| 8298 | 8299 | } |
| 8299 | fn updateExportsInner( | |
| 8300 | fn updateExportInner( | |
| 8300 | 8301 | elf: *Elf, |
| 8301 | 8302 | pt: Zcu.PerThread, |
| 8302 | exported: Zcu.Exported, | |
| 8303 | export_indices: []const Zcu.Export.Index, | |
| 8303 | export_index: Zcu.Export.Index, | |
| 8304 | 8304 | ) Error!void { |
| 8305 | 8305 | const zcu = pt.zcu; |
| 8306 | 8306 | const ip = &zcu.intern_pool; |
| 8307 | 8307 | |
| 8308 | switch (exported) { | |
| 8308 | const @"export" = export_index.ptr(zcu); | |
| 8309 | ||
| 8310 | switch (@"export".exported) { | |
| 8309 | 8311 | .nav => |nav| log.debug("updateExports({f})", .{ip.getNav(nav).fqn.fmt(ip)}), |
| 8310 | 8312 | .uav => |uav| log.debug("updateExports(@as({f}, {f}))", .{ |
| 8311 | 8313 | Type.fromInterned(ip.typeOf(uav)).fmt(pt), |
| 8312 | 8314 | Value.fromInterned(uav).fmtValue(pt), |
| 8313 | 8315 | }), |
| 8314 | 8316 | } |
| 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) { | |
| 8317 | 8319 | .nav => |nav| .{ |
| 8318 | 8320 | (try elf.navMapIndex(zcu, nav)).symbol(elf), |
| 8319 | 8321 | elf.navType(ip.getNav(nav).resolved.?), |
| ... | ... | @@ -8331,50 +8333,42 @@ fn updateExportsInner( |
| 8331 | 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 | } | |
| 8373 | 8336 | |
| 8374 | pub 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 | }; | |
| 8378 | 8372 | } |
| 8379 | 8373 | |
| 8380 | 8374 | fn 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 | 3102 | pub fn updateExports( |
| 3103 | 3103 | self: *MachO, |
| 3104 | 3104 | pt: Zcu.PerThread, |
| 3105 | exported: Zcu.Exported, | |
| 3106 | 3105 | export_indices: []const Zcu.Export.Index, |
| 3107 | 3106 | ) link.Error!void { |
| 3108 | return self.getZigObject().?.updateExports(self, pt, exported, export_indices); | |
| 3109 | } | |
| 3110 | ||
| 3111 | pub 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); | |
| 3117 | 3108 | } |
| 3118 | 3109 | |
| 3119 | 3110 | pub fn freeNav(self: *MachO, nav: InternPool.Nav.Index) void { |
src/link/MachO/ZigObject.zig+54-62| ... | ... | @@ -1238,7 +1238,6 @@ pub fn updateExports( |
| 1238 | 1238 | self: *ZigObject, |
| 1239 | 1239 | macho_file: *MachO, |
| 1240 | 1240 | pt: Zcu.PerThread, |
| 1241 | exported: Zcu.Exported, | |
| 1242 | 1241 | export_indices: []const Zcu.Export.Index, |
| 1243 | 1242 | ) link.Error!void { |
| 1244 | 1243 | const tracy = trace(@src()); |
| ... | ... | @@ -1246,26 +1245,60 @@ pub fn updateExports( |
| 1246 | 1245 | |
| 1247 | 1246 | const zcu = pt.zcu; |
| 1248 | 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]; | |
| 1262 | 1248 | |
| 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 | ||
| 1265 | 1298 | if (exp.opts.section.unwrap()) |section_name| { |
| 1266 | 1299 | if (!section_name.eqlSlice("__text", &zcu.intern_pool)) { |
| 1267 | 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 | 1302 | gpa, |
| 1270 | 1303 | exp.src, |
| 1271 | 1304 | "Unimplemented: ExportOptions.section", |
| ... | ... | @@ -1275,7 +1308,7 @@ pub fn updateExports( |
| 1275 | 1308 | } |
| 1276 | 1309 | } |
| 1277 | 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 | 1312 | gpa, |
| 1280 | 1313 | exp.src, |
| 1281 | 1314 | "Unimplemented: GlobalLinkage.link_once", |
| ... | ... | @@ -1285,13 +1318,9 @@ pub fn updateExports( |
| 1285 | 1318 | } |
| 1286 | 1319 | |
| 1287 | 1320 | 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 | ||
| 1295 | 1324 | const global_nlist = &self.symtab.items(.nlist)[global_nlist_index]; |
| 1296 | 1325 | const atom_index = self.symtab.items(.atom)[nlist_idx]; |
| 1297 | 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 | 1429 | } |
| 1401 | 1430 | } |
| 1402 | 1431 | |
| 1403 | pub 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 | ||
| 1431 | 1432 | pub fn getGlobalSymbol(self: *ZigObject, macho_file: *MachO, name: []const u8, lib_name: ?[]const u8) !u32 { |
| 1432 | 1433 | _ = lib_name; |
| 1433 | 1434 | const gpa = macho_file.base.comp.gpa; |
| ... | ... | @@ -1722,15 +1723,6 @@ const AvMetadata = struct { |
| 1722 | 1723 | symbol_index: Symbol.Index, |
| 1723 | 1724 | /// A list of all exports aliases of this Av. |
| 1724 | 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 | }; |
| 1735 | 1727 | |
| 1736 | 1728 | const 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 | 219 | pub fn updateExports( |
| 220 | 220 | linker: *Linker, |
| 221 | 221 | pt: Zcu.PerThread, |
| 222 | exported: Zcu.Exported, | |
| 223 | 222 | export_indices: []const Zcu.Export.Index, |
| 224 | ) !void { | |
| 223 | ) link.Error!void { | |
| 225 | 224 | const zcu = pt.zcu; |
| 226 | 225 | const ip = &zcu.intern_pool; |
| 227 | 226 | 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); | |
| 240 | 236 | try linker.entry_points.append(gpa, .{ |
| 241 | 237 | .nav = nav_index, |
| 242 | 238 | .name = exp.opts.name.toSlice(ip), |
src/link/Wasm.zig+33-50| ... | ... | @@ -3,11 +3,11 @@ |
| 3 | 3 | //! performed without any knowledge of functions and globals provided by the |
| 4 | 4 | //! Zcu. If there is no Zcu, effectively all linking is done in `prelink`. |
| 5 | 5 | //! |
| 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. | |
| 11 | 11 | |
| 12 | 12 | const Wasm = @This(); |
| 13 | 13 | const Archive = @import("Wasm/Archive.zig"); |
| ... | ... | @@ -219,9 +219,9 @@ data_imports_len_prelink: u32 = 0, |
| 219 | 219 | /// objects. |
| 220 | 220 | /// |
| 221 | 221 | /// 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`. | |
| 225 | 225 | /// |
| 226 | 226 | /// `flush` gets a copy of this table, and then Zcu exports are applied to |
| 227 | 227 | /// 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 | 232 | /// objects. |
| 233 | 233 | /// |
| 234 | 234 | /// 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`. | |
| 238 | 238 | /// |
| 239 | 239 | /// `flush` gets a copy of this table, and then Zcu exports are applied to |
| 240 | 240 | /// 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 | 3744 | } |
| 3745 | 3745 | } |
| 3746 | 3746 | |
| 3747 | pub 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 | ||
| 3765 | 3747 | pub fn updateExports( |
| 3766 | 3748 | wasm: *Wasm, |
| 3767 | 3749 | pt: Zcu.PerThread, |
| 3768 | exported: Zcu.Exported, | |
| 3769 | 3750 | export_indices: []const Zcu.Export.Index, |
| 3770 | 3751 | ) !void { |
| 3771 | 3752 | const zcu = pt.zcu; |
| 3772 | 3753 | const gpa = zcu.gpa; |
| 3773 | 3754 | const ip = &zcu.intern_pool; |
| 3774 | 3755 | 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 | ||
| 3793 | 3760 | for (export_indices) |export_idx| { |
| 3794 | 3761 | const exp = export_idx.ptr(zcu); |
| 3795 | 3762 | const name_slice = exp.opts.name.toSlice(ip); |
| 3796 | 3763 | const name = try wasm.internString(name_slice); |
| 3797 | switch (exported) { | |
| 3764 | switch (exp.exported) { | |
| 3798 | 3765 | .nav => |nav_index| { |
| 3799 | 3766 | log.debug("updateExports '{s}' nav={d}", .{ name_slice, @backingInt(nav_index) }); |
| 3800 | 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 | xxxx 00000000 2 NULL STATIC | this_is_a_long_name.foo_strong |
| 9 | 9 | xxxx 00000008 2 NULL STATIC | this_is_a_long_name.foo_array |
| 10 | 10 | xxxx 00000000 2 NULL EXTERNAL | foo_strong |
| 11 | xxxx 00000000 2 NULL EXTERNAL | foo_strong_alias | |
| 12 | 11 | xxxx 00000008 2 NULL EXTERNAL | foo_array |
| 13 | 12 | xxxx 00000000 UNDEF NULL WEAK_EXTERNAL | fooWeak |
| 14 | 13 | | Weak External [falls back to relative ordinal 000000+2 via SEARCH_ALIAS] |
| 15 | 14 | xxxx 00000000 1 NULL() EXTERNAL | .weak.fooWeak.default.foo_strong |
| 15 | xxxx 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 | 6 | lib.lib(this_is_a_long_name.obj): COFF object |
| 7 | 7 | xxxx 00000000 4 NULL() EXTERNAL | this_is_a_long_name.fooWeak |
| 8 | 8 | xxxx 00000000 2 NULL EXTERNAL | foo_strong |
| 9 | xxxx 00000000 2 NULL EXTERNAL | foo_strong_alias | |
| 10 | 9 | xxxx 00000010 2 NULL EXTERNAL | foo_array |
| 11 | 10 | xxxx 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] | |
| 12 | xxxx 00000000 2 NULL EXTERNAL | foo_strong_alias |