From 5991328c96b6146999cfa74ede42901f8c01f2fa Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Fri, 22 Mar 2019 20:43:06 +0000 Subject: [PATCH] [WebAssembly] Add linker options to control feature checking Summary: Adds --check-features and --no-check-features. The default for now is to enable the checking, but this might change in the future. Also adds --features=foo,bar for precisely controlling the features used in the output binary. Depends on D59173. Reviewers: sbc100, aheejin Subscribers: dschuff, jgravelle-google, sunfish, jdoerfert, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D59274 llvm-svn: 356805 --- lld/test/wasm/target-feature-disallowed.yaml | 26 ++++++++++++++ lld/test/wasm/target-feature-none.yaml | 33 ++++++++++++++++++ lld/test/wasm/target-feature-required.yaml | 52 ++++++++++++++++++++++++++-- lld/test/wasm/target-feature-used.yaml | 41 +++++++++++++++++++++- lld/wasm/Config.h | 2 ++ lld/wasm/Driver.cpp | 10 ++++++ lld/wasm/Options.td | 7 ++++ lld/wasm/Writer.cpp | 36 ++++++++++++++++--- 8 files changed, 200 insertions(+), 7 deletions(-) create mode 100644 lld/test/wasm/target-feature-none.yaml diff --git a/lld/test/wasm/target-feature-disallowed.yaml b/lld/test/wasm/target-feature-disallowed.yaml index 687d86b..e9445ff 100644 --- a/lld/test/wasm/target-feature-disallowed.yaml +++ b/lld/test/wasm/target-feature-disallowed.yaml @@ -1,5 +1,9 @@ # RUN: yaml2obj %s -o %t1.o +# RUN: wasm-ld --no-entry --features=foo,bar,baz -o - %t1.o | obj2yaml | FileCheck %s --check-prefix SPECIFIED + +# RUN: wasm-ld --no-entry --features=bar,baz,quux -o - %t1.o | obj2yaml | FileCheck %s --check-prefix UNSPECIFIED + # RUN: yaml2obj %S/Inputs/disallow-feature-foo.yaml -o %t.disallowed.o # RUN: wasm-ld --no-entry -o %t.disallowed.exe %t1.o %t.disallowed.o # RUN: obj2yaml < %t.disallowed.exe | FileCheck %s --check-prefix DISALLOWED @@ -31,6 +35,28 @@ Sections: Name: "bar" ... +# SPECIFIED: - Type: CUSTOM +# SPECIFIED-NEXT: Name: target_features +# SPECIFIED-NEXT: Features: +# SPECIFIED-NEXT: - Prefix: USED +# SPECIFIED-NEXT: Name: bar +# SPECIFIED-NEXT: - Prefix: USED +# SPECIFIED-NEXT: Name: baz +# SPECIFIED-NEXT: - Prefix: USED +# SPECIFIED-NEXT: Name: foo +# SPECIFIED-NEXT: ... + +# UNSPECIFIED: - Type: CUSTOM +# UNSPECIFIED-NEXT: Name: target_features +# UNSPECIFIED-NEXT: Features: +# UNSPECIFIED-NEXT: - Prefix: USED +# UNSPECIFIED-NEXT: Name: bar +# UNSPECIFIED-NEXT: - Prefix: USED +# UNSPECIFIED-NEXT: Name: baz +# UNSPECIFIED-NEXT: - Prefix: USED +# UNSPECIFIED-NEXT: Name: quux +# UNSPECIFIED-NEXT: ... + # DISALLOWED: - Type: CUSTOM # DISALLOWED-NEXT: Name: target_features # DISALLOWED-NEXT: Features: diff --git a/lld/test/wasm/target-feature-none.yaml b/lld/test/wasm/target-feature-none.yaml new file mode 100644 index 0000000..82fc527 --- /dev/null +++ b/lld/test/wasm/target-feature-none.yaml @@ -0,0 +1,33 @@ +# RUN: yaml2obj %s -o %t1.o + +# RUN: wasm-ld --no-entry -o - %t1.o | obj2yaml | FileCheck %s --check-prefix EMPTY + +# RUN: wasm-ld --no-entry --features= -o - %t1.o | obj2yaml | FileCheck %s --check-prefix EMPTY + +# RUN: wasm-ld --no-entry --features=foo,bar,baz -o - %t1.o | obj2yaml | FileCheck %s --check-prefix SPECIFIED + +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: CUSTOM + Name: linking + Version: 2 + - Type: CUSTOM + Name: target_features + Features: [ ] +... + +# section is not emitted if it would be empty +# EMPTY-NOT: target_features + +# SPECIFIED: - Type: CUSTOM +# SPECIFIED-NEXT: Name: target_features +# SPECIFIED-NEXT: Features: +# SPECIFIED-NEXT: - Prefix: USED +# SPECIFIED-NEXT: Name: bar +# SPECIFIED-NEXT: - Prefix: USED +# SPECIFIED-NEXT: Name: baz +# SPECIFIED-NEXT: - Prefix: USED +# SPECIFIED-NEXT: Name: foo +# SPECIFIED-NEXT: ... diff --git a/lld/test/wasm/target-feature-required.yaml b/lld/test/wasm/target-feature-required.yaml index 59031d4..3982d7d 100644 --- a/lld/test/wasm/target-feature-required.yaml +++ b/lld/test/wasm/target-feature-required.yaml @@ -1,5 +1,11 @@ # RUN: yaml2obj %s -o %t1.o +# RUN: wasm-ld --no-entry --features=foo,bar,baz -o - %t1.o | obj2yaml | FileCheck %s --check-prefix SPECIFIED + +# RUN: not wasm-ld --no-entry --features=bar,baz,quux -o - %t1.o 2>&1 | FileCheck %s --check-prefix UNSPECIFIED + +# RUN: wasm-ld --no-entry --no-check-features --features=bar,baz,quux -o - %t1.o | obj2yaml | FileCheck %s --check-prefix UNSPECIFIED-NOCHECK + # RUN: yaml2obj %S/Inputs/require-feature-foo.yaml -o %t.required.o # RUN: wasm-ld --no-entry -o %t.required.exe %t1.o %t.required.o # RUN: obj2yaml < %t.required.exe | FileCheck %s --check-prefix REQUIRED @@ -7,9 +13,13 @@ # RUN: yaml2obj %S/Inputs/disallow-feature-foo.yaml -o %t.disallowed.o # RUN: not wasm-ld --no-entry -o /dev/null %t1.o %t.disallowed.o 2>&1 | FileCheck %s --check-prefix DISALLOWED +# RUN: wasm-ld --no-entry --no-check-features -o - %t1.o %t.disallowed.o | obj2yaml | FileCheck %s --check-prefix DISALLOWED-NOCHECK + # RUN: yaml2obj %S/Inputs/no-feature-foo.yaml -o %t.none.o # RUN: not wasm-ld --no-entry -o /dev/null %t1.o %t.none.o 2>&1 | FileCheck %s --check-prefix NONE +# RUN: wasm-ld --no-entry --no-check-features -o - %t1.o %t.none.o | obj2yaml | FileCheck %s --check-prefix NONE-NOCHECK + # Check that the following combinations of feature linkage policies # give the expected results: # @@ -31,6 +41,30 @@ Sections: Name: "foo" ... +# SPECIFIED: - Type: CUSTOM +# SPECIFIED-NEXT: Name: target_features +# SPECIFIED-NEXT: Features: +# SPECIFIED-NEXT: - Prefix: USED +# SPECIFIED-NEXT: Name: bar +# SPECIFIED-NEXT: - Prefix: USED +# SPECIFIED-NEXT: Name: baz +# SPECIFIED-NEXT: - Prefix: USED +# SPECIFIED-NEXT: Name: foo +# SPECIFIED-NEXT: ... + +# UNSPECIFIED: Target feature 'foo' is not allowed.{{$}} + +# UNSPECIFIED-NOCHECK: - Type: CUSTOM +# UNSPECIFIED-NOCHECK-NEXT: Name: target_features +# UNSPECIFIED-NOCHECK-NEXT: Features: +# UNSPECIFIED-NOCHECK-NEXT: - Prefix: USED +# UNSPECIFIED-NOCHECK-NEXT: Name: bar +# UNSPECIFIED-NOCHECK-NEXT: - Prefix: USED +# UNSPECIFIED-NOCHECK-NEXT: Name: baz +# UNSPECIFIED-NOCHECK-NEXT: - Prefix: USED +# UNSPECIFIED-NOCHECK-NEXT: Name: quux +# UNSPECIFIED-NOCHECK-NEXT: ... + # REQUIRED: - Type: CUSTOM # REQUIRED-NEXT: Name: target_features # REQUIRED-NEXT: Features: @@ -38,6 +72,20 @@ Sections: # REQUIRED-NEXT: Name: foo # REQUIRED-NEXT: ... -# DISALLOWED: Target feature "foo" is disallowed +# DISALLOWED: Target feature 'foo' is disallowed. Use --no-check-features to suppress.{{$}} + +# DISALLOWED-NOCHECK: - Type: CUSTOM +# DISALLOWED-NOCHECK-NEXT: Name: target_features +# DISALLOWED-NOCHECK-NEXT: Features: +# DISALLOWED-NOCHECK-NEXT: - Prefix: USED +# DISALLOWED-NOCHECK-NEXT: Name: foo +# DISALLOWED-NOCHECK-NEXT: ... + +# NONE: Missing required target feature 'foo'. Use --no-check-features to suppress.{{$}} -# NONE: Missing required target feature "foo" +# NONE-NOCHECK: - Type: CUSTOM +# NONE-NOCHECK-NEXT: Name: target_features +# NONE-NOCHECK-NEXT: Features: +# NONE-NOCHECK-NEXT: - Prefix: USED +# NONE-NOCHECK-NEXT: Name: foo +# NONE-NOCHECK-NEXT: ... diff --git a/lld/test/wasm/target-feature-used.yaml b/lld/test/wasm/target-feature-used.yaml index de121ff..7489088 100644 --- a/lld/test/wasm/target-feature-used.yaml +++ b/lld/test/wasm/target-feature-used.yaml @@ -1,5 +1,11 @@ # RUN: yaml2obj %s -o %t1.o +# RUN: wasm-ld --no-entry --features=foo,bar,baz -o - %t1.o | obj2yaml | FileCheck %s --check-prefix SPECIFIED + +# RUN: not wasm-ld --no-entry --features=bar,baz,quux -o - %t1.o 2>&1 | FileCheck %s --check-prefix UNSPECIFIED + +# RUN: wasm-ld --no-entry --no-check-features --features=bar,baz,quux -o - %t1.o | obj2yaml | FileCheck %s --check-prefix UNSPECIFIED-NOCHECK + # RUN: yaml2obj %S/Inputs/use-feature-foo.yaml -o %t.used.o # RUN: wasm-ld --no-entry -o %t.used.exe %t1.o %t.used.o # RUN: obj2yaml < %t.used.exe | FileCheck %s --check-prefix USED @@ -11,6 +17,8 @@ # RUN: yaml2obj %S/Inputs/disallow-feature-foo.yaml -o %t.disallowed.o # RUN: not wasm-ld --no-entry -o /dev/null %t1.o %t.disallowed.o 2>&1 | FileCheck %s --check-prefix DISALLOWED +# RUN: wasm-ld --no-entry --no-check-features -o - %t1.o %t.disallowed.o | obj2yaml | FileCheck %s --check-prefix DISALLOWED-NOCHECK + # RUN: yaml2obj %S/Inputs/no-feature-foo.yaml -o %t.none.o # RUN: wasm-ld --no-entry -o %t.none.exe %t1.o %t.none.o # RUN: obj2yaml %t.none.exe | FileCheck %s --check-prefix NONE @@ -37,6 +45,30 @@ Sections: Name: "foo" ... +# SPECIFIED: - Type: CUSTOM +# SPECIFIED-NEXT: Name: target_features +# SPECIFIED-NEXT: Features: +# SPECIFIED-NEXT: - Prefix: USED +# SPECIFIED-NEXT: Name: bar +# SPECIFIED-NEXT: - Prefix: USED +# SPECIFIED-NEXT: Name: baz +# SPECIFIED-NEXT: - Prefix: USED +# SPECIFIED-NEXT: Name: foo +# SPECIFIED-NEXT: ... + +# UNSPECIFIED: Target feature 'foo' is not allowed.{{$}} + +# UNSPECIFIED-NOCHECK: - Type: CUSTOM +# UNSPECIFIED-NOCHECK-NEXT: Name: target_features +# UNSPECIFIED-NOCHECK-NEXT: Features: +# UNSPECIFIED-NOCHECK-NEXT: - Prefix: USED +# UNSPECIFIED-NOCHECK-NEXT: Name: bar +# UNSPECIFIED-NOCHECK-NEXT: - Prefix: USED +# UNSPECIFIED-NOCHECK-NEXT: Name: baz +# UNSPECIFIED-NOCHECK-NEXT: - Prefix: USED +# UNSPECIFIED-NOCHECK-NEXT: Name: quux +# UNSPECIFIED-NOCHECK-NEXT: ... + # USED: - Type: CUSTOM # USED-NEXT: Name: target_features # USED-NEXT: Features: @@ -51,7 +83,14 @@ Sections: # REQUIRED-NEXT: Name: foo # REQUIRED-NEXT: ... -# DISALLOWED: Target feature "foo" is disallowed +# DISALLOWED: Target feature 'foo' is disallowed. Use --no-check-features to suppress.{{$}} + +# DISALLOWED-NOCHECK: - Type: CUSTOM +# DISALLOWED-NOCHECK-NEXT: Name: target_features +# DISALLOWED-NOCHECK-NEXT: Features: +# DISALLOWED-NOCHECK-NEXT: - Prefix: USED +# DISALLOWED-NOCHECK-NEXT: Name: foo +# DISALLOWED-NOCHECK-NEXT: ... # NONE: - Type: CUSTOM # NONE-NEXT: Name: target_features diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h index c5f22eb..ccd7f37 100644 --- a/lld/wasm/Config.h +++ b/lld/wasm/Config.h @@ -19,6 +19,7 @@ namespace wasm { struct Configuration { bool AllowUndefined; + bool CheckFeatures; bool CompressRelocations; bool Demangle; bool DisableVerify; @@ -54,6 +55,7 @@ struct Configuration { llvm::StringSet<> AllowUndefinedSymbols; std::vector SearchPaths; llvm::CachePruningPolicy ThinLTOCachePolicy; + llvm::Optional> Features; // True if we are creating position-independent code. bool Pic; diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp index 19ea38c..f9dfd5c 100644 --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -21,6 +21,7 @@ #include "lld/Common/Version.h" #include "llvm/ADT/Twine.h" #include "llvm/Object/Wasm.h" +#include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Path.h" @@ -292,6 +293,8 @@ static StringRef getEntry(opt::InputArgList &Args, StringRef Default) { // of these values. static void setConfigs(opt::InputArgList &Args) { Config->AllowUndefined = Args.hasArg(OPT_allow_undefined); + Config->CheckFeatures = + Args.hasFlag(OPT_check_features, OPT_no_check_features, true); Config->CompressRelocations = Args.hasArg(OPT_compress_relocations); Config->Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, true); Config->DisableVerify = Args.hasArg(OPT_disable_verify); @@ -339,6 +342,13 @@ static void setConfigs(opt::InputArgList &Args) { Config->MaxMemory = args::getInteger(Args, OPT_max_memory, 0); Config->ZStackSize = args::getZOptionValue(Args, OPT_z, "stack-size", WasmPageSize); + + if (auto *Arg = Args.getLastArg(OPT_features)) { + Config->Features = + llvm::Optional>(std::vector()); + for (StringRef S : Arg->getValues()) + Config->Features->push_back(S); + } } // Some command line options or some combinations of them are not allowed. diff --git a/lld/wasm/Options.td b/lld/wasm/Options.td index 591d624..67d1715 100644 --- a/lld/wasm/Options.td +++ b/lld/wasm/Options.td @@ -155,6 +155,13 @@ defm whole_archive: B<"whole-archive", "Force load of all members in a static library", "Do not force load of all members in a static library (default)">; +defm check_features: B<"check-features", + "Check feature compatibility of linked objects (default)", + "Ignore feature compatibility of linked objects">; + +def features: CommaJoined<["--", "-"], "features=">, + HelpText<"Comma-separated used features, inferred from input objects by default.">; + // Aliases def: JoinedOrSeparate<["-"], "e">, Alias; def: J<"entry=">, Alias; diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp index 897908a..5f12ce7 100644 --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -878,18 +878,30 @@ void Writer::createSections() { } void Writer::calculateTargetFeatures() { + SmallSet Used; SmallSet Required; SmallSet Disallowed; + // Only infer used features if user did not specify features + bool InferFeatures = !Config->Features.hasValue(); + + if (!InferFeatures) { + for (auto &Feature : Config->Features.getValue()) + TargetFeatures.insert(Feature); + // No need to read or check features + if (!Config->CheckFeatures) + return; + } + // Find the sets of used, required, and disallowed features for (ObjFile *File : Symtab->ObjectFiles) { for (auto &Feature : File->getWasmObj()->getTargetFeatures()) { switch (Feature.Prefix) { case WASM_FEATURE_PREFIX_USED: - TargetFeatures.insert(Feature.Name); + Used.insert(Feature.Name); break; case WASM_FEATURE_PREFIX_REQUIRED: - TargetFeatures.insert(Feature.Name); + Used.insert(Feature.Name); Required.insert(Feature.Name); break; case WASM_FEATURE_PREFIX_DISALLOWED: @@ -902,6 +914,20 @@ void Writer::calculateTargetFeatures() { } } + if (InferFeatures) + TargetFeatures.insert(Used.begin(), Used.end()); + + if (!Config->CheckFeatures) + return; + + // Validate that used features are allowed in output + if (!InferFeatures) { + for (auto &Feature : Used) { + if (!TargetFeatures.count(Feature)) + error(Twine("Target feature '") + Feature + "' is not allowed."); + } + } + // Validate the required and disallowed constraints for each file for (ObjFile *File : Symtab->ObjectFiles) { SmallSet ObjectFeatures; @@ -910,11 +936,13 @@ void Writer::calculateTargetFeatures() { continue; ObjectFeatures.insert(Feature.Name); if (Disallowed.count(Feature.Name)) - error("Target feature \"" + Feature.Name + "\" is disallowed"); + error(Twine("Target feature '") + Feature.Name + + "' is disallowed. Use --no-check-features to suppress."); } for (auto &Feature : Required) { if (!ObjectFeatures.count(Feature)) - error(Twine("Missing required target feature \"") + Feature + "\""); + error(Twine("Missing required target feature '") + Feature + + "'. Use --no-check-features to suppress."); } } } -- 2.7.4