diff --git a/src/Sema.zig b/src/Sema.zig index e78041fc91819a65787410250222a1da6fd649de..64a76bf84fe8f69051eae384dc35025863a224b4 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5674,7 +5674,6 @@ fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void .opts = options, .src = src, .exported = target, - .status = .in_progress, }); } @@ -5722,7 +5721,6 @@ pub fn analyzeExportSelfNav( .opts = .{ .name = name }, .src = src, .exported = .{ .nav = export_nav }, - .status = .in_progress, }); } diff --git a/src/Zcu.zig b/src/Zcu.zig index 5f64440990c95be60453ee38c1dde3d2850a058f..8b0011c91c252cbafba2bb34b8cf4c76393f8dd7 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -757,14 +757,6 @@ pub const Export = struct { opts: Options, src: LazySrcLoc, exported: Exported, - status: enum { - in_progress, - failed, - /// Indicates that the failure was due to a temporary issue, such as an I/O error - /// when writing to the output file. Retrying the export may succeed. - failed_retryable, - complete, - }, pub const Options = struct { name: InternPool.NullTerminatedString, @@ -3789,13 +3781,8 @@ pub fn resetUnit(zcu: *Zcu, unit: AnalUnit) void { } break :exports; }; - for (zcu.all_exports.items[base..][0..len], base..) |exp, exp_index_usize| { + for (base..base + len) |exp_index_usize| { const exp_index: Export.Index = @fromBackingInt(@intCast(exp_index_usize)); - if (zcu.llvm_object) |llvm_object| { - _ = llvm_object; // TODO: delete exports from LLVM - } else if (zcu.comp.bin_file) |lf| { - lf.deleteExport(exp.exported, exp.opts.name); - } if (zcu.failed_exports.fetchSwapRemove(exp_index)) |failed_kv| { failed_kv.value.destroy(gpa); } @@ -3966,25 +3953,6 @@ pub fn getTarget(zcu: *const Zcu) *const Target { return &zcu.root_mod.resolved_target.result; } -pub fn handleUpdateExports( - zcu: *Zcu, - export_indices: []const Export.Index, - result: link.Error!void, -) (Allocator.Error || Io.Cancelable)!void { - const gpa = zcu.gpa; - result catch |err| switch (err) { - else => |e| return e, - error.AlreadyReported => { - const export_idx = export_indices[0]; - const new_export = export_idx.ptr(zcu); - new_export.status = .failed_retryable; - try zcu.failed_exports.ensureUnusedCapacity(gpa, 1); - const msg = try ErrorMsg.create(gpa, new_export.src, "unable to export: {s}", .{@errorName(err)}); - zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, msg); - }, - }; -} - pub fn addGlobalAssembly(zcu: *Zcu, unit: AnalUnit, source: []const u8) !void { const gpa = zcu.gpa; const gop = try zcu.global_assembly.getOrPut(gpa, unit); diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 64240f77c1c721bfcf90be0def4bb92aa7b07e99..7de477dff17bbef9e24ac088df9f61f7c2ef1c2d 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -3554,7 +3554,7 @@ fn lockAndClearFileCompileError(pt: Zcu.PerThread, file_index: Zcu.File.Index, f /// Called from `Compilation.update`, after everything is done, just before /// reporting compile errors. In this function we emit exported symbol collision /// errors and communicate exported symbols to the linker backend. -pub fn processExports(pt: Zcu.PerThread) !void { +pub fn processExports(pt: Zcu.PerThread) (Allocator.Error || Io.Cancelable)!void { const zcu = pt.zcu; const gpa = zcu.gpa; @@ -3563,169 +3563,72 @@ pub fn processExports(pt: Zcu.PerThread) !void { return; } - // First, construct a mapping of every exported value and Nav to the indices of all its different exports. - var nav_exports: std.array_hash_map.Auto(InternPool.Nav.Index, std.ArrayList(Zcu.Export.Index)) = .empty; - var uav_exports: std.array_hash_map.Auto(InternPool.Index, std.ArrayList(Zcu.Export.Index)) = .empty; - defer { - for (nav_exports.values()) |*exports| { - exports.deinit(gpa); - } - nav_exports.deinit(gpa); - for (uav_exports.values()) |*exports| { - exports.deinit(gpa); - } - uav_exports.deinit(gpa); - } - - // We note as a heuristic: - // * It is rare to export a value. - // * It is rare for one Nav to be exported multiple times. - // So, this ensureTotalCapacity serves as a reasonable (albeit very approximate) optimization. - try nav_exports.ensureTotalCapacity(gpa, zcu.single_exports.count() + zcu.multi_exports.count()); + var alive_exports: std.ArrayList(Zcu.Export.Index) = .empty; + defer alive_exports.deinit(gpa); const unit_references = try zcu.resolveReferences(); + try alive_exports.ensureUnusedCapacity(gpa, zcu.single_exports.count()); for (zcu.single_exports.keys(), zcu.single_exports.values()) |exporter, export_idx| { - const exp = export_idx.ptr(zcu); - if (!unit_references.contains(exporter)) { - // This export might already have been sent to the linker on a previous update, in which case we need to delete it. - // The linker export API should be modified to eliminate this call. #23616 - if (zcu.comp.bin_file) |lf| { - if (zcu.llvm_object == null) { - lf.deleteExport(exp.exported, exp.opts.name); - } - } - continue; - } - const value_ptr, const found_existing = switch (exp.exported) { - .nav => |nav| gop: { - const gop = try nav_exports.getOrPut(gpa, nav); - break :gop .{ gop.value_ptr, gop.found_existing }; - }, - .uav => |uav| gop: { - const gop = try uav_exports.getOrPut(gpa, uav); - break :gop .{ gop.value_ptr, gop.found_existing }; - }, - }; - if (!found_existing) value_ptr.* = .empty; - try value_ptr.append(gpa, export_idx); + if (!unit_references.contains(exporter)) continue; + alive_exports.appendAssumeCapacity(export_idx); } for (zcu.multi_exports.keys(), zcu.multi_exports.values()) |exporter, info| { - const exports = zcu.all_exports.items[info.index..][0..info.len]; - if (!unit_references.contains(exporter)) { - // This export might already have been sent to the linker on a previous update, in which case we need to delete it. - // The linker export API should be modified to eliminate this loop. #23616 - if (zcu.comp.bin_file) |lf| { - if (zcu.llvm_object == null) { - for (exports) |exp| { - lf.deleteExport(exp.exported, exp.opts.name); - } - } - } - continue; + if (!unit_references.contains(exporter)) continue; + try alive_exports.ensureUnusedCapacity(gpa, info.len); + for (0..info.len) |off| { + const export_idx: Zcu.Export.Index = @fromBackingInt(@intCast(info.index + off)); + alive_exports.appendAssumeCapacity(export_idx); } - for (exports, info.index..) |exp, export_idx| { - const value_ptr, const found_existing = switch (exp.exported) { - .nav => |nav| gop: { - const gop = try nav_exports.getOrPut(gpa, nav); - break :gop .{ gop.value_ptr, gop.found_existing }; - }, - .uav => |uav| gop: { - const gop = try uav_exports.getOrPut(gpa, uav); - break :gop .{ gop.value_ptr, gop.found_existing }; - }, - }; - if (!found_existing) value_ptr.* = .empty; - try value_ptr.append(gpa, @fromBackingInt(@intCast(export_idx))); + } + + // Detect export name collisions + { + var exports_by_name: std.array_hash_map.Auto( + InternPool.NullTerminatedString, + Zcu.Export.Index, + ) = .empty; + defer exports_by_name.deinit(gpa); + + try exports_by_name.ensureUnusedCapacity(gpa, alive_exports.items.len); + + for (alive_exports.items) |export_index| { + const exp = export_index.ptr(zcu); + const gop = exports_by_name.getOrPutAssumeCapacity(exp.opts.name); + if (gop.found_existing) { + const existing_exp = gop.value_ptr.*.ptr(zcu); + try zcu.failed_exports.ensureUnusedCapacity(gpa, 1); + const msg = try Zcu.ErrorMsg.create( + gpa, + exp.src, + "exported symbol collision: {f}", + .{exp.opts.name.fmt(&zcu.intern_pool)}, + ); + errdefer msg.destroy(gpa); + try zcu.errNote(existing_exp.src, msg, "other symbol here", .{}); + zcu.failed_exports.putAssumeCapacityNoClobber(export_index, msg); + } else { + gop.value_ptr.* = export_index; + } } } // If there are compile errors, we won't call `updateExports`. Not only would it be redundant // work, but the linker may not have seen an exported `Nav` due to a compile error, so linker // implementations would have to handle that case. This early return avoids that. - const skip_linker_work = zcu.comp.anyErrors(); - - // Map symbol names to `Export` for name collision detection. - var symbol_exports: SymbolExports = .{}; - defer symbol_exports.deinit(gpa); - - for (nav_exports.keys(), nav_exports.values()) |exported_nav, exports_list| { - const exported: Zcu.Exported = .{ .nav = exported_nav }; - try pt.processExportsInner(&symbol_exports, exported, exports_list.items, skip_linker_work); - } - - for (uav_exports.keys(), uav_exports.values()) |exported_uav, exports_list| { - const exported: Zcu.Exported = .{ .uav = exported_uav }; - try pt.processExportsInner(&symbol_exports, exported, exports_list.items, skip_linker_work); - } -} - -const SymbolExports = std.array_hash_map.Auto(InternPool.NullTerminatedString, Zcu.Export.Index); - -fn processExportsInner( - pt: Zcu.PerThread, - symbol_exports: *SymbolExports, - exported: Zcu.Exported, - export_indices: []const Zcu.Export.Index, - skip_linker_work: bool, -) error{ OutOfMemory, Canceled }!void { - const zcu = pt.zcu; - const gpa = zcu.gpa; - const ip = &zcu.intern_pool; - - for (export_indices) |export_idx| { - const new_export = export_idx.ptr(zcu); - const gop = try symbol_exports.getOrPut(gpa, new_export.opts.name); - if (gop.found_existing) { - new_export.status = .failed_retryable; - try zcu.failed_exports.ensureUnusedCapacity(gpa, 1); - const msg = try Zcu.ErrorMsg.create(gpa, new_export.src, "exported symbol collision: {f}", .{ - new_export.opts.name.fmt(ip), - }); - errdefer msg.destroy(gpa); - const other_export = gop.value_ptr.ptr(zcu); - try zcu.errNote(other_export.src, msg, "other symbol here", .{}); - zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, msg); - new_export.status = .failed; - } else { - gop.value_ptr.* = export_idx; - } - } - - switch (exported) { - .nav => |nav_index| if (failed: { - const nav = ip.getNav(nav_index); - if (zcu.failed_codegen.contains(nav_index)) break :failed true; - if (nav.analysis != null) { - const unit: AnalUnit = .wrap(.{ .nav_val = nav_index }); - if (zcu.failed_analysis.contains(unit)) break :failed true; - if (zcu.transitive_failed_analysis.contains(unit)) break :failed true; - } - const val: Value = switch ((nav.resolved orelse break :failed true).value) { - .none => break :failed true, - else => |val| .fromInterned(val), - }; - // If the value is a function, we also need to check if that function succeeded analysis. - if (val.typeOf(zcu).zigTypeTag(zcu) == .@"fn") { - const func_unit = AnalUnit.wrap(.{ .func = val.toIntern() }); - if (zcu.failed_analysis.contains(func_unit)) break :failed true; - if (zcu.transitive_failed_analysis.contains(func_unit)) break :failed true; - } - break :failed false; - }) { - // This `Nav` is failed, so was never sent to codegen. There should be a compile error. - assert(skip_linker_work); - }, - .uav => {}, - } - - if (skip_linker_work) return; + if (zcu.comp.anyErrors()) return; if (zcu.llvm_object) |llvm_object| { - try zcu.handleUpdateExports(export_indices, llvm_object.updateExports(exported, export_indices)); + llvm_object.updateExports(alive_exports.items) catch |err| switch (err) { + else => |e| return e, + error.AlreadyReported => {}, + }; } else if (zcu.comp.bin_file) |lf| { - try zcu.handleUpdateExports(export_indices, lf.updateExports(pt, exported, export_indices)); + lf.updateExports(pt, alive_exports.items) catch |err| switch (err) { + else => |e| return e, + error.AlreadyReported => {}, + }; } } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 30b7f30feaec4e2d33c844815960064d6aa5f44a..9c54defbfe9858f30acb100ea36c62d22c00ef96 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1441,43 +1441,46 @@ pub const Object = struct { pub fn updateExports( o: *Object, - exported: Zcu.Exported, export_indices: []const Zcu.Export.Index, ) link.Error!void { const zcu = o.zcu; const ip = &zcu.intern_pool; - const ty: Type, const llvm_ptr: Builder.Constant = switch (exported) { - .nav => |nav| exp: { - const nav_ty: Type = .fromInterned(ip.getNav(nav).resolved.?.type); - const nav_ref = try o.lowerNavRef(nav); - break :exp .{ nav_ty, nav_ref }; - }, - .uav => |uav| exp: { - const uav_ty = Value.fromInterned(uav).typeOf(zcu); - const uav_ref = try o.lowerUavRef( - uav, - uav_ty.abiAlignment(zcu).toLlvm(), - target_util.defaultAddressSpace(zcu.getTarget(), .global_constant), - ); - break :exp .{ uav_ty, uav_ref }; - }, - }; - switch (llvm_ptr.unwrap()) { - .global => |global| return o.updateExportedGlobal(global, ty, export_indices), - .constant => @panic("LLVM TODO: export zero-bit value"), + for (export_indices) |export_index| { + const ty: Type, const llvm_ptr: Builder.Constant = switch (export_index.ptr(zcu).exported) { + .nav => |nav| exp: { + const nav_ty: Type = .fromInterned(ip.getNav(nav).resolved.?.type); + const nav_ref = try o.lowerNavRef(nav); + break :exp .{ nav_ty, nav_ref }; + }, + .uav => |uav| exp: { + const uav_ty = Value.fromInterned(uav).typeOf(zcu); + const uav_ref = try o.lowerUavRef( + uav, + uav_ty.abiAlignment(zcu).toLlvm(), + target_util.defaultAddressSpace(zcu.getTarget(), .global_constant), + ); + break :exp .{ uav_ty, uav_ref }; + }, + }; + switch (llvm_ptr.unwrap()) { + .global => |global| try o.addGlobalExport(global, ty, export_index), + .constant => @panic("LLVM TODO: export zero-bit value"), + } } } - fn updateExportedGlobal( + fn addGlobalExport( o: *Object, llvm_global: Builder.Global.Index, ty: Type, - export_indices: []const Zcu.Export.Index, + export_index: Zcu.Export.Index, ) link.Error!void { const zcu = o.zcu; const comp = zcu.comp; const ip = &zcu.intern_pool; + const exp = export_index.ptr(zcu); + // If we're on COFF and linking with LLD, the linker cares about our exports to determine the subsystem in use. coff_export_flags: { const lf = comp.bin_file orelse break :coff_export_flags; @@ -1488,23 +1491,20 @@ pub const Object = struct { }; if (ty.zigTypeTag(zcu) != .@"fn") break :coff_export_flags; const flags = &coff.lld_export_flags; - for (export_indices) |export_index| { - const name = export_index.ptr(zcu).opts.name; - if (name.eqlSlice("main", ip)) flags.c_main = true; - if (name.eqlSlice("WinMain", ip)) flags.winmain = true; - if (name.eqlSlice("wWinMain", ip)) flags.wwinmain = true; - if (name.eqlSlice("WinMainCRTStartup", ip)) flags.winmain_crt_startup = true; - if (name.eqlSlice("wWinMainCRTStartup", ip)) flags.wwinmain_crt_startup = true; - if (name.eqlSlice("DllMainCRTStartup", ip)) flags.dllmain_crt_startup = true; - if (name.eqlSlice("_DllMainCRTStartup", ip)) flags.dllmain_crt_startup = true; - } + if (exp.opts.name.eqlSlice("main", ip)) flags.c_main = true; + if (exp.opts.name.eqlSlice("WinMain", ip)) flags.winmain = true; + if (exp.opts.name.eqlSlice("wWinMain", ip)) flags.wwinmain = true; + if (exp.opts.name.eqlSlice("WinMainCRTStartup", ip)) flags.winmain_crt_startup = true; + if (exp.opts.name.eqlSlice("wWinMainCRTStartup", ip)) flags.wwinmain_crt_startup = true; + if (exp.opts.name.eqlSlice("DllMainCRTStartup", ip)) flags.dllmain_crt_startup = true; + if (exp.opts.name.eqlSlice("_DllMainCRTStartup", ip)) flags.dllmain_crt_startup = true; } - // If the first export specifies a linksection, set the exported variable's section to that - // one. This is kind of a hack because `std.lang.ExportOptions.section` doesn't actually - // make much sense: the linksection should be associated with the declaration itself rather - // than some particular symbol it is exported as! - if (export_indices[0].ptr(zcu).opts.section.toSlice(ip)) |section_slice| { + // If the export specifies a linksection, set the exported variable's section to that one. + // This is kind of a hack because `std.lang.ExportOptions.section` doesn't actually make + // much sense: the linksection should be associated with the declaration itself rather than + // some particular symbol it is exported as! + if (exp.opts.section.toSlice(ip)) |section_slice| { const variable = &llvm_global.ptrConst(&o.builder).kind.variable; variable.setSection(try o.builder.string(section_slice), &o.builder); } @@ -1519,29 +1519,54 @@ pub const Object = struct { // TODO: we currently do not delete old exports. To do that we'll need to track which // globals actually *are* exports. - for (export_indices, 0..) |export_idx, export_i| { - const exp = export_idx.ptr(zcu); - const exp_name = try o.builder.strtabString(exp.opts.name.toSlice(ip)); + const exp_name = try o.builder.strtabString(exp.opts.name.toSlice(ip)); - // Our goal is to make an alias with the name `exp_name`, but if that name is already - // taken by some existing global, we need to figure out what to do with that existing - // global. - // - // The name, aliasee, and type will be set within this block. Other properties of the - // alias will be set below. - const alias_global: Builder.Global.Index = global: { + // Our goal is to make an alias with the name `exp_name`, but if that name is already + // taken by some existing global, we need to figure out what to do with that existing + // global. + // + // The name, aliasee, and type will be set within this block. Other properties of the + // alias will be set below. + const alias_global: Builder.Global.Index = global: { - // WORKAROUND (see https://github.com/llvm/llvm-project/issues/213504, https://github.com/llvm/llvm-project/issues/214835) - // For NVPTX, LLVM throws "NVPTX aliasee must be a non-kernel function definition" if we try to alias a kernel - // On AMDGCN, LLVM does not generate an alias for the kernel descriptor symbol on associated functions - // To solve these, we rename the global - if (workaround_alias_bugs and export_i == 0) { - try llvm_global.rename(exp_name, &o.builder); - break :global llvm_global; - } + // WORKAROUND (see https://github.com/llvm/llvm-project/issues/213504, https://github.com/llvm/llvm-project/issues/214835) + // For NVPTX, LLVM throws "NVPTX aliasee must be a non-kernel function definition" if we try to alias a kernel + // On AMDGCN, LLVM does not generate an alias for the kernel descriptor symbol on associated functions + // To solve these, we rename the global + if (workaround_alias_bugs) { + try llvm_global.rename(exp_name, &o.builder); + break :global llvm_global; + } - const existing_global = o.builder.getGlobal(exp_name) orelse { - // There is no existing global with this name, so make a new alias. + const existing_global = o.builder.getGlobal(exp_name) orelse { + // There is no existing global with this name, so make a new alias. + const alias = try o.builder.addAlias( + exp_name, + llvm_global_ty, + llvm_global.ptrConst(&o.builder).addr_space, + llvm_global.toConst(), + ); + break :global alias.ptrConst(&o.builder).global; + }; + // There is an existing global with this name, so we can't just create an alias. We + // need to figure out what to do with the existing global instead. + switch (existing_global.ptrConst(&o.builder).kind) { + .alias => |alias| { + // We can just repurpose the existing alias. + alias.setAliasee(llvm_global.toConst(), &o.builder); + alias.ptrConst(&o.builder).global.ptr(&o.builder).type = llvm_global.typeOf(&o.builder); + alias.ptrConst(&o.builder).global.ptr(&o.builder).addr_space = llvm_global.ptrConst(&o.builder).addr_space; + break :global existing_global; + }, + .variable, .function => { + // This must be an extern, which is no good to us---we need an alias. The + // extern should refer to the value we're exporting, so replace it with the + // exported value. That will free up the name for us to create a new alias. + // We need to make a new global which is an alias. Replace this existing one + // with the target global, making the name available and fixing references + // to this global to point to the target. + try existing_global.replace(llvm_global, &o.builder); + // The name is now free, so create an alias. const alias = try o.builder.addAlias( exp_name, llvm_global_ty, @@ -1549,58 +1574,28 @@ pub const Object = struct { llvm_global.toConst(), ); break :global alias.ptrConst(&o.builder).global; - }; - // There is an existing global with this name, so we can't just create an alias. We - // need to figure out what to do with the existing global instead. - switch (existing_global.ptrConst(&o.builder).kind) { - .alias => |alias| { - // We can just repurpose the existing alias. - alias.setAliasee(llvm_global.toConst(), &o.builder); - alias.ptrConst(&o.builder).global.ptr(&o.builder).type = llvm_global.typeOf(&o.builder); - alias.ptrConst(&o.builder).global.ptr(&o.builder).addr_space = llvm_global.ptrConst(&o.builder).addr_space; - break :global existing_global; - }, - .variable, .function => { - // This must be an extern, which is no good to us---we need an alias. The - // extern should refer to the value we're exporting, so replace it with the - // exported value. That will free up the name for us to create a new alias. - // We need to make a new global which is an alias. Replace this existing one - // with the target global, making the name available and fixing references - // to this global to point to the target. - try existing_global.replace(llvm_global, &o.builder); - // The name is now free, so create an alias. - const alias = try o.builder.addAlias( - exp_name, - llvm_global_ty, - llvm_global.ptrConst(&o.builder).addr_space, - llvm_global.toConst(), - ); - break :global alias.ptrConst(&o.builder).global; - }, - .replaced => unreachable, // a replaced global would have lost the name `exp_name` - } - }; + }, + .replaced => unreachable, // a replaced global would have lost the name `exp_name` + } + }; - // Now for a bit of setup which + // We need the alias to *not* be `unnamed_addr` to ensure that the alias address equals + // the address of the original global. + alias_global.setUnnamedAddr(.default, &o.builder); - // We need the alias to *not* be `unnamed_addr` to ensure that the alias address equals - // the address of the original global. - alias_global.setUnnamedAddr(.default, &o.builder); - - if (comp.config.dll_export_fns and exp.opts.visibility != .hidden) - alias_global.setDllStorageClass(.dllexport, &o.builder); - alias_global.setLinkage(switch (exp.opts.linkage) { - .internal => if (o.builder.strip) .private else .internal, // we still did useful work in replacing an existing symbol if there was one - .strong => .external, - .weak => .weak_odr, - .link_once => .linkonce_odr, - }, &o.builder); - alias_global.setVisibility(switch (exp.opts.visibility) { - .default => .default, - .hidden => .hidden, - .protected => .protected, - }, &o.builder); - } + if (comp.config.dll_export_fns and exp.opts.visibility != .hidden) + alias_global.setDllStorageClass(.dllexport, &o.builder); + alias_global.setLinkage(switch (exp.opts.linkage) { + .internal => if (o.builder.strip) .private else .internal, // we still did useful work in replacing an existing symbol if there was one + .strong => .external, + .weak => .weak_odr, + .link_once => .linkonce_odr, + }, &o.builder); + alias_global.setVisibility(switch (exp.opts.visibility) { + .default => .default, + .hidden => .hidden, + .protected => .protected, + }, &o.builder); } pub fn updateContainerType(o: *Object, pt: Zcu.PerThread, ty: InternPool.Index, success: bool) Allocator.Error!void { diff --git a/src/link.zig b/src/link.zig index 2917b6b7e59618f2adfde27b273d5b4cd19d0f92..b72a8e8c5959cdce72036b29df359b6cbc286dd8 100644 --- a/src/link.zig +++ b/src/link.zig @@ -788,7 +788,6 @@ pub const File = struct { } } - /// May be called before or after updateExports for any given Nav. /// Asserts that the ZCU is not using the LLVM backend. fn updateNav(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Error!void { assert(base.comp.zcu.?.llvm_object == null); @@ -831,7 +830,6 @@ pub const File = struct { } } - /// May be called before or after updateExports for any given Decl. /// The active tag of `mir` is determined by the backend used for the module this function is in. /// Never called when LLVM is codegenning the ZCU. fn updateFunc( @@ -971,15 +969,16 @@ pub const File = struct { } } - /// This is called for every exported thing. `exports` is almost always - /// a list of size 1, meaning that `exported` is exported once. However, it is possible - /// to export the same thing with multiple different symbol names (aliases). - /// May be called before or after updateDecl for any given Decl. + /// This is called once per update, before `flush`. + /// + /// `export_indices` contains the index of every export from the ZCU which should be performed + /// on this update. "Removal" of exports is signaled implicitly by the export being in this + /// slice on one update but not the next. + /// /// Never called when LLVM is codegenning the ZCU. pub fn updateExports( base: *File, pt: Zcu.PerThread, - exported: Zcu.Exported, export_indices: []const Zcu.Export.Index, ) Error!void { assert(base.comp.zcu.?.llvm_object == null); @@ -992,7 +991,7 @@ pub const File = struct { .plan9 => unreachable, inline else => |tag| { dev.check(tag.devFeature()); - return @as(*tag.Type(), @fieldParentPtr("base", base)).updateExports(pt, exported, export_indices); + return @as(*tag.Type(), @fieldParentPtr("base", base)).updateExports(pt, export_indices); }, } } @@ -1071,28 +1070,6 @@ pub const File = struct { } } - /// Never called when LLVM is codegenning the ZCU. - pub fn deleteExport( - base: *File, - exported: Zcu.Exported, - name: InternPool.NullTerminatedString, - ) void { - assert(base.comp.zcu.?.llvm_object == null); - - switch (base.tag) { - .lld => unreachable, - .plan9 => unreachable, - - .spirv, - => {}, - - inline else => |tag| { - dev.check(tag.devFeature()); - return @as(*tag.Type(), @fieldParentPtr("base", base)).deleteExport(exported, name); - }, - } - } - pub const DumpResult = enum { unimplemented, needs_extensions, diff --git a/src/link/C.zig b/src/link/C.zig index cf82ed146fa853cc449074721eb294834cde6d1b..94a53479639a156a88365cfac5d9d14ee4dfec42 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -1228,58 +1228,62 @@ const Flush = struct { pub fn updateExports( c: *C, pt: Zcu.PerThread, - exported: Zcu.Exported, export_indices: []const Zcu.Export.Index, ) Allocator.Error!void { const zcu = pt.zcu; const gpa = zcu.gpa; + c.exported_navs.clearRetainingCapacity(); + c.exported_uavs.clearRetainingCapacity(); + var arena: std.heap.ArenaAllocator = .init(gpa); defer arena.deinit(); - var dg: codegen.DeclGen = .{ - .gpa = gpa, - .arena = arena.allocator(), - .pt = pt, - .mod = zcu.root_mod, - .owner_nav = .none, - .is_naked_fn = false, - .expected_block = null, - .ctype_deps = .empty, - .uavs = .empty, - }; - defer { - assert(dg.uavs.count() == 0); - dg.ctype_deps.deinit(gpa); + var by_exported: std.array_hash_map.Auto(Zcu.Exported, std.ArrayList(Zcu.Export.Index)) = .empty; + try by_exported.ensureUnusedCapacity(arena.allocator(), export_indices.len); + + for (export_indices) |exp_index| { + const exported = exp_index.ptr(zcu).exported; + const gop = by_exported.getOrPutAssumeCapacity(exported); + if (!gop.found_existing) { + gop.value_ptr.* = .empty; + } + try gop.value_ptr.append(arena.allocator(), exp_index); } - const code: String = code: { - var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes); - defer c.string_bytes = aw.toArrayList(); - const start = aw.written().len; - codegen.genExports(&dg, &aw.writer, exported, export_indices) catch |err| switch (err) { - error.WriteFailed => return error.OutOfMemory, - error.OutOfMemory => |e| return e, + for (by_exported.keys(), by_exported.values()) |exported, *exports_of_this| { + var dg: codegen.DeclGen = .{ + .gpa = gpa, + .arena = arena.allocator(), + .pt = pt, + .mod = zcu.root_mod, + .owner_nav = .none, + .is_naked_fn = false, + .expected_block = null, + .ctype_deps = .empty, + .uavs = .empty, }; - break :code .{ - .start = @intCast(start), - .len = @intCast(aw.written().len - start), + defer { + assert(dg.uavs.count() == 0); + dg.ctype_deps.deinit(gpa); + } + const code: String = code: { + var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes); + defer c.string_bytes = aw.toArrayList(); + const start = aw.written().len; + codegen.genExports(&dg, &aw.writer, exported, exports_of_this.items) catch |err| switch (err) { + error.WriteFailed => return error.OutOfMemory, + error.OutOfMemory => |e| return e, + }; + break :code .{ + .start = @intCast(start), + .len = @intCast(aw.written().len - start), + }; }; - }; - switch (exported) { - .nav => |nav| try c.exported_navs.put(gpa, nav, code), - .uav => |uav| try c.exported_uavs.put(gpa, uav, code), - } -} - -pub fn deleteExport( - self: *C, - exported: Zcu.Exported, - _: InternPool.NullTerminatedString, -) void { - switch (exported) { - .nav => |nav| _ = self.exported_navs.swapRemove(nav), - .uav => |uav| _ = self.exported_uavs.swapRemove(uav), + switch (exported) { + .nav => |nav| try c.exported_navs.put(gpa, nav, code), + .uav => |uav| try c.exported_uavs.put(gpa, uav, code), + } } } diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 78195f75241f3808610760eedb7d4b83eba681d5..7303c030011bd8935595353982ca2458b93ecae8 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -5893,10 +5893,6 @@ pub fn flush( const comp = coff.base.comp; - // TODO: When https://github.com/ziglang/zig/issues/23617 is in, - // this should be set after updateExports instead - coff.exports_complete = true; - while (try coff.resolve(tid)) {} while (try coff.idle(tid)) {} @@ -7374,30 +7370,38 @@ fn virtualSlide(coff: *Coff, start_section_index: usize, start_rva: u32) !void { pub fn updateExports( coff: *Coff, pt: Zcu.PerThread, - exported: Zcu.Exported, export_indices: []const Zcu.Export.Index, ) link.Error!void { + // TODO: delete old exports from first/second linker member table + // TODO: delete old exports from symbol table inside section const diags = &coff.base.comp.link_diags; - return coff.updateExportsInner(pt, exported, export_indices) catch |err| switch (err) { - error.MappedFileIo => return diags.fail( - "failed to write output file: {t}", - .{coff.mf.io_err.?}, - ), - else => |e| return e, - }; + var alias_syms: std.array_hash_map.Auto(Symbol.Index, Symbol.Index) = .empty; + defer alias_syms.deinit(coff.base.comp.gpa); + for (export_indices) |export_index| { + coff.updateExportInner(pt, export_index, &alias_syms) catch |err| switch (err) { + error.MappedFileIo => return diags.fail( + "failed to write output file: {t}", + .{coff.mf.io_err.?}, + ), + else => |e| return e, + }; + } + coff.exports_complete = true; } -fn updateExportsInner( +fn updateExportInner( coff: *Coff, pt: Zcu.PerThread, - exported: Zcu.Exported, - export_indices: []const Zcu.Export.Index, + export_index: Zcu.Export.Index, + alias_syms: *std.array_hash_map.Auto(Symbol.Index, Symbol.Index), ) !void { const zcu = pt.zcu; const gpa = zcu.gpa; const ip = &zcu.intern_pool; - try coff.symbols.ensureUnusedCapacity(gpa, export_indices.len); - const exported_si: Symbol.Index = switch (exported) { + const exp = export_index.ptr(zcu); + + try coff.symbols.ensureUnusedCapacity(gpa, 1); + const exported_si: Symbol.Index = switch (exp.exported) { .nav => |nav| try coff.navSymbol(zcu, nav), .uav => |uav| @fromBackingInt(@intCast(@backingInt(try coff.lowerUav( pt, @@ -7405,7 +7409,7 @@ fn updateExportsInner( Type.fromInterned(ip.typeOf(uav)).abiAlignment(zcu), )))), }; - switch (exported) { + switch (exp.exported) { .nav => |nav| log.debug("updateExports({f}) = {d}", .{ ip.getNav(nav).fqn.fmt(ip), exported_si }), .uav => |uav| log.debug("updateExports(@as({f}, {f})) = {d}", .{ Type.fromInterned(ip.typeOf(uav)).fmt(pt), @@ -7419,154 +7423,137 @@ fn updateExportsInner( const machine = coff.targetLoad(&coff.headerPtr().machine); const exported_ni = exported_si.node(coff); const exported_sym = exported_si.get(coff); - var prev_alias_si = exported_si; - for (export_indices) |export_index| { - const @"export" = export_index.ptr(zcu); - const name = @"export".opts.name.toSlice(ip); - - // TODO: add an errMsg if this conflicts with an existing symbol - const export_si = try coff.globalSymbol(.{ .name = name }); - const export_sym = export_si.get(coff); - export_sym.ni = exported_ni; - export_sym.rva = exported_sym.rva; - export_sym.section_number = exported_sym.section_number; - if (@"export".opts.linkage == .weak and !coff.isImage()) { - // exported_si needs to be ahead of export_si in the symbol table, - // so that its sti is known when creating the weak external aux entry - try coff.pendingSymbolTableEntry(exported_si); - export_sym.flags.weak_external_strat = .alias; - export_sym.setValue(.{ .weak_alias_si = exported_si }); - } - defer export_si.applyTargetRelocs(coff, .none) catch unreachable; - - // The last symbol in the alias list holds the size - const prev_alias_sym = prev_alias_si.get(coff); - switch (prev_alias_sym.flags.extra_tag) { - .size => export_sym.setExtra(.{ .size = prev_alias_sym.extra.size }), - // This export should have been deleted - .next_alias_si => assert(prev_alias_sym.extra.next_alias_si == export_si), - else => unreachable, - } - - prev_alias_sym.setExtra(.{ .next_alias_si = export_si }); - prev_alias_si = export_si; - - if (!coff.isImage()) continue; - - const entries_ctx = ExportTable.Adapter{ .coff = coff }; - const gop = try coff.export_table.entries.getOrPutAdapted( - gpa, - name, - entries_ctx, - ); - - if (!gop.found_existing) { - errdefer _ = coff.export_table.entries.pop(); - - const export_count = coff.export_table.entries.count(); - if (export_count > std.math.maxInt(@FieldType(std.coff.ExportDirectoryTable, "number_of_entries"))) - return coff.base.comp.link_diags.fail("exceeded maximum number of exports", .{}); - - const name_index: u32 = @intCast(coff.export_table.name_table_ni.location(&coff.mf).resolve(&coff.mf)[1]); - const new_name_table_size = name_index + name.len + 1; - if (new_name_table_size > std.math.maxInt(@FieldType(ExportTable.Entry, "name_index"))) - return coff.base.comp.link_diags.fail("exports name table limit reached", .{}); - - try coff.export_table.name_table_ni.resize(&coff.mf, gpa, new_name_table_size); - - const name_table_slice = coff.export_table.name_table_ni.slice(&coff.mf); - @memcpy(name_table_slice[name_index..][0 .. name.len + 1], name[0 .. name.len + 1]); - - // If the new name sorts after the current tail of the sorted list, we don't need to re-sort - { - const ordinal_table_slice = coff.exportOrdinalTableSlice(); - if (ordinal_table_slice.len > 0 and !coff.export_table.pending_sort) { - const tail_index: ExportTable.Ordinal = - @fromBackingInt(@intCast(ordinal_table_slice[ordinal_table_slice.len - 1].unbiased_ordinal)); - const tail_entry = tail_index.get(coff); - const tail_name = name_table_slice[tail_entry.name_index..][0..tail_entry.name_len]; - coff.export_table.pending_sort = std.mem.lessThan(u8, name, tail_name); - } - } - - const edt = coff.exportDirectoryTable(); - coff.targetStore(&edt.number_of_names, @intCast(export_count)); - edt.number_of_entries = edt.number_of_names; - - // TODO: These should all be resized ahead of time to fit all exports - // after https://github.com/ziglang/zig/issues/23616 - try coff.export_table.export_address_table_si.node(coff).resize( - &coff.mf, - gpa, - export_count * @sizeOf(std.coff.ExportAddressTableEntry), - ); - - try coff.export_table.name_pointer_table_ni.resize( - &coff.mf, - gpa, - export_count * @sizeOf(std.coff.ExportNamePointerTableEntry), - ); - - try coff.export_table.ordinal_table_ni.resize( - &coff.mf, - gpa, - export_count * @sizeOf(std.coff.ExportOrdinalTableEntry), - ); - - coff.targetStore( - &coff.exportNamePointerTableSlice()[gop.index].name_rva, - @intCast(coff.computeNodeRva(coff.export_table.name_table_ni) + name_index), - ); - coff.targetStore( - &coff.exportOrdinalTableSlice()[gop.index].unbiased_ordinal, - @intCast(gop.index), - ); - - gop.value_ptr.* = .{ - .si = export_si, - .name_index = @intCast(name_index), - .name_len = @intCast(name.len), - .export_address_table_ri = @fromBackingInt(@intCast(coff.relocs.items.len)), - }; - - try coff.addReloc( - coff.export_table.export_address_table_si, - @intCast(@sizeOf(std.coff.ExportAddressTableEntry) * gop.index), - export_si, - .{ .known = 0 }, - switch (machine) { - else => |tag| @panic(@tagName(tag)), - .AMD64 => .{ .AMD64 = .ADDR32NB }, - .I386 => .{ .I386 = .DIR32NB }, - }, - ); - } else { - gop.value_ptr.si = export_si; - const reloc = gop.value_ptr.*.export_address_table_ri.get(coff); - reloc.target = export_si; - } + const @"export" = export_index.ptr(zcu); + const name = @"export".opts.name.toSlice(ip); + + // TODO: add an errMsg if this conflicts with an existing symbol + const export_si = try coff.globalSymbol(.{ .name = name }); + const export_sym = export_si.get(coff); + export_sym.ni = exported_ni; + export_sym.rva = exported_sym.rva; + export_sym.section_number = exported_sym.section_number; + if (@"export".opts.linkage == .weak and !coff.isImage()) { + // exported_si needs to be ahead of export_si in the symbol table, + // so that its sti is known when creating the weak external aux entry + try coff.pendingSymbolTableEntry(exported_si); + export_sym.flags.weak_external_strat = .alias; + export_sym.setValue(.{ .weak_alias_si = exported_si }); } -} + defer export_si.applyTargetRelocs(coff, .none) catch unreachable; -pub fn deleteExport( - coff: *Coff, - exported: Zcu.Exported, - name: InternPool.NullTerminatedString, -) void { - const zcu = coff.base.comp.zcu.?; - const ip = &zcu.intern_pool; - - const exported_si: Symbol.Index = switch (exported) { - .nav => |nav| coff.navs.get(nav).?, - .uav => |uav| coff.uavs.get(uav).?, + const prev_alias_si: Symbol.Index = si: { + const gop = try alias_syms.getOrPut(gpa, exported_si); + const prev_alias_si = if (gop.found_existing) gop.value_ptr.* else exported_si; + gop.value_ptr.* = export_si; + break :si prev_alias_si; }; - const name_slice = name.toSlice(ip); - log.debug("deleteExport({s}, {d})", .{ name_slice, exported_si }); + // The last symbol in the alias list holds the size + const prev_alias_sym = prev_alias_si.get(coff); + switch (prev_alias_sym.flags.extra_tag) { + .size => export_sym.setExtra(.{ .size = prev_alias_sym.extra.size }), + // This export should have been deleted + .next_alias_si => assert(prev_alias_sym.extra.next_alias_si == export_si), + else => unreachable, + } - // TODO: Delete from first / second linker member table - // TODO: Delete from symbol table inside section + prev_alias_sym.setExtra(.{ .next_alias_si = export_si }); + + if (!coff.isImage()) return; + + const entries_ctx = ExportTable.Adapter{ .coff = coff }; + const gop = try coff.export_table.entries.getOrPutAdapted( + gpa, + name, + entries_ctx, + ); + + if (!gop.found_existing) { + errdefer _ = coff.export_table.entries.pop(); + + const export_count = coff.export_table.entries.count(); + if (export_count > std.math.maxInt(@FieldType(std.coff.ExportDirectoryTable, "number_of_entries"))) + return coff.base.comp.link_diags.fail("exceeded maximum number of exports", .{}); + + const name_index: u32 = @intCast(coff.export_table.name_table_ni.location(&coff.mf).resolve(&coff.mf)[1]); + const new_name_table_size = name_index + name.len + 1; + if (new_name_table_size > std.math.maxInt(@FieldType(ExportTable.Entry, "name_index"))) + return coff.base.comp.link_diags.fail("exports name table limit reached", .{}); + + try coff.export_table.name_table_ni.resize(&coff.mf, gpa, new_name_table_size); + + const name_table_slice = coff.export_table.name_table_ni.slice(&coff.mf); + @memcpy(name_table_slice[name_index..][0 .. name.len + 1], name[0 .. name.len + 1]); + + // If the new name sorts after the current tail of the sorted list, we don't need to re-sort + { + const ordinal_table_slice = coff.exportOrdinalTableSlice(); + if (ordinal_table_slice.len > 0 and !coff.export_table.pending_sort) { + const tail_index: ExportTable.Ordinal = + @fromBackingInt(@intCast(ordinal_table_slice[ordinal_table_slice.len - 1].unbiased_ordinal)); + const tail_entry = tail_index.get(coff); + const tail_name = name_table_slice[tail_entry.name_index..][0..tail_entry.name_len]; + coff.export_table.pending_sort = std.mem.lessThan(u8, name, tail_name); + } + } + + const edt = coff.exportDirectoryTable(); + coff.targetStore(&edt.number_of_names, @intCast(export_count)); + edt.number_of_entries = edt.number_of_names; + + // TODO: These should all be resized ahead of time to fit all exports + // after https://github.com/ziglang/zig/issues/23616 + try coff.export_table.export_address_table_si.node(coff).resize( + &coff.mf, + gpa, + export_count * @sizeOf(std.coff.ExportAddressTableEntry), + ); + + try coff.export_table.name_pointer_table_ni.resize( + &coff.mf, + gpa, + export_count * @sizeOf(std.coff.ExportNamePointerTableEntry), + ); + + try coff.export_table.ordinal_table_ni.resize( + &coff.mf, + gpa, + export_count * @sizeOf(std.coff.ExportOrdinalTableEntry), + ); + + coff.targetStore( + &coff.exportNamePointerTableSlice()[gop.index].name_rva, + @intCast(coff.computeNodeRva(coff.export_table.name_table_ni) + name_index), + ); + coff.targetStore( + &coff.exportOrdinalTableSlice()[gop.index].unbiased_ordinal, + @intCast(gop.index), + ); + + gop.value_ptr.* = .{ + .si = export_si, + .name_index = @intCast(name_index), + .name_len = @intCast(name.len), + .export_address_table_ri = @fromBackingInt(@intCast(coff.relocs.items.len)), + }; + + try coff.addReloc( + coff.export_table.export_address_table_si, + @intCast(@sizeOf(std.coff.ExportAddressTableEntry) * gop.index), + export_si, + .{ .known = 0 }, + switch (machine) { + else => |tag| @panic(@tagName(tag)), + .AMD64 => .{ .AMD64 = .ADDR32NB }, + .I386 => .{ .I386 = .DIR32NB }, + }, + ); + } else { + gop.value_ptr.si = export_si; + const reloc = gop.value_ptr.*.export_address_table_ri.get(coff); + reloc.target = export_si; + } } fn dumpStderr(coff: *Coff, tid: Zcu.PerThread.Id) !void { diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 80844d3d8832b7b326411038a286cc2391b4d097..d372c2e58a9990eb17812e39b9dc3a2e4588b62b 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1685,24 +1685,15 @@ pub fn updateContainerType( pub fn updateExports( self: *Elf, pt: Zcu.PerThread, - exported: Zcu.Exported, export_indices: []const Zcu.Export.Index, ) link.Error!void { - return self.zigObjectPtr().?.updateExports(self, pt, exported, export_indices); + return self.zigObjectPtr().?.updateExports(self, pt, export_indices); } pub fn updateLineNumber(self: *Elf, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) link.Error!void { return self.zigObjectPtr().?.updateLineNumber(pt, ti_id); } -pub fn deleteExport( - self: *Elf, - exported: Zcu.Exported, - name: InternPool.NullTerminatedString, -) void { - return self.zigObjectPtr().?.deleteExport(self, exported, name); -} - fn checkDuplicates(self: *Elf) !void { const gpa = self.base.comp.gpa; diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index cd3378c8d69f457bb45e028def978067a219196d..50c1060e386a47f13eb177c7539535c642658416 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -1857,7 +1857,6 @@ pub fn updateExports( self: *ZigObject, elf_file: *Elf, pt: Zcu.PerThread, - exported: Zcu.Exported, export_indices: []const Zcu.Export.Index, ) link.Error!void { const tracy = trace(@src()); @@ -1865,27 +1864,49 @@ pub fn updateExports( const zcu = pt.zcu; const gpa = elf_file.base.comp.gpa; - const metadata = switch (exported) { - .nav => |nav| blk: { - _ = try self.getOrCreateMetadataForNav(zcu, nav); - break :blk self.navs.getPtr(nav).?; - }, - .uav => |uav| self.uavs.getPtr(uav) orelse blk: { - _ = try self.lowerUav(elf_file, pt, uav, .none); - break :blk self.uavs.getPtr(uav).?; - }, - }; - const sym_index = metadata.symbol_index; - const esym_index = self.symbol(sym_index).esym_index; - const esym = self.symtab.items(.elf_sym)[esym_index]; - const esym_shndx = self.symtab.items(.shndx)[esym_index]; - for (export_indices) |export_idx| { - const exp = export_idx.ptr(zcu); + // Delete all existing exports first + for (self.navs.values()) |*metadata| { + for (metadata.exports.items) |sym_index| { + const esym_index = self.symbol(sym_index).esym_index; + const esym = &self.symtab.items(.elf_sym)[esym_index]; + _ = self.globals_lookup.remove(esym.st_name); + esym.* = Elf.null_sym; + self.symtab.items(.shndx)[esym_index] = elf.SHN_UNDEF; + } + metadata.exports.clearRetainingCapacity(); + } + for (self.uavs.values()) |*metadata| { + for (metadata.exports.items) |sym_index| { + const esym_index = self.symbol(sym_index).esym_index; + const esym = &self.symtab.items(.elf_sym)[esym_index]; + _ = self.globals_lookup.remove(esym.st_name); + esym.* = Elf.null_sym; + self.symtab.items(.shndx)[esym_index] = elf.SHN_UNDEF; + } + metadata.exports.clearRetainingCapacity(); + } + + for (export_indices) |export_index| { + const exp = export_index.ptr(zcu); + const metadata = switch (exp.exported) { + .nav => |nav| blk: { + _ = try self.getOrCreateMetadataForNav(zcu, nav); + break :blk self.navs.getPtr(nav).?; + }, + .uav => |uav| self.uavs.getPtr(uav) orelse blk: { + _ = try self.lowerUav(elf_file, pt, uav, .none); + break :blk self.uavs.getPtr(uav).?; + }, + }; + const sym_index = metadata.symbol_index; + const esym_index = self.symbol(sym_index).esym_index; + const esym = self.symtab.items(.elf_sym)[esym_index]; + const esym_shndx = self.symtab.items(.shndx)[esym_index]; if (exp.opts.section.unwrap()) |section_name| { if (!section_name.eqlSlice(".text", &zcu.intern_pool)) { - try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1); - zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, try Zcu.ErrorMsg.create( + try zcu.failed_exports.ensureUnusedCapacity(gpa, 1); + zcu.failed_exports.putAssumeCapacityNoClobber(export_index, try Zcu.ErrorMsg.create( gpa, exp.src, "Unimplemented: ExportOptions.section", @@ -1899,8 +1920,8 @@ pub fn updateExports( .strong => elf.STB_GLOBAL, .weak => elf.STB_WEAK, .link_once => { - try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1); - zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, try Zcu.ErrorMsg.create( + try zcu.failed_exports.ensureUnusedCapacity(gpa, 1); + zcu.failed_exports.putAssumeCapacityNoClobber(export_index, try Zcu.ErrorMsg.create( gpa, exp.src, "Unimplemented: GlobalLinkage.LinkOnce", @@ -1912,13 +1933,8 @@ pub fn updateExports( const stt_bits: u8 = @as(u4, @truncate(esym.st_info)); const exp_name = exp.opts.name.toSlice(&zcu.intern_pool); const name_off = try self.strtab.insert(gpa, exp_name); - const global_sym_index = if (metadata.@"export"(self, exp_name)) |exp_index| - exp_index.* - else blk: { - const global_sym_index = try self.getGlobalSymbol(elf_file, exp_name, null); - try metadata.exports.append(gpa, global_sym_index); - break :blk global_sym_index; - }; + const global_sym_index = try self.getGlobalSymbol(elf_file, exp_name, null); + try metadata.exports.append(gpa, global_sym_index); const value = self.symbol(sym_index).value; const global_sym = self.symbol(global_sym_index); @@ -1947,27 +1963,6 @@ pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.T } } -pub fn deleteExport( - self: *ZigObject, - elf_file: *Elf, - exported: Zcu.Exported, - name: InternPool.NullTerminatedString, -) void { - const metadata = switch (exported) { - .nav => |nav| self.navs.getPtr(nav), - .uav => |uav| self.uavs.getPtr(uav), - } orelse return; - const zcu = elf_file.base.comp.zcu.?; - const exp_name = name.toSlice(&zcu.intern_pool); - const sym_index = metadata.@"export"(self, exp_name) orelse return; - log.debug("deleting export '{s}'", .{exp_name}); - const esym_index = self.symbol(sym_index.*).esym_index; - const esym = &self.symtab.items(.elf_sym)[esym_index]; - _ = self.globals_lookup.remove(esym.st_name); - esym.* = Elf.null_sym; - self.symtab.items(.shndx)[esym_index] = elf.SHN_UNDEF; -} - pub fn getGlobalSymbol(self: *ZigObject, elf_file: *Elf, name: []const u8, lib_name: ?[]const u8) !u32 { _ = lib_name; const gpa = elf_file.base.comp.gpa; @@ -2362,14 +2357,6 @@ const AvMetadata = struct { exports: std.ArrayList(Symbol.Index) = .empty, /// Set to true if the AV has been initialized and allocated. allocated: bool = false, - - fn @"export"(m: AvMetadata, zig_object: *ZigObject, name: []const u8) ?*u32 { - for (m.exports.items) |*exp| { - const exp_name = zig_object.getString(zig_object.symbol(exp.*).name_offset); - if (mem.eql(u8, name, exp_name)) return exp; - } - return null; - } }; fn checkNavAllocated(pt: Zcu.PerThread, index: InternPool.Nav.Index, meta: AvMetadata) void { diff --git a/src/link/Elf2.zig b/src/link/Elf2.zig index b0085f18a404a767553ffbf99ccc63bea7672e2a..ba66d29a4d33c89c4a7aa97a582ab4248e89d05c 100644 --- a/src/link/Elf2.zig +++ b/src/link/Elf2.zig @@ -8287,33 +8287,35 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad pub fn updateExports( elf: *Elf, pt: Zcu.PerThread, - exported: Zcu.Exported, export_indices: []const Zcu.Export.Index, ) link.Error!void { const diags = &elf.base.comp.link_diags; - return elf.updateExportsInner(pt, exported, export_indices) catch |err| switch (err) { - else => |e| return e, - error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), - }; + for (export_indices) |export_index| { + elf.updateExportInner(pt, export_index) catch |err| switch (err) { + else => |e| return e, + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), + }; + } } -fn updateExportsInner( +fn updateExportInner( elf: *Elf, pt: Zcu.PerThread, - exported: Zcu.Exported, - export_indices: []const Zcu.Export.Index, + export_index: Zcu.Export.Index, ) Error!void { const zcu = pt.zcu; const ip = &zcu.intern_pool; - switch (exported) { + const @"export" = export_index.ptr(zcu); + + switch (@"export".exported) { .nav => |nav| log.debug("updateExports({f})", .{ip.getNav(nav).fqn.fmt(ip)}), .uav => |uav| log.debug("updateExports(@as({f}, {f}))", .{ Type.fromInterned(ip.typeOf(uav)).fmt(pt), Value.fromInterned(uav).fmtValue(pt), }), } - try elf.ensureUnusedSymbolCapacity(@intCast(export_indices.len), .maybe_global); - const exported_lsi: Symbol.LocalIndex, const @"type": std.elf.STT = switch (exported) { + try elf.ensureUnusedSymbolCapacity(1, .maybe_global); + const exported_lsi: Symbol.LocalIndex, const @"type": std.elf.STT = switch (@"export".exported) { .nav => |nav| .{ (try elf.navMapIndex(zcu, nav)).symbol(elf), elf.navType(ip.getNav(nav).resolved.?), @@ -8331,50 +8333,42 @@ fn updateExportsInner( .fromSection(elf.targetLoad(&exported_sym.shndx)), }, }; - for (export_indices) |export_index| { - const @"export" = export_index.ptr(zcu); - const name = @"export".opts.name.toSlice(ip); - _ = elf.addGlobalSymbolAssumeCapacity(.{ - .node = .none, - .name = try .string(elf, name), - .value = value, - .size = @intCast(size), - .type = @"type", - .bind = switch (@"export".opts.linkage) { - .strong => .strong, - .weak => .weak, - .internal => return elf.base.comp.link_diags.fail("TODO(Elf2): '.internal' linkage", .{}), - .link_once => return elf.base.comp.link_diags.fail("TODO(Elf2): '.link_once' linkage", .{}), - }, - .visibility = switch (@"export".opts.visibility) { - .default => .DEFAULT, - .hidden => .HIDDEN, - .protected => .PROTECTED, - }, - .shndx = shndx, - }) catch |err| switch (err) { - error.MultipleDefinitions => { - // HACK: because we currently don't/can't delete these exports, we would typically - // get these errors on every non-initial incremental update. Hack around that by - // only emitting this error if the symbol we're conflicting with comes from an input - // section (as opposed to the ZCU). - const conflicting_global = elf.globalByName(try elf.string(.strtab, name)).?; - const conflicting_node = conflicting_global.symtab_index.ptr(elf).node; - if (elf.getNode(conflicting_node) == .input_section) { - return elf.base.comp.link_diags.fail( - "multiple definitions of '{s}'", - .{name}, - ); - } - }, - }; - } -} -pub fn deleteExport(elf: *Elf, exported: Zcu.Exported, name: InternPool.NullTerminatedString) void { - _ = elf; - _ = exported; - _ = name; + const name = @"export".opts.name.toSlice(ip); + _ = elf.addGlobalSymbolAssumeCapacity(.{ + .node = .none, + .name = try .string(elf, name), + .value = value, + .size = @intCast(size), + .type = @"type", + .bind = switch (@"export".opts.linkage) { + .strong => .strong, + .weak => .weak, + .internal => return elf.base.comp.link_diags.fail("TODO(Elf2): '.internal' linkage", .{}), + .link_once => return elf.base.comp.link_diags.fail("TODO(Elf2): '.link_once' linkage", .{}), + }, + .visibility = switch (@"export".opts.visibility) { + .default => .DEFAULT, + .hidden => .HIDDEN, + .protected => .PROTECTED, + }, + .shndx = shndx, + }) catch |err| switch (err) { + error.MultipleDefinitions => { + // HACK: because we currently don't/can't delete these exports, we would typically + // get these errors on every non-initial incremental update. Hack around that by + // only emitting this error if the symbol we're conflicting with comes from an input + // section (as opposed to the ZCU). + const conflicting_global = elf.globalByName(try elf.string(.strtab, name)).?; + const conflicting_node = conflicting_global.symtab_index.ptr(elf).node; + if (elf.getNode(conflicting_node) == .input_section) { + return elf.base.comp.link_diags.fail( + "multiple definitions of '{s}'", + .{name}, + ); + } + }, + }; } fn dumpStderr(elf: *Elf, tid: Zcu.PerThread.Id) !void { diff --git a/src/link/MachO.zig b/src/link/MachO.zig index eb94246a43fe3b4d38d2473f5d53a9512890a20a..c03d4eac74b73b293e66bf08258a69fea466b721 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -3102,18 +3102,9 @@ pub fn updateLineNumber(self: *MachO, pt: Zcu.PerThread, ti_id: InternPool.Track pub fn updateExports( self: *MachO, pt: Zcu.PerThread, - exported: Zcu.Exported, export_indices: []const Zcu.Export.Index, ) link.Error!void { - return self.getZigObject().?.updateExports(self, pt, exported, export_indices); -} - -pub fn deleteExport( - self: *MachO, - exported: Zcu.Exported, - name: InternPool.NullTerminatedString, -) void { - return self.getZigObject().?.deleteExport(self, exported, name); + return self.getZigObject().?.updateExports(self, pt, export_indices); } pub fn freeNav(self: *MachO, nav: InternPool.Nav.Index) void { diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index 71a4bc6dc70c0465a079f035d459a07d8720ceed..bce67375a27e72c8a0db3be6d73518c2a704dc78 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -1238,7 +1238,6 @@ pub fn updateExports( self: *ZigObject, macho_file: *MachO, pt: Zcu.PerThread, - exported: Zcu.Exported, export_indices: []const Zcu.Export.Index, ) link.Error!void { const tracy = trace(@src()); @@ -1246,26 +1245,60 @@ pub fn updateExports( const zcu = pt.zcu; const gpa = macho_file.base.comp.gpa; - const metadata = switch (exported) { - .nav => |nav| blk: { - _ = try self.getOrCreateMetadataForNav(macho_file, nav); - break :blk self.navs.getPtr(nav).?; - }, - .uav => |uav| self.uavs.getPtr(uav) orelse blk: { - _ = try self.lowerUav(macho_file, pt, uav, .none); - break :blk self.uavs.getPtr(uav).?; - }, - }; - const sym_index = metadata.symbol_index; - const nlist_idx = self.symbols.items[sym_index].nlist_idx; - const nlist = self.symtab.items(.nlist)[nlist_idx]; - for (export_indices) |export_idx| { - const exp = export_idx.ptr(zcu); + // Delete all existing exports first + for (self.navs.values()) |*metadata| { + for (metadata.exports.items) |nlist_index| { + const nlist = &self.symtab.items(.nlist)[nlist_index]; + self.symtab.items(.size)[nlist_index] = 0; + _ = self.globals_lookup.remove(nlist.n_strx); + // TODO actually remove the export + // const sym_index = macho_file.globals.get(nlist.n_strx).?; + // const sym = &self.symbols.items[sym_index]; + // if (sym.file == self.index) { + // sym.* = .{}; + // } + nlist.* = MachO.null_sym; + } + metadata.exports.clearRetainingCapacity(); + } + for (self.uavs.values()) |*metadata| { + for (metadata.exports.items) |nlist_index| { + const nlist = &self.symtab.items(.nlist)[nlist_index]; + self.symtab.items(.size)[nlist_index] = 0; + _ = self.globals_lookup.remove(nlist.n_strx); + // TODO actually remove the export + // const sym_index = macho_file.globals.get(nlist.n_strx).?; + // const sym = &self.symbols.items[sym_index]; + // if (sym.file == self.index) { + // sym.* = .{}; + // } + nlist.* = MachO.null_sym; + } + metadata.exports.clearRetainingCapacity(); + } + + for (export_indices) |export_index| { + const exp = export_index.ptr(zcu); + + const metadata = switch (exp.exported) { + .nav => |nav| blk: { + _ = try self.getOrCreateMetadataForNav(macho_file, nav); + break :blk self.navs.getPtr(nav).?; + }, + .uav => |uav| self.uavs.getPtr(uav) orelse blk: { + _ = try self.lowerUav(macho_file, pt, uav, .none); + break :blk self.uavs.getPtr(uav).?; + }, + }; + const sym_index = metadata.symbol_index; + const nlist_idx = self.symbols.items[sym_index].nlist_idx; + const nlist = self.symtab.items(.nlist)[nlist_idx]; + if (exp.opts.section.unwrap()) |section_name| { if (!section_name.eqlSlice("__text", &zcu.intern_pool)) { try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1); - zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, try Zcu.ErrorMsg.create( + zcu.failed_exports.putAssumeCapacityNoClobber(export_index, try Zcu.ErrorMsg.create( gpa, exp.src, "Unimplemented: ExportOptions.section", @@ -1275,7 +1308,7 @@ pub fn updateExports( } } if (exp.opts.linkage == .link_once) { - try zcu.failed_exports.putNoClobber(zcu.gpa, export_idx, try Zcu.ErrorMsg.create( + try zcu.failed_exports.putNoClobber(zcu.gpa, export_index, try Zcu.ErrorMsg.create( gpa, exp.src, "Unimplemented: GlobalLinkage.link_once", @@ -1285,13 +1318,9 @@ pub fn updateExports( } const exp_name = exp.opts.name.toSlice(&zcu.intern_pool); - const global_nlist_index = if (metadata.@"export"(self, exp_name)) |exp_index| - exp_index.* - else blk: { - const global_nlist_index = try self.getGlobalSymbol(macho_file, exp_name, null); - try metadata.exports.append(gpa, global_nlist_index); - break :blk global_nlist_index; - }; + const global_nlist_index = try self.getGlobalSymbol(macho_file, exp_name, null); + try metadata.exports.append(gpa, global_nlist_index); + const global_nlist = &self.symtab.items(.nlist)[global_nlist_index]; const atom_index = self.symtab.items(.atom)[nlist_idx]; 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 } } -pub fn deleteExport( - self: *ZigObject, - macho_file: *MachO, - exported: Zcu.Exported, - name: InternPool.NullTerminatedString, -) void { - const zcu = macho_file.base.comp.zcu.?; - - const metadata = switch (exported) { - .nav => |nav| self.navs.getPtr(nav), - .uav => |uav| self.uavs.getPtr(uav), - } orelse return; - const nlist_index = metadata.@"export"(self, name.toSlice(&zcu.intern_pool)) orelse return; - - log.debug("deleting export '{f}'", .{name.fmt(&zcu.intern_pool)}); - - const nlist = &self.symtab.items(.nlist)[nlist_index.*]; - self.symtab.items(.size)[nlist_index.*] = 0; - _ = self.globals_lookup.remove(nlist.n_strx); - // TODO actually remove the export - // const sym_index = macho_file.globals.get(nlist.n_strx).?; - // const sym = &self.symbols.items[sym_index]; - // if (sym.file == self.index) { - // sym.* = .{}; - // } - nlist.* = MachO.null_sym; -} - pub fn getGlobalSymbol(self: *ZigObject, macho_file: *MachO, name: []const u8, lib_name: ?[]const u8) !u32 { _ = lib_name; const gpa = macho_file.base.comp.gpa; @@ -1722,15 +1723,6 @@ const AvMetadata = struct { symbol_index: Symbol.Index, /// A list of all exports aliases of this Av. exports: std.ArrayList(Symbol.Index) = .empty, - - fn @"export"(m: AvMetadata, zig_object: *ZigObject, name: []const u8) ?*u32 { - for (m.exports.items) |*exp| { - const nlist = zig_object.symtab.items(.nlist)[exp.*]; - const exp_name = zig_object.strtab.getAssumeExists(nlist.n_strx); - if (mem.eql(u8, name, exp_name)) return exp; - } - return null; - } }; const LazySymbolMetadata = struct { diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index aecbc039266604cc905586b71032e91d83b096d0..3129a511da65dee9d42447994d782f294b1dfb08 100644 --- a/src/link/SpirV.zig +++ b/src/link/SpirV.zig @@ -219,24 +219,20 @@ pub fn updateNav(linker: *Linker, pt: Zcu.PerThread, nav: InternPool.Nav.Index) pub fn updateExports( linker: *Linker, pt: Zcu.PerThread, - exported: Zcu.Exported, export_indices: []const Zcu.Export.Index, -) !void { +) link.Error!void { const zcu = pt.zcu; const ip = &zcu.intern_pool; const gpa = linker.base.comp.gpa; - const nav_index = switch (exported) { - .nav => |nav| nav, - .uav => |uav| { - _ = uav; - @panic("TODO: implement Linker linker code for exporting a constant value"); - }, - }; - const nav_ty = ip.getNav(nav_index).resolved.?.type; - if (ip.isFunctionType(nav_ty)) { - const cc = Type.fromInterned(nav_ty).fnCallingConvention(zcu); - for (export_indices) |export_idx| { - const exp = export_idx.ptr(zcu); + for (export_indices) |exp_index| { + const exp = exp_index.ptr(zcu); + const nav_index = switch (exp.exported) { + .nav => |nav| nav, + .uav => @panic("TODO: implement Linker linker code for exporting a constant value"), + }; + const nav_ty = ip.getNav(nav_index).resolved.?.type; + if (ip.isFunctionType(nav_ty)) { + const cc = Type.fromInterned(nav_ty).fnCallingConvention(zcu); try linker.entry_points.append(gpa, .{ .nav = nav_index, .name = exp.opts.name.toSlice(ip), diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 8dc5fbcc14160160dc0c1b9d0c66d3a3ef3cc4de..5a59c890e84080fbdff073b37ceb7bc77bfd1484 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -3,11 +3,11 @@ //! performed without any knowledge of functions and globals provided by the //! Zcu. If there is no Zcu, effectively all linking is done in `prelink`. //! -//! `updateFunc`, `updateNav`, `updateExports`, and `deleteExport` are handled -//! by merely tracking references to the relevant functions and globals. All -//! the linking logic between objects and Zcu happens in `flush`. Many -//! components of the final output are computed on-the-fly at this time rather -//! than being precomputed and stored separately. +//! `updateFunc`, `updateNav`, and `updateExports` are handled by merely +//! tracking references to the relevant functions and globals. All the linking +//! logic between objects and Zcu happens in `flush`. Many components of the +//! final output are computed on-the-fly at this time rather than being +//! precomputed and stored separately. const Wasm = @This(); const Archive = @import("Wasm/Archive.zig"); @@ -219,9 +219,9 @@ data_imports_len_prelink: u32 = 0, /// objects. /// /// During the Zcu phase, entries are not deleted from this table -/// because doing so would be irreversible when a `deleteExport` call is -/// handled. However, entries are added during the Zcu phase when extern -/// functions are passed to `updateNav`. +/// because doing so would be irreversible when an export is deleted. +/// However, entries are added during the Zcu phase when extern functions +/// are passed to `updateNav`. /// /// `flush` gets a copy of this table, and then Zcu exports are applied to /// 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, /// objects. /// /// During the Zcu phase, entries are not deleted from this table -/// because doing so would be irreversible when a `deleteExport` call is -/// handled. However, entries are added during the Zcu phase when extern -/// functions are passed to `updateNav`. +/// because doing so would be irreversible when an export is deleted. +/// However, entries are added during the Zcu phase when extern functions +/// are passed to `updateNav`. /// /// `flush` gets a copy of this table, and then Zcu exports are applied to /// 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 } } -pub fn deleteExport( - wasm: *Wasm, - exported: Zcu.Exported, - name: InternPool.NullTerminatedString, -) void { - const zcu = wasm.base.comp.zcu.?; - const ip = &zcu.intern_pool; - const name_slice = name.toSlice(ip); - const export_name = wasm.getExistingString(name_slice).?; - switch (exported) { - .nav => |nav_index| { - log.debug("deleteExport '{s}' nav={d}", .{ name_slice, @backingInt(nav_index) }); - assert(wasm.nav_exports.swapRemove(.{ .nav_index = nav_index, .name = export_name })); - }, - .uav => |uav_index| assert(wasm.uav_exports.swapRemove(.{ .uav_index = uav_index, .name = export_name })), - } -} - pub fn updateExports( wasm: *Wasm, pt: Zcu.PerThread, - exported: Zcu.Exported, export_indices: []const Zcu.Export.Index, ) !void { const zcu = pt.zcu; const gpa = zcu.gpa; const ip = &zcu.intern_pool; const is_obj = wasm.base.comp.config.output_mode == .Obj; - switch (exported) { - .nav => {}, // handled in updateNav - .uav => |uav_index| { // export may be the only reference - const zds: ZcuDataStarts = .init(wasm); - if (is_obj) { - const gop = try wasm.uavs_obj.getOrPut(gpa, uav_index); - if (!gop.found_existing) gop.value_ptr.* = undefined; - } else { - const gop = try wasm.uavs_exe.getOrPut(gpa, uav_index); - if (!gop.found_existing) gop.value_ptr.* = .{ - .code = undefined, - .count = 0, - }; - gop.value_ptr.count += 1; - } - try zds.finish(wasm, pt); - }, - } + + wasm.nav_exports.clearRetainingCapacity(); + wasm.uav_exports.clearRetainingCapacity(); + for (export_indices) |export_idx| { const exp = export_idx.ptr(zcu); const name_slice = exp.opts.name.toSlice(ip); const name = try wasm.internString(name_slice); - switch (exported) { + switch (exp.exported) { .nav => |nav_index| { log.debug("updateExports '{s}' nav={d}", .{ name_slice, @backingInt(nav_index) }); try wasm.nav_exports.put(gpa, .{ .nav_index = nav_index, .name = name }, export_idx); }, - .uav => |uav_index| try wasm.uav_exports.put(gpa, .{ .uav_index = uav_index, .name = name }, export_idx), + .uav => |uav_index| { + // Lower the UAV, as the export may be the only reference. + const zds: ZcuDataStarts = .init(wasm); + if (is_obj) { + const gop = try wasm.uavs_obj.getOrPut(gpa, uav_index); + if (!gop.found_existing) gop.value_ptr.* = undefined; + } else { + const gop = try wasm.uavs_exe.getOrPut(gpa, uav_index); + if (!gop.found_existing) gop.value_ptr.* = .{ + .code = undefined, + .count = 0, + }; + gop.value_ptr.count += 1; + } + try zds.finish(wasm, pt); + try wasm.uav_exports.put(gpa, .{ .uav_index = uav_index, .name = name }, export_idx); + }, } } } diff --git a/test/link/snapshots/static-lib.llvm.dmp b/test/link/snapshots/static-lib.llvm.dmp index f64e76be1134c735e77bb6e2e10d0983a8858d35..aad7f0c4f4930c74a4f69aa7e8a2a24efbde1c86 100644 --- a/test/link/snapshots/static-lib.llvm.dmp +++ b/test/link/snapshots/static-lib.llvm.dmp @@ -8,8 +8,8 @@ xxxx 00000000 1 NULL() STATIC | this_is_a_long_name.fooWeak xxxx 00000000 2 NULL STATIC | this_is_a_long_name.foo_strong xxxx 00000008 2 NULL STATIC | this_is_a_long_name.foo_array xxxx 00000000 2 NULL EXTERNAL | foo_strong -xxxx 00000000 2 NULL EXTERNAL | foo_strong_alias xxxx 00000008 2 NULL EXTERNAL | foo_array xxxx 00000000 UNDEF NULL WEAK_EXTERNAL | fooWeak | Weak External [falls back to relative ordinal 000000+2 via SEARCH_ALIAS] xxxx 00000000 1 NULL() EXTERNAL | .weak.fooWeak.default.foo_strong +xxxx 00000000 2 NULL EXTERNAL | foo_strong_alias diff --git a/test/link/snapshots/static-lib.no-llvm.dmp b/test/link/snapshots/static-lib.no-llvm.dmp index 07e2219bfd57889557bd5eda680dc5de4ba62e42..df71ce11305e3442e4b20cebdbfdb18ded1b7d61 100644 --- a/test/link/snapshots/static-lib.no-llvm.dmp +++ b/test/link/snapshots/static-lib.no-llvm.dmp @@ -6,7 +6,7 @@ xxxx 00000004 2 NULL EXTERNAL | foo2 lib.lib(this_is_a_long_name.obj): COFF object xxxx 00000000 4 NULL() EXTERNAL | this_is_a_long_name.fooWeak xxxx 00000000 2 NULL EXTERNAL | foo_strong -xxxx 00000000 2 NULL EXTERNAL | foo_strong_alias xxxx 00000010 2 NULL EXTERNAL | foo_array xxxx 00000000 UNDEF NULL() WEAK_EXTERNAL | fooWeak - | Weak External [falls back to relative ordinal 000000-4 via SEARCH_ALIAS] + | Weak External [falls back to relative ordinal 000000-3 via SEARCH_ALIAS] +xxxx 00000000 2 NULL EXTERNAL | foo_strong_alias