From 82de51a3aefe70695a39ef44341c7ef79d8c040d Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Tue, 26 Mar 2019 04:11:05 +0000 Subject: [PATCH] Reland "[WebAssembly] Add linker options to control feature checking" Do not pipe binary data between processes in lit tests this time, since it turns out that can break on Windows. This reverts commit 84c8652fc3085155d0f9c355455e5a797c6d9db6. llvm-svn: 356975 --- lld/test/wasm/target-feature-disallowed.yaml | 36 ++++++++++-- lld/test/wasm/target-feature-none.yaml | 36 ++++++++++++ lld/test/wasm/target-feature-required.yaml | 60 ++++++++++++++++++-- lld/test/wasm/target-feature-used.yaml | 56 +++++++++++++++--- lld/wasm/Config.h | 2 + lld/wasm/Driver.cpp | 10 ++++ lld/wasm/Options.td | 7 +++ lld/wasm/Writer.cpp | 36 ++++++++++-- 8 files changed, 224 insertions(+), 19 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 687d86b40d6b..d919aeaa8b66 100644 --- a/lld/test/wasm/target-feature-disallowed.yaml +++ b/lld/test/wasm/target-feature-disallowed.yaml @@ -1,12 +1,18 @@ # RUN: yaml2obj %s -o %t1.o +# RUN: wasm-ld --no-entry --features=foo,bar,baz -o %t.specified.wasm %t1.o +# RUN: obj2yaml %t.specified.wasm | FileCheck %s --check-prefix SPECIFIED + +# RUN: wasm-ld --no-entry --features=bar,baz,quux -o %t.unspecified.wasm %t1.o +# RUN: obj2yaml %t.unspecified.wasm | 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 +# RUN: wasm-ld --no-entry -o %t.disallowed.wasm %t1.o %t.disallowed.o +# RUN: obj2yaml %t.disallowed.wasm | FileCheck %s --check-prefix DISALLOWED # 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 +# RUN: wasm-ld --no-entry -o %t.none.wasm %t1.o %t.none.o +# RUN: obj2yaml %t.none.wasm | FileCheck %s --check-prefix NONE # Check that the following combinations of feature linkage policies # give the expected results: @@ -31,6 +37,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 000000000000..974f72cbf7e0 --- /dev/null +++ b/lld/test/wasm/target-feature-none.yaml @@ -0,0 +1,36 @@ +# RUN: yaml2obj %s -o %t1.o + +# RUN: wasm-ld --no-entry -o %t.empty.wasm %t1.o +# RUN: obj2yaml %t.empty.wasm | FileCheck %s --check-prefix EMPTY + +# RUN: wasm-ld --no-entry --features= -o %t.empty.2.wasm %t1.o +# RUN: obj2yaml %t.empty.2.wasm | FileCheck %s --check-prefix EMPTY + +# RUN: wasm-ld --no-entry --features=foo,bar,baz -o %t.specified.wasm %t1.o +# RUN: obj2yaml %t.specified.wasm | 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 59031d4c013d..85923043f239 100644 --- a/lld/test/wasm/target-feature-required.yaml +++ b/lld/test/wasm/target-feature-required.yaml @@ -1,15 +1,29 @@ # RUN: yaml2obj %s -o %t1.o +# RUN: wasm-ld --no-entry --features=foo,bar,baz -o %t.specified.wasm %t1.o +# RUN: obj2yaml %t.specified.wasm | 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 %t.unspecified.wasm %t1.o +# RUN: obj2yaml %t.unspecified.wasm | 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 +# RUN: wasm-ld --no-entry -o %t.required.wasm %t1.o %t.required.o +# RUN: obj2yaml %t.required.wasm | FileCheck %s --check-prefix REQUIRED # 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 %t.disallowed.wasm %t1.o %t.disallowed.o +# RUN: obj2yaml %t.disallowed.wasm | 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 %t.none.wasm %t1.o %t.none.o +# RUN: obj2yaml %t.none.wasm | FileCheck %s --check-prefix NONE-NOCHECK + # Check that the following combinations of feature linkage policies # give the expected results: # @@ -31,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: ... + # REQUIRED: - Type: CUSTOM # REQUIRED-NEXT: Name: target_features # REQUIRED-NEXT: Features: @@ -38,6 +76,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.{{$}} -# NONE: Missing required target feature "foo" +# 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-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 de121ff77049..693afbfcfa63 100644 --- a/lld/test/wasm/target-feature-used.yaml +++ b/lld/test/wasm/target-feature-used.yaml @@ -1,19 +1,30 @@ # RUN: yaml2obj %s -o %t1.o +# RUN: wasm-ld --no-entry --features=foo,bar,baz -o %t.specified.wasm %t1.o +# RUN: obj2yaml %t.specified.wasm | 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 %t.unspecified.wasm %t1.o +# RUN: obj2yaml %t.unspecified.wasm| 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 +# RUN: wasm-ld --no-entry -o %t.used.wasm %t1.o %t.used.o +# RUN: obj2yaml %t.used.wasm | FileCheck %s --check-prefix USED # 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 +# RUN: wasm-ld --no-entry -o %t.required.wasm %t1.o %t.required.o +# RUN: obj2yaml %t.required.wasm | FileCheck %s --check-prefix REQUIRED # 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 %t.disallowed.wasm %t1.o %t.disallowed.o +# RUN: obj2yaml %t.disallowed.wasm | 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 +# RUN: wasm-ld --no-entry -o %t.none.wasm %t1.o %t.none.o +# RUN: obj2yaml %t.none.wasm | FileCheck %s --check-prefix NONE # Check that the following combinations of feature linkage policies # give the expected results: @@ -37,6 +48,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 +86,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 c5f22eb537dd..ccd7f3717f9c 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 19ea38c1d94c..f9dfd5ce464e 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 591d624e666d..67d171581d89 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 897908a5ad08..5f12ce739222 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."); } } }