| ... | ... | @@ -795,6 +795,8 @@ fn validateFeatures( |
| 795 | 795 | |
| 796 | 796 | // when false, we fail linking. We only verify this after a loop to catch all invalid features. |
| 797 | 797 | var valid_feature_set = true; |
| 798 | // will be set to true when there's any TLS segment found in any of the object files |
| 799 | var has_tls = false; |
| 798 | 800 | |
| 799 | 801 | // When the user has given an explicit list of features to enable, |
| 800 | 802 | // we extract them and insert each into the 'allowed' list. |
| ... | ... | @@ -825,6 +827,12 @@ fn validateFeatures( |
| 825 | 827 | }, |
| 826 | 828 | } |
| 827 | 829 | } |
| 830 | |
| 831 | for (object.segment_info) |segment| { |
| 832 | if (segment.isTLS()) { |
| 833 | has_tls = true; |
| 834 | } |
| 835 | } |
| 828 | 836 | } |
| 829 | 837 | |
| 830 | 838 | // when we infer the features, we allow each feature found in the 'used' set |
| ... | ... | @@ -836,7 +844,7 @@ fn validateFeatures( |
| 836 | 844 | allowed[used_index] = is_enabled; |
| 837 | 845 | emit_features_count.* += @boolToInt(is_enabled); |
| 838 | 846 | } else if (is_enabled and !allowed[used_index]) { |
| 839 | | log.err("feature '{s}' not allowed, but used by linked object", .{(@intToEnum(types.Feature.Tag, used_index)).toString()}); |
| 847 | log.err("feature '{}' not allowed, but used by linked object", .{@intToEnum(types.Feature.Tag, used_index)}); |
| 840 | 848 | log.err(" defined in '{s}'", .{wasm.objects.items[used_set >> 1].name}); |
| 841 | 849 | valid_feature_set = false; |
| 842 | 850 | } |
| ... | ... | @@ -846,6 +854,30 @@ fn validateFeatures( |
| 846 | 854 | return error.InvalidFeatureSet; |
| 847 | 855 | } |
| 848 | 856 | |
| 857 | if (wasm.base.options.shared_memory) { |
| 858 | const disallowed_feature = disallowed[@enumToInt(types.Feature.Tag.shared_mem)]; |
| 859 | if (@truncate(u1, disallowed_feature) != 0) { |
| 860 | log.err( |
| 861 | "shared-memory is disallowed by '{s}' because it wasn't compiled with 'atomics' and 'bulk-memory' features enabled", |
| 862 | .{wasm.objects.items[disallowed_feature >> 1].name}, |
| 863 | ); |
| 864 | valid_feature_set = false; |
| 865 | } |
| 866 | |
| 867 | for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| { |
| 868 | if (!allowed[@enumToInt(feature)]) { |
| 869 | log.err("feature '{}' is not used but is required for shared-memory", .{feature}); |
| 870 | } |
| 871 | } |
| 872 | } |
| 873 | |
| 874 | if (has_tls) { |
| 875 | for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| { |
| 876 | if (!allowed[@enumToInt(feature)]) { |
| 877 | log.err("feature '{}' is not used but is required for thread-local storage", .{feature}); |
| 878 | } |
| 879 | } |
| 880 | } |
| 849 | 881 | // For each linked object, validate the required and disallowed features |
| 850 | 882 | for (wasm.objects.items) |object| { |
| 851 | 883 | var object_used_features = [_]bool{false} ** known_features_count; |
| ... | ... | @@ -854,7 +886,7 @@ fn validateFeatures( |
| 854 | 886 | // from here a feature is always used |
| 855 | 887 | const disallowed_feature = disallowed[@enumToInt(feature.tag)]; |
| 856 | 888 | if (@truncate(u1, disallowed_feature) != 0) { |
| 857 | | log.err("feature '{s}' is disallowed, but used by linked object", .{feature.tag.toString()}); |
| 889 | log.err("feature '{}' is disallowed, but used by linked object", .{feature.tag}); |
| 858 | 890 | log.err(" disallowed by '{s}'", .{wasm.objects.items[disallowed_feature >> 1].name}); |
| 859 | 891 | log.err(" used in '{s}'", .{object.name}); |
| 860 | 892 | valid_feature_set = false; |
| ... | ... | @@ -867,7 +899,7 @@ fn validateFeatures( |
| 867 | 899 | for (required, 0..) |required_feature, feature_index| { |
| 868 | 900 | const is_required = @truncate(u1, required_feature) != 0; |
| 869 | 901 | if (is_required and !object_used_features[feature_index]) { |
| 870 | | log.err("feature '{s}' is required but not used in linked object", .{(@intToEnum(types.Feature.Tag, feature_index)).toString()}); |
| 902 | log.err("feature '{}' is required but not used in linked object", .{@intToEnum(types.Feature.Tag, feature_index)}); |
| 871 | 903 | log.err(" required by '{s}'", .{wasm.objects.items[required_feature >> 1].name}); |
| 872 | 904 | log.err(" missing in '{s}'", .{object.name}); |
| 873 | 905 | valid_feature_set = false; |
| ... | ... | @@ -3432,7 +3464,8 @@ fn emitFeaturesSection(binary_bytes: *std.ArrayList(u8), enabled_features: []con |
| 3432 | 3464 | if (enabled) { |
| 3433 | 3465 | const feature: types.Feature = .{ .prefix = .used, .tag = @intToEnum(types.Feature.Tag, feature_index) }; |
| 3434 | 3466 | try leb.writeULEB128(writer, @enumToInt(feature.prefix)); |
| 3435 | | const string = feature.tag.toString(); |
| 3467 | var buf: [100]u8 = undefined; |
| 3468 | const string = try std.fmt.bufPrint(&buf, "{}", .{feature.tag}); |
| 3436 | 3469 | try leb.writeULEB128(writer, @intCast(u32, string.len)); |
| 3437 | 3470 | try writer.writeAll(string); |
| 3438 | 3471 | } |