Skip to content

Commit

Permalink
[WebAssembly] Add linker options to control feature checking
Browse files Browse the repository at this point in the history
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
  • Loading branch information
tlively committed Mar 22, 2019
1 parent c48e223 commit 5991328
Show file tree
Hide file tree
Showing 8 changed files with 200 additions and 7 deletions.
26 changes: 26 additions & 0 deletions lld/test/wasm/target-feature-disallowed.yaml
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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:
Expand Down
33 changes: 33 additions & 0 deletions lld/test/wasm/target-feature-none.yaml
Original file line number Diff line number Diff line change
@@ -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: ...
52 changes: 50 additions & 2 deletions lld/test/wasm/target-feature-required.yaml
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
# 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

# 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:
#
Expand All @@ -31,13 +41,51 @@ 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:
# REQUIRED-NEXT: - Prefix: USED
# 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: ...
41 changes: 40 additions & 1 deletion lld/test/wasm/target-feature-used.yaml
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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:
Expand All @@ -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
Expand Down
2 changes: 2 additions & 0 deletions lld/wasm/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ namespace wasm {

struct Configuration {
bool AllowUndefined;
bool CheckFeatures;
bool CompressRelocations;
bool Demangle;
bool DisableVerify;
Expand Down Expand Up @@ -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;
Expand Down
10 changes: 10 additions & 0 deletions lld/wasm/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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.
Expand Down
7 changes: 7 additions & 0 deletions lld/wasm/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand Down
36 changes: 32 additions & 4 deletions lld/wasm/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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;
Expand All @@ -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.");
}
}
}
Expand Down

0 comments on commit 5991328

Please sign in to comment.