| ... | ... | @@ -311,13 +311,23 @@ pub const StringTable = struct { |
| 311 | 311 | pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Wasm { |
| 312 | 312 | assert(options.target.ofmt == .wasm); |
| 313 | 313 | |
| 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 | 315 | return createEmpty(allocator, options); |
| 316 | 316 | } |
| 317 | 317 | |
| 318 | 318 | const wasm_bin = try createEmpty(allocator, options); |
| 319 | 319 | errdefer wasm_bin.base.destroy(); |
| 320 | 320 | |
| 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 | 331 | // TODO: read the file and keep valid parts instead of truncating |
| 322 | 332 | const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true }); |
| 323 | 333 | wasm_bin.base.file = file; |
| ... | ... | @@ -2134,7 +2144,6 @@ pub fn createDebugSectionForIndex(wasm: *Wasm, index: *?u32, name: []const u8) ! |
| 2134 | 2144 | const new_index = @intCast(u32, wasm.segments.items.len); |
| 2135 | 2145 | index.* = new_index; |
| 2136 | 2146 | try wasm.appendDummySegment(); |
| 2137 | | // _ = index; |
| 2138 | 2147 | |
| 2139 | 2148 | const sym_index = wasm.symbols_free_list.popOrNull() orelse idx: { |
| 2140 | 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 | 2211 | } |
| 2203 | 2212 | return; |
| 2204 | 2213 | } |
| 2214 | |
| 2205 | 2215 | if (build_options.have_llvm and wasm.base.options.use_lld) { |
| 2206 | 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 | 2219 | } else { |
| 2208 | 2220 | return wasm.flushModule(comp, prog_node); |
| 2209 | 2221 | } |
| 2210 | 2222 | } |
| 2211 | 2223 | |
| 2212 | | pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { |
| 2224 | fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { |
| 2213 | 2225 | const tracy = trace(@src()); |
| 2214 | 2226 | defer tracy.end(); |
| 2215 | 2227 | |
| ... | ... | @@ -2219,7 +2231,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2219 | 2231 | } |
| 2220 | 2232 | } |
| 2221 | 2233 | |
| 2222 | | var sub_prog_node = prog_node.start("WASM Flush", 0); |
| 2234 | var sub_prog_node = prog_node.start("Wasm Flush", 0); |
| 2223 | 2235 | sub_prog_node.activate(); |
| 2224 | 2236 | defer sub_prog_node.end(); |
| 2225 | 2237 | |
| ... | ... | @@ -2726,6 +2738,523 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2726 | 2738 | try wasm.base.file.?.writevAll(&iovec); |
| 2727 | 2739 | } |
| 2728 | 2740 | |
| 2741 | pub 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 | |
| 2729 | 3258 | fn emitDebugSection(binary_bytes: *std.ArrayList(u8), data: []const u8, name: []const u8) !void { |
| 2730 | 3259 | if (data.len == 0) return; |
| 2731 | 3260 | const header_offset = try reserveCustomSectionHeader(binary_bytes); |