| ... | @@ -651,6 +651,112 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void { | ... | @@ -651,6 +651,112 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void { |
| 651 | } | 651 | } |
| 652 | } | 652 | } |
| 653 | | 653 | |
| | 654 | fn validateFeatures(wasm: *const Wasm, arena: Allocator) !void { |
| | 655 | const cpu_features = wasm.base.options.target.cpu.features; |
| | 656 | const infer = cpu_features.isEmpty(); // when the user did not define any features, we infer them from linked objects. |
| | 657 | var allowed = std.AutoHashMap(std.Target.wasm.Feature, void).init(arena); |
| | 658 | var used = std.AutoArrayHashMap(std.Target.wasm.Feature, []const u8).init(arena); |
| | 659 | var disallowed = std.AutoHashMap(std.Target.wasm.Feature, []const u8).init(arena); |
| | 660 | var required = std.AutoHashMap(std.Target.wasm.Feature, []const u8).init(arena); |
| | 661 | |
| | 662 | // when false, we fail linking. We only verify this after a loop to catch all invalid features. |
| | 663 | var valid_feature_set = true; |
| | 664 | |
| | 665 | // When the user has given an explicit list of features to enable, |
| | 666 | // we extract them and insert each into the 'allowed' list. |
| | 667 | if (!infer) { |
| | 668 | try allowed.ensureUnusedCapacity(std.Target.wasm.all_features.len); |
| | 669 | // std.builtin.Type.EnumField |
| | 670 | inline for (@typeInfo(std.Target.wasm.Feature).Enum.fields) |feature_field| { |
| | 671 | if (cpu_features.isEnabled(feature_field.value)) { |
| | 672 | allowed.putAssumeCapacityNoClobber(@intToEnum(std.Target.wasm.Feature, feature_field.value), {}); |
| | 673 | } |
| | 674 | } |
| | 675 | } |
| | 676 | |
| | 677 | // extract all the used, disallowed and required features from each |
| | 678 | // linked object file so we can test them. |
| | 679 | for (wasm.objects.items) |object| { |
| | 680 | for (object.features) |feature| { |
| | 681 | switch (feature.prefix) { |
| | 682 | .used => { |
| | 683 | const gop = try used.getOrPut(feature.tag); |
| | 684 | if (!gop.found_existing) { |
| | 685 | gop.value_ptr.* = object.name; |
| | 686 | } |
| | 687 | }, |
| | 688 | .disallowed => { |
| | 689 | const gop = try disallowed.getOrPut(feature.tag); |
| | 690 | if (!gop.found_existing) { |
| | 691 | gop.value_ptr.* = object.name; |
| | 692 | } |
| | 693 | }, |
| | 694 | .required => { |
| | 695 | const gop = try required.getOrPut(feature.tag); |
| | 696 | if (!gop.found_existing) { |
| | 697 | gop.value_ptr.* = object.name; |
| | 698 | } |
| | 699 | const used_gop = try used.getOrPut(feature.tag); |
| | 700 | if (!used_gop.found_existing) { |
| | 701 | used_gop.value_ptr.* = object.name; |
| | 702 | } |
| | 703 | }, |
| | 704 | } |
| | 705 | } |
| | 706 | } |
| | 707 | |
| | 708 | // when we infer the features, we allow each feature found in the 'used' set |
| | 709 | // and insert it into the 'allowed' set. When features are not inferred, |
| | 710 | // we validate that a used feature is allowed. |
| | 711 | if (infer) try allowed.ensureUnusedCapacity(@intCast(u32, used.count())); |
| | 712 | for (used.keys()) |used_feature, used_index| { |
| | 713 | if (infer) { |
| | 714 | allowed.putAssumeCapacityNoClobber(used_feature, {}); |
| | 715 | } else if (!allowed.contains(used_feature)) { |
| | 716 | log.err("feature '{s}' not allowed, but used by linked object", .{@tagName(used_feature)}); |
| | 717 | log.err(" defined in '{s}'", .{used.values()[used_index]}); |
| | 718 | valid_feature_set = false; |
| | 719 | } |
| | 720 | } |
| | 721 | |
| | 722 | if (!valid_feature_set) { |
| | 723 | return error.InvalidFeatureSet; |
| | 724 | } |
| | 725 | |
| | 726 | // For each linked object, validate the required and disallowed features |
| | 727 | for (wasm.objects.items) |object| { |
| | 728 | var object_used_features = std.AutoHashMap(std.Target.wasm.Feature, void).init(arena); |
| | 729 | try object_used_features.ensureTotalCapacity(@intCast(u32, object.features.len)); |
| | 730 | for (object.features) |feature| { |
| | 731 | if (feature.prefix == .disallowed) continue; // already defined in 'disallowed' set. |
| | 732 | // from here a feature is always used |
| | 733 | if (disallowed.get(feature.tag)) |disallowed_object_name| { |
| | 734 | log.err("feature '{s}' is disallowed, but used by linked object", .{@tagName(feature.tag)}); |
| | 735 | log.err(" disallowed by '{s}'", .{disallowed_object_name}); |
| | 736 | log.err(" used in '{s}'", .{object.name}); |
| | 737 | valid_feature_set = false; |
| | 738 | } |
| | 739 | |
| | 740 | object_used_features.putAssumeCapacity(feature.tag, {}); |
| | 741 | } |
| | 742 | |
| | 743 | // validate the linked object file has each required feature |
| | 744 | var required_it = required.iterator(); |
| | 745 | while (required_it.next()) |required_feature| { |
| | 746 | if (!object_used_features.contains(required_feature.key_ptr.*)) { |
| | 747 | log.err("feature '{s}' is required but not used in linked object", .{@tagName(required_feature.key_ptr.*)}); |
| | 748 | log.err(" required by '{s}'", .{required_feature.value_ptr.*}); |
| | 749 | log.err(" missing in '{s}'", .{object.name}); |
| | 750 | valid_feature_set = false; |
| | 751 | } |
| | 752 | } |
| | 753 | } |
| | 754 | |
| | 755 | if (!valid_feature_set) { |
| | 756 | return error.InvalidFeatureSet; |
| | 757 | } |
| | 758 | } |
| | 759 | |
| 654 | fn checkUndefinedSymbols(wasm: *const Wasm) !void { | 760 | fn checkUndefinedSymbols(wasm: *const Wasm) !void { |
| 655 | if (wasm.base.options.output_mode == .Obj) return; | 761 | if (wasm.base.options.output_mode == .Obj) return; |
| 656 | | 762 | |
| ... | @@ -2158,6 +2264,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod | ... | @@ -2158,6 +2264,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2158 | try wasm.resolveSymbolsInObject(@intCast(u16, object_index)); | 2264 | try wasm.resolveSymbolsInObject(@intCast(u16, object_index)); |
| 2159 | } | 2265 | } |
| 2160 | | 2266 | |
| | 2267 | try wasm.validateFeatures(arena); |
| 2161 | try wasm.resolveSymbolsInArchives(); | 2268 | try wasm.resolveSymbolsInArchives(); |
| 2162 | try wasm.checkUndefinedSymbols(); | 2269 | try wasm.checkUndefinedSymbols(); |
| 2163 | | 2270 | |