authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-12-13 19:19:05+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-12-13 19:19:05+01:00
log41199bba4bae4783634ee52ecd8d111f8f82fd4c
treebf9230dc76c78a07a0629113a91a02e9d4571bde
parent4832677c3bce61725c67306c4683921296abdff9
signature Commit is signed but in an unrecognized format.

wasm-linker: Create separate path for one-shot

When invoking the self-hosted linker using `-fno-LLD` while using the LLVM backend or invoking it as a linker, we create a seperate path. This path will link the object file generated by LLVM and the supplied object files just once, allowing to simplify the implementation between incremental and regular linking.

1 files changed, 533 insertions(+), 4 deletions(-)

src/link/Wasm.zig+533-4
...@@ -311,13 +311,23 @@ pub const StringTable = struct {...@@ -311,13 +311,23 @@ pub const StringTable = struct {
311pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Wasm {311pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Wasm {
312 assert(options.target.ofmt == .wasm);312 assert(options.target.ofmt == .wasm);
313313
314 if (build_options.have_llvm and options.use_llvm) {314 if (build_options.have_llvm and options.use_llvm and options.use_lld) {
315 return createEmpty(allocator, options);315 return createEmpty(allocator, options);
316 }316 }
317317
318 const wasm_bin = try createEmpty(allocator, options);318 const wasm_bin = try createEmpty(allocator, options);
319 errdefer wasm_bin.base.destroy();319 errdefer wasm_bin.base.destroy();
320320
321 // We are not using LLD at this point, so ensure we set the intermediary basename
322 if (build_options.have_llvm and options.use_llvm and options.module != null) {
323 // TODO this intermediary_basename isn't enough; in the case of `zig build-exe`,
324 // we also want to put the intermediary object file in the cache while the
325 // main emit directory is the cwd.
326 wasm_bin.base.intermediary_basename = try std.fmt.allocPrint(allocator, "{s}{s}", .{
327 options.emit.?.sub_path, options.target.ofmt.fileExt(options.target.cpu.arch),
328 });
329 }
330
321 // TODO: read the file and keep valid parts instead of truncating331 // TODO: read the file and keep valid parts instead of truncating
322 const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true });332 const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true });
323 wasm_bin.base.file = file;333 wasm_bin.base.file = file;
...@@ -2134,7 +2144,6 @@ pub fn createDebugSectionForIndex(wasm: *Wasm, index: *?u32, name: []const u8) !...@@ -2134,7 +2144,6 @@ pub fn createDebugSectionForIndex(wasm: *Wasm, index: *?u32, name: []const u8) !
2134 const new_index = @intCast(u32, wasm.segments.items.len);2144 const new_index = @intCast(u32, wasm.segments.items.len);
2135 index.* = new_index;2145 index.* = new_index;
2136 try wasm.appendDummySegment();2146 try wasm.appendDummySegment();
2137 // _ = index;
21382147
2139 const sym_index = wasm.symbols_free_list.popOrNull() orelse idx: {2148 const sym_index = wasm.symbols_free_list.popOrNull() orelse idx: {
2140 const tmp_index = @intCast(u32, wasm.symbols.items.len);2149 const tmp_index = @intCast(u32, wasm.symbols.items.len);
...@@ -2202,14 +2211,17 @@ pub fn flush(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) lin...@@ -2202,14 +2211,17 @@ pub fn flush(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) lin
2202 }2211 }
2203 return;2212 return;
2204 }2213 }
2214
2205 if (build_options.have_llvm and wasm.base.options.use_lld) {2215 if (build_options.have_llvm and wasm.base.options.use_lld) {
2206 return wasm.linkWithLLD(comp, prog_node);2216 return wasm.linkWithLLD(comp, prog_node);
2217 } else if (build_options.have_llvm and wasm.base.options.use_llvm and !wasm.base.options.use_lld) {
2218 return wasm.linkWithZld(comp, prog_node);
2207 } else {2219 } else {
2208 return wasm.flushModule(comp, prog_node);2220 return wasm.flushModule(comp, prog_node);
2209 }2221 }
2210}2222}
22112223
2212pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void {2224fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void {
2213 const tracy = trace(@src());2225 const tracy = trace(@src());
2214 defer tracy.end();2226 defer tracy.end();
22152227
...@@ -2219,7 +2231,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -2219,7 +2231,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
2219 }2231 }
2220 }2232 }
22212233
2222 var sub_prog_node = prog_node.start("WASM Flush", 0);2234 var sub_prog_node = prog_node.start("Wasm Flush", 0);
2223 sub_prog_node.activate();2235 sub_prog_node.activate();
2224 defer sub_prog_node.end();2236 defer sub_prog_node.end();
22252237
...@@ -2726,6 +2738,523 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -2726,6 +2738,523 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
2726 try wasm.base.file.?.writevAll(&iovec);2738 try wasm.base.file.?.writevAll(&iovec);
2727}2739}
27282740
2741pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void {
2742 const tracy = trace(@src());
2743 defer tracy.end();
2744
2745 if (build_options.have_llvm) {
2746 if (wasm.llvm_object) |llvm_object| {
2747 return try llvm_object.flushModule(comp, prog_node);
2748 }
2749 }
2750
2751 var sub_prog_node = prog_node.start("Wasm Flush", 0);
2752 sub_prog_node.activate();
2753 defer sub_prog_node.end();
2754
2755 // ensure the error names table is populated when an error name is referenced
2756 try wasm.populateErrorNameTable();
2757
2758 // The amount of sections that will be written
2759 var section_count: u32 = 0;
2760 // Index of the code section. Used to tell relocation table where the section lives.
2761 var code_section_index: ?u32 = null;
2762 // Index of the data section. Used to tell relocation table where the section lives.
2763 var data_section_index: ?u32 = null;
2764
2765 // Used for all temporary memory allocated during flushin
2766 var arena_instance = std.heap.ArenaAllocator.init(wasm.base.allocator);
2767 defer arena_instance.deinit();
2768 const arena = arena_instance.allocator();
2769
2770 // Positional arguments to the linker such as object files and static archives.
2771 var positionals = std.ArrayList([]const u8).init(arena);
2772 try positionals.ensureUnusedCapacity(wasm.base.options.objects.len);
2773
2774 for (wasm.base.options.objects) |object| {
2775 positionals.appendAssumeCapacity(object.path);
2776 }
2777
2778 for (comp.c_object_table.keys()) |c_object| {
2779 try positionals.append(c_object.status.success.object_path);
2780 }
2781
2782 if (comp.compiler_rt_lib) |lib| {
2783 try positionals.append(lib.full_object_path);
2784 }
2785
2786 try wasm.parseInputFiles(positionals.items);
2787
2788 for (wasm.objects.items) |_, object_index| {
2789 try wasm.resolveSymbolsInObject(@intCast(u16, object_index));
2790 }
2791
2792 var emit_features_count: u32 = 0;
2793 var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined;
2794 try wasm.validateFeatures(&enabled_features, &emit_features_count);
2795 try wasm.resolveSymbolsInArchives();
2796 try wasm.checkUndefinedSymbols();
2797
2798 // When we finish/error we reset the state of the linker
2799 // So we can rebuild the binary file on each incremental update
2800 defer wasm.resetState();
2801 try wasm.setupStart();
2802 try wasm.setupImports();
2803 if (wasm.base.options.module) |mod| {
2804 var decl_it = wasm.decls.keyIterator();
2805 while (decl_it.next()) |decl_index_ptr| {
2806 const decl = mod.declPtr(decl_index_ptr.*);
2807 if (decl.isExtern()) continue;
2808 const atom = &decl.*.link.wasm;
2809 if (decl.ty.zigTypeTag() == .Fn) {
2810 try wasm.parseAtom(atom, .{ .function = decl.fn_link.wasm });
2811 } else if (decl.getVariable()) |variable| {
2812 if (!variable.is_mutable) {
2813 try wasm.parseAtom(atom, .{ .data = .read_only });
2814 } else if (variable.init.isUndefDeep()) {
2815 try wasm.parseAtom(atom, .{ .data = .uninitialized });
2816 } else {
2817 try wasm.parseAtom(atom, .{ .data = .initialized });
2818 }
2819 } else {
2820 try wasm.parseAtom(atom, .{ .data = .read_only });
2821 }
2822
2823 // also parse atoms for a decl's locals
2824 for (atom.locals.items) |*local_atom| {
2825 try wasm.parseAtom(local_atom, .{ .data = .read_only });
2826 }
2827 }
2828
2829 if (wasm.dwarf) |*dwarf| {
2830 try dwarf.flushModule(wasm.base.options.module.?);
2831 }
2832 }
2833
2834 for (wasm.objects.items) |*object, object_index| {
2835 try object.parseIntoAtoms(wasm.base.allocator, @intCast(u16, object_index), wasm);
2836 }
2837
2838 try wasm.allocateAtoms();
2839 try wasm.setupMemory();
2840 wasm.mapFunctionTable();
2841 try wasm.mergeSections();
2842 try wasm.mergeTypes();
2843 try wasm.setupExports();
2844
2845 const header_size = 5 + 1;
2846 const is_obj = wasm.base.options.output_mode == .Obj or (!wasm.base.options.use_llvm and wasm.base.options.use_lld);
2847
2848 var binary_bytes = std.ArrayList(u8).init(wasm.base.allocator);
2849 defer binary_bytes.deinit();
2850 const binary_writer = binary_bytes.writer();
2851
2852 // We write the magic bytes at the end so they will only be written
2853 // if everything succeeded as expected. So populate with 0's for now.
2854 try binary_writer.writeAll(&[_]u8{0} ** 8);
2855 // (Re)set file pointer to 0
2856 try wasm.base.file.?.setEndPos(0);
2857 try wasm.base.file.?.seekTo(0);
2858
2859 // Type section
2860 if (wasm.func_types.items.len != 0) {
2861 const header_offset = try reserveVecSectionHeader(&binary_bytes);
2862 log.debug("Writing type section. Count: ({d})", .{wasm.func_types.items.len});
2863 for (wasm.func_types.items) |func_type| {
2864 try leb.writeULEB128(binary_writer, std.wasm.function_type);
2865 try leb.writeULEB128(binary_writer, @intCast(u32, func_type.params.len));
2866 for (func_type.params) |param_ty| {
2867 try leb.writeULEB128(binary_writer, std.wasm.valtype(param_ty));
2868 }
2869 try leb.writeULEB128(binary_writer, @intCast(u32, func_type.returns.len));
2870 for (func_type.returns) |ret_ty| {
2871 try leb.writeULEB128(binary_writer, std.wasm.valtype(ret_ty));
2872 }
2873 }
2874
2875 try writeVecSectionHeader(
2876 binary_bytes.items,
2877 header_offset,
2878 .type,
2879 @intCast(u32, binary_bytes.items.len - header_offset - header_size),
2880 @intCast(u32, wasm.func_types.items.len),
2881 );
2882 section_count += 1;
2883 }
2884
2885 // Import section
2886 const import_memory = wasm.base.options.import_memory or is_obj;
2887 const import_table = wasm.base.options.import_table or is_obj;
2888 if (wasm.imports.count() != 0 or import_memory or import_table) {
2889 const header_offset = try reserveVecSectionHeader(&binary_bytes);
2890
2891 // import table is always first table so emit that first
2892 if (import_table) {
2893 const table_imp: types.Import = .{
2894 .module_name = try wasm.string_table.put(wasm.base.allocator, wasm.host_name),
2895 .name = try wasm.string_table.put(wasm.base.allocator, "__indirect_function_table"),
2896 .kind = .{
2897 .table = .{
2898 .limits = .{
2899 .min = @intCast(u32, wasm.function_table.count()),
2900 .max = null,
2901 },
2902 .reftype = .funcref,
2903 },
2904 },
2905 };
2906 try wasm.emitImport(binary_writer, table_imp);
2907 }
2908
2909 var it = wasm.imports.iterator();
2910 while (it.next()) |entry| {
2911 assert(entry.key_ptr.*.getSymbol(wasm).isUndefined());
2912 const import = entry.value_ptr.*;
2913 try wasm.emitImport(binary_writer, import);
2914 }
2915
2916 if (import_memory) {
2917 const mem_name = if (is_obj) "__linear_memory" else "memory";
2918 const mem_imp: types.Import = .{
2919 .module_name = try wasm.string_table.put(wasm.base.allocator, wasm.host_name),
2920 .name = try wasm.string_table.put(wasm.base.allocator, mem_name),
2921 .kind = .{ .memory = wasm.memories.limits },
2922 };
2923 try wasm.emitImport(binary_writer, mem_imp);
2924 }
2925
2926 try writeVecSectionHeader(
2927 binary_bytes.items,
2928 header_offset,
2929 .import,
2930 @intCast(u32, binary_bytes.items.len - header_offset - header_size),
2931 @intCast(u32, wasm.imports.count() + @boolToInt(import_memory) + @boolToInt(import_table)),
2932 );
2933 section_count += 1;
2934 }
2935
2936 // Function section
2937 if (wasm.functions.count() != 0) {
2938 const header_offset = try reserveVecSectionHeader(&binary_bytes);
2939 for (wasm.functions.values()) |function| {
2940 try leb.writeULEB128(binary_writer, function.type_index);
2941 }
2942
2943 try writeVecSectionHeader(
2944 binary_bytes.items,
2945 header_offset,
2946 .function,
2947 @intCast(u32, binary_bytes.items.len - header_offset - header_size),
2948 @intCast(u32, wasm.functions.count()),
2949 );
2950 section_count += 1;
2951 }
2952
2953 // Table section
2954 const export_table = wasm.base.options.export_table;
2955 if (!import_table and wasm.function_table.count() != 0) {
2956 const header_offset = try reserveVecSectionHeader(&binary_bytes);
2957
2958 try leb.writeULEB128(binary_writer, std.wasm.reftype(.funcref));
2959 try emitLimits(binary_writer, .{
2960 .min = @intCast(u32, wasm.function_table.count()) + 1,
2961 .max = null,
2962 });
2963
2964 try writeVecSectionHeader(
2965 binary_bytes.items,
2966 header_offset,
2967 .table,
2968 @intCast(u32, binary_bytes.items.len - header_offset - header_size),
2969 @as(u32, 1),
2970 );
2971 section_count += 1;
2972 }
2973
2974 // Memory section
2975 if (!import_memory) {
2976 const header_offset = try reserveVecSectionHeader(&binary_bytes);
2977
2978 try emitLimits(binary_writer, wasm.memories.limits);
2979 try writeVecSectionHeader(
2980 binary_bytes.items,
2981 header_offset,
2982 .memory,
2983 @intCast(u32, binary_bytes.items.len - header_offset - header_size),
2984 @as(u32, 1), // wasm currently only supports 1 linear memory segment
2985 );
2986 section_count += 1;
2987 }
2988
2989 // Global section (used to emit stack pointer)
2990 if (wasm.wasm_globals.items.len > 0) {
2991 const header_offset = try reserveVecSectionHeader(&binary_bytes);
2992
2993 for (wasm.wasm_globals.items) |global| {
2994 try binary_writer.writeByte(std.wasm.valtype(global.global_type.valtype));
2995 try binary_writer.writeByte(@boolToInt(global.global_type.mutable));
2996 try emitInit(binary_writer, global.init);
2997 }
2998
2999 try writeVecSectionHeader(
3000 binary_bytes.items,
3001 header_offset,
3002 .global,
3003 @intCast(u32, binary_bytes.items.len - header_offset - header_size),
3004 @intCast(u32, wasm.wasm_globals.items.len),
3005 );
3006 section_count += 1;
3007 }
3008
3009 // Export section
3010 if (wasm.exports.items.len != 0 or export_table or !import_memory) {
3011 const header_offset = try reserveVecSectionHeader(&binary_bytes);
3012
3013 for (wasm.exports.items) |exp| {
3014 const name = wasm.string_table.get(exp.name);
3015 try leb.writeULEB128(binary_writer, @intCast(u32, name.len));
3016 try binary_writer.writeAll(name);
3017 try leb.writeULEB128(binary_writer, @enumToInt(exp.kind));
3018 try leb.writeULEB128(binary_writer, exp.index);
3019 }
3020
3021 if (export_table) {
3022 try leb.writeULEB128(binary_writer, @intCast(u32, "__indirect_function_table".len));
3023 try binary_writer.writeAll("__indirect_function_table");
3024 try binary_writer.writeByte(std.wasm.externalKind(.table));
3025 try leb.writeULEB128(binary_writer, @as(u32, 0)); // function table is always the first table
3026 }
3027
3028 if (!import_memory) {
3029 try leb.writeULEB128(binary_writer, @intCast(u32, "memory".len));
3030 try binary_writer.writeAll("memory");
3031 try binary_writer.writeByte(std.wasm.externalKind(.memory));
3032 try leb.writeULEB128(binary_writer, @as(u32, 0));
3033 }
3034
3035 try writeVecSectionHeader(
3036 binary_bytes.items,
3037 header_offset,
3038 .@"export",
3039 @intCast(u32, binary_bytes.items.len - header_offset - header_size),
3040 @intCast(u32, wasm.exports.items.len) + @boolToInt(export_table) + @boolToInt(!import_memory),
3041 );
3042 section_count += 1;
3043 }
3044
3045 // element section (function table)
3046 if (wasm.function_table.count() > 0) {
3047 const header_offset = try reserveVecSectionHeader(&binary_bytes);
3048
3049 var flags: u32 = 0x2; // Yes we have a table
3050 try leb.writeULEB128(binary_writer, flags);
3051 try leb.writeULEB128(binary_writer, @as(u32, 0)); // index of that table. TODO: Store synthetic symbols
3052 try emitInit(binary_writer, .{ .i32_const = 1 }); // We start at index 1, so unresolved function pointers are invalid
3053 try leb.writeULEB128(binary_writer, @as(u8, 0));
3054 try leb.writeULEB128(binary_writer, @intCast(u32, wasm.function_table.count()));
3055 var symbol_it = wasm.function_table.keyIterator();
3056 while (symbol_it.next()) |symbol_loc_ptr| {
3057 try leb.writeULEB128(binary_writer, symbol_loc_ptr.*.getSymbol(wasm).index);
3058 }
3059
3060 try writeVecSectionHeader(
3061 binary_bytes.items,
3062 header_offset,
3063 .element,
3064 @intCast(u32, binary_bytes.items.len - header_offset - header_size),
3065 @as(u32, 1),
3066 );
3067 section_count += 1;
3068 }
3069
3070 // Code section
3071 var code_section_size: u32 = 0;
3072 if (wasm.code_section_index) |code_index| {
3073 const header_offset = try reserveVecSectionHeader(&binary_bytes);
3074 var atom: *Atom = wasm.atoms.get(code_index).?.getFirst();
3075
3076 // The code section must be sorted in line with the function order.
3077 var sorted_atoms = try std.ArrayList(*Atom).initCapacity(wasm.base.allocator, wasm.functions.count());
3078 defer sorted_atoms.deinit();
3079
3080 while (true) {
3081 if (!is_obj) {
3082 atom.resolveRelocs(wasm);
3083 }
3084 sorted_atoms.appendAssumeCapacity(atom);
3085 atom = atom.next orelse break;
3086 }
3087
3088 const atom_sort_fn = struct {
3089 fn sort(ctx: *const Wasm, lhs: *const Atom, rhs: *const Atom) bool {
3090 const lhs_sym = lhs.symbolLoc().getSymbol(ctx);
3091 const rhs_sym = rhs.symbolLoc().getSymbol(ctx);
3092 return lhs_sym.index < rhs_sym.index;
3093 }
3094 }.sort;
3095
3096 std.sort.sort(*Atom, sorted_atoms.items, wasm, atom_sort_fn);
3097
3098 for (sorted_atoms.items) |sorted_atom| {
3099 try leb.writeULEB128(binary_writer, sorted_atom.size);
3100 try binary_writer.writeAll(sorted_atom.code.items);
3101 }
3102
3103 code_section_size = @intCast(u32, binary_bytes.items.len - header_offset - header_size);
3104 try writeVecSectionHeader(
3105 binary_bytes.items,
3106 header_offset,
3107 .code,
3108 code_section_size,
3109 @intCast(u32, wasm.functions.count()),
3110 );
3111 code_section_index = section_count;
3112 section_count += 1;
3113 }
3114
3115 // Data section
3116 if (wasm.data_segments.count() != 0) {
3117 const header_offset = try reserveVecSectionHeader(&binary_bytes);
3118
3119 var it = wasm.data_segments.iterator();
3120 var segment_count: u32 = 0;
3121 while (it.next()) |entry| {
3122 // do not output 'bss' section unless we import memory and therefore
3123 // want to guarantee the data is zero initialized
3124 if (!import_memory and std.mem.eql(u8, entry.key_ptr.*, ".bss")) continue;
3125 segment_count += 1;
3126 const atom_index = entry.value_ptr.*;
3127 var atom: *Atom = wasm.atoms.getPtr(atom_index).?.*.getFirst();
3128 const segment = wasm.segments.items[atom_index];
3129
3130 // flag and index to memory section (currently, there can only be 1 memory section in wasm)
3131 try leb.writeULEB128(binary_writer, @as(u32, 0));
3132 // offset into data section
3133 try emitInit(binary_writer, .{ .i32_const = @bitCast(i32, segment.offset) });
3134 try leb.writeULEB128(binary_writer, segment.size);
3135
3136 // fill in the offset table and the data segments
3137 var current_offset: u32 = 0;
3138 while (true) {
3139 if (!is_obj) {
3140 atom.resolveRelocs(wasm);
3141 }
3142
3143 // Pad with zeroes to ensure all segments are aligned
3144 if (current_offset != atom.offset) {
3145 const diff = atom.offset - current_offset;
3146 try binary_writer.writeByteNTimes(0, diff);
3147 current_offset += diff;
3148 }
3149 assert(current_offset == atom.offset);
3150 assert(atom.code.items.len == atom.size);
3151 try binary_writer.writeAll(atom.code.items);
3152
3153 current_offset += atom.size;
3154 if (atom.next) |next| {
3155 atom = next;
3156 } else {
3157 // also pad with zeroes when last atom to ensure
3158 // segments are aligned.
3159 if (current_offset != segment.size) {
3160 try binary_writer.writeByteNTimes(0, segment.size - current_offset);
3161 current_offset += segment.size - current_offset;
3162 }
3163 break;
3164 }
3165 }
3166 assert(current_offset == segment.size);
3167 }
3168
3169 try writeVecSectionHeader(
3170 binary_bytes.items,
3171 header_offset,
3172 .data,
3173 @intCast(u32, binary_bytes.items.len - header_offset - header_size),
3174 @intCast(u32, segment_count),
3175 );
3176 data_section_index = section_count;
3177 section_count += 1;
3178 }
3179
3180 if (is_obj) {
3181 // relocations need to point to the index of a symbol in the final symbol table. To save memory,
3182 // we never store all symbols in a single table, but store a location reference instead.
3183 // This means that for a relocatable object file, we need to generate one and provide it to the relocation sections.
3184 var symbol_table = std.AutoArrayHashMap(SymbolLoc, u32).init(arena);
3185 try wasm.emitLinkSection(&binary_bytes, &symbol_table);
3186 if (code_section_index) |code_index| {
3187 try wasm.emitCodeRelocations(&binary_bytes, code_index, symbol_table);
3188 }
3189 if (data_section_index) |data_index| {
3190 try wasm.emitDataRelocations(&binary_bytes, data_index, symbol_table);
3191 }
3192 } else if (!wasm.base.options.strip) {
3193 try wasm.emitNameSection(&binary_bytes, arena);
3194 }
3195
3196 if (!wasm.base.options.strip) {
3197 if (wasm.dwarf) |*dwarf| {
3198 const mod = wasm.base.options.module.?;
3199 try dwarf.writeDbgAbbrev();
3200 // for debug info and ranges, the address is always 0,
3201 // as locations are always offsets relative to 'code' section.
3202 try dwarf.writeDbgInfoHeader(mod, 0, code_section_size);
3203 try dwarf.writeDbgAranges(0, code_section_size);
3204 try dwarf.writeDbgLineHeader();
3205 }
3206
3207 var debug_bytes = std.ArrayList(u8).init(wasm.base.allocator);
3208 defer debug_bytes.deinit();
3209
3210 const DebugSection = struct {
3211 name: []const u8,
3212 index: ?u32,
3213 };
3214
3215 const debug_sections: []const DebugSection = &.{
3216 .{ .name = ".debug_info", .index = wasm.debug_info_index },
3217 .{ .name = ".debug_pubtypes", .index = wasm.debug_pubtypes_index },
3218 .{ .name = ".debug_abbrev", .index = wasm.debug_abbrev_index },
3219 .{ .name = ".debug_line", .index = wasm.debug_line_index },
3220 .{ .name = ".debug_str", .index = wasm.debug_str_index },
3221 .{ .name = ".debug_pubnames", .index = wasm.debug_pubnames_index },
3222 .{ .name = ".debug_loc", .index = wasm.debug_loc_index },
3223 .{ .name = ".debug_ranges", .index = wasm.debug_ranges_index },
3224 };
3225
3226 for (debug_sections) |item| {
3227 if (item.index) |index| {
3228 var atom = wasm.atoms.get(index).?.getFirst();
3229 while (true) {
3230 atom.resolveRelocs(wasm);
3231 try debug_bytes.appendSlice(atom.code.items);
3232 atom = atom.next orelse break;
3233 }
3234 try emitDebugSection(&binary_bytes, debug_bytes.items, item.name);
3235 debug_bytes.clearRetainingCapacity();
3236 }
3237 }
3238
3239 try emitProducerSection(&binary_bytes);
3240 if (emit_features_count > 0) {
3241 try emitFeaturesSection(&binary_bytes, &enabled_features, emit_features_count);
3242 }
3243 }
3244
3245 // Only when writing all sections executed properly we write the magic
3246 // bytes. This allows us to easily detect what went wrong while generating
3247 // the final binary.
3248 mem.copy(u8, binary_bytes.items, &(std.wasm.magic ++ std.wasm.version));
3249
3250 // finally, write the entire binary into the file.
3251 var iovec = [_]std.os.iovec_const{.{
3252 .iov_base = binary_bytes.items.ptr,
3253 .iov_len = binary_bytes.items.len,
3254 }};
3255 try wasm.base.file.?.writevAll(&iovec);
3256}
3257
2729fn emitDebugSection(binary_bytes: *std.ArrayList(u8), data: []const u8, name: []const u8) !void {3258fn emitDebugSection(binary_bytes: *std.ArrayList(u8), data: []const u8, name: []const u8) !void {
2730 if (data.len == 0) return;3259 if (data.len == 0) return;
2731 const header_offset = try reserveCustomSectionHeader(binary_bytes);3260 const header_offset = try reserveCustomSectionHeader(binary_bytes);