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 {
2727 const wasm = emit.wasm;
2828 const comp = wasm.base.comp;
2929 const gpa = comp.gpa;
30 const is_obj = comp.config.output_mode == .Obj;
30 const is_obj = wasm.base.isRelocatable();
3131 const target = &comp.root_mod.resolved_target.result;
3232 const is_wasm32 = target.cpu.arch == .wasm32;
3333
......@@ -1028,7 +1028,7 @@ fn navRefOff(wasm: *Wasm, code: *ArrayList(u8), data: Mir.NavRefOff, is_wasm32:
10281028 const zcu = comp.zcu.?;
10291029 const ip = &zcu.intern_pool;
10301030 const gpa = comp.gpa;
1031 const is_obj = comp.config.output_mode == .Obj;
1031 const is_obj = wasm.base.isRelocatable();
10321032 const nav_ty = ip.getNav(data.nav_index).resolved.?.type;
10331033 assert(!ip.isFunctionType(nav_ty));
10341034
src/link/Wasm.zig+46-45
......@@ -79,6 +79,7 @@ growable_table: bool,
7979name: []const u8,
8080/// List of relocatable files to be linked into the final binary.
8181objects: std.ArrayList(Object) = .empty,
82archive_builder: Archive.Builder = .{},
8283
8384func_types: std.array_hash_map.Auto(FunctionType, void) = .empty,
8485/// Provides a mapping of both imports and provided functions to symbol name.
......@@ -467,8 +468,7 @@ pub const OutputDataIndex = enum(u32) {
467468 }
468469
469470 pub fn fromUav(wasm: *const Wasm, ip_index: InternPool.Index) OutputDataIndex {
470 const comp = wasm.base.comp;
471 const resolution: ObjectDataImport.Resolution = if (comp.config.output_mode == .Obj)
471 const resolution: ObjectDataImport.Resolution = if (wasm.base.isRelocatable())
472472 .pack(wasm, .{ .uav_obj = @fromBackingInt(@intCast(wasm.uavs_obj.getIndex(ip_index).?)) })
473473 else
474474 .pack(wasm, .{ .uav_exe = @fromBackingInt(@intCast(wasm.uavs_exe.getIndex(ip_index).?)) });
......@@ -482,7 +482,7 @@ pub const OutputDataIndex = enum(u32) {
482482 if (nav.getExtern(ip) != null) {
483483 return fromSymbolName(wasm, wasm.imports.get(nav_index).?);
484484 }
485 const resolution: ObjectDataImport.Resolution = if (wasm.base.comp.config.output_mode == .Obj)
485 const resolution: ObjectDataImport.Resolution = if (wasm.base.isRelocatable())
486486 .pack(wasm, .{ .nav_obj = @fromBackingInt(@intCast(wasm.navs_obj.getIndex(nav_index).?)) })
487487 else
488488 .pack(wasm, .{ .nav_exe = @fromBackingInt(@intCast(wasm.navs_exe.getIndex(nav_index).?)) });
......@@ -500,7 +500,7 @@ pub const GlobalIndex = enum(u32) {
500500 /// Same as `stack_pointer` but with a safety assertion.
501501 pub fn stackPointer(wasm: *const Wasm) ObjectGlobal.Index {
502502 const comp = wasm.base.comp;
503 assert(comp.config.output_mode != .Obj);
503 assert(!wasm.base.isRelocatable());
504504 assert(comp.zcu != null);
505505 return .stack_pointer;
506506 }
......@@ -879,8 +879,7 @@ const ZcuDataStarts = struct {
879879 uavs_i: u32,
880880
881881 fn init(wasm: *const Wasm) ZcuDataStarts {
882 const comp = wasm.base.comp;
883 const is_obj = comp.config.output_mode == .Obj;
882 const is_obj = wasm.base.isRelocatable();
884883 return if (is_obj) initObj(wasm) else initExe(wasm);
885884 }
886885
......@@ -897,8 +896,7 @@ const ZcuDataStarts = struct {
897896 }
898897
899898 fn finish(zds: ZcuDataStarts, wasm: *Wasm, pt: Zcu.PerThread) !void {
900 const comp = wasm.base.comp;
901 const is_obj = comp.config.output_mode == .Obj;
899 const is_obj = wasm.base.isRelocatable();
902900 return if (is_obj) finishObj(zds, wasm, pt) else finishExe(zds, wasm, pt);
903901 }
904902
......@@ -1276,8 +1274,7 @@ pub const GlobalImport = extern struct {
12761274 const object_global_index = i - first_object_global;
12771275 if (object_global_index < wasm.object_globals.items.len)
12781276 return .{ .object_global = @fromBackingInt(@intCast(object_global_index)) };
1279 const comp = wasm.base.comp;
1280 const is_obj = comp.config.output_mode == .Obj;
1277 const is_obj = wasm.base.isRelocatable();
12811278 const uav_index = object_global_index - wasm.object_globals.items.len;
12821279 if (is_obj) {
12831280 if (uav_index < wasm.uavs_obj.entries.len) {
......@@ -1323,7 +1320,7 @@ pub const GlobalImport = extern struct {
13231320 }
13241321
13251322 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();
13271324 return pack(wasm, if (is_obj) .{
13281325 .uav_obj = @fromBackingInt(@intCast(wasm.uavs_obj.getIndex(ip_index).?)),
13291326 } else .{
......@@ -1332,8 +1329,7 @@ pub const GlobalImport = extern struct {
13321329 }
13331330
13341331 pub fn fromIpNav(wasm: *const Wasm, ip_nav: InternPool.Nav.Index) Resolution {
1335 const comp = wasm.base.comp;
1336 const is_obj = comp.config.output_mode == .Obj;
1332 const is_obj = wasm.base.isRelocatable();
13371333 return pack(wasm, if (is_obj) .{
13381334 .nav_obj = @fromBackingInt(@intCast(wasm.navs_obj.getIndex(ip_nav).?)),
13391335 } else .{
......@@ -1819,8 +1815,7 @@ pub const ObjectDataImport = extern struct {
18191815 else
18201816 object_index - wasm.object_datas.items.len;
18211817
1822 const comp = wasm.base.comp;
1823 const is_obj = comp.config.output_mode == .Obj;
1818 const is_obj = wasm.base.isRelocatable();
18241819 if (is_obj) {
18251820 const nav_index = if (uav_index < wasm.uavs_obj.entries.len)
18261821 return .{ .uav_obj = @fromBackingInt(@intCast(uav_index)) }
......@@ -1863,7 +1858,7 @@ pub const ObjectDataImport = extern struct {
18631858 }
18641859
18651860 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();
18671862 return pack(wasm, if (is_obj) .{
18681863 .uav_obj = @fromBackingInt(@intCast(wasm.uavs_obj.getIndex(ip_index).?)),
18691864 } else .{
......@@ -1872,7 +1867,7 @@ pub const ObjectDataImport = extern struct {
18721867 }
18731868
18741869 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();
18761871 return pack(wasm, if (is_obj) .{
18771872 .nav_obj = @fromBackingInt(@intCast(wasm.navs_obj.getIndex(nav_index).?)),
18781873 } else .{
......@@ -2117,8 +2112,7 @@ pub const DataSegmentId = enum(u32) {
21172112 else
21182113 object_index - wasm.object_data_segments.items.len;
21192114
2120 const comp = wasm.base.comp;
2121 const is_obj = comp.config.output_mode == .Obj;
2115 const is_obj = wasm.base.isRelocatable();
21222116 if (is_obj) {
21232117 const nav_index = if (uav_index < wasm.uavs_obj.entries.len)
21242118 return .{ .uav_obj = @fromBackingInt(@intCast(uav_index)) }
......@@ -2139,8 +2133,7 @@ pub const DataSegmentId = enum(u32) {
21392133 }
21402134
21412135 pub fn fromNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) DataSegmentId {
2142 const comp = wasm.base.comp;
2143 const is_obj = comp.config.output_mode == .Obj;
2136 const is_obj = wasm.base.isRelocatable();
21442137 return pack(wasm, if (is_obj) .{
21452138 .nav_obj = @fromBackingInt(@intCast(wasm.navs_obj.getIndex(nav_index).?)),
21462139 } else .{
......@@ -2717,7 +2710,7 @@ pub const FunctionImportId = enum(u32) {
27172710 /// Asserts not emitting an object, and `Wasm.import_symbols` is false.
27182711 pub fn undefinedAllowed(id: FunctionImportId, wasm: *const Wasm) bool {
27192712 assert(!wasm.import_symbols);
2720 assert(wasm.base.comp.config.output_mode != .Obj);
2713 assert(!wasm.base.isRelocatable());
27212714 return switch (unpack(id, wasm)) {
27222715 .object_function_import => |i| {
27232716 const import = i.value(wasm);
......@@ -3343,6 +3336,7 @@ pub fn createEmpty(
33433336 assert(target.ofmt == .wasm);
33443337
33453338 const output_mode = comp.config.output_mode;
3339 const is_relocatable = output_mode == .Obj or (output_mode == .Lib and comp.config.link_mode == .static);
33463340 const wasi_exec_model = comp.config.wasi_exec_model;
33473341
33483342 const wasm = try arena.create(Wasm);
......@@ -3356,7 +3350,7 @@ pub fn createEmpty(
33563350 // majority of cases, and therefore express "no garbage collection"
33573351 // in terms of setting the no_strip and must_link flags on all
33583352 // symbols.
3359 .gc_sections = options.gc_sections orelse (output_mode != .Obj),
3353 .gc_sections = options.gc_sections orelse !is_relocatable,
33603354 .print_gc_sections = options.print_gc_sections,
33613355 .stack_size = options.stack_size orelse switch (target.os.tag) {
33623356 .freestanding => 1 * 1024 * 1024, // 1 MiB
......@@ -3436,6 +3430,11 @@ fn parseObject(wasm: *Wasm, obj: link.Input.Object) !void {
34363430 };
34373431 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
34393438 var ss: Object.ScratchSpace = .{};
34403439 defer ss.deinit(gpa);
34413440
......@@ -3526,6 +3525,7 @@ pub fn deinit(wasm: *Wasm) void {
35263525 wasm.object_relocations_table.deinit(gpa);
35273526 wasm.object_comdat_symbols.deinit(gpa);
35283527 wasm.objects.deinit(gpa);
3528 wasm.archive_builder.deinit(gpa);
35293529
35303530 wasm.func_types.deinit(gpa);
35313531 wasm.function_exports.deinit(gpa);
......@@ -3575,7 +3575,7 @@ pub fn updateFunc(
35753575 const zcu = pt.zcu;
35763576 const gpa = zcu.gpa;
35773577 const ip = &zcu.intern_pool;
3578 const is_obj = zcu.comp.config.output_mode == .Obj;
3578 const is_obj = wasm.base.isRelocatable();
35793579 const target = &zcu.comp.root_mod.resolved_target.result;
35803580 const owner_nav = zcu.funcInfo(func_index).owner_nav;
35813581 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
36563656 const nav = ip.getNav(nav_index);
36573657 const comp = wasm.base.comp;
36583658 const gpa = comp.gpa;
3659 const is_obj = comp.config.output_mode == .Obj;
3659 const is_obj = wasm.base.isRelocatable();
36603660 const target = &comp.root_mod.resolved_target.result;
36613661
36623662 switch (ip.indexToKey(nav.resolved.?.value)) {
......@@ -3740,7 +3740,7 @@ pub fn updateExports(
37403740 const zcu = pt.zcu;
37413741 const gpa = zcu.gpa;
37423742 const ip = &zcu.intern_pool;
3743 const is_obj = wasm.base.comp.config.output_mode == .Obj;
3743 const is_obj = wasm.base.isRelocatable();
37443744
37453745 for (export_indices) |export_idx| {
37463746 const exp = export_idx.ptr(zcu);
......@@ -3797,7 +3797,11 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void {
37973797 .dso => unreachable,
37983798 .tbd => unreachable,
37993799 .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 },
38013805 }
38023806}
38033807
......@@ -3811,7 +3815,7 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.Error!void {
38113815 const comp = wasm.base.comp;
38123816 const gpa = comp.gpa;
38133817 const rdynamic = comp.config.rdynamic;
3814 const is_obj = comp.config.output_mode == .Obj;
3818 const is_obj = wasm.base.isRelocatable();
38153819
38163820 assert(wasm.missing_exports.entries.len == 0);
38173821 for (wasm.export_symbol_names) |exp_name| {
......@@ -3915,7 +3919,7 @@ pub fn markFunctionImport(
39153919
39163920 const comp = wasm.base.comp;
39173921 const gpa = comp.gpa;
3918 const is_obj = comp.config.output_mode == .Obj;
3922 const is_obj = wasm.base.isRelocatable();
39193923
39203924 try wasm.functions.ensureUnusedCapacity(gpa, 1);
39213925
......@@ -3949,7 +3953,7 @@ fn markFunction(wasm: *Wasm, i: ObjectFunctionIndex, override_export: bool) link
39493953 if (gop.found_existing) return;
39503954
39513955 const rdynamic = comp.config.rdynamic;
3952 const is_obj = comp.config.output_mode == .Obj;
3956 const is_obj = wasm.base.isRelocatable();
39533957 const function = i.ptr(wasm);
39543958 try markObject(wasm, function.object_index);
39553959
......@@ -3991,7 +3995,7 @@ fn markGlobalImport(
39913995
39923996 const comp = wasm.base.comp;
39933997 const gpa = comp.gpa;
3994 const is_obj = comp.config.output_mode == .Obj;
3998 const is_obj = wasm.base.isRelocatable();
39953999
39964000 try wasm.globals.ensureUnusedCapacity(gpa, 1);
39974001
......@@ -4034,7 +4038,7 @@ fn markGlobal(wasm: *Wasm, i: ObjectGlobalIndex, override_export: bool) link.Err
40344038 if (gop.found_existing) return;
40354039
40364040 const rdynamic = comp.config.rdynamic;
4037 const is_obj = comp.config.output_mode == .Obj;
4041 const is_obj = wasm.base.isRelocatable();
40384042 const global = i.ptr(wasm);
40394043 try markObject(wasm, global.object_index);
40404044
......@@ -4057,7 +4061,7 @@ pub fn markTableImport(
40574061
40584062 const comp = wasm.base.comp;
40594063 const gpa = comp.gpa;
4060 const is_obj = comp.config.output_mode == .Obj;
4064 const is_obj = wasm.base.isRelocatable();
40614065
40624066 try wasm.tables.ensureUnusedCapacity(gpa, 1);
40634067
......@@ -4103,7 +4107,7 @@ pub fn markDataImport(
41034107
41044108 const comp = wasm.base.comp;
41054109 const gpa = comp.gpa;
4106 const is_obj = comp.config.output_mode == .Obj;
4110 const is_obj = wasm.base.isRelocatable();
41074111
41084112 if (import.resolution == .unresolved) {
41094113 if (!is_obj) {
......@@ -4682,7 +4686,7 @@ pub fn addNavReloc(
46824686 const ip = &zcu.intern_pool;
46834687 const gpa = comp.gpa;
46844688
4685 const is_obj = comp.config.output_mode == .Obj;
4689 const is_obj = wasm.base.isRelocatable();
46864690
46874691 if (nav_ty.zigTypeTag(zcu) == .@"fn") {
46884692 const gop = try wasm.zcu_indirect_function_set.getOrPut(gpa, nav_index);
......@@ -4751,7 +4755,7 @@ pub fn addUavReloc(
47514755 gop.value_ptr.* = if (gop.found_existing) gop.value_ptr.maxStrict(this_align) else this_align;
47524756 }
47534757
4754 if (comp.config.output_mode == .Obj) {
4758 if (wasm.base.isRelocatable()) {
47554759 const gop = try wasm.uavs_obj.getOrPut(gpa, uav_val);
47564760 if (!gop.found_existing) gop.value_ptr.* = undefined; // to avoid recursion, `ZcuDataStarts` will lower the value later
47574761 try wasm.zcu_relocations.append(gpa, .{
......@@ -4782,7 +4786,7 @@ pub fn addUavReloc(
47824786pub fn refNavObj(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsObjIndex {
47834787 const comp = wasm.base.comp;
47844788 const gpa = comp.gpa;
4785 assert(comp.config.output_mode == .Obj);
4789 assert(wasm.base.isRelocatable());
47864790 const gop = try wasm.navs_obj.getOrPut(gpa, nav_index);
47874791 if (!gop.found_existing) gop.value_ptr.* = .{
47884792 // Lowering the value is delayed to avoid recursion.
......@@ -4795,7 +4799,7 @@ pub fn refNavObj(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsObjIndex {
47954799pub fn refNavExe(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsExeIndex {
47964800 const comp = wasm.base.comp;
47974801 const gpa = comp.gpa;
4798 assert(comp.config.output_mode != .Obj);
4802 assert(!wasm.base.isRelocatable());
47994803 const gop = try wasm.navs_exe.getOrPut(gpa, nav_index);
48004804 if (gop.found_existing) {
48014805 gop.value_ptr.count += 1;
......@@ -4812,8 +4816,7 @@ pub fn refNavExe(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsExeIndex {
48124816/// Asserts it is called after `Flush.data_segments` is fully populated and sorted.
48134817pub fn uavAddr(wasm: *const Wasm, ip_index: InternPool.Index) u32 {
48144818 assert(wasm.flush_buffer.memory_layout_finished);
4815 const comp = wasm.base.comp;
4816 assert(comp.config.output_mode != .Obj);
4819 assert(!wasm.base.isRelocatable());
48174820 const uav_index: UavsExeIndex = @fromBackingInt(@intCast(wasm.uavs_exe.getIndex(ip_index).?));
48184821 const ds_id: DataSegmentId = .pack(wasm, .{ .uav_exe = uav_index });
48194822 return wasm.flush_buffer.data_segments.get(ds_id).?;
......@@ -4834,7 +4837,7 @@ pub fn syntheticDataAddr(wasm: *const Wasm, resolution: ObjectDataImport.Resolut
48344837pub fn navAddr(wasm: *const Wasm, nav_index: InternPool.Nav.Index) u32 {
48354838 assert(wasm.flush_buffer.memory_layout_finished);
48364839 const comp = wasm.base.comp;
4837 assert(comp.config.output_mode != .Obj);
4840 assert(!wasm.base.isRelocatable());
48384841 if (wasm.navs_exe.getIndex(nav_index)) |i| {
48394842 const navs_exe_index: NavsExeIndex = @fromBackingInt(@intCast(i));
48404843 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 {
48884891/// Asserts it is called after `Flush.data_segments` is fully populated and sorted.
48894892pub fn errorNameTableAddr(wasm: *Wasm) u32 {
48904893 assert(wasm.flush_buffer.memory_layout_finished);
4891 const comp = wasm.base.comp;
4892 assert(comp.config.output_mode != .Obj);
4894 assert(!wasm.base.isRelocatable());
48934895 return wasm.flush_buffer.data_segments.get(.__zig_error_name_table).?;
48944896}
48954897
48964898pub fn tagIndexTableAddr(wasm: *Wasm, ip_index: InternPool.Index) u32 {
48974899 assert(wasm.flush_buffer.memory_layout_finished);
4898 const comp = wasm.base.comp;
4899 assert(comp.config.output_mode != .Obj);
4900 assert(!wasm.base.isRelocatable());
49004901 const f = &wasm.flush_buffer;
49014902 const table_base_addr = f.data_segments.get(.__zig_tag_name_table).?;
49024903 return table_base_addr + wasm.tagIndexTableOffset(ip_index);
src/link/Wasm/Archive.zig+138
......@@ -68,6 +68,143 @@ const Header = extern struct {
6868 }
6969};
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
71208pub fn deinit(archive: *Archive, gpa: Allocator) void {
72209 deinitToc(gpa, &archive.toc);
73210 archive.* = undefined;
......@@ -102,6 +239,7 @@ pub fn parse(gpa: Allocator, file_contents: []const u8) !Archive {
102239
103240 const sym_tab = file_contents[pos..][0 .. sym_tab_size - 4 - symbol_positions_size];
104241 pos += sym_tab.len;
242 pos = mem.alignForward(usize, pos, 2);
105243
106244 var toc: Toc = .empty;
107245 errdefer deinitToc(gpa, &toc);
src/link/Wasm/Flush.zig+27-4
......@@ -247,7 +247,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
247247 .wasm64 => true,
248248 else => unreachable,
249249 };
250 const is_obj = comp.config.output_mode == .Obj;
250 const is_obj = wasm.base.isRelocatable();
251251 const allow_undefined = is_obj or wasm.import_symbols;
252252 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 {
17251725
17261726 // Finally, write the entire binary into the file.
17271727 var file_writer = wasm.base.file.?.writer(io, &.{});
1728 file_writer.interface.writeAll(binary_bytes.items) catch |err| switch (err) {
1729 error.WriteFailed => return file_writer.err.?,
1730 };
1728 if (wasm.base.isStaticLib()) {
1729 const builder = &wasm.archive_builder;
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 }
17311754 file_writer.end() catch |err| switch (err) {
17321755 error.WriteFailed => return file_writer.err.?,
17331756 else => |e| return e,