| ... | @@ -111,7 +111,11 @@ functions: std.AutoArrayHashMapUnmanaged(struct { file: ?u16, index: u32 }, std. | ... | @@ -111,7 +111,11 @@ functions: std.AutoArrayHashMapUnmanaged(struct { file: ?u16, index: u32 }, std. |
| 111 | /// Output global section | 111 | /// Output global section |
| 112 | wasm_globals: std.ArrayListUnmanaged(std.wasm.Global) = .{}, | 112 | wasm_globals: std.ArrayListUnmanaged(std.wasm.Global) = .{}, |
| 113 | /// Memory section | 113 | /// Memory section |
| 114 | memories: std.wasm.Memory = .{ .limits = .{ .min = 0, .max = null } }, | 114 | memories: std.wasm.Memory = .{ .limits = .{ |
| | 115 | .min = 0, |
| | 116 | .max = undefined, |
| | 117 | .flags = 0, |
| | 118 | } }, |
| 115 | /// Output table section | 119 | /// Output table section |
| 116 | tables: std.ArrayListUnmanaged(std.wasm.Table) = .{}, | 120 | tables: std.ArrayListUnmanaged(std.wasm.Table) = .{}, |
| 117 | /// Output export section | 121 | /// Output export section |
| ... | @@ -135,6 +139,8 @@ archives: std.ArrayListUnmanaged(Archive) = .{}, | ... | @@ -135,6 +139,8 @@ archives: std.ArrayListUnmanaged(Archive) = .{}, |
| 135 | | 139 | |
| 136 | /// A map of global names (read: offset into string table) to their symbol location | 140 | /// A map of global names (read: offset into string table) to their symbol location |
| 137 | globals: std.AutoHashMapUnmanaged(u32, SymbolLoc) = .{}, | 141 | globals: std.AutoHashMapUnmanaged(u32, SymbolLoc) = .{}, |
| | 142 | /// The list of GOT symbols and their location |
| | 143 | got_symbols: std.ArrayListUnmanaged(SymbolLoc) = .{}, |
| 138 | /// Maps discarded symbols and their positions to the location of the symbol | 144 | /// Maps discarded symbols and their positions to the location of the symbol |
| 139 | /// it was resolved to | 145 | /// it was resolved to |
| 140 | discarded: std.AutoHashMapUnmanaged(SymbolLoc, SymbolLoc) = .{}, | 146 | discarded: std.AutoHashMapUnmanaged(SymbolLoc, SymbolLoc) = .{}, |
| ... | @@ -176,6 +182,24 @@ pub const Segment = struct { | ... | @@ -176,6 +182,24 @@ pub const Segment = struct { |
| 176 | alignment: u32, | 182 | alignment: u32, |
| 177 | size: u32, | 183 | size: u32, |
| 178 | offset: u32, | 184 | offset: u32, |
| | 185 | flags: u32, |
| | 186 | |
| | 187 | pub const Flag = enum(u32) { |
| | 188 | WASM_DATA_SEGMENT_IS_PASSIVE = 0x01, |
| | 189 | WASM_DATA_SEGMENT_HAS_MEMINDEX = 0x02, |
| | 190 | }; |
| | 191 | |
| | 192 | pub fn isPassive(segment: Segment) bool { |
| | 193 | return segment.flags & @enumToInt(Flag.WASM_DATA_SEGMENT_IS_PASSIVE) != 0; |
| | 194 | } |
| | 195 | |
| | 196 | /// For a given segment, determines if it needs passive initialization |
| | 197 | fn needsPassiveInitialization(segment: Segment, import_mem: bool, name: []const u8) bool { |
| | 198 | if (import_mem and !std.mem.eql(u8, name, ".bss")) { |
| | 199 | return true; |
| | 200 | } |
| | 201 | return segment.isPassive(); |
| | 202 | } |
| 179 | }; | 203 | }; |
| 180 | | 204 | |
| 181 | pub const Export = struct { | 205 | pub const Export = struct { |
| ... | @@ -396,7 +420,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option | ... | @@ -396,7 +420,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option |
| 396 | const loc = try wasm_bin.createSyntheticSymbol("__indirect_function_table", .table); | 420 | const loc = try wasm_bin.createSyntheticSymbol("__indirect_function_table", .table); |
| 397 | const symbol = loc.getSymbol(wasm_bin); | 421 | const symbol = loc.getSymbol(wasm_bin); |
| 398 | const table: std.wasm.Table = .{ | 422 | const table: std.wasm.Table = .{ |
| 399 | .limits = .{ .min = 0, .max = null }, // will be overwritten during `mapFunctionTable` | 423 | .limits = .{ .flags = 0, .min = 0, .max = undefined }, // will be overwritten during `mapFunctionTable` |
| 400 | .reftype = .funcref, | 424 | .reftype = .funcref, |
| 401 | }; | 425 | }; |
| 402 | if (options.output_mode == .Obj or options.import_table) { | 426 | if (options.output_mode == .Obj or options.import_table) { |
| ... | @@ -429,6 +453,30 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option | ... | @@ -429,6 +453,30 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option |
| 429 | // at the end during `initializeCallCtorsFunction`. | 453 | // at the end during `initializeCallCtorsFunction`. |
| 430 | } | 454 | } |
| 431 | | 455 | |
| | 456 | // shared-memory symbols for TLS support |
| | 457 | if (wasm_bin.base.options.shared_memory) { |
| | 458 | { |
| | 459 | const loc = try wasm_bin.createSyntheticSymbol("__tls_base", .global); |
| | 460 | const symbol = loc.getSymbol(wasm_bin); |
| | 461 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); |
| | 462 | } |
| | 463 | { |
| | 464 | const loc = try wasm_bin.createSyntheticSymbol("__tls_size", .global); |
| | 465 | const symbol = loc.getSymbol(wasm_bin); |
| | 466 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); |
| | 467 | } |
| | 468 | { |
| | 469 | const loc = try wasm_bin.createSyntheticSymbol("__tls_align", .global); |
| | 470 | const symbol = loc.getSymbol(wasm_bin); |
| | 471 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); |
| | 472 | } |
| | 473 | { |
| | 474 | const loc = try wasm_bin.createSyntheticSymbol("__wasm_tls_init", .function); |
| | 475 | const symbol = loc.getSymbol(wasm_bin); |
| | 476 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); |
| | 477 | } |
| | 478 | } |
| | 479 | |
| 432 | // if (!options.strip and options.module != null) { | 480 | // if (!options.strip and options.module != null) { |
| 433 | // wasm_bin.dwarf = Dwarf.init(allocator, &wasm_bin.base, options.target); | 481 | // wasm_bin.dwarf = Dwarf.init(allocator, &wasm_bin.base, options.target); |
| 434 | // try wasm_bin.initDebugSections(); | 482 | // try wasm_bin.initDebugSections(); |
| ... | @@ -597,6 +645,15 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool { | ... | @@ -597,6 +645,15 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool { |
| 597 | return true; | 645 | return true; |
| 598 | } | 646 | } |
| 599 | | 647 | |
| | 648 | fn requiresTLSReloc(wasm: *const Wasm) bool { |
| | 649 | for (wasm.got_symbols.items) |loc| { |
| | 650 | if (loc.getSymbol(wasm).isTLS()) { |
| | 651 | return true; |
| | 652 | } |
| | 653 | } |
| | 654 | return false; |
| | 655 | } |
| | 656 | |
| 600 | fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { | 657 | fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { |
| 601 | const object: Object = wasm.objects.items[object_index]; | 658 | const object: Object = wasm.objects.items[object_index]; |
| 602 | log.debug("Resolving symbols in object: '{s}'", .{object.name}); | 659 | log.debug("Resolving symbols in object: '{s}'", .{object.name}); |
| ... | @@ -775,6 +832,220 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void { | ... | @@ -775,6 +832,220 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void { |
| 775 | } | 832 | } |
| 776 | } | 833 | } |
| 777 | | 834 | |
| | 835 | fn setupInitMemoryFunction(wasm: *Wasm) !void { |
| | 836 | // Passive segments are used to avoid memory being reinitialized on each |
| | 837 | // thread's instantiation. These passive segments are initialized and |
| | 838 | // dropped in __wasm_init_memory, which is registered as the start function |
| | 839 | // We also initialize bss segments (using memory.fill) as part of this |
| | 840 | // function. |
| | 841 | if (!wasm.hasPassiveInitializationSegments()) { |
| | 842 | return; |
| | 843 | } |
| | 844 | |
| | 845 | const flag_address: u32 = if (wasm.base.options.shared_memory) address: { |
| | 846 | // when we have passive initialization segments and shared memory |
| | 847 | // `setupMemory` will create this symbol and set its virtual address. |
| | 848 | const loc = wasm.findGlobalSymbol("__wasm_init_memory_flag").?; |
| | 849 | break :address loc.getSymbol(wasm).virtual_address; |
| | 850 | } else 0; |
| | 851 | |
| | 852 | var function_body = std.ArrayList(u8).init(wasm.base.allocator); |
| | 853 | defer function_body.deinit(); |
| | 854 | const writer = function_body.writer(); |
| | 855 | |
| | 856 | // we have 0 locals |
| | 857 | try leb.writeULEB128(writer, @as(u32, 0)); |
| | 858 | |
| | 859 | if (wasm.base.options.shared_memory) { |
| | 860 | // destination blocks |
| | 861 | // based on values we jump to corresponding label |
| | 862 | try writer.writeByte(std.wasm.opcode(.block)); // $drop |
| | 863 | try writer.writeByte(std.wasm.block_empty); // block type |
| | 864 | |
| | 865 | try writer.writeByte(std.wasm.opcode(.block)); // $wait |
| | 866 | try writer.writeByte(std.wasm.block_empty); // block type |
| | 867 | |
| | 868 | try writer.writeByte(std.wasm.opcode(.block)); // $init |
| | 869 | try writer.writeByte(std.wasm.block_empty); // block type |
| | 870 | |
| | 871 | // atomically check |
| | 872 | try writer.writeByte(std.wasm.opcode(.i32_const)); |
| | 873 | try leb.writeULEB128(writer, flag_address); |
| | 874 | try writer.writeByte(std.wasm.opcode(.i32_const)); |
| | 875 | try leb.writeULEB128(writer, @as(u32, 0)); |
| | 876 | try writer.writeByte(std.wasm.opcode(.i32_const)); |
| | 877 | try leb.writeULEB128(writer, @as(u32, 1)); |
| | 878 | try writer.writeByte(std.wasm.opcode(.atomics_prefix)); |
| | 879 | try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.i32_atomic_rmw_cmpxchg)); |
| | 880 | try leb.writeULEB128(writer, @as(u32, 2)); // alignment |
| | 881 | try leb.writeULEB128(writer, @as(u32, 0)); // offset |
| | 882 | |
| | 883 | // based on the value from the atomic check, jump to the label. |
| | 884 | try writer.writeByte(std.wasm.opcode(.br_table)); |
| | 885 | try leb.writeULEB128(writer, @as(u32, 2)); // length of the table (we have 3 blocks but because of the mandatory default the length is 2). |
| | 886 | try leb.writeULEB128(writer, @as(u32, 0)); // $init |
| | 887 | try leb.writeULEB128(writer, @as(u32, 1)); // $wait |
| | 888 | try leb.writeULEB128(writer, @as(u32, 2)); // $drop |
| | 889 | try writer.writeByte(std.wasm.opcode(.end)); |
| | 890 | } |
| | 891 | |
| | 892 | var it = wasm.data_segments.iterator(); |
| | 893 | var segment_index: u32 = 0; |
| | 894 | while (it.next()) |entry| : (segment_index += 1) { |
| | 895 | const segment: Segment = wasm.segments.items[entry.value_ptr.*]; |
| | 896 | if (segment.needsPassiveInitialization(wasm.base.options.import_memory, entry.key_ptr.*)) { |
| | 897 | // For passive BSS segments we can simple issue a memory.fill(0). |
| | 898 | // For non-BSS segments we do a memory.init. Both these |
| | 899 | // instructions take as their first argument the destination |
| | 900 | // address. |
| | 901 | try writer.writeByte(std.wasm.opcode(.i32_const)); |
| | 902 | try leb.writeULEB128(writer, segment.offset); |
| | 903 | |
| | 904 | if (wasm.base.options.shared_memory and std.mem.eql(u8, entry.key_ptr.*, ".tdata")) { |
| | 905 | // When we initialize the TLS segment we also set the `__tls_base` |
| | 906 | // global. This allows the runtime to use this static copy of the |
| | 907 | // TLS data for the first/main thread. |
| | 908 | try writer.writeByte(std.wasm.opcode(.i32_const)); |
| | 909 | try leb.writeULEB128(writer, segment.offset); |
| | 910 | try writer.writeByte(std.wasm.opcode(.global_set)); |
| | 911 | const loc = wasm.findGlobalSymbol("__tls_base").?; |
| | 912 | try leb.writeULEB128(writer, loc.getSymbol(wasm).index); |
| | 913 | } |
| | 914 | |
| | 915 | try writer.writeByte(std.wasm.opcode(.i32_const)); |
| | 916 | try leb.writeULEB128(writer, @as(u32, 0)); |
| | 917 | try writer.writeByte(std.wasm.opcode(.i32_const)); |
| | 918 | try leb.writeULEB128(writer, segment.size); |
| | 919 | try writer.writeByte(std.wasm.opcode(.misc_prefix)); |
| | 920 | if (std.mem.eql(u8, entry.key_ptr.*, ".bss")) { |
| | 921 | // fill bss segment with zeroes |
| | 922 | try leb.writeULEB128(writer, std.wasm.miscOpcode(.memory_fill)); |
| | 923 | } else { |
| | 924 | // initialize the segment |
| | 925 | try leb.writeULEB128(writer, std.wasm.miscOpcode(.memory_init)); |
| | 926 | try leb.writeULEB128(writer, segment_index); |
| | 927 | } |
| | 928 | try writer.writeByte(0); // memory index immediate |
| | 929 | } |
| | 930 | } |
| | 931 | |
| | 932 | if (wasm.base.options.shared_memory) { |
| | 933 | // we set the init memory flag to value '2' |
| | 934 | try writer.writeByte(std.wasm.opcode(.i32_const)); |
| | 935 | try leb.writeULEB128(writer, flag_address); |
| | 936 | try writer.writeByte(std.wasm.opcode(.i32_const)); |
| | 937 | try leb.writeULEB128(writer, @as(u32, 2)); |
| | 938 | try writer.writeByte(std.wasm.opcode(.atomics_prefix)); |
| | 939 | try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.i32_atomic_store)); |
| | 940 | try leb.writeULEB128(writer, @as(u32, 2)); // alignment |
| | 941 | try leb.writeULEB128(writer, @as(u32, 0)); // offset |
| | 942 | |
| | 943 | // notify any waiters for segment initialization completion |
| | 944 | try writer.writeByte(std.wasm.opcode(.i32_const)); |
| | 945 | try leb.writeULEB128(writer, flag_address); |
| | 946 | try writer.writeByte(std.wasm.opcode(.i32_const)); |
| | 947 | try leb.writeILEB128(writer, @as(i32, -1)); // number of waiters |
| | 948 | try writer.writeByte(std.wasm.opcode(.atomics_prefix)); |
| | 949 | try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.memory_atomic_notify)); |
| | 950 | try leb.writeULEB128(writer, @as(u32, 2)); // alignment |
| | 951 | try leb.writeULEB128(writer, @as(u32, 0)); // offset |
| | 952 | try writer.writeByte(std.wasm.opcode(.drop)); |
| | 953 | |
| | 954 | // branch and drop segments |
| | 955 | try writer.writeByte(std.wasm.opcode(.br)); |
| | 956 | try leb.writeULEB128(writer, @as(u32, 1)); |
| | 957 | |
| | 958 | // wait for thread to initialize memory segments |
| | 959 | try writer.writeByte(std.wasm.opcode(.end)); // end $wait |
| | 960 | try writer.writeByte(std.wasm.opcode(.i32_const)); |
| | 961 | try leb.writeULEB128(writer, flag_address); |
| | 962 | try writer.writeByte(std.wasm.opcode(.i32_const)); |
| | 963 | try leb.writeULEB128(writer, @as(u32, 1)); // expected flag value |
| | 964 | try writer.writeByte(std.wasm.opcode(.i32_const)); |
| | 965 | try leb.writeILEB128(writer, @as(i32, -1)); // timeout |
| | 966 | try writer.writeByte(std.wasm.opcode(.atomics_prefix)); |
| | 967 | try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.memory_atomic_wait32)); |
| | 968 | try leb.writeULEB128(writer, @as(u32, 2)); // alignment |
| | 969 | try leb.writeULEB128(writer, @as(u32, 0)); // offset |
| | 970 | try writer.writeByte(std.wasm.opcode(.drop)); |
| | 971 | |
| | 972 | try writer.writeByte(std.wasm.opcode(.end)); // end $drop |
| | 973 | } |
| | 974 | |
| | 975 | it.reset(); |
| | 976 | segment_index = 0; |
| | 977 | while (it.next()) |entry| : (segment_index += 1) { |
| | 978 | const name = entry.key_ptr.*; |
| | 979 | const segment: Segment = wasm.segments.items[entry.value_ptr.*]; |
| | 980 | if (segment.needsPassiveInitialization(wasm.base.options.import_memory, name) and |
| | 981 | !std.mem.eql(u8, name, ".bss")) |
| | 982 | { |
| | 983 | // The TLS region should not be dropped since its is needed |
| | 984 | // during the initialization of each thread (__wasm_init_tls). |
| | 985 | if (wasm.base.options.shared_memory and std.mem.eql(u8, name, ".tdata")) { |
| | 986 | continue; |
| | 987 | } |
| | 988 | |
| | 989 | try writer.writeByte(std.wasm.opcode(.misc_prefix)); |
| | 990 | try leb.writeULEB128(writer, std.wasm.miscOpcode(.data_drop)); |
| | 991 | try leb.writeULEB128(writer, segment_index); |
| | 992 | } |
| | 993 | } |
| | 994 | |
| | 995 | // End of the function body |
| | 996 | try writer.writeByte(std.wasm.opcode(.end)); |
| | 997 | |
| | 998 | try wasm.createSyntheticFunction( |
| | 999 | "__wasm_init_memory", |
| | 1000 | std.wasm.Type{ .params = &.{}, .returns = &.{} }, |
| | 1001 | &function_body, |
| | 1002 | ); |
| | 1003 | } |
| | 1004 | |
| | 1005 | /// Constructs a synthetic function that performs runtime relocations for |
| | 1006 | /// TLS symbols. This function is called by `__wasm_init_tls`. |
| | 1007 | fn setupTLSRelocationsFunction(wasm: *Wasm) !void { |
| | 1008 | // When we have TLS GOT entries and shared memory is enabled, |
| | 1009 | // we must perform runtime relocations or else we don't create the function. |
| | 1010 | if (!wasm.base.options.shared_memory or !wasm.requiresTLSReloc()) { |
| | 1011 | return; |
| | 1012 | } |
| | 1013 | |
| | 1014 | // const loc = try wasm.createSyntheticSymbol("__wasm_apply_global_tls_relocs"); |
| | 1015 | var function_body = std.ArrayList(u8).init(wasm.base.allocator); |
| | 1016 | defer function_body.deinit(); |
| | 1017 | const writer = function_body.writer(); |
| | 1018 | |
| | 1019 | // locals (we have none) |
| | 1020 | try writer.writeByte(0); |
| | 1021 | for (wasm.got_symbols.items, 0..) |got_loc, got_index| { |
| | 1022 | const sym: *Symbol = got_loc.getSymbol(wasm); |
| | 1023 | if (!sym.isTLS()) continue; // only relocate TLS symbols |
| | 1024 | if (sym.tag == .data and sym.isDefined()) { |
| | 1025 | // get __tls_base |
| | 1026 | try writer.writeByte(std.wasm.opcode(.global_get)); |
| | 1027 | try leb.writeULEB128(writer, wasm.findGlobalSymbol("__tls_base").?.getSymbol(wasm).index); |
| | 1028 | |
| | 1029 | // add the virtual address of the symbol |
| | 1030 | try writer.writeByte(std.wasm.opcode(.i32_const)); |
| | 1031 | try leb.writeULEB128(writer, sym.virtual_address); |
| | 1032 | } else if (sym.tag == .function) { |
| | 1033 | @panic("TODO: relocate GOT entry of function"); |
| | 1034 | } else continue; |
| | 1035 | |
| | 1036 | try writer.writeByte(std.wasm.opcode(.i32_add)); |
| | 1037 | try writer.writeByte(std.wasm.opcode(.global_set)); |
| | 1038 | try leb.writeULEB128(writer, wasm.imported_globals_count + @intCast(u32, wasm.wasm_globals.items.len + got_index)); |
| | 1039 | } |
| | 1040 | try writer.writeByte(std.wasm.opcode(.end)); |
| | 1041 | |
| | 1042 | try wasm.createSyntheticFunction( |
| | 1043 | "__wasm_apply_global_tls_relocs", |
| | 1044 | std.wasm.Type{ .params = &.{}, .returns = &.{} }, |
| | 1045 | &function_body, |
| | 1046 | ); |
| | 1047 | } |
| | 1048 | |
| 778 | fn validateFeatures( | 1049 | fn validateFeatures( |
| 779 | wasm: *const Wasm, | 1050 | wasm: *const Wasm, |
| 780 | to_emit: *[@typeInfo(types.Feature.Tag).Enum.fields.len]bool, | 1051 | to_emit: *[@typeInfo(types.Feature.Tag).Enum.fields.len]bool, |
| ... | @@ -791,6 +1062,8 @@ fn validateFeatures( | ... | @@ -791,6 +1062,8 @@ fn validateFeatures( |
| 791 | | 1062 | |
| 792 | // when false, we fail linking. We only verify this after a loop to catch all invalid features. | 1063 | // when false, we fail linking. We only verify this after a loop to catch all invalid features. |
| 793 | var valid_feature_set = true; | 1064 | var valid_feature_set = true; |
| | 1065 | // will be set to true when there's any TLS segment found in any of the object files |
| | 1066 | var has_tls = false; |
| 794 | | 1067 | |
| 795 | // When the user has given an explicit list of features to enable, | 1068 | // When the user has given an explicit list of features to enable, |
| 796 | // we extract them and insert each into the 'allowed' list. | 1069 | // we extract them and insert each into the 'allowed' list. |
| ... | @@ -821,6 +1094,12 @@ fn validateFeatures( | ... | @@ -821,6 +1094,12 @@ fn validateFeatures( |
| 821 | }, | 1094 | }, |
| 822 | } | 1095 | } |
| 823 | } | 1096 | } |
| | 1097 | |
| | 1098 | for (object.segment_info) |segment| { |
| | 1099 | if (segment.isTLS()) { |
| | 1100 | has_tls = true; |
| | 1101 | } |
| | 1102 | } |
| 824 | } | 1103 | } |
| 825 | | 1104 | |
| 826 | // when we infer the features, we allow each feature found in the 'used' set | 1105 | // when we infer the features, we allow each feature found in the 'used' set |
| ... | @@ -832,7 +1111,7 @@ fn validateFeatures( | ... | @@ -832,7 +1111,7 @@ fn validateFeatures( |
| 832 | allowed[used_index] = is_enabled; | 1111 | allowed[used_index] = is_enabled; |
| 833 | emit_features_count.* += @boolToInt(is_enabled); | 1112 | emit_features_count.* += @boolToInt(is_enabled); |
| 834 | } else if (is_enabled and !allowed[used_index]) { | 1113 | } else if (is_enabled and !allowed[used_index]) { |
| 835 | log.err("feature '{s}' not allowed, but used by linked object", .{(@intToEnum(types.Feature.Tag, used_index)).toString()}); | 1114 | log.err("feature '{}' not allowed, but used by linked object", .{@intToEnum(types.Feature.Tag, used_index)}); |
| 836 | log.err(" defined in '{s}'", .{wasm.objects.items[used_set >> 1].name}); | 1115 | log.err(" defined in '{s}'", .{wasm.objects.items[used_set >> 1].name}); |
| 837 | valid_feature_set = false; | 1116 | valid_feature_set = false; |
| 838 | } | 1117 | } |
| ... | @@ -842,6 +1121,30 @@ fn validateFeatures( | ... | @@ -842,6 +1121,30 @@ fn validateFeatures( |
| 842 | return error.InvalidFeatureSet; | 1121 | return error.InvalidFeatureSet; |
| 843 | } | 1122 | } |
| 844 | | 1123 | |
| | 1124 | if (wasm.base.options.shared_memory) { |
| | 1125 | const disallowed_feature = disallowed[@enumToInt(types.Feature.Tag.shared_mem)]; |
| | 1126 | if (@truncate(u1, disallowed_feature) != 0) { |
| | 1127 | log.err( |
| | 1128 | "shared-memory is disallowed by '{s}' because it wasn't compiled with 'atomics' and 'bulk-memory' features enabled", |
| | 1129 | .{wasm.objects.items[disallowed_feature >> 1].name}, |
| | 1130 | ); |
| | 1131 | valid_feature_set = false; |
| | 1132 | } |
| | 1133 | |
| | 1134 | for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| { |
| | 1135 | if (!allowed[@enumToInt(feature)]) { |
| | 1136 | log.err("feature '{}' is not used but is required for shared-memory", .{feature}); |
| | 1137 | } |
| | 1138 | } |
| | 1139 | } |
| | 1140 | |
| | 1141 | if (has_tls) { |
| | 1142 | for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| { |
| | 1143 | if (!allowed[@enumToInt(feature)]) { |
| | 1144 | log.err("feature '{}' is not used but is required for thread-local storage", .{feature}); |
| | 1145 | } |
| | 1146 | } |
| | 1147 | } |
| 845 | // For each linked object, validate the required and disallowed features | 1148 | // For each linked object, validate the required and disallowed features |
| 846 | for (wasm.objects.items) |object| { | 1149 | for (wasm.objects.items) |object| { |
| 847 | var object_used_features = [_]bool{false} ** known_features_count; | 1150 | var object_used_features = [_]bool{false} ** known_features_count; |
| ... | @@ -850,7 +1153,7 @@ fn validateFeatures( | ... | @@ -850,7 +1153,7 @@ fn validateFeatures( |
| 850 | // from here a feature is always used | 1153 | // from here a feature is always used |
| 851 | const disallowed_feature = disallowed[@enumToInt(feature.tag)]; | 1154 | const disallowed_feature = disallowed[@enumToInt(feature.tag)]; |
| 852 | if (@truncate(u1, disallowed_feature) != 0) { | 1155 | if (@truncate(u1, disallowed_feature) != 0) { |
| 853 | log.err("feature '{s}' is disallowed, but used by linked object", .{feature.tag.toString()}); | 1156 | log.err("feature '{}' is disallowed, but used by linked object", .{feature.tag}); |
| 854 | log.err(" disallowed by '{s}'", .{wasm.objects.items[disallowed_feature >> 1].name}); | 1157 | log.err(" disallowed by '{s}'", .{wasm.objects.items[disallowed_feature >> 1].name}); |
| 855 | log.err(" used in '{s}'", .{object.name}); | 1158 | log.err(" used in '{s}'", .{object.name}); |
| 856 | valid_feature_set = false; | 1159 | valid_feature_set = false; |
| ... | @@ -863,7 +1166,7 @@ fn validateFeatures( | ... | @@ -863,7 +1166,7 @@ fn validateFeatures( |
| 863 | for (required, 0..) |required_feature, feature_index| { | 1166 | for (required, 0..) |required_feature, feature_index| { |
| 864 | const is_required = @truncate(u1, required_feature) != 0; | 1167 | const is_required = @truncate(u1, required_feature) != 0; |
| 865 | if (is_required and !object_used_features[feature_index]) { | 1168 | if (is_required and !object_used_features[feature_index]) { |
| 866 | log.err("feature '{s}' is required but not used in linked object", .{(@intToEnum(types.Feature.Tag, feature_index)).toString()}); | 1169 | log.err("feature '{}' is required but not used in linked object", .{@intToEnum(types.Feature.Tag, feature_index)}); |
| 867 | log.err(" required by '{s}'", .{wasm.objects.items[required_feature >> 1].name}); | 1170 | log.err(" required by '{s}'", .{wasm.objects.items[required_feature >> 1].name}); |
| 868 | log.err(" missing in '{s}'", .{object.name}); | 1171 | log.err(" missing in '{s}'", .{object.name}); |
| 869 | valid_feature_set = false; | 1172 | valid_feature_set = false; |
| ... | @@ -894,6 +1197,13 @@ fn resolveLazySymbols(wasm: *Wasm) !void { | ... | @@ -894,6 +1197,13 @@ fn resolveLazySymbols(wasm: *Wasm) !void { |
| 894 | try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); | 1197 | try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); |
| 895 | _ = wasm.resolved_symbols.swapRemove(loc); | 1198 | _ = wasm.resolved_symbols.swapRemove(loc); |
| 896 | } | 1199 | } |
| | 1200 | |
| | 1201 | if (!wasm.base.options.shared_memory) { |
| | 1202 | if (wasm.undefs.fetchSwapRemove("__tls_base")) |kv| { |
| | 1203 | const loc = try wasm.createSyntheticSymbol("__tls_base", .global); |
| | 1204 | try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); |
| | 1205 | } |
| | 1206 | } |
| 897 | } | 1207 | } |
| 898 | | 1208 | |
| 899 | // Tries to find a global symbol by its name. Returns null when not found, | 1209 | // Tries to find a global symbol by its name. Returns null when not found, |
| ... | @@ -1517,7 +1827,7 @@ fn mapFunctionTable(wasm: *Wasm) void { | ... | @@ -1517,7 +1827,7 @@ fn mapFunctionTable(wasm: *Wasm) void { |
| 1517 | const sym_loc = wasm.findGlobalSymbol("__indirect_function_table").?; | 1827 | const sym_loc = wasm.findGlobalSymbol("__indirect_function_table").?; |
| 1518 | const symbol = sym_loc.getSymbol(wasm); | 1828 | const symbol = sym_loc.getSymbol(wasm); |
| 1519 | const table = &wasm.tables.items[symbol.index - wasm.imported_tables_count]; | 1829 | const table = &wasm.tables.items[symbol.index - wasm.imported_tables_count]; |
| 1520 | table.limits = .{ .min = index, .max = index }; | 1830 | table.limits = .{ .min = index, .max = index, .flags = 0x1 }; |
| 1521 | } | 1831 | } |
| 1522 | } | 1832 | } |
| 1523 | | 1833 | |
| ... | @@ -1630,6 +1940,7 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void { | ... | @@ -1630,6 +1940,7 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void { |
| 1630 | .alignment = atom.alignment, | 1940 | .alignment = atom.alignment, |
| 1631 | .size = atom.size, | 1941 | .size = atom.size, |
| 1632 | .offset = 0, | 1942 | .offset = 0, |
| | 1943 | .flags = 0, |
| 1633 | }); | 1944 | }); |
| 1634 | } | 1945 | } |
| 1635 | | 1946 | |
| ... | @@ -1668,10 +1979,15 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void { | ... | @@ -1668,10 +1979,15 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void { |
| 1668 | break :result index; | 1979 | break :result index; |
| 1669 | } else { | 1980 | } else { |
| 1670 | const index = @intCast(u32, wasm.segments.items.len); | 1981 | const index = @intCast(u32, wasm.segments.items.len); |
| | 1982 | var flags: u32 = 0; |
| | 1983 | if (wasm.base.options.shared_memory) { |
| | 1984 | flags |= @enumToInt(Segment.Flag.WASM_DATA_SEGMENT_IS_PASSIVE); |
| | 1985 | } |
| 1671 | try wasm.segments.append(wasm.base.allocator, .{ | 1986 | try wasm.segments.append(wasm.base.allocator, .{ |
| 1672 | .alignment = atom.alignment, | 1987 | .alignment = atom.alignment, |
| 1673 | .size = 0, | 1988 | .size = 0, |
| 1674 | .offset = 0, | 1989 | .offset = 0, |
| | 1990 | .flags = flags, |
| 1675 | }); | 1991 | }); |
| 1676 | gop.value_ptr.* = index; | 1992 | gop.value_ptr.* = index; |
| 1677 | | 1993 | |
| ... | @@ -1907,10 +2223,23 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void { | ... | @@ -1907,10 +2223,23 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void { |
| 1907 | try writer.writeByte(std.wasm.opcode(.end)); | 2223 | try writer.writeByte(std.wasm.opcode(.end)); |
| 1908 | } | 2224 | } |
| 1909 | | 2225 | |
| 1910 | const loc = wasm.findGlobalSymbol("__wasm_call_ctors").?; | 2226 | try wasm.createSyntheticFunction( |
| | 2227 | "__wasm_call_ctors", |
| | 2228 | std.wasm.Type{ .params = &.{}, .returns = &.{} }, |
| | 2229 | &function_body, |
| | 2230 | ); |
| | 2231 | } |
| | 2232 | |
| | 2233 | fn createSyntheticFunction( |
| | 2234 | wasm: *Wasm, |
| | 2235 | symbol_name: []const u8, |
| | 2236 | func_ty: std.wasm.Type, |
| | 2237 | function_body: *std.ArrayList(u8), |
| | 2238 | ) !void { |
| | 2239 | const loc = wasm.findGlobalSymbol(symbol_name) orelse |
| | 2240 | try wasm.createSyntheticSymbol(symbol_name, .function); |
| 1911 | const symbol = loc.getSymbol(wasm); | 2241 | const symbol = loc.getSymbol(wasm); |
| 1912 | // create type (() -> nil) as we do not have any parameters or return value. | 2242 | const ty_index = try wasm.putOrGetFuncType(func_ty); |
| 1913 | const ty_index = try wasm.putOrGetFuncType(.{ .params = &[_]std.wasm.Valtype{}, .returns = &[_]std.wasm.Valtype{} }); | | |
| 1914 | // create function with above type | 2243 | // create function with above type |
| 1915 | const func_index = wasm.imported_functions_count + @intCast(u32, wasm.functions.count()); | 2244 | const func_index = wasm.imported_functions_count + @intCast(u32, wasm.functions.count()); |
| 1916 | try wasm.functions.putNoClobber( | 2245 | try wasm.functions.putNoClobber( |
| ... | @@ -1942,6 +2271,68 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void { | ... | @@ -1942,6 +2271,68 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void { |
| 1942 | atom.offset = prev_atom.offset + prev_atom.size; | 2271 | atom.offset = prev_atom.offset + prev_atom.size; |
| 1943 | } | 2272 | } |
| 1944 | | 2273 | |
| | 2274 | fn initializeTLSFunction(wasm: *Wasm) !void { |
| | 2275 | if (!wasm.base.options.shared_memory) return; |
| | 2276 | |
| | 2277 | var function_body = std.ArrayList(u8).init(wasm.base.allocator); |
| | 2278 | defer function_body.deinit(); |
| | 2279 | const writer = function_body.writer(); |
| | 2280 | |
| | 2281 | // locals |
| | 2282 | try writer.writeByte(0); |
| | 2283 | |
| | 2284 | // If there's a TLS segment, initialize it during runtime using the bulk-memory feature |
| | 2285 | if (wasm.data_segments.getIndex(".tdata")) |data_index| { |
| | 2286 | const segment_index = wasm.data_segments.entries.items(.value)[data_index]; |
| | 2287 | const segment = wasm.segments.items[segment_index]; |
| | 2288 | |
| | 2289 | const param_local: u32 = 0; |
| | 2290 | |
| | 2291 | try writer.writeByte(std.wasm.opcode(.local_get)); |
| | 2292 | try leb.writeULEB128(writer, param_local); |
| | 2293 | |
| | 2294 | const tls_base_loc = wasm.findGlobalSymbol("__tls_base").?; |
| | 2295 | try writer.writeByte(std.wasm.opcode(.global_get)); |
| | 2296 | try leb.writeULEB128(writer, tls_base_loc.getSymbol(wasm).index); |
| | 2297 | |
| | 2298 | // load stack values for the bulk-memory operation |
| | 2299 | { |
| | 2300 | try writer.writeByte(std.wasm.opcode(.local_get)); |
| | 2301 | try leb.writeULEB128(writer, param_local); |
| | 2302 | |
| | 2303 | try writer.writeByte(std.wasm.opcode(.i32_const)); |
| | 2304 | try leb.writeULEB128(writer, @as(u32, 0)); //segment offset |
| | 2305 | |
| | 2306 | try writer.writeByte(std.wasm.opcode(.i32_const)); |
| | 2307 | try leb.writeULEB128(writer, @as(u32, segment.size)); //segment offset |
| | 2308 | } |
| | 2309 | |
| | 2310 | // perform the bulk-memory operation to initialize the data segment |
| | 2311 | try writer.writeByte(std.wasm.opcode(.misc_prefix)); |
| | 2312 | try leb.writeULEB128(writer, std.wasm.miscOpcode(.memory_init)); |
| | 2313 | // segment immediate |
| | 2314 | try leb.writeULEB128(writer, @intCast(u32, data_index)); |
| | 2315 | // memory index immediate (always 0) |
| | 2316 | try leb.writeULEB128(writer, @as(u32, 0)); |
| | 2317 | } |
| | 2318 | |
| | 2319 | // If we have to perform any TLS relocations, call the corresponding function |
| | 2320 | // which performs all runtime TLS relocations. This is a synthetic function, |
| | 2321 | // generated by the linker. |
| | 2322 | if (wasm.findGlobalSymbol("__wasm_apply_global_tls_relocs")) |loc| { |
| | 2323 | try writer.writeByte(std.wasm.opcode(.call)); |
| | 2324 | try leb.writeULEB128(writer, loc.getSymbol(wasm).index); |
| | 2325 | } |
| | 2326 | |
| | 2327 | try writer.writeByte(std.wasm.opcode(.end)); |
| | 2328 | |
| | 2329 | try wasm.createSyntheticFunction( |
| | 2330 | "__wasm_init_tls", |
| | 2331 | std.wasm.Type{ .params = &.{.i32}, .returns = &.{} }, |
| | 2332 | &function_body, |
| | 2333 | ); |
| | 2334 | } |
| | 2335 | |
| 1945 | fn setupImports(wasm: *Wasm) !void { | 2336 | fn setupImports(wasm: *Wasm) !void { |
| 1946 | log.debug("Merging imports", .{}); | 2337 | log.debug("Merging imports", .{}); |
| 1947 | var discarded_it = wasm.discarded.keyIterator(); | 2338 | var discarded_it = wasm.discarded.keyIterator(); |
| ... | @@ -2224,11 +2615,50 @@ fn setupMemory(wasm: *Wasm) !void { | ... | @@ -2224,11 +2615,50 @@ fn setupMemory(wasm: *Wasm) !void { |
| 2224 | while (data_seg_it.next()) |entry| { | 2615 | while (data_seg_it.next()) |entry| { |
| 2225 | const segment = &wasm.segments.items[entry.value_ptr.*]; | 2616 | const segment = &wasm.segments.items[entry.value_ptr.*]; |
| 2226 | memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, segment.alignment); | 2617 | memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, segment.alignment); |
| | 2618 | |
| | 2619 | // set TLS-related symbols |
| | 2620 | if (mem.eql(u8, entry.key_ptr.*, ".tdata")) { |
| | 2621 | if (wasm.findGlobalSymbol("__tls_size")) |loc| { |
| | 2622 | const sym = loc.getSymbol(wasm); |
| | 2623 | sym.index = @intCast(u32, wasm.wasm_globals.items.len) + wasm.imported_globals_count; |
| | 2624 | try wasm.wasm_globals.append(wasm.base.allocator, .{ |
| | 2625 | .global_type = .{ .valtype = .i32, .mutable = false }, |
| | 2626 | .init = .{ .i32_const = @intCast(i32, segment.size) }, |
| | 2627 | }); |
| | 2628 | } |
| | 2629 | if (wasm.findGlobalSymbol("__tls_align")) |loc| { |
| | 2630 | const sym = loc.getSymbol(wasm); |
| | 2631 | sym.index = @intCast(u32, wasm.wasm_globals.items.len) + wasm.imported_globals_count; |
| | 2632 | try wasm.wasm_globals.append(wasm.base.allocator, .{ |
| | 2633 | .global_type = .{ .valtype = .i32, .mutable = false }, |
| | 2634 | .init = .{ .i32_const = @intCast(i32, segment.alignment) }, |
| | 2635 | }); |
| | 2636 | } |
| | 2637 | if (wasm.findGlobalSymbol("__tls_base")) |loc| { |
| | 2638 | const sym = loc.getSymbol(wasm); |
| | 2639 | sym.index = @intCast(u32, wasm.wasm_globals.items.len) + wasm.imported_globals_count; |
| | 2640 | try wasm.wasm_globals.append(wasm.base.allocator, .{ |
| | 2641 | .global_type = .{ .valtype = .i32, .mutable = wasm.base.options.shared_memory }, |
| | 2642 | .init = .{ .i32_const = if (wasm.base.options.shared_memory) @as(u32, 0) else @intCast(i32, memory_ptr) }, |
| | 2643 | }); |
| | 2644 | } |
| | 2645 | } |
| | 2646 | |
| 2227 | memory_ptr += segment.size; | 2647 | memory_ptr += segment.size; |
| 2228 | segment.offset = offset; | 2648 | segment.offset = offset; |
| 2229 | offset += segment.size; | 2649 | offset += segment.size; |
| 2230 | } | 2650 | } |
| 2231 | | 2651 | |
| | 2652 | // create the memory init flag which is used by the init memory function |
| | 2653 | if (wasm.base.options.shared_memory and wasm.hasPassiveInitializationSegments()) { |
| | 2654 | // align to pointer size |
| | 2655 | memory_ptr = mem.alignForwardGeneric(u64, memory_ptr, 4); |
| | 2656 | const loc = try wasm.createSyntheticSymbol("__wasm_init_memory_flag", .data); |
| | 2657 | const sym = loc.getSymbol(wasm); |
| | 2658 | sym.virtual_address = @intCast(u32, memory_ptr); |
| | 2659 | memory_ptr += 4; |
| | 2660 | } |
| | 2661 | |
| 2232 | if (!place_stack_first and !is_obj) { | 2662 | if (!place_stack_first and !is_obj) { |
| 2233 | memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, stack_alignment); | 2663 | memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, stack_alignment); |
| 2234 | memory_ptr += stack_size; | 2664 | memory_ptr += stack_size; |
| ... | @@ -2286,6 +2716,10 @@ fn setupMemory(wasm: *Wasm) !void { | ... | @@ -2286,6 +2716,10 @@ fn setupMemory(wasm: *Wasm) !void { |
| 2286 | return error.MemoryTooBig; | 2716 | return error.MemoryTooBig; |
| 2287 | } | 2717 | } |
| 2288 | wasm.memories.limits.max = @intCast(u32, max_memory / page_size); | 2718 | wasm.memories.limits.max = @intCast(u32, max_memory / page_size); |
| | 2719 | wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_HAS_MAX); |
| | 2720 | if (wasm.base.options.shared_memory) { |
| | 2721 | wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_IS_SHARED); |
| | 2722 | } |
| 2289 | log.debug("Maximum memory pages: {?d}", .{wasm.memories.limits.max}); | 2723 | log.debug("Maximum memory pages: {?d}", .{wasm.memories.limits.max}); |
| 2290 | } | 2724 | } |
| 2291 | } | 2725 | } |
| ... | @@ -2305,7 +2739,16 @@ pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, relocatable_index: u32 | ... | @@ -2305,7 +2739,16 @@ pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, relocatable_index: u32 |
| 2305 | const result = try wasm.data_segments.getOrPut(wasm.base.allocator, segment_info.outputName(merge_segment)); | 2739 | const result = try wasm.data_segments.getOrPut(wasm.base.allocator, segment_info.outputName(merge_segment)); |
| 2306 | if (!result.found_existing) { | 2740 | if (!result.found_existing) { |
| 2307 | result.value_ptr.* = index; | 2741 | result.value_ptr.* = index; |
| 2308 | try wasm.appendDummySegment(); | 2742 | var flags: u32 = 0; |
| | 2743 | if (wasm.base.options.shared_memory) { |
| | 2744 | flags |= @enumToInt(Segment.Flag.WASM_DATA_SEGMENT_IS_PASSIVE); |
| | 2745 | } |
| | 2746 | try wasm.segments.append(wasm.base.allocator, .{ |
| | 2747 | .alignment = 1, |
| | 2748 | .size = 0, |
| | 2749 | .offset = 0, |
| | 2750 | .flags = flags, |
| | 2751 | }); |
| 2309 | return index; | 2752 | return index; |
| 2310 | } else return result.value_ptr.*; | 2753 | } else return result.value_ptr.*; |
| 2311 | }, | 2754 | }, |
| ... | @@ -2379,6 +2822,7 @@ fn appendDummySegment(wasm: *Wasm) !void { | ... | @@ -2379,6 +2822,7 @@ fn appendDummySegment(wasm: *Wasm) !void { |
| 2379 | .alignment = 1, | 2822 | .alignment = 1, |
| 2380 | .size = 0, | 2823 | .size = 0, |
| 2381 | .offset = 0, | 2824 | .offset = 0, |
| | 2825 | .flags = 0, |
| 2382 | }); | 2826 | }); |
| 2383 | } | 2827 | } |
| 2384 | | 2828 | |
| ... | @@ -2746,6 +3190,9 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l | ... | @@ -2746,6 +3190,9 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l |
| 2746 | try wasm.mergeSections(); | 3190 | try wasm.mergeSections(); |
| 2747 | try wasm.mergeTypes(); | 3191 | try wasm.mergeTypes(); |
| 2748 | try wasm.initializeCallCtorsFunction(); | 3192 | try wasm.initializeCallCtorsFunction(); |
| | 3193 | try wasm.setupInitMemoryFunction(); |
| | 3194 | try wasm.setupTLSRelocationsFunction(); |
| | 3195 | try wasm.initializeTLSFunction(); |
| 2749 | try wasm.setupExports(); | 3196 | try wasm.setupExports(); |
| 2750 | try wasm.writeToFile(enabled_features, emit_features_count, arena); | 3197 | try wasm.writeToFile(enabled_features, emit_features_count, arena); |
| 2751 | | 3198 | |
| ... | @@ -2874,6 +3321,9 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod | ... | @@ -2874,6 +3321,9 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2874 | try wasm.mergeSections(); | 3321 | try wasm.mergeSections(); |
| 2875 | try wasm.mergeTypes(); | 3322 | try wasm.mergeTypes(); |
| 2876 | try wasm.initializeCallCtorsFunction(); | 3323 | try wasm.initializeCallCtorsFunction(); |
| | 3324 | try wasm.setupInitMemoryFunction(); |
| | 3325 | try wasm.setupTLSRelocationsFunction(); |
| | 3326 | try wasm.initializeTLSFunction(); |
| 2877 | try wasm.setupExports(); | 3327 | try wasm.setupExports(); |
| 2878 | try wasm.writeToFile(enabled_features, emit_features_count, arena); | 3328 | try wasm.writeToFile(enabled_features, emit_features_count, arena); |
| 2879 | } | 3329 | } |
| ... | @@ -3096,6 +3546,19 @@ fn writeToFile( | ... | @@ -3096,6 +3546,19 @@ fn writeToFile( |
| 3096 | section_count += 1; | 3546 | section_count += 1; |
| 3097 | } | 3547 | } |
| 3098 | | 3548 | |
| | 3549 | // When the shared-memory option is enabled, we *must* emit the 'data count' section. |
| | 3550 | const data_segments_count = wasm.data_segments.count() - @boolToInt(wasm.data_segments.contains(".bss") and import_memory); |
| | 3551 | if (data_segments_count != 0 and wasm.base.options.shared_memory) { |
| | 3552 | const header_offset = try reserveVecSectionHeader(&binary_bytes); |
| | 3553 | try writeVecSectionHeader( |
| | 3554 | binary_bytes.items, |
| | 3555 | header_offset, |
| | 3556 | .data_count, |
| | 3557 | @intCast(u32, binary_bytes.items.len - header_offset - header_size), |
| | 3558 | @intCast(u32, data_segments_count), |
| | 3559 | ); |
| | 3560 | } |
| | 3561 | |
| 3099 | // Code section | 3562 | // Code section |
| 3100 | var code_section_size: u32 = 0; | 3563 | var code_section_size: u32 = 0; |
| 3101 | if (wasm.code_section_index) |code_index| { | 3564 | if (wasm.code_section_index) |code_index| { |
| ... | @@ -3146,7 +3609,7 @@ fn writeToFile( | ... | @@ -3146,7 +3609,7 @@ fn writeToFile( |
| 3146 | } | 3609 | } |
| 3147 | | 3610 | |
| 3148 | // Data section | 3611 | // Data section |
| 3149 | if (wasm.data_segments.count() != 0) { | 3612 | if (data_segments_count != 0) { |
| 3150 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | 3613 | const header_offset = try reserveVecSectionHeader(&binary_bytes); |
| 3151 | | 3614 | |
| 3152 | var it = wasm.data_segments.iterator(); | 3615 | var it = wasm.data_segments.iterator(); |
| ... | @@ -3161,10 +3624,15 @@ fn writeToFile( | ... | @@ -3161,10 +3624,15 @@ fn writeToFile( |
| 3161 | segment_count += 1; | 3624 | segment_count += 1; |
| 3162 | var atom_index = wasm.atoms.get(segment_index).?; | 3625 | var atom_index = wasm.atoms.get(segment_index).?; |
| 3163 | | 3626 | |
| 3164 | // flag and index to memory section (currently, there can only be 1 memory section in wasm) | 3627 | try leb.writeULEB128(binary_writer, segment.flags); |
| 3165 | try leb.writeULEB128(binary_writer, @as(u32, 0)); | 3628 | if (segment.flags & @enumToInt(Wasm.Segment.Flag.WASM_DATA_SEGMENT_HAS_MEMINDEX) != 0) { |
| | 3629 | try leb.writeULEB128(binary_writer, @as(u32, 0)); // memory is always index 0 as we only have 1 memory entry |
| | 3630 | } |
| | 3631 | // when a segment is passive, it's initialized during runtime. |
| | 3632 | if (!segment.isPassive()) { |
| | 3633 | try emitInit(binary_writer, .{ .i32_const = @bitCast(i32, segment.offset) }); |
| | 3634 | } |
| 3166 | // offset into data section | 3635 | // offset into data section |
| 3167 | try emitInit(binary_writer, .{ .i32_const = @bitCast(i32, segment.offset) }); | | |
| 3168 | try leb.writeULEB128(binary_writer, segment.size); | 3636 | try leb.writeULEB128(binary_writer, segment.size); |
| 3169 | | 3637 | |
| 3170 | // fill in the offset table and the data segments | 3638 | // fill in the offset table and the data segments |
| ... | @@ -3413,7 +3881,8 @@ fn emitFeaturesSection(binary_bytes: *std.ArrayList(u8), enabled_features: []con | ... | @@ -3413,7 +3881,8 @@ fn emitFeaturesSection(binary_bytes: *std.ArrayList(u8), enabled_features: []con |
| 3413 | if (enabled) { | 3881 | if (enabled) { |
| 3414 | const feature: types.Feature = .{ .prefix = .used, .tag = @intToEnum(types.Feature.Tag, feature_index) }; | 3882 | const feature: types.Feature = .{ .prefix = .used, .tag = @intToEnum(types.Feature.Tag, feature_index) }; |
| 3415 | try leb.writeULEB128(writer, @enumToInt(feature.prefix)); | 3883 | try leb.writeULEB128(writer, @enumToInt(feature.prefix)); |
| 3416 | const string = feature.tag.toString(); | 3884 | var buf: [100]u8 = undefined; |
| | 3885 | const string = try std.fmt.bufPrint(&buf, "{}", .{feature.tag}); |
| 3417 | try leb.writeULEB128(writer, @intCast(u32, string.len)); | 3886 | try leb.writeULEB128(writer, @intCast(u32, string.len)); |
| 3418 | try writer.writeAll(string); | 3887 | try writer.writeAll(string); |
| 3419 | } | 3888 | } |
| ... | @@ -3507,10 +3976,10 @@ fn emitNameSubsection(wasm: *Wasm, section_id: std.wasm.NameSubsection, names: a | ... | @@ -3507,10 +3976,10 @@ fn emitNameSubsection(wasm: *Wasm, section_id: std.wasm.NameSubsection, names: a |
| 3507 | } | 3976 | } |
| 3508 | | 3977 | |
| 3509 | fn emitLimits(writer: anytype, limits: std.wasm.Limits) !void { | 3978 | fn emitLimits(writer: anytype, limits: std.wasm.Limits) !void { |
| 3510 | try leb.writeULEB128(writer, @boolToInt(limits.max != null)); | 3979 | try writer.writeByte(limits.flags); |
| 3511 | try leb.writeULEB128(writer, limits.min); | 3980 | try leb.writeULEB128(writer, limits.min); |
| 3512 | if (limits.max) |max| { | 3981 | if (limits.hasFlag(.WASM_LIMITS_FLAG_HAS_MAX)) { |
| 3513 | try leb.writeULEB128(writer, max); | 3982 | try leb.writeULEB128(writer, limits.max); |
| 3514 | } | 3983 | } |
| 3515 | } | 3984 | } |
| 3516 | | 3985 | |
| ... | @@ -4205,6 +4674,17 @@ fn emitDataRelocations( | ... | @@ -4205,6 +4674,17 @@ fn emitDataRelocations( |
| 4205 | try writeCustomSectionHeader(binary_bytes.items, header_offset, size); | 4674 | try writeCustomSectionHeader(binary_bytes.items, header_offset, size); |
| 4206 | } | 4675 | } |
| 4207 | | 4676 | |
| | 4677 | fn hasPassiveInitializationSegments(wasm: *const Wasm) bool { |
| | 4678 | var it = wasm.data_segments.iterator(); |
| | 4679 | while (it.next()) |entry| { |
| | 4680 | const segment: Segment = wasm.segments.items[entry.value_ptr.*]; |
| | 4681 | if (segment.needsPassiveInitialization(wasm.base.options.import_memory, entry.key_ptr.*)) { |
| | 4682 | return true; |
| | 4683 | } |
| | 4684 | } |
| | 4685 | return false; |
| | 4686 | } |
| | 4687 | |
| 4208 | pub fn getTypeIndex(wasm: *const Wasm, func_type: std.wasm.Type) ?u32 { | 4688 | pub fn getTypeIndex(wasm: *const Wasm, func_type: std.wasm.Type) ?u32 { |
| 4209 | var index: u32 = 0; | 4689 | var index: u32 = 0; |
| 4210 | while (index < wasm.func_types.items.len) : (index += 1) { | 4690 | while (index < wasm.func_types.items.len) : (index += 1) { |