authorgravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2026-10-17 23:39:40+02:00
committergravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2026-10-20 23:10:13+02:00
log29c18b40dcb74789ed054e9fe599a4a8f644b08e
tree2dceb411c2cafc522613b7d29d5b19757469933a
parent56fad80e8ce14a23f6642e73f545ed6c8b394929

wasm: implement static archive


4 files changed, 213 insertions(+), 51 deletions(-)

src/codegen/wasm/Emit.zig+2-2
...@@ -27,7 +27,7 @@ pub fn lower(emit: *Emit) Error!void {...@@ -27,7 +27,7 @@ pub fn lower(emit: *Emit) Error!void {
27 const wasm = emit.wasm;27 const wasm = emit.wasm;
28 const comp = wasm.base.comp;28 const comp = wasm.base.comp;
29 const gpa = comp.gpa;29 const gpa = comp.gpa;
30 const is_obj = comp.config.output_mode == .Obj;30 const is_obj = wasm.base.isRelocatable();
31 const target = &comp.root_mod.resolved_target.result;31 const target = &comp.root_mod.resolved_target.result;
32 const is_wasm32 = target.cpu.arch == .wasm32;32 const is_wasm32 = target.cpu.arch == .wasm32;
3333
...@@ -1028,7 +1028,7 @@ fn navRefOff(wasm: *Wasm, code: *ArrayList(u8), data: Mir.NavRefOff, is_wasm32:...@@ -1028,7 +1028,7 @@ fn navRefOff(wasm: *Wasm, code: *ArrayList(u8), data: Mir.NavRefOff, is_wasm32:
1028 const zcu = comp.zcu.?;1028 const zcu = comp.zcu.?;
1029 const ip = &zcu.intern_pool;1029 const ip = &zcu.intern_pool;
1030 const gpa = comp.gpa;1030 const gpa = comp.gpa;
1031 const is_obj = comp.config.output_mode == .Obj;1031 const is_obj = wasm.base.isRelocatable();
1032 const nav_ty = ip.getNav(data.nav_index).resolved.?.type;1032 const nav_ty = ip.getNav(data.nav_index).resolved.?.type;
1033 assert(!ip.isFunctionType(nav_ty));1033 assert(!ip.isFunctionType(nav_ty));
10341034
src/link/Wasm.zig+46-45
...@@ -79,6 +79,7 @@ growable_table: bool,...@@ -79,6 +79,7 @@ growable_table: bool,
79name: []const u8,79name: []const u8,
80/// List of relocatable files to be linked into the final binary.80/// List of relocatable files to be linked into the final binary.
81objects: std.ArrayList(Object) = .empty,81objects: std.ArrayList(Object) = .empty,
82archive_builder: Archive.Builder = .{},
8283
83func_types: std.array_hash_map.Auto(FunctionType, void) = .empty,84func_types: std.array_hash_map.Auto(FunctionType, void) = .empty,
84/// Provides a mapping of both imports and provided functions to symbol name.85/// Provides a mapping of both imports and provided functions to symbol name.
...@@ -467,8 +468,7 @@ pub const OutputDataIndex = enum(u32) {...@@ -467,8 +468,7 @@ pub const OutputDataIndex = enum(u32) {
467 }468 }
468469
469 pub fn fromUav(wasm: *const Wasm, ip_index: InternPool.Index) OutputDataIndex {470 pub fn fromUav(wasm: *const Wasm, ip_index: InternPool.Index) OutputDataIndex {
470 const comp = wasm.base.comp;471 const resolution: ObjectDataImport.Resolution = if (wasm.base.isRelocatable())
471 const resolution: ObjectDataImport.Resolution = if (comp.config.output_mode == .Obj)
472 .pack(wasm, .{ .uav_obj = @fromBackingInt(@intCast(wasm.uavs_obj.getIndex(ip_index).?)) })472 .pack(wasm, .{ .uav_obj = @fromBackingInt(@intCast(wasm.uavs_obj.getIndex(ip_index).?)) })
473 else473 else
474 .pack(wasm, .{ .uav_exe = @fromBackingInt(@intCast(wasm.uavs_exe.getIndex(ip_index).?)) });474 .pack(wasm, .{ .uav_exe = @fromBackingInt(@intCast(wasm.uavs_exe.getIndex(ip_index).?)) });
...@@ -482,7 +482,7 @@ pub const OutputDataIndex = enum(u32) {...@@ -482,7 +482,7 @@ pub const OutputDataIndex = enum(u32) {
482 if (nav.getExtern(ip) != null) {482 if (nav.getExtern(ip) != null) {
483 return fromSymbolName(wasm, wasm.imports.get(nav_index).?);483 return fromSymbolName(wasm, wasm.imports.get(nav_index).?);
484 }484 }
485 const resolution: ObjectDataImport.Resolution = if (wasm.base.comp.config.output_mode == .Obj)485 const resolution: ObjectDataImport.Resolution = if (wasm.base.isRelocatable())
486 .pack(wasm, .{ .nav_obj = @fromBackingInt(@intCast(wasm.navs_obj.getIndex(nav_index).?)) })486 .pack(wasm, .{ .nav_obj = @fromBackingInt(@intCast(wasm.navs_obj.getIndex(nav_index).?)) })
487 else487 else
488 .pack(wasm, .{ .nav_exe = @fromBackingInt(@intCast(wasm.navs_exe.getIndex(nav_index).?)) });488 .pack(wasm, .{ .nav_exe = @fromBackingInt(@intCast(wasm.navs_exe.getIndex(nav_index).?)) });
...@@ -500,7 +500,7 @@ pub const GlobalIndex = enum(u32) {...@@ -500,7 +500,7 @@ pub const GlobalIndex = enum(u32) {
500 /// Same as `stack_pointer` but with a safety assertion.500 /// Same as `stack_pointer` but with a safety assertion.
501 pub fn stackPointer(wasm: *const Wasm) ObjectGlobal.Index {501 pub fn stackPointer(wasm: *const Wasm) ObjectGlobal.Index {
502 const comp = wasm.base.comp;502 const comp = wasm.base.comp;
503 assert(comp.config.output_mode != .Obj);503 assert(!wasm.base.isRelocatable());
504 assert(comp.zcu != null);504 assert(comp.zcu != null);
505 return .stack_pointer;505 return .stack_pointer;
506 }506 }
...@@ -879,8 +879,7 @@ const ZcuDataStarts = struct {...@@ -879,8 +879,7 @@ const ZcuDataStarts = struct {
879 uavs_i: u32,879 uavs_i: u32,
880880
881 fn init(wasm: *const Wasm) ZcuDataStarts {881 fn init(wasm: *const Wasm) ZcuDataStarts {
882 const comp = wasm.base.comp;882 const is_obj = wasm.base.isRelocatable();
883 const is_obj = comp.config.output_mode == .Obj;
884 return if (is_obj) initObj(wasm) else initExe(wasm);883 return if (is_obj) initObj(wasm) else initExe(wasm);
885 }884 }
886885
...@@ -897,8 +896,7 @@ const ZcuDataStarts = struct {...@@ -897,8 +896,7 @@ const ZcuDataStarts = struct {
897 }896 }
898897
899 fn finish(zds: ZcuDataStarts, wasm: *Wasm, pt: Zcu.PerThread) !void {898 fn finish(zds: ZcuDataStarts, wasm: *Wasm, pt: Zcu.PerThread) !void {
900 const comp = wasm.base.comp;899 const is_obj = wasm.base.isRelocatable();
901 const is_obj = comp.config.output_mode == .Obj;
902 return if (is_obj) finishObj(zds, wasm, pt) else finishExe(zds, wasm, pt);900 return if (is_obj) finishObj(zds, wasm, pt) else finishExe(zds, wasm, pt);
903 }901 }
904902
...@@ -1276,8 +1274,7 @@ pub const GlobalImport = extern struct {...@@ -1276,8 +1274,7 @@ pub const GlobalImport = extern struct {
1276 const object_global_index = i - first_object_global;1274 const object_global_index = i - first_object_global;
1277 if (object_global_index < wasm.object_globals.items.len)1275 if (object_global_index < wasm.object_globals.items.len)
1278 return .{ .object_global = @fromBackingInt(@intCast(object_global_index)) };1276 return .{ .object_global = @fromBackingInt(@intCast(object_global_index)) };
1279 const comp = wasm.base.comp;1277 const is_obj = wasm.base.isRelocatable();
1280 const is_obj = comp.config.output_mode == .Obj;
1281 const uav_index = object_global_index - wasm.object_globals.items.len;1278 const uav_index = object_global_index - wasm.object_globals.items.len;
1282 if (is_obj) {1279 if (is_obj) {
1283 if (uav_index < wasm.uavs_obj.entries.len) {1280 if (uav_index < wasm.uavs_obj.entries.len) {
...@@ -1323,7 +1320,7 @@ pub const GlobalImport = extern struct {...@@ -1323,7 +1320,7 @@ pub const GlobalImport = extern struct {
1323 }1320 }
13241321
1325 pub fn fromIpIndex(wasm: *const Wasm, ip_index: InternPool.Index) Resolution {1322 pub fn fromIpIndex(wasm: *const Wasm, ip_index: InternPool.Index) Resolution {
1326 const is_obj = wasm.base.comp.config.output_mode == .Obj;1323 const is_obj = wasm.base.isRelocatable();
1327 return pack(wasm, if (is_obj) .{1324 return pack(wasm, if (is_obj) .{
1328 .uav_obj = @fromBackingInt(@intCast(wasm.uavs_obj.getIndex(ip_index).?)),1325 .uav_obj = @fromBackingInt(@intCast(wasm.uavs_obj.getIndex(ip_index).?)),
1329 } else .{1326 } else .{
...@@ -1332,8 +1329,7 @@ pub const GlobalImport = extern struct {...@@ -1332,8 +1329,7 @@ pub const GlobalImport = extern struct {
1332 }1329 }
13331330
1334 pub fn fromIpNav(wasm: *const Wasm, ip_nav: InternPool.Nav.Index) Resolution {1331 pub fn fromIpNav(wasm: *const Wasm, ip_nav: InternPool.Nav.Index) Resolution {
1335 const comp = wasm.base.comp;1332 const is_obj = wasm.base.isRelocatable();
1336 const is_obj = comp.config.output_mode == .Obj;
1337 return pack(wasm, if (is_obj) .{1333 return pack(wasm, if (is_obj) .{
1338 .nav_obj = @fromBackingInt(@intCast(wasm.navs_obj.getIndex(ip_nav).?)),1334 .nav_obj = @fromBackingInt(@intCast(wasm.navs_obj.getIndex(ip_nav).?)),
1339 } else .{1335 } else .{
...@@ -1819,8 +1815,7 @@ pub const ObjectDataImport = extern struct {...@@ -1819,8 +1815,7 @@ pub const ObjectDataImport = extern struct {
1819 else1815 else
1820 object_index - wasm.object_datas.items.len;1816 object_index - wasm.object_datas.items.len;
18211817
1822 const comp = wasm.base.comp;1818 const is_obj = wasm.base.isRelocatable();
1823 const is_obj = comp.config.output_mode == .Obj;
1824 if (is_obj) {1819 if (is_obj) {
1825 const nav_index = if (uav_index < wasm.uavs_obj.entries.len)1820 const nav_index = if (uav_index < wasm.uavs_obj.entries.len)
1826 return .{ .uav_obj = @fromBackingInt(@intCast(uav_index)) }1821 return .{ .uav_obj = @fromBackingInt(@intCast(uav_index)) }
...@@ -1863,7 +1858,7 @@ pub const ObjectDataImport = extern struct {...@@ -1863,7 +1858,7 @@ pub const ObjectDataImport = extern struct {
1863 }1858 }
18641859
1865 pub fn fromIpIndex(wasm: *const Wasm, ip_index: InternPool.Index) Resolution {1860 pub fn fromIpIndex(wasm: *const Wasm, ip_index: InternPool.Index) Resolution {
1866 const is_obj = wasm.base.comp.config.output_mode == .Obj;1861 const is_obj = wasm.base.isRelocatable();
1867 return pack(wasm, if (is_obj) .{1862 return pack(wasm, if (is_obj) .{
1868 .uav_obj = @fromBackingInt(@intCast(wasm.uavs_obj.getIndex(ip_index).?)),1863 .uav_obj = @fromBackingInt(@intCast(wasm.uavs_obj.getIndex(ip_index).?)),
1869 } else .{1864 } else .{
...@@ -1872,7 +1867,7 @@ pub const ObjectDataImport = extern struct {...@@ -1872,7 +1867,7 @@ pub const ObjectDataImport = extern struct {
1872 }1867 }
18731868
1874 pub fn fromIpNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) Resolution {1869 pub fn fromIpNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) Resolution {
1875 const is_obj = wasm.base.comp.config.output_mode == .Obj;1870 const is_obj = wasm.base.isRelocatable();
1876 return pack(wasm, if (is_obj) .{1871 return pack(wasm, if (is_obj) .{
1877 .nav_obj = @fromBackingInt(@intCast(wasm.navs_obj.getIndex(nav_index).?)),1872 .nav_obj = @fromBackingInt(@intCast(wasm.navs_obj.getIndex(nav_index).?)),
1878 } else .{1873 } else .{
...@@ -2117,8 +2112,7 @@ pub const DataSegmentId = enum(u32) {...@@ -2117,8 +2112,7 @@ pub const DataSegmentId = enum(u32) {
2117 else2112 else
2118 object_index - wasm.object_data_segments.items.len;2113 object_index - wasm.object_data_segments.items.len;
21192114
2120 const comp = wasm.base.comp;2115 const is_obj = wasm.base.isRelocatable();
2121 const is_obj = comp.config.output_mode == .Obj;
2122 if (is_obj) {2116 if (is_obj) {
2123 const nav_index = if (uav_index < wasm.uavs_obj.entries.len)2117 const nav_index = if (uav_index < wasm.uavs_obj.entries.len)
2124 return .{ .uav_obj = @fromBackingInt(@intCast(uav_index)) }2118 return .{ .uav_obj = @fromBackingInt(@intCast(uav_index)) }
...@@ -2139,8 +2133,7 @@ pub const DataSegmentId = enum(u32) {...@@ -2139,8 +2133,7 @@ pub const DataSegmentId = enum(u32) {
2139 }2133 }
21402134
2141 pub fn fromNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) DataSegmentId {2135 pub fn fromNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) DataSegmentId {
2142 const comp = wasm.base.comp;2136 const is_obj = wasm.base.isRelocatable();
2143 const is_obj = comp.config.output_mode == .Obj;
2144 return pack(wasm, if (is_obj) .{2137 return pack(wasm, if (is_obj) .{
2145 .nav_obj = @fromBackingInt(@intCast(wasm.navs_obj.getIndex(nav_index).?)),2138 .nav_obj = @fromBackingInt(@intCast(wasm.navs_obj.getIndex(nav_index).?)),
2146 } else .{2139 } else .{
...@@ -2717,7 +2710,7 @@ pub const FunctionImportId = enum(u32) {...@@ -2717,7 +2710,7 @@ pub const FunctionImportId = enum(u32) {
2717 /// Asserts not emitting an object, and `Wasm.import_symbols` is false.2710 /// Asserts not emitting an object, and `Wasm.import_symbols` is false.
2718 pub fn undefinedAllowed(id: FunctionImportId, wasm: *const Wasm) bool {2711 pub fn undefinedAllowed(id: FunctionImportId, wasm: *const Wasm) bool {
2719 assert(!wasm.import_symbols);2712 assert(!wasm.import_symbols);
2720 assert(wasm.base.comp.config.output_mode != .Obj);2713 assert(!wasm.base.isRelocatable());
2721 return switch (unpack(id, wasm)) {2714 return switch (unpack(id, wasm)) {
2722 .object_function_import => |i| {2715 .object_function_import => |i| {
2723 const import = i.value(wasm);2716 const import = i.value(wasm);
...@@ -3343,6 +3336,7 @@ pub fn createEmpty(...@@ -3343,6 +3336,7 @@ pub fn createEmpty(
3343 assert(target.ofmt == .wasm);3336 assert(target.ofmt == .wasm);
33443337
3345 const output_mode = comp.config.output_mode;3338 const output_mode = comp.config.output_mode;
3339 const is_relocatable = output_mode == .Obj or (output_mode == .Lib and comp.config.link_mode == .static);
3346 const wasi_exec_model = comp.config.wasi_exec_model;3340 const wasi_exec_model = comp.config.wasi_exec_model;
33473341
3348 const wasm = try arena.create(Wasm);3342 const wasm = try arena.create(Wasm);
...@@ -3356,7 +3350,7 @@ pub fn createEmpty(...@@ -3356,7 +3350,7 @@ pub fn createEmpty(
3356 // majority of cases, and therefore express "no garbage collection"3350 // majority of cases, and therefore express "no garbage collection"
3357 // in terms of setting the no_strip and must_link flags on all3351 // in terms of setting the no_strip and must_link flags on all
3358 // symbols.3352 // symbols.
3359 .gc_sections = options.gc_sections orelse (output_mode != .Obj),3353 .gc_sections = options.gc_sections orelse !is_relocatable,
3360 .print_gc_sections = options.print_gc_sections,3354 .print_gc_sections = options.print_gc_sections,
3361 .stack_size = options.stack_size orelse switch (target.os.tag) {3355 .stack_size = options.stack_size orelse switch (target.os.tag) {
3362 .freestanding => 1 * 1024 * 1024, // 1 MiB3356 .freestanding => 1 * 1024 * 1024, // 1 MiB
...@@ -3436,6 +3430,11 @@ fn parseObject(wasm: *Wasm, obj: link.Input.Object) !void {...@@ -3436,6 +3430,11 @@ fn parseObject(wasm: *Wasm, obj: link.Input.Object) !void {
3436 };3430 };
3437 if (n != file_contents.len) return error.UnexpectedEndOfFile;3431 if (n != file_contents.len) return error.UnexpectedEndOfFile;
34383432
3433 if (wasm.base.isStaticLib()) {
3434 try wasm.archive_builder.append(gpa, std.fs.path.basename(obj.path.sub_path), file_contents);
3435 return;
3436 }
3437
3439 var ss: Object.ScratchSpace = .{};3438 var ss: Object.ScratchSpace = .{};
3440 defer ss.deinit(gpa);3439 defer ss.deinit(gpa);
34413440
...@@ -3526,6 +3525,7 @@ pub fn deinit(wasm: *Wasm) void {...@@ -3526,6 +3525,7 @@ pub fn deinit(wasm: *Wasm) void {
3526 wasm.object_relocations_table.deinit(gpa);3525 wasm.object_relocations_table.deinit(gpa);
3527 wasm.object_comdat_symbols.deinit(gpa);3526 wasm.object_comdat_symbols.deinit(gpa);
3528 wasm.objects.deinit(gpa);3527 wasm.objects.deinit(gpa);
3528 wasm.archive_builder.deinit(gpa);
35293529
3530 wasm.func_types.deinit(gpa);3530 wasm.func_types.deinit(gpa);
3531 wasm.function_exports.deinit(gpa);3531 wasm.function_exports.deinit(gpa);
...@@ -3575,7 +3575,7 @@ pub fn updateFunc(...@@ -3575,7 +3575,7 @@ pub fn updateFunc(
3575 const zcu = pt.zcu;3575 const zcu = pt.zcu;
3576 const gpa = zcu.gpa;3576 const gpa = zcu.gpa;
3577 const ip = &zcu.intern_pool;3577 const ip = &zcu.intern_pool;
3578 const is_obj = zcu.comp.config.output_mode == .Obj;3578 const is_obj = wasm.base.isRelocatable();
3579 const target = &zcu.comp.root_mod.resolved_target.result;3579 const target = &zcu.comp.root_mod.resolved_target.result;
3580 const owner_nav = zcu.funcInfo(func_index).owner_nav;3580 const owner_nav = zcu.funcInfo(func_index).owner_nav;
3581 log.debug("updateFunc {f}", .{ip.getNav(owner_nav).fqn.fmt(ip)});3581 log.debug("updateFunc {f}", .{ip.getNav(owner_nav).fqn.fmt(ip)});
...@@ -3656,7 +3656,7 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index...@@ -3656,7 +3656,7 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index
3656 const nav = ip.getNav(nav_index);3656 const nav = ip.getNav(nav_index);
3657 const comp = wasm.base.comp;3657 const comp = wasm.base.comp;
3658 const gpa = comp.gpa;3658 const gpa = comp.gpa;
3659 const is_obj = comp.config.output_mode == .Obj;3659 const is_obj = wasm.base.isRelocatable();
3660 const target = &comp.root_mod.resolved_target.result;3660 const target = &comp.root_mod.resolved_target.result;
36613661
3662 switch (ip.indexToKey(nav.resolved.?.value)) {3662 switch (ip.indexToKey(nav.resolved.?.value)) {
...@@ -3740,7 +3740,7 @@ pub fn updateExports(...@@ -3740,7 +3740,7 @@ pub fn updateExports(
3740 const zcu = pt.zcu;3740 const zcu = pt.zcu;
3741 const gpa = zcu.gpa;3741 const gpa = zcu.gpa;
3742 const ip = &zcu.intern_pool;3742 const ip = &zcu.intern_pool;
3743 const is_obj = wasm.base.comp.config.output_mode == .Obj;3743 const is_obj = wasm.base.isRelocatable();
37443744
3745 for (export_indices) |export_idx| {3745 for (export_indices) |export_idx| {
3746 const exp = export_idx.ptr(zcu);3746 const exp = export_idx.ptr(zcu);
...@@ -3797,7 +3797,11 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void {...@@ -3797,7 +3797,11 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void {
3797 .dso => unreachable,3797 .dso => unreachable,
3798 .tbd => unreachable,3798 .tbd => unreachable,
3799 .object => |obj| try parseObject(wasm, obj),3799 .object => |obj| try parseObject(wasm, obj),
3800 .archive => |obj| try parseArchive(wasm, obj),3800 .archive => |obj| if (wasm.base.isStaticLib()) {
3801 obj.file.close(io); // ignore archives like LLVM
3802 } else {
3803 try parseArchive(wasm, obj);
3804 },
3801 }3805 }
3802}3806}
38033807
...@@ -3811,7 +3815,7 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.Error!void {...@@ -3811,7 +3815,7 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.Error!void {
3811 const comp = wasm.base.comp;3815 const comp = wasm.base.comp;
3812 const gpa = comp.gpa;3816 const gpa = comp.gpa;
3813 const rdynamic = comp.config.rdynamic;3817 const rdynamic = comp.config.rdynamic;
3814 const is_obj = comp.config.output_mode == .Obj;3818 const is_obj = wasm.base.isRelocatable();
38153819
3816 assert(wasm.missing_exports.entries.len == 0);3820 assert(wasm.missing_exports.entries.len == 0);
3817 for (wasm.export_symbol_names) |exp_name| {3821 for (wasm.export_symbol_names) |exp_name| {
...@@ -3915,7 +3919,7 @@ pub fn markFunctionImport(...@@ -3915,7 +3919,7 @@ pub fn markFunctionImport(
39153919
3916 const comp = wasm.base.comp;3920 const comp = wasm.base.comp;
3917 const gpa = comp.gpa;3921 const gpa = comp.gpa;
3918 const is_obj = comp.config.output_mode == .Obj;3922 const is_obj = wasm.base.isRelocatable();
39193923
3920 try wasm.functions.ensureUnusedCapacity(gpa, 1);3924 try wasm.functions.ensureUnusedCapacity(gpa, 1);
39213925
...@@ -3949,7 +3953,7 @@ fn markFunction(wasm: *Wasm, i: ObjectFunctionIndex, override_export: bool) link...@@ -3949,7 +3953,7 @@ fn markFunction(wasm: *Wasm, i: ObjectFunctionIndex, override_export: bool) link
3949 if (gop.found_existing) return;3953 if (gop.found_existing) return;
39503954
3951 const rdynamic = comp.config.rdynamic;3955 const rdynamic = comp.config.rdynamic;
3952 const is_obj = comp.config.output_mode == .Obj;3956 const is_obj = wasm.base.isRelocatable();
3953 const function = i.ptr(wasm);3957 const function = i.ptr(wasm);
3954 try markObject(wasm, function.object_index);3958 try markObject(wasm, function.object_index);
39553959
...@@ -3991,7 +3995,7 @@ fn markGlobalImport(...@@ -3991,7 +3995,7 @@ fn markGlobalImport(
39913995
3992 const comp = wasm.base.comp;3996 const comp = wasm.base.comp;
3993 const gpa = comp.gpa;3997 const gpa = comp.gpa;
3994 const is_obj = comp.config.output_mode == .Obj;3998 const is_obj = wasm.base.isRelocatable();
39953999
3996 try wasm.globals.ensureUnusedCapacity(gpa, 1);4000 try wasm.globals.ensureUnusedCapacity(gpa, 1);
39974001
...@@ -4034,7 +4038,7 @@ fn markGlobal(wasm: *Wasm, i: ObjectGlobalIndex, override_export: bool) link.Err...@@ -4034,7 +4038,7 @@ fn markGlobal(wasm: *Wasm, i: ObjectGlobalIndex, override_export: bool) link.Err
4034 if (gop.found_existing) return;4038 if (gop.found_existing) return;
40354039
4036 const rdynamic = comp.config.rdynamic;4040 const rdynamic = comp.config.rdynamic;
4037 const is_obj = comp.config.output_mode == .Obj;4041 const is_obj = wasm.base.isRelocatable();
4038 const global = i.ptr(wasm);4042 const global = i.ptr(wasm);
4039 try markObject(wasm, global.object_index);4043 try markObject(wasm, global.object_index);
40404044
...@@ -4057,7 +4061,7 @@ pub fn markTableImport(...@@ -4057,7 +4061,7 @@ pub fn markTableImport(
40574061
4058 const comp = wasm.base.comp;4062 const comp = wasm.base.comp;
4059 const gpa = comp.gpa;4063 const gpa = comp.gpa;
4060 const is_obj = comp.config.output_mode == .Obj;4064 const is_obj = wasm.base.isRelocatable();
40614065
4062 try wasm.tables.ensureUnusedCapacity(gpa, 1);4066 try wasm.tables.ensureUnusedCapacity(gpa, 1);
40634067
...@@ -4103,7 +4107,7 @@ pub fn markDataImport(...@@ -4103,7 +4107,7 @@ pub fn markDataImport(
41034107
4104 const comp = wasm.base.comp;4108 const comp = wasm.base.comp;
4105 const gpa = comp.gpa;4109 const gpa = comp.gpa;
4106 const is_obj = comp.config.output_mode == .Obj;4110 const is_obj = wasm.base.isRelocatable();
41074111
4108 if (import.resolution == .unresolved) {4112 if (import.resolution == .unresolved) {
4109 if (!is_obj) {4113 if (!is_obj) {
...@@ -4682,7 +4686,7 @@ pub fn addNavReloc(...@@ -4682,7 +4686,7 @@ pub fn addNavReloc(
4682 const ip = &zcu.intern_pool;4686 const ip = &zcu.intern_pool;
4683 const gpa = comp.gpa;4687 const gpa = comp.gpa;
46844688
4685 const is_obj = comp.config.output_mode == .Obj;4689 const is_obj = wasm.base.isRelocatable();
46864690
4687 if (nav_ty.zigTypeTag(zcu) == .@"fn") {4691 if (nav_ty.zigTypeTag(zcu) == .@"fn") {
4688 const gop = try wasm.zcu_indirect_function_set.getOrPut(gpa, nav_index);4692 const gop = try wasm.zcu_indirect_function_set.getOrPut(gpa, nav_index);
...@@ -4751,7 +4755,7 @@ pub fn addUavReloc(...@@ -4751,7 +4755,7 @@ pub fn addUavReloc(
4751 gop.value_ptr.* = if (gop.found_existing) gop.value_ptr.maxStrict(this_align) else this_align;4755 gop.value_ptr.* = if (gop.found_existing) gop.value_ptr.maxStrict(this_align) else this_align;
4752 }4756 }
47534757
4754 if (comp.config.output_mode == .Obj) {4758 if (wasm.base.isRelocatable()) {
4755 const gop = try wasm.uavs_obj.getOrPut(gpa, uav_val);4759 const gop = try wasm.uavs_obj.getOrPut(gpa, uav_val);
4756 if (!gop.found_existing) gop.value_ptr.* = undefined; // to avoid recursion, `ZcuDataStarts` will lower the value later4760 if (!gop.found_existing) gop.value_ptr.* = undefined; // to avoid recursion, `ZcuDataStarts` will lower the value later
4757 try wasm.zcu_relocations.append(gpa, .{4761 try wasm.zcu_relocations.append(gpa, .{
...@@ -4782,7 +4786,7 @@ pub fn addUavReloc(...@@ -4782,7 +4786,7 @@ pub fn addUavReloc(
4782pub fn refNavObj(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsObjIndex {4786pub fn refNavObj(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsObjIndex {
4783 const comp = wasm.base.comp;4787 const comp = wasm.base.comp;
4784 const gpa = comp.gpa;4788 const gpa = comp.gpa;
4785 assert(comp.config.output_mode == .Obj);4789 assert(wasm.base.isRelocatable());
4786 const gop = try wasm.navs_obj.getOrPut(gpa, nav_index);4790 const gop = try wasm.navs_obj.getOrPut(gpa, nav_index);
4787 if (!gop.found_existing) gop.value_ptr.* = .{4791 if (!gop.found_existing) gop.value_ptr.* = .{
4788 // Lowering the value is delayed to avoid recursion.4792 // Lowering the value is delayed to avoid recursion.
...@@ -4795,7 +4799,7 @@ pub fn refNavObj(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsObjIndex {...@@ -4795,7 +4799,7 @@ pub fn refNavObj(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsObjIndex {
4795pub fn refNavExe(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsExeIndex {4799pub fn refNavExe(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsExeIndex {
4796 const comp = wasm.base.comp;4800 const comp = wasm.base.comp;
4797 const gpa = comp.gpa;4801 const gpa = comp.gpa;
4798 assert(comp.config.output_mode != .Obj);4802 assert(!wasm.base.isRelocatable());
4799 const gop = try wasm.navs_exe.getOrPut(gpa, nav_index);4803 const gop = try wasm.navs_exe.getOrPut(gpa, nav_index);
4800 if (gop.found_existing) {4804 if (gop.found_existing) {
4801 gop.value_ptr.count += 1;4805 gop.value_ptr.count += 1;
...@@ -4812,8 +4816,7 @@ pub fn refNavExe(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsExeIndex {...@@ -4812,8 +4816,7 @@ pub fn refNavExe(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsExeIndex {
4812/// Asserts it is called after `Flush.data_segments` is fully populated and sorted.4816/// Asserts it is called after `Flush.data_segments` is fully populated and sorted.
4813pub fn uavAddr(wasm: *const Wasm, ip_index: InternPool.Index) u32 {4817pub fn uavAddr(wasm: *const Wasm, ip_index: InternPool.Index) u32 {
4814 assert(wasm.flush_buffer.memory_layout_finished);4818 assert(wasm.flush_buffer.memory_layout_finished);
4815 const comp = wasm.base.comp;4819 assert(!wasm.base.isRelocatable());
4816 assert(comp.config.output_mode != .Obj);
4817 const uav_index: UavsExeIndex = @fromBackingInt(@intCast(wasm.uavs_exe.getIndex(ip_index).?));4820 const uav_index: UavsExeIndex = @fromBackingInt(@intCast(wasm.uavs_exe.getIndex(ip_index).?));
4818 const ds_id: DataSegmentId = .pack(wasm, .{ .uav_exe = uav_index });4821 const ds_id: DataSegmentId = .pack(wasm, .{ .uav_exe = uav_index });
4819 return wasm.flush_buffer.data_segments.get(ds_id).?;4822 return wasm.flush_buffer.data_segments.get(ds_id).?;
...@@ -4834,7 +4837,7 @@ pub fn syntheticDataAddr(wasm: *const Wasm, resolution: ObjectDataImport.Resolut...@@ -4834,7 +4837,7 @@ pub fn syntheticDataAddr(wasm: *const Wasm, resolution: ObjectDataImport.Resolut
4834pub fn navAddr(wasm: *const Wasm, nav_index: InternPool.Nav.Index) u32 {4837pub fn navAddr(wasm: *const Wasm, nav_index: InternPool.Nav.Index) u32 {
4835 assert(wasm.flush_buffer.memory_layout_finished);4838 assert(wasm.flush_buffer.memory_layout_finished);
4836 const comp = wasm.base.comp;4839 const comp = wasm.base.comp;
4837 assert(comp.config.output_mode != .Obj);4840 assert(!wasm.base.isRelocatable());
4838 if (wasm.navs_exe.getIndex(nav_index)) |i| {4841 if (wasm.navs_exe.getIndex(nav_index)) |i| {
4839 const navs_exe_index: NavsExeIndex = @fromBackingInt(@intCast(i));4842 const navs_exe_index: NavsExeIndex = @fromBackingInt(@intCast(i));
4840 log.debug("navAddr {s} {}", .{ navs_exe_index.name(wasm), nav_index });4843 log.debug("navAddr {s} {}", .{ navs_exe_index.name(wasm), nav_index });
...@@ -4888,15 +4891,13 @@ pub fn navAddr(wasm: *const Wasm, nav_index: InternPool.Nav.Index) u32 {...@@ -4888,15 +4891,13 @@ pub fn navAddr(wasm: *const Wasm, nav_index: InternPool.Nav.Index) u32 {
4888/// Asserts it is called after `Flush.data_segments` is fully populated and sorted.4891/// Asserts it is called after `Flush.data_segments` is fully populated and sorted.
4889pub fn errorNameTableAddr(wasm: *Wasm) u32 {4892pub fn errorNameTableAddr(wasm: *Wasm) u32 {
4890 assert(wasm.flush_buffer.memory_layout_finished);4893 assert(wasm.flush_buffer.memory_layout_finished);
4891 const comp = wasm.base.comp;4894 assert(!wasm.base.isRelocatable());
4892 assert(comp.config.output_mode != .Obj);
4893 return wasm.flush_buffer.data_segments.get(.__zig_error_name_table).?;4895 return wasm.flush_buffer.data_segments.get(.__zig_error_name_table).?;
4894}4896}
48954897
4896pub fn tagIndexTableAddr(wasm: *Wasm, ip_index: InternPool.Index) u32 {4898pub fn tagIndexTableAddr(wasm: *Wasm, ip_index: InternPool.Index) u32 {
4897 assert(wasm.flush_buffer.memory_layout_finished);4899 assert(wasm.flush_buffer.memory_layout_finished);
4898 const comp = wasm.base.comp;4900 assert(!wasm.base.isRelocatable());
4899 assert(comp.config.output_mode != .Obj);
4900 const f = &wasm.flush_buffer;4901 const f = &wasm.flush_buffer;
4901 const table_base_addr = f.data_segments.get(.__zig_tag_name_table).?;4902 const table_base_addr = f.data_segments.get(.__zig_tag_name_table).?;
4902 return table_base_addr + wasm.tagIndexTableOffset(ip_index);4903 return table_base_addr + wasm.tagIndexTableOffset(ip_index);
src/link/Wasm/Archive.zig+138
...@@ -68,6 +68,143 @@ const Header = extern struct {...@@ -68,6 +68,143 @@ const Header = extern struct {
68 }68 }
69};69};
7070
71pub const Builder = struct {
72 file_names: std.ArrayList(u8) = .empty,
73 members: std.ArrayList(u8) = .empty,
74 symbol_names: std.ArrayList(u8) = .empty,
75 symbol_offsets: std.ArrayList(u32) = .empty,
76
77 pub fn deinit(b: *Builder, gpa: Allocator) void {
78 b.file_names.deinit(gpa);
79 b.members.deinit(gpa);
80 b.symbol_names.deinit(gpa);
81 b.symbol_offsets.deinit(gpa);
82 b.* = undefined;
83 }
84
85 pub fn append(b: *Builder, gpa: Allocator, name: []const u8, data: []const u8) !void {
86 const member_offset: u32 = @intCast(b.members.items.len);
87 const name_offset = b.file_names.items.len;
88
89 try b.file_names.appendSlice(gpa, name);
90 try b.file_names.appendSlice(gpa, "/\n");
91
92 var name_buf: [16]u8 = undefined;
93 const header_name = try std.mem.print(&name_buf, "/{d}", .{name_offset});
94 const member_size = @sizeOf(Header) + mem.alignForward(usize, data.len, 2);
95 var w: std.Io.Writer = .fixed(try b.members.addManyAsSlice(gpa, member_size));
96 try writeHeader(&w, header_name, data.len);
97 try w.writeAll(data);
98 if (data.len % 2 != 0) try w.writeByte('\n');
99
100 try b.collectSymbols(gpa, data, member_offset);
101 }
102
103 pub fn write(b: *Builder, w: *std.Io.Writer) std.Io.Writer.Error!void {
104 const symtab_size = 4 + 4 * b.symbol_offsets.items.len + b.symbol_names.items.len;
105 const members_offset = ARMAG.len + @sizeOf(Header) + mem.alignForward(usize, symtab_size, 2) +
106 @sizeOf(Header) + mem.alignForward(usize, b.file_names.items.len, 2);
107
108 try w.writeAll(ARMAG);
109
110 try writeHeader(w, "/", symtab_size);
111 try w.writeInt(u32, @intCast(b.symbol_offsets.items.len), .big);
112 for (b.symbol_offsets.items) |offset| try w.writeInt(u32, @intCast(members_offset + offset), .big);
113 try w.writeAll(b.symbol_names.items);
114 if (symtab_size % 2 != 0) try w.writeByte('\n');
115
116 try writeHeader(w, "//", b.file_names.items.len);
117 try w.writeAll(b.file_names.items);
118 if (b.file_names.items.len % 2 != 0) try w.writeByte('\n');
119
120 try w.writeAll(b.members.items);
121 }
122
123 fn collectSymbols(b: *Builder, gpa: Allocator, data: []const u8, member_offset: u32) !void {
124 var reader: std.Io.Reader = .fixed(data);
125 if (!mem.eql(u8, try reader.take(4), &std.wasm.magic)) return error.BadObjectMagic;
126 if (!mem.eql(u8, try reader.take(4), &std.wasm.version)) return error.UnsupportedVersion;
127
128 while (reader.seek < data.len) {
129 const section_tag = try reader.takeEnum(std.wasm.Section, .little);
130 const section_len = try reader.takeLeb128(u32);
131 var section: std.Io.Reader = .fixed(try reader.take(section_len));
132 if (section_tag != .custom) continue;
133
134 const section_name = try readBytes(&section);
135 if (mem.eql(u8, section_name, "linking")) {
136 const section_version = try section.takeLeb128(u32);
137 if (section_version != 2) return error.UnsupportedVersion;
138
139 while (section.seek < section.buffer.len) {
140 const sub_type = try section.takeLeb128(u8);
141 const payload_len = try section.takeLeb128(u32);
142
143 var symtab: std.Io.Reader = .fixed(try section.take(payload_len));
144 if (sub_type != @backingInt(Object.SubsectionType.symbol_table)) continue;
145
146 const count = try symtab.takeLeb128(u32);
147 for (0..count) |_| {
148 const tag = try symtab.takeEnum(Object.Symbol.Tag, .little);
149 const flags = try symtab.takeLeb128(u32);
150 const symbol_flags: Wasm.SymbolFlags = @bitCast(flags);
151
152 var name: []const u8 = undefined;
153 switch (tag) {
154 .data => {
155 name = try readBytes(&symtab);
156 if (!symbol_flags.undefined) {
157 _ = try symtab.takeLeb128(u32);
158 _ = try symtab.takeLeb128(u64);
159 _ = try symtab.takeLeb128(u64);
160 }
161 },
162 .section => {
163 _ = try symtab.takeLeb128(u32);
164 continue;
165 },
166 .function, .global, .event, .table => {
167 _ = try symtab.takeLeb128(u32);
168 if (!symbol_flags.undefined or symbol_flags.explicit_name) {
169 name = try readBytes(&symtab);
170 }
171 },
172 }
173
174 if (!symbol_flags.undefined and symbol_flags.binding != .local) {
175 try b.symbol_names.appendSlice(gpa, name);
176 try b.symbol_names.append(gpa, 0);
177 try b.symbol_offsets.append(gpa, member_offset);
178 }
179 }
180 }
181 return;
182 }
183 }
184 return error.MissingLinkingSection;
185 }
186};
187
188fn writeHeader(w: *std.Io.Writer, name: []const u8, size: usize) std.Io.Writer.Error!void {
189 var header: Header = .{
190 .name = @splat(' '),
191 .date = "0 ".*,
192 .uid = "0 ".*,
193 .gid = "0 ".*,
194 .mode = "644 ".*,
195 .size = @splat(' '),
196 .fmag = ARFMAG.*,
197 };
198 @memcpy(header.name[0..name.len], name);
199 _ = std.mem.print(&header.size, "{d}", .{size}) catch unreachable;
200 try w.writeAll(mem.asBytes(&header));
201}
202
203fn readBytes(reader: *std.Io.Reader) ![]const u8 {
204 const len = try reader.takeLeb128(u32);
205 return reader.take(len);
206}
207
71pub fn deinit(archive: *Archive, gpa: Allocator) void {208pub fn deinit(archive: *Archive, gpa: Allocator) void {
72 deinitToc(gpa, &archive.toc);209 deinitToc(gpa, &archive.toc);
73 archive.* = undefined;210 archive.* = undefined;
...@@ -102,6 +239,7 @@ pub fn parse(gpa: Allocator, file_contents: []const u8) !Archive {...@@ -102,6 +239,7 @@ pub fn parse(gpa: Allocator, file_contents: []const u8) !Archive {
102239
103 const sym_tab = file_contents[pos..][0 .. sym_tab_size - 4 - symbol_positions_size];240 const sym_tab = file_contents[pos..][0 .. sym_tab_size - 4 - symbol_positions_size];
104 pos += sym_tab.len;241 pos += sym_tab.len;
242 pos = mem.alignForward(usize, pos, 2);
105243
106 var toc: Toc = .empty;244 var toc: Toc = .empty;
107 errdefer deinitToc(gpa, &toc);245 errdefer deinitToc(gpa, &toc);
src/link/Wasm/Flush.zig+27-4
...@@ -247,7 +247,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -247,7 +247,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
247 .wasm64 => true,247 .wasm64 => true,
248 else => unreachable,248 else => unreachable,
249 };249 };
250 const is_obj = comp.config.output_mode == .Obj;250 const is_obj = wasm.base.isRelocatable();
251 const allow_undefined = is_obj or wasm.import_symbols;251 const allow_undefined = is_obj or wasm.import_symbols;
252 const zcu_references = if (comp.zcu) |zcu| try zcu.resolveReferences() else null;252 const zcu_references = if (comp.zcu) |zcu| try zcu.resolveReferences() else null;
253253
...@@ -1725,9 +1725,32 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -1725,9 +1725,32 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
17251725
1726 // Finally, write the entire binary into the file.1726 // Finally, write the entire binary into the file.
1727 var file_writer = wasm.base.file.?.writer(io, &.{});1727 var file_writer = wasm.base.file.?.writer(io, &.{});
1728 file_writer.interface.writeAll(binary_bytes.items) catch |err| switch (err) {1728 if (wasm.base.isStaticLib()) {
1729 error.WriteFailed => return file_writer.err.?,1729 const builder = &wasm.archive_builder;
1730 };1730 // restore for incremental
1731 const file_names_len = builder.file_names.items.len;
1732 defer builder.file_names.shrinkRetainingCapacity(file_names_len);
1733 const members_len = builder.members.items.len;
1734 defer builder.members.shrinkRetainingCapacity(members_len);
1735 const symbol_names_len = builder.symbol_names.items.len;
1736 defer builder.symbol_names.shrinkRetainingCapacity(symbol_names_len);
1737 const symbol_offsets_len = builder.symbol_offsets.items.len;
1738 defer builder.symbol_offsets.shrinkRetainingCapacity(symbol_offsets_len);
1739
1740 // TODO: pass ZCU data and symbols to write, so reparsing not needed
1741 if (comp.zcu != null) {
1742 const name = try std.fmt.allocPrint(gpa, "{s}_zcu.o", .{comp.root_name});
1743 defer gpa.free(name);
1744 try builder.append(gpa, name, binary_bytes.items);
1745 }
1746 builder.write(&file_writer.interface) catch |err| switch (err) {
1747 error.WriteFailed => return file_writer.err.?,
1748 };
1749 } else {
1750 file_writer.interface.writeAll(binary_bytes.items) catch |err| switch (err) {
1751 error.WriteFailed => return file_writer.err.?,
1752 };
1753 }
1731 file_writer.end() catch |err| switch (err) {1754 file_writer.end() catch |err| switch (err) {
1732 error.WriteFailed => return file_writer.err.?,1755 error.WriteFailed => return file_writer.err.?,
1733 else => |e| return e,1756 else => |e| return e,