diff --git a/src/codegen/wasm/Emit.zig b/src/codegen/wasm/Emit.zig index 6193650464133168e21379e8cf810d8fb9e9ac12..2a39fed32ef4447f2e7d16ce3eb90062bb667cd5 100644 --- a/src/codegen/wasm/Emit.zig +++ b/src/codegen/wasm/Emit.zig @@ -27,7 +27,7 @@ pub fn lower(emit: *Emit) Error!void { const wasm = emit.wasm; const comp = wasm.base.comp; const gpa = comp.gpa; - const is_obj = comp.config.output_mode == .Obj; + const is_obj = wasm.base.isRelocatable(); const target = &comp.root_mod.resolved_target.result; const is_wasm32 = target.cpu.arch == .wasm32; @@ -1028,7 +1028,7 @@ fn navRefOff(wasm: *Wasm, code: *ArrayList(u8), data: Mir.NavRefOff, is_wasm32: const zcu = comp.zcu.?; const ip = &zcu.intern_pool; const gpa = comp.gpa; - const is_obj = comp.config.output_mode == .Obj; + const is_obj = wasm.base.isRelocatable(); const nav_ty = ip.getNav(data.nav_index).resolved.?.type; assert(!ip.isFunctionType(nav_ty)); diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 6e77b0f1f8a251c11b1027ce442ed15971711613..24e706478b7e3769a610c81d4b148d24d8b6a72d 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -79,6 +79,7 @@ growable_table: bool, name: []const u8, /// List of relocatable files to be linked into the final binary. objects: std.ArrayList(Object) = .empty, +archive_builder: Archive.Builder = .{}, func_types: std.array_hash_map.Auto(FunctionType, void) = .empty, /// Provides a mapping of both imports and provided functions to symbol name. @@ -467,8 +468,7 @@ pub const OutputDataIndex = enum(u32) { } pub fn fromUav(wasm: *const Wasm, ip_index: InternPool.Index) OutputDataIndex { - const comp = wasm.base.comp; - const resolution: ObjectDataImport.Resolution = if (comp.config.output_mode == .Obj) + const resolution: ObjectDataImport.Resolution = if (wasm.base.isRelocatable()) .pack(wasm, .{ .uav_obj = @fromBackingInt(@intCast(wasm.uavs_obj.getIndex(ip_index).?)) }) else .pack(wasm, .{ .uav_exe = @fromBackingInt(@intCast(wasm.uavs_exe.getIndex(ip_index).?)) }); @@ -482,7 +482,7 @@ pub const OutputDataIndex = enum(u32) { if (nav.getExtern(ip) != null) { return fromSymbolName(wasm, wasm.imports.get(nav_index).?); } - const resolution: ObjectDataImport.Resolution = if (wasm.base.comp.config.output_mode == .Obj) + const resolution: ObjectDataImport.Resolution = if (wasm.base.isRelocatable()) .pack(wasm, .{ .nav_obj = @fromBackingInt(@intCast(wasm.navs_obj.getIndex(nav_index).?)) }) else .pack(wasm, .{ .nav_exe = @fromBackingInt(@intCast(wasm.navs_exe.getIndex(nav_index).?)) }); @@ -500,7 +500,7 @@ pub const GlobalIndex = enum(u32) { /// Same as `stack_pointer` but with a safety assertion. pub fn stackPointer(wasm: *const Wasm) ObjectGlobal.Index { const comp = wasm.base.comp; - assert(comp.config.output_mode != .Obj); + assert(!wasm.base.isRelocatable()); assert(comp.zcu != null); return .stack_pointer; } @@ -879,8 +879,7 @@ const ZcuDataStarts = struct { uavs_i: u32, fn init(wasm: *const Wasm) ZcuDataStarts { - const comp = wasm.base.comp; - const is_obj = comp.config.output_mode == .Obj; + const is_obj = wasm.base.isRelocatable(); return if (is_obj) initObj(wasm) else initExe(wasm); } @@ -897,8 +896,7 @@ const ZcuDataStarts = struct { } fn finish(zds: ZcuDataStarts, wasm: *Wasm, pt: Zcu.PerThread) !void { - const comp = wasm.base.comp; - const is_obj = comp.config.output_mode == .Obj; + const is_obj = wasm.base.isRelocatable(); return if (is_obj) finishObj(zds, wasm, pt) else finishExe(zds, wasm, pt); } @@ -1276,8 +1274,7 @@ pub const GlobalImport = extern struct { const object_global_index = i - first_object_global; if (object_global_index < wasm.object_globals.items.len) return .{ .object_global = @fromBackingInt(@intCast(object_global_index)) }; - const comp = wasm.base.comp; - const is_obj = comp.config.output_mode == .Obj; + const is_obj = wasm.base.isRelocatable(); const uav_index = object_global_index - wasm.object_globals.items.len; if (is_obj) { if (uav_index < wasm.uavs_obj.entries.len) { @@ -1323,7 +1320,7 @@ pub const GlobalImport = extern struct { } pub fn fromIpIndex(wasm: *const Wasm, ip_index: InternPool.Index) Resolution { - const is_obj = wasm.base.comp.config.output_mode == .Obj; + const is_obj = wasm.base.isRelocatable(); return pack(wasm, if (is_obj) .{ .uav_obj = @fromBackingInt(@intCast(wasm.uavs_obj.getIndex(ip_index).?)), } else .{ @@ -1332,8 +1329,7 @@ pub const GlobalImport = extern struct { } pub fn fromIpNav(wasm: *const Wasm, ip_nav: InternPool.Nav.Index) Resolution { - const comp = wasm.base.comp; - const is_obj = comp.config.output_mode == .Obj; + const is_obj = wasm.base.isRelocatable(); return pack(wasm, if (is_obj) .{ .nav_obj = @fromBackingInt(@intCast(wasm.navs_obj.getIndex(ip_nav).?)), } else .{ @@ -1819,8 +1815,7 @@ pub const ObjectDataImport = extern struct { else object_index - wasm.object_datas.items.len; - const comp = wasm.base.comp; - const is_obj = comp.config.output_mode == .Obj; + const is_obj = wasm.base.isRelocatable(); if (is_obj) { const nav_index = if (uav_index < wasm.uavs_obj.entries.len) return .{ .uav_obj = @fromBackingInt(@intCast(uav_index)) } @@ -1863,7 +1858,7 @@ pub const ObjectDataImport = extern struct { } pub fn fromIpIndex(wasm: *const Wasm, ip_index: InternPool.Index) Resolution { - const is_obj = wasm.base.comp.config.output_mode == .Obj; + const is_obj = wasm.base.isRelocatable(); return pack(wasm, if (is_obj) .{ .uav_obj = @fromBackingInt(@intCast(wasm.uavs_obj.getIndex(ip_index).?)), } else .{ @@ -1872,7 +1867,7 @@ pub const ObjectDataImport = extern struct { } pub fn fromIpNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) Resolution { - const is_obj = wasm.base.comp.config.output_mode == .Obj; + const is_obj = wasm.base.isRelocatable(); return pack(wasm, if (is_obj) .{ .nav_obj = @fromBackingInt(@intCast(wasm.navs_obj.getIndex(nav_index).?)), } else .{ @@ -2117,8 +2112,7 @@ pub const DataSegmentId = enum(u32) { else object_index - wasm.object_data_segments.items.len; - const comp = wasm.base.comp; - const is_obj = comp.config.output_mode == .Obj; + const is_obj = wasm.base.isRelocatable(); if (is_obj) { const nav_index = if (uav_index < wasm.uavs_obj.entries.len) return .{ .uav_obj = @fromBackingInt(@intCast(uav_index)) } @@ -2139,8 +2133,7 @@ pub const DataSegmentId = enum(u32) { } pub fn fromNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) DataSegmentId { - const comp = wasm.base.comp; - const is_obj = comp.config.output_mode == .Obj; + const is_obj = wasm.base.isRelocatable(); return pack(wasm, if (is_obj) .{ .nav_obj = @fromBackingInt(@intCast(wasm.navs_obj.getIndex(nav_index).?)), } else .{ @@ -2717,7 +2710,7 @@ pub const FunctionImportId = enum(u32) { /// Asserts not emitting an object, and `Wasm.import_symbols` is false. pub fn undefinedAllowed(id: FunctionImportId, wasm: *const Wasm) bool { assert(!wasm.import_symbols); - assert(wasm.base.comp.config.output_mode != .Obj); + assert(!wasm.base.isRelocatable()); return switch (unpack(id, wasm)) { .object_function_import => |i| { const import = i.value(wasm); @@ -3343,6 +3336,7 @@ pub fn createEmpty( assert(target.ofmt == .wasm); const output_mode = comp.config.output_mode; + const is_relocatable = output_mode == .Obj or (output_mode == .Lib and comp.config.link_mode == .static); const wasi_exec_model = comp.config.wasi_exec_model; const wasm = try arena.create(Wasm); @@ -3356,7 +3350,7 @@ pub fn createEmpty( // majority of cases, and therefore express "no garbage collection" // in terms of setting the no_strip and must_link flags on all // symbols. - .gc_sections = options.gc_sections orelse (output_mode != .Obj), + .gc_sections = options.gc_sections orelse !is_relocatable, .print_gc_sections = options.print_gc_sections, .stack_size = options.stack_size orelse switch (target.os.tag) { .freestanding => 1 * 1024 * 1024, // 1 MiB @@ -3436,6 +3430,11 @@ fn parseObject(wasm: *Wasm, obj: link.Input.Object) !void { }; if (n != file_contents.len) return error.UnexpectedEndOfFile; + if (wasm.base.isStaticLib()) { + try wasm.archive_builder.append(gpa, std.fs.path.basename(obj.path.sub_path), file_contents); + return; + } + var ss: Object.ScratchSpace = .{}; defer ss.deinit(gpa); @@ -3526,6 +3525,7 @@ pub fn deinit(wasm: *Wasm) void { wasm.object_relocations_table.deinit(gpa); wasm.object_comdat_symbols.deinit(gpa); wasm.objects.deinit(gpa); + wasm.archive_builder.deinit(gpa); wasm.func_types.deinit(gpa); wasm.function_exports.deinit(gpa); @@ -3575,7 +3575,7 @@ pub fn updateFunc( const zcu = pt.zcu; const gpa = zcu.gpa; const ip = &zcu.intern_pool; - const is_obj = zcu.comp.config.output_mode == .Obj; + const is_obj = wasm.base.isRelocatable(); const target = &zcu.comp.root_mod.resolved_target.result; const owner_nav = zcu.funcInfo(func_index).owner_nav; 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 const nav = ip.getNav(nav_index); const comp = wasm.base.comp; const gpa = comp.gpa; - const is_obj = comp.config.output_mode == .Obj; + const is_obj = wasm.base.isRelocatable(); const target = &comp.root_mod.resolved_target.result; switch (ip.indexToKey(nav.resolved.?.value)) { @@ -3740,7 +3740,7 @@ pub fn updateExports( const zcu = pt.zcu; const gpa = zcu.gpa; const ip = &zcu.intern_pool; - const is_obj = wasm.base.comp.config.output_mode == .Obj; + const is_obj = wasm.base.isRelocatable(); for (export_indices) |export_idx| { const exp = export_idx.ptr(zcu); @@ -3797,7 +3797,11 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void { .dso => unreachable, .tbd => unreachable, .object => |obj| try parseObject(wasm, obj), - .archive => |obj| try parseArchive(wasm, obj), + .archive => |obj| if (wasm.base.isStaticLib()) { + obj.file.close(io); // ignore archives like LLVM + } else { + try parseArchive(wasm, obj); + }, } } @@ -3811,7 +3815,7 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.Error!void { const comp = wasm.base.comp; const gpa = comp.gpa; const rdynamic = comp.config.rdynamic; - const is_obj = comp.config.output_mode == .Obj; + const is_obj = wasm.base.isRelocatable(); assert(wasm.missing_exports.entries.len == 0); for (wasm.export_symbol_names) |exp_name| { @@ -3915,7 +3919,7 @@ pub fn markFunctionImport( const comp = wasm.base.comp; const gpa = comp.gpa; - const is_obj = comp.config.output_mode == .Obj; + const is_obj = wasm.base.isRelocatable(); try wasm.functions.ensureUnusedCapacity(gpa, 1); @@ -3949,7 +3953,7 @@ fn markFunction(wasm: *Wasm, i: ObjectFunctionIndex, override_export: bool) link if (gop.found_existing) return; const rdynamic = comp.config.rdynamic; - const is_obj = comp.config.output_mode == .Obj; + const is_obj = wasm.base.isRelocatable(); const function = i.ptr(wasm); try markObject(wasm, function.object_index); @@ -3991,7 +3995,7 @@ fn markGlobalImport( const comp = wasm.base.comp; const gpa = comp.gpa; - const is_obj = comp.config.output_mode == .Obj; + const is_obj = wasm.base.isRelocatable(); try wasm.globals.ensureUnusedCapacity(gpa, 1); @@ -4034,7 +4038,7 @@ fn markGlobal(wasm: *Wasm, i: ObjectGlobalIndex, override_export: bool) link.Err if (gop.found_existing) return; const rdynamic = comp.config.rdynamic; - const is_obj = comp.config.output_mode == .Obj; + const is_obj = wasm.base.isRelocatable(); const global = i.ptr(wasm); try markObject(wasm, global.object_index); @@ -4057,7 +4061,7 @@ pub fn markTableImport( const comp = wasm.base.comp; const gpa = comp.gpa; - const is_obj = comp.config.output_mode == .Obj; + const is_obj = wasm.base.isRelocatable(); try wasm.tables.ensureUnusedCapacity(gpa, 1); @@ -4103,7 +4107,7 @@ pub fn markDataImport( const comp = wasm.base.comp; const gpa = comp.gpa; - const is_obj = comp.config.output_mode == .Obj; + const is_obj = wasm.base.isRelocatable(); if (import.resolution == .unresolved) { if (!is_obj) { @@ -4682,7 +4686,7 @@ pub fn addNavReloc( const ip = &zcu.intern_pool; const gpa = comp.gpa; - const is_obj = comp.config.output_mode == .Obj; + const is_obj = wasm.base.isRelocatable(); if (nav_ty.zigTypeTag(zcu) == .@"fn") { const gop = try wasm.zcu_indirect_function_set.getOrPut(gpa, nav_index); @@ -4751,7 +4755,7 @@ pub fn addUavReloc( gop.value_ptr.* = if (gop.found_existing) gop.value_ptr.maxStrict(this_align) else this_align; } - if (comp.config.output_mode == .Obj) { + if (wasm.base.isRelocatable()) { const gop = try wasm.uavs_obj.getOrPut(gpa, uav_val); if (!gop.found_existing) gop.value_ptr.* = undefined; // to avoid recursion, `ZcuDataStarts` will lower the value later try wasm.zcu_relocations.append(gpa, .{ @@ -4782,7 +4786,7 @@ pub fn addUavReloc( pub fn refNavObj(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsObjIndex { const comp = wasm.base.comp; const gpa = comp.gpa; - assert(comp.config.output_mode == .Obj); + assert(wasm.base.isRelocatable()); const gop = try wasm.navs_obj.getOrPut(gpa, nav_index); if (!gop.found_existing) gop.value_ptr.* = .{ // Lowering the value is delayed to avoid recursion. @@ -4795,7 +4799,7 @@ pub fn refNavObj(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsObjIndex { pub fn refNavExe(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsExeIndex { const comp = wasm.base.comp; const gpa = comp.gpa; - assert(comp.config.output_mode != .Obj); + assert(!wasm.base.isRelocatable()); const gop = try wasm.navs_exe.getOrPut(gpa, nav_index); if (gop.found_existing) { gop.value_ptr.count += 1; @@ -4812,8 +4816,7 @@ pub fn refNavExe(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsExeIndex { /// Asserts it is called after `Flush.data_segments` is fully populated and sorted. pub fn uavAddr(wasm: *const Wasm, ip_index: InternPool.Index) u32 { assert(wasm.flush_buffer.memory_layout_finished); - const comp = wasm.base.comp; - assert(comp.config.output_mode != .Obj); + assert(!wasm.base.isRelocatable()); const uav_index: UavsExeIndex = @fromBackingInt(@intCast(wasm.uavs_exe.getIndex(ip_index).?)); const ds_id: DataSegmentId = .pack(wasm, .{ .uav_exe = uav_index }); return wasm.flush_buffer.data_segments.get(ds_id).?; @@ -4834,7 +4837,7 @@ pub fn syntheticDataAddr(wasm: *const Wasm, resolution: ObjectDataImport.Resolut pub fn navAddr(wasm: *const Wasm, nav_index: InternPool.Nav.Index) u32 { assert(wasm.flush_buffer.memory_layout_finished); const comp = wasm.base.comp; - assert(comp.config.output_mode != .Obj); + assert(!wasm.base.isRelocatable()); if (wasm.navs_exe.getIndex(nav_index)) |i| { const navs_exe_index: NavsExeIndex = @fromBackingInt(@intCast(i)); 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 { /// Asserts it is called after `Flush.data_segments` is fully populated and sorted. pub fn errorNameTableAddr(wasm: *Wasm) u32 { assert(wasm.flush_buffer.memory_layout_finished); - const comp = wasm.base.comp; - assert(comp.config.output_mode != .Obj); + assert(!wasm.base.isRelocatable()); return wasm.flush_buffer.data_segments.get(.__zig_error_name_table).?; } pub fn tagIndexTableAddr(wasm: *Wasm, ip_index: InternPool.Index) u32 { assert(wasm.flush_buffer.memory_layout_finished); - const comp = wasm.base.comp; - assert(comp.config.output_mode != .Obj); + assert(!wasm.base.isRelocatable()); const f = &wasm.flush_buffer; const table_base_addr = f.data_segments.get(.__zig_tag_name_table).?; return table_base_addr + wasm.tagIndexTableOffset(ip_index); diff --git a/src/link/Wasm/Archive.zig b/src/link/Wasm/Archive.zig index 48665264f3cab69532a8e70d371fe506578d3ea8..37d9bda33ca039bb54aad55327acb5ced957be5f 100644 --- a/src/link/Wasm/Archive.zig +++ b/src/link/Wasm/Archive.zig @@ -68,6 +68,143 @@ const Header = extern struct { } }; +pub const Builder = struct { + file_names: std.ArrayList(u8) = .empty, + members: std.ArrayList(u8) = .empty, + symbol_names: std.ArrayList(u8) = .empty, + symbol_offsets: std.ArrayList(u32) = .empty, + + pub fn deinit(b: *Builder, gpa: Allocator) void { + b.file_names.deinit(gpa); + b.members.deinit(gpa); + b.symbol_names.deinit(gpa); + b.symbol_offsets.deinit(gpa); + b.* = undefined; + } + + pub fn append(b: *Builder, gpa: Allocator, name: []const u8, data: []const u8) !void { + const member_offset: u32 = @intCast(b.members.items.len); + const name_offset = b.file_names.items.len; + + try b.file_names.appendSlice(gpa, name); + try b.file_names.appendSlice(gpa, "/\n"); + + var name_buf: [16]u8 = undefined; + const header_name = try std.mem.print(&name_buf, "/{d}", .{name_offset}); + const member_size = @sizeOf(Header) + mem.alignForward(usize, data.len, 2); + var w: std.Io.Writer = .fixed(try b.members.addManyAsSlice(gpa, member_size)); + try writeHeader(&w, header_name, data.len); + try w.writeAll(data); + if (data.len % 2 != 0) try w.writeByte('\n'); + + try b.collectSymbols(gpa, data, member_offset); + } + + pub fn write(b: *Builder, w: *std.Io.Writer) std.Io.Writer.Error!void { + const symtab_size = 4 + 4 * b.symbol_offsets.items.len + b.symbol_names.items.len; + const members_offset = ARMAG.len + @sizeOf(Header) + mem.alignForward(usize, symtab_size, 2) + + @sizeOf(Header) + mem.alignForward(usize, b.file_names.items.len, 2); + + try w.writeAll(ARMAG); + + try writeHeader(w, "/", symtab_size); + try w.writeInt(u32, @intCast(b.symbol_offsets.items.len), .big); + for (b.symbol_offsets.items) |offset| try w.writeInt(u32, @intCast(members_offset + offset), .big); + try w.writeAll(b.symbol_names.items); + if (symtab_size % 2 != 0) try w.writeByte('\n'); + + try writeHeader(w, "//", b.file_names.items.len); + try w.writeAll(b.file_names.items); + if (b.file_names.items.len % 2 != 0) try w.writeByte('\n'); + + try w.writeAll(b.members.items); + } + + fn collectSymbols(b: *Builder, gpa: Allocator, data: []const u8, member_offset: u32) !void { + var reader: std.Io.Reader = .fixed(data); + if (!mem.eql(u8, try reader.take(4), &std.wasm.magic)) return error.BadObjectMagic; + if (!mem.eql(u8, try reader.take(4), &std.wasm.version)) return error.UnsupportedVersion; + + while (reader.seek < data.len) { + const section_tag = try reader.takeEnum(std.wasm.Section, .little); + const section_len = try reader.takeLeb128(u32); + var section: std.Io.Reader = .fixed(try reader.take(section_len)); + if (section_tag != .custom) continue; + + const section_name = try readBytes(§ion); + if (mem.eql(u8, section_name, "linking")) { + const section_version = try section.takeLeb128(u32); + if (section_version != 2) return error.UnsupportedVersion; + + while (section.seek < section.buffer.len) { + const sub_type = try section.takeLeb128(u8); + const payload_len = try section.takeLeb128(u32); + + var symtab: std.Io.Reader = .fixed(try section.take(payload_len)); + if (sub_type != @backingInt(Object.SubsectionType.symbol_table)) continue; + + const count = try symtab.takeLeb128(u32); + for (0..count) |_| { + const tag = try symtab.takeEnum(Object.Symbol.Tag, .little); + const flags = try symtab.takeLeb128(u32); + const symbol_flags: Wasm.SymbolFlags = @bitCast(flags); + + var name: []const u8 = undefined; + switch (tag) { + .data => { + name = try readBytes(&symtab); + if (!symbol_flags.undefined) { + _ = try symtab.takeLeb128(u32); + _ = try symtab.takeLeb128(u64); + _ = try symtab.takeLeb128(u64); + } + }, + .section => { + _ = try symtab.takeLeb128(u32); + continue; + }, + .function, .global, .event, .table => { + _ = try symtab.takeLeb128(u32); + if (!symbol_flags.undefined or symbol_flags.explicit_name) { + name = try readBytes(&symtab); + } + }, + } + + if (!symbol_flags.undefined and symbol_flags.binding != .local) { + try b.symbol_names.appendSlice(gpa, name); + try b.symbol_names.append(gpa, 0); + try b.symbol_offsets.append(gpa, member_offset); + } + } + } + return; + } + } + return error.MissingLinkingSection; + } +}; + +fn writeHeader(w: *std.Io.Writer, name: []const u8, size: usize) std.Io.Writer.Error!void { + var header: Header = .{ + .name = @splat(' '), + .date = "0 ".*, + .uid = "0 ".*, + .gid = "0 ".*, + .mode = "644 ".*, + .size = @splat(' '), + .fmag = ARFMAG.*, + }; + @memcpy(header.name[0..name.len], name); + _ = std.mem.print(&header.size, "{d}", .{size}) catch unreachable; + try w.writeAll(mem.asBytes(&header)); +} + +fn readBytes(reader: *std.Io.Reader) ![]const u8 { + const len = try reader.takeLeb128(u32); + return reader.take(len); +} + pub fn deinit(archive: *Archive, gpa: Allocator) void { deinitToc(gpa, &archive.toc); archive.* = undefined; @@ -102,6 +239,7 @@ pub fn parse(gpa: Allocator, file_contents: []const u8) !Archive { const sym_tab = file_contents[pos..][0 .. sym_tab_size - 4 - symbol_positions_size]; pos += sym_tab.len; + pos = mem.alignForward(usize, pos, 2); var toc: Toc = .empty; errdefer deinitToc(gpa, &toc); diff --git a/src/link/Wasm/Flush.zig b/src/link/Wasm/Flush.zig index 745e3023dc0a9e61e68f7f6164c6ed220915447f..00ad4ef87ae0beabd19a9256d3a09318aac7923f 100644 --- a/src/link/Wasm/Flush.zig +++ b/src/link/Wasm/Flush.zig @@ -247,7 +247,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { .wasm64 => true, else => unreachable, }; - const is_obj = comp.config.output_mode == .Obj; + const is_obj = wasm.base.isRelocatable(); const allow_undefined = is_obj or wasm.import_symbols; const zcu_references = if (comp.zcu) |zcu| try zcu.resolveReferences() else null; @@ -1725,9 +1725,32 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { // Finally, write the entire binary into the file. var file_writer = wasm.base.file.?.writer(io, &.{}); - file_writer.interface.writeAll(binary_bytes.items) catch |err| switch (err) { - error.WriteFailed => return file_writer.err.?, - }; + if (wasm.base.isStaticLib()) { + const builder = &wasm.archive_builder; + // restore for incremental + const file_names_len = builder.file_names.items.len; + defer builder.file_names.shrinkRetainingCapacity(file_names_len); + const members_len = builder.members.items.len; + defer builder.members.shrinkRetainingCapacity(members_len); + const symbol_names_len = builder.symbol_names.items.len; + defer builder.symbol_names.shrinkRetainingCapacity(symbol_names_len); + const symbol_offsets_len = builder.symbol_offsets.items.len; + defer builder.symbol_offsets.shrinkRetainingCapacity(symbol_offsets_len); + + // TODO: pass ZCU data and symbols to write, so reparsing not needed + if (comp.zcu != null) { + const name = try std.fmt.allocPrint(gpa, "{s}_zcu.o", .{comp.root_name}); + defer gpa.free(name); + try builder.append(gpa, name, binary_bytes.items); + } + builder.write(&file_writer.interface) catch |err| switch (err) { + error.WriteFailed => return file_writer.err.?, + }; + } else { + file_writer.interface.writeAll(binary_bytes.items) catch |err| switch (err) { + error.WriteFailed => return file_writer.err.?, + }; + } file_writer.end() catch |err| switch (err) { error.WriteFailed => return file_writer.err.?, else => |e| return e,