authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-03-13 19:30:33+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-03-18 20:13:29+01:00
logfb9d3cd50e9a6d112277d7de158ef857162c01d9
treee6ea2bd7be54d3cedf03b9fc4a27f47b3a970d5d
parent09abd53da701a5ef4db4b81463e2535e192a5eee
signaturelock-open Commit is signed but in an unrecognized format.

wasm-linker: feature verifiction for shared-mem

When the user enables shared-memory, we must ensure the linked objects have the 'atomics' and 'bulk-memory' features allowed.

3 files changed, 47 insertions(+), 10 deletions(-)

src/link/Wasm.zig+37-4
...@@ -795,6 +795,8 @@ fn validateFeatures(...@@ -795,6 +795,8 @@ fn validateFeatures(
795795
796 // when false, we fail linking. We only verify this after a loop to catch all invalid features.796 // when false, we fail linking. We only verify this after a loop to catch all invalid features.
797 var valid_feature_set = true;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;
798800
799 // When the user has given an explicit list of features to enable,801 // When the user has given an explicit list of features to enable,
800 // we extract them and insert each into the 'allowed' list.802 // we extract them and insert each into the 'allowed' list.
...@@ -825,6 +827,12 @@ fn validateFeatures(...@@ -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 }
829837
830 // when we infer the features, we allow each feature found in the 'used' set838 // when we infer the features, we allow each feature found in the 'used' set
...@@ -836,7 +844,7 @@ fn validateFeatures(...@@ -836,7 +844,7 @@ fn validateFeatures(
836 allowed[used_index] = is_enabled;844 allowed[used_index] = is_enabled;
837 emit_features_count.* += @boolToInt(is_enabled);845 emit_features_count.* += @boolToInt(is_enabled);
838 } else if (is_enabled and !allowed[used_index]) {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 log.err(" defined in '{s}'", .{wasm.objects.items[used_set >> 1].name});848 log.err(" defined in '{s}'", .{wasm.objects.items[used_set >> 1].name});
841 valid_feature_set = false;849 valid_feature_set = false;
842 }850 }
...@@ -846,6 +854,30 @@ fn validateFeatures(...@@ -846,6 +854,30 @@ fn validateFeatures(
846 return error.InvalidFeatureSet;854 return error.InvalidFeatureSet;
847 }855 }
848856
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 // For each linked object, validate the required and disallowed features881 // For each linked object, validate the required and disallowed features
850 for (wasm.objects.items) |object| {882 for (wasm.objects.items) |object| {
851 var object_used_features = [_]bool{false} ** known_features_count;883 var object_used_features = [_]bool{false} ** known_features_count;
...@@ -854,7 +886,7 @@ fn validateFeatures(...@@ -854,7 +886,7 @@ fn validateFeatures(
854 // from here a feature is always used886 // from here a feature is always used
855 const disallowed_feature = disallowed[@enumToInt(feature.tag)];887 const disallowed_feature = disallowed[@enumToInt(feature.tag)];
856 if (@truncate(u1, disallowed_feature) != 0) {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 log.err(" disallowed by '{s}'", .{wasm.objects.items[disallowed_feature >> 1].name});890 log.err(" disallowed by '{s}'", .{wasm.objects.items[disallowed_feature >> 1].name});
859 log.err(" used in '{s}'", .{object.name});891 log.err(" used in '{s}'", .{object.name});
860 valid_feature_set = false;892 valid_feature_set = false;
...@@ -867,7 +899,7 @@ fn validateFeatures(...@@ -867,7 +899,7 @@ fn validateFeatures(
867 for (required, 0..) |required_feature, feature_index| {899 for (required, 0..) |required_feature, feature_index| {
868 const is_required = @truncate(u1, required_feature) != 0;900 const is_required = @truncate(u1, required_feature) != 0;
869 if (is_required and !object_used_features[feature_index]) {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 log.err(" required by '{s}'", .{wasm.objects.items[required_feature >> 1].name});903 log.err(" required by '{s}'", .{wasm.objects.items[required_feature >> 1].name});
872 log.err(" missing in '{s}'", .{object.name});904 log.err(" missing in '{s}'", .{object.name});
873 valid_feature_set = false;905 valid_feature_set = false;
...@@ -3432,7 +3464,8 @@ fn emitFeaturesSection(binary_bytes: *std.ArrayList(u8), enabled_features: []con...@@ -3432,7 +3464,8 @@ fn emitFeaturesSection(binary_bytes: *std.ArrayList(u8), enabled_features: []con
3432 if (enabled) {3464 if (enabled) {
3433 const feature: types.Feature = .{ .prefix = .used, .tag = @intToEnum(types.Feature.Tag, feature_index) };3465 const feature: types.Feature = .{ .prefix = .used, .tag = @intToEnum(types.Feature.Tag, feature_index) };
3434 try leb.writeULEB128(writer, @enumToInt(feature.prefix));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 try leb.writeULEB128(writer, @intCast(u32, string.len));3469 try leb.writeULEB128(writer, @intCast(u32, string.len));
3437 try writer.writeAll(string);3470 try writer.writeAll(string);
3438 }3471 }
src/link/Wasm/Object.zig+2-2
...@@ -601,8 +601,8 @@ fn Parser(comptime ReaderType: type) type {...@@ -601,8 +601,8 @@ fn Parser(comptime ReaderType: type) type {
601 });601 });
602602
603 for (relocations) |*relocation| {603 for (relocations) |*relocation| {
604 const rel_type = try leb.readULEB128(u8, reader);604 const rel_type = try reader.readByte();
605 const rel_type_enum = @intToEnum(types.Relocation.RelocationType, rel_type);605 const rel_type_enum = std.meta.intToEnum(types.Relocation.RelocationType, rel_type) catch return error.MalformedSection;
606 relocation.* = .{606 relocation.* = .{
607 .relocation_type = rel_type_enum,607 .relocation_type = rel_type_enum,
608 .offset = try leb.readULEB128(u32, reader),608 .offset = try leb.readULEB128(u32, reader),
src/link/Wasm/types.zig+8-4
...@@ -50,6 +50,8 @@ pub const Relocation = struct {...@@ -50,6 +50,8 @@ pub const Relocation = struct {
50 .R_WASM_MEMORY_ADDR_LEB64,50 .R_WASM_MEMORY_ADDR_LEB64,
51 .R_WASM_MEMORY_ADDR_SLEB64,51 .R_WASM_MEMORY_ADDR_SLEB64,
52 .R_WASM_MEMORY_ADDR_I64,52 .R_WASM_MEMORY_ADDR_I64,
53 .R_WASM_MEMORY_ADDR_TLS_SLEB,
54 .R_WASM_MEMORY_ADDR_TLS_SLEB64,
53 .R_WASM_FUNCTION_OFFSET_I32,55 .R_WASM_FUNCTION_OFFSET_I32,
54 .R_WASM_SECTION_OFFSET_I32,56 .R_WASM_SECTION_OFFSET_I32,
55 => true,57 => true,
...@@ -218,8 +220,10 @@ pub const Feature = struct {...@@ -218,8 +220,10 @@ pub const Feature = struct {
218 return @intToEnum(Tag, @enumToInt(feature));220 return @intToEnum(Tag, @enumToInt(feature));
219 }221 }
220222
221 pub fn toString(tag: Tag) []const u8 {223 pub fn format(tag: Tag, comptime fmt: []const u8, opt: std.fmt.FormatOptions, writer: anytype) !void {
222 return switch (tag) {224 _ = fmt;
225 _ = opt;
226 try writer.writeAll(switch (tag) {
223 .atomics => "atomics",227 .atomics => "atomics",
224 .bulk_memory => "bulk-memory",228 .bulk_memory => "bulk-memory",
225 .exception_handling => "exception-handling",229 .exception_handling => "exception-handling",
...@@ -233,7 +237,7 @@ pub const Feature = struct {...@@ -233,7 +237,7 @@ pub const Feature = struct {
233 .simd128 => "simd128",237 .simd128 => "simd128",
234 .tail_call => "tail-call",238 .tail_call => "tail-call",
235 .shared_mem => "shared-mem",239 .shared_mem => "shared-mem",
236 };240 });
237 }241 }
238 };242 };
239243
...@@ -246,7 +250,7 @@ pub const Feature = struct {...@@ -246,7 +250,7 @@ pub const Feature = struct {
246 pub fn format(feature: Feature, comptime fmt: []const u8, opt: std.fmt.FormatOptions, writer: anytype) !void {250 pub fn format(feature: Feature, comptime fmt: []const u8, opt: std.fmt.FormatOptions, writer: anytype) !void {
247 _ = opt;251 _ = opt;
248 _ = fmt;252 _ = fmt;
249 try writer.print("{c} {s}", .{ feature.prefix, feature.tag.toString() });253 try writer.print("{c} {}", .{ feature.prefix, feature.tag });
250 }254 }
251};255};
252256