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
This commit is contained in:
Thomas Lively 2019-03-26 04:11:05 +00:00
parent 2d000e395e
commit 82de51a3ae
8 changed files with 224 additions and 19 deletions

View File

@ -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:

View File

@ -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: ...

View File

@ -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: ...

View File

@ -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

View File

@ -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<llvm::StringRef> SearchPaths;
llvm::CachePruningPolicy ThinLTOCachePolicy;
llvm::Optional<std::vector<std::string>> Features;
// True if we are creating position-independent code.
bool Pic;

View File

@ -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<std::string>>(std::vector<std::string>());
for (StringRef S : Arg->getValues())
Config->Features->push_back(S);
}
}
// Some command line options or some combinations of them are not allowed.

View File

@ -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<entry>;
def: J<"entry=">, Alias<entry>;

View File

@ -878,18 +878,30 @@ void Writer::createSections() {
}
void Writer::calculateTargetFeatures() {
SmallSet<std::string, 8> Used;
SmallSet<std::string, 8> Required;
SmallSet<std::string, 8> 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<std::string, 8> 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.");
}
}
}