[lld][WebAssemlby] Improve support for -L / -l and add testing

- Add support -Bdynamic/-Bstatic and their aliases
- Add support for `--library` and `--library-path` long form args
- Add test based on test/ELF/libsearch.s
- In `-Bdynamic` mode search for `.so` files in preference to `.a`.
- Unlike ELF continue to default to static mode until `-pie` or
  `-shared` are used.

Differential Revision: https://reviews.llvm.org/D135087
This commit is contained in:
Sam Clegg 2022-10-03 08:31:23 -07:00
parent d67def8704
commit 0a9756fc15
7 changed files with 176 additions and 13 deletions

View File

@ -0,0 +1,8 @@
.globl _bar,_dynamic
.section .data,"",@
_bar:
.size _bar,4
_dynamic:
.size _dynamic,4

View File

@ -0,0 +1,9 @@
.globl _bar,_static
.section .data,"",@
_bar:
.int32 42
.size _bar,4
_static:
.size _static,4

View File

@ -0,0 +1,2 @@
.section .bar,"",@
.quad _bar

99
lld/test/wasm/libsearch.s Normal file
View File

@ -0,0 +1,99 @@
// Based on lld/test/ELF/libsearch.s
// RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %s -o %t.o
// RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown \
// RUN: %p/Inputs/libsearch-dyn.s -o %tdyn.o
// RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown \
// RUN: %p/Inputs/libsearch-st.s -o %tst.o
// RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown \
// RUN: %p/Inputs/use-bar.s -o %tbar.o
// RUN: mkdir -p %t.dir
// RUN: wasm-ld -shared --experimental-pic %tdyn.o -o %t.dir/libls.so
// RUN: cp -f %t.dir/libls.so %t.dir/libls2.so
// RUN: rm -f %t.dir/libls.a
// RUN: llvm-ar rcs %t.dir/libls.a %tst.o
// Should fail if no library specified
// RUN: not wasm-ld -l 2>&1 \
// RUN: | FileCheck --check-prefix=NOLIBRARY %s
// NOLIBRARY: -l: missing argument
// Should link normally, because _bar is not used
// RUN: wasm-ld -o %t3 %t.o
// Should not link because of undefined symbol _bar
// RUN: not wasm-ld --no-gc-sections -o /dev/null %t.o %tbar.o 2>&1 \
// RUN: | FileCheck --check-prefix=UNDEFINED %s
// UNDEFINED: wasm-ld: error: {{.*}}: undefined symbol: _bar
// Should fail if cannot find specified library (without -L switch)
// RUN: not wasm-ld -o /dev/null %t.o -lls 2>&1 \
// RUN: | FileCheck --check-prefix=NOLIB %s
// NOLIB: unable to find library -lls
// Should use explicitly specified static library
// Also ensure that we accept -L <arg>
// RUN: wasm-ld --emit-relocs --no-gc-sections -o %t3 %t.o -L %t.dir -l:libls.a
// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=STATIC %s
// STATIC: Symbols [
// STATIC: Name: _static
// Should use explicitly specified dynamic library
// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t3 %t.o -L%t.dir -l:libls.so
// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=DYNAMIC %s
// DYNAMIC: Symbols [
// DYNAMIC-NOT: Name: _static
// Should prefer static to dynamic when linking regular executable.
// RUN: wasm-ld --emit-relocs --no-gc-sections -o %t3 %t.o -L%t.dir -lls
// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=STATIC %s
// Should prefer dynamic when linking PIE.
// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t3 %t.o -L%t.dir -lls
// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=DYNAMIC %s
// Check for library search order
// RUN: mkdir -p %t.dir2
// RUN: cp %t.dir/libls.a %t.dir2
// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t3 %t.o -L%t.dir2 -L%t.dir -lls
// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=STATIC %s
// -L can be placed after -l
// RUN: wasm-ld -o %t3 %t.o -lls -L%t.dir
// Check long forms as well
// RUN: wasm-ld --emit-relocs --no-gc-sections -o %t3 %t.o --library-path=%t.dir --library=ls
// RUN: wasm-ld --emit-relocs --no-gc-sections -o %t3 %t.o --library-path %t.dir --library ls
// Should not search for dynamic libraries if -Bstatic is specified
// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t3 %t.o -L%t.dir -Bstatic -lls
// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=STATIC %s
// RUN: not wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o /dev/null %t.o -L%t.dir -Bstatic -lls2 2>&1 \
// RUN: | FileCheck --check-prefix=NOLIB2 %s
// NOLIB2: unable to find library -lls2
// -Bdynamic should restore default behaviour
// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t3 %t.o -L%t.dir -Bstatic -Bdynamic -lls
// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=DYNAMIC %s
// -Bstatic and -Bdynamic should affect only libraries which follow them
// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t3 %t.o -L%t.dir -lls -Bstatic -Bdynamic
// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=DYNAMIC %s
// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t3 %t.o -L%t.dir -Bstatic -lls -Bdynamic
// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=STATIC %s
// Check aliases as well
// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t3 %t.o -L%t.dir -dn -lls
// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=STATIC %s
// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t3 %t.o -L%t.dir -non_shared -lls
// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=STATIC %s
// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t3 %t.o -L%t.dir -static -lls
// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=STATIC %s
// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t3 %t.o -L%t.dir -Bstatic -dy -lls
// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=DYNAMIC %s
// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t3 %t.o -L%t.dir -Bstatic -call_shared -lls
// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=DYNAMIC %s
.globl _start, _bar
_start:
.functype _start () -> ()
end_function

View File

@ -53,6 +53,7 @@ struct Configuration {
bool stripAll;
bool stripDebug;
bool stackFirst;
bool isStatic = false;
bool trace;
uint64_t globalBase;
uint64_t initialMemory;

View File

@ -279,27 +279,58 @@ void LinkerDriver::addFile(StringRef path) {
}
}
static Optional<std::string> findFromSearchPaths(StringRef path) {
for (StringRef dir : config->searchPaths)
if (Optional<std::string> s = findFile(dir, path))
return s;
return None;
}
// This is for -l<basename>. We'll look for lib<basename>.a from
// search paths.
static Optional<std::string> searchLibraryBaseName(StringRef name) {
for (StringRef dir : config->searchPaths) {
// Currently we don't enable dyanmic linking at all unless -shared or -pie
// are used, so don't even look for .so files in that case..
if (config->isPic && !config->isStatic)
if (Optional<std::string> s = findFile(dir, "lib" + name + ".so"))
return s;
if (Optional<std::string> s = findFile(dir, "lib" + name + ".a"))
return s;
}
return None;
}
// This is for -l<namespec>.
static Optional<std::string> searchLibrary(StringRef name) {
if (name.startswith(":"))
return findFromSearchPaths(name.substr(1));
return searchLibraryBaseName(name);
}
// Add a given library by searching it from input search paths.
void LinkerDriver::addLibrary(StringRef name) {
for (StringRef dir : config->searchPaths) {
if (Optional<std::string> s = findFile(dir, "lib" + name + ".a")) {
addFile(*s);
return;
}
}
error("unable to find library -l" + name);
if (Optional<std::string> path = searchLibrary(name))
addFile(saver().save(*path));
else
error("unable to find library -l" + name, ErrorTag::LibNotFound, {name});
}
void LinkerDriver::createFiles(opt::InputArgList &args) {
for (auto *arg : args) {
switch (arg->getOption().getID()) {
case OPT_l:
case OPT_library:
addLibrary(arg->getValue());
break;
case OPT_INPUT:
addFile(arg->getValue());
break;
case OPT_Bstatic:
config->isStatic = true;
break;
case OPT_Bdynamic:
config->isStatic = false;
break;
case OPT_whole_archive:
inWholeArchive = true;
break;
@ -382,7 +413,7 @@ static void readConfigs(opt::InputArgList &args) {
config->printGcSections =
args.hasFlag(OPT_print_gc_sections, OPT_no_print_gc_sections, false);
config->saveTemps = args.hasArg(OPT_save_temps);
config->searchPaths = args::getStrings(args, OPT_L);
config->searchPaths = args::getStrings(args, OPT_library_path);
config->shared = args.hasArg(OPT_shared);
config->stripAll = args.hasArg(OPT_strip_all);
config->stripDebug = args.hasArg(OPT_strip_debug);
@ -898,12 +929,12 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
cl::ParseCommandLineOptions(v.size(), v.data());
readConfigs(args);
setConfigs();
createFiles(args);
if (errorCount())
return;
setConfigs();
checkOptions(args);
if (errorCount())
return;

View File

@ -38,6 +38,10 @@ multiclass B<string name, string help1, string help2> {
// The following flags are shared with the ELF linker
def Bsymbolic: F<"Bsymbolic">, HelpText<"Bind defined symbols locally">;
def Bdynamic: F<"Bdynamic">, HelpText<"Link against shared libraries (default)">;
def Bstatic: F<"Bstatic">, HelpText<"Do not link against shared libraries">;
defm color_diagnostics: B<"color-diagnostics",
"Alias for --color-diagnostics=always",
"Alias for --color-diagnostics=never">;
@ -80,10 +84,10 @@ defm merge_data_segments: BB<"merge-data-segments",
def help: F<"help">, HelpText<"Print option help">;
def l: JoinedOrSeparate<["-"], "l">, MetaVarName<"<libName>">,
def library: JoinedOrSeparate<["-"], "l">, MetaVarName<"<libName>">,
HelpText<"Root name of library to use">;
def L: JoinedOrSeparate<["-"], "L">, MetaVarName<"<dir>">,
def library_path: JoinedOrSeparate<["-"], "L">, MetaVarName<"<dir>">,
HelpText<"Add a directory to the library search path">;
def m: JoinedOrSeparate<["-"], "m">, HelpText<"Set target emulation">;
@ -219,8 +223,17 @@ def features: CommaJoined<["--", "-"], "features=">,
// Aliases
def: JoinedOrSeparate<["-"], "e">, Alias<entry>;
def: J<"entry=">, Alias<entry>;
def: F<"call_shared">, Alias<Bdynamic>, HelpText<"Alias for --Bdynamic">;
def: F<"dy">, Alias<Bdynamic>, HelpText<"Alias for --Bdynamic">;
def: F<"dn">, Alias<Bstatic>, HelpText<"Alias for --Bstatic">;
def: F<"non_shared">, Alias<Bstatic>, HelpText<"Alias for --Bstatic">;
def: F<"static">, Alias<Bstatic>, HelpText<"Alias for --Bstatic">;
def: Flag<["-"], "E">, Alias<export_dynamic>, HelpText<"Alias for --export-dynamic">;
def: Flag<["-"], "i">, Alias<initial_memory>;
def: Separate<["--", "-"], "library">, Alias<library>;
def: Joined<["--", "-"], "library=">, Alias<library>;
def: Separate<["--", "-"], "library-path">, Alias<library_path>;
def: Joined<["--", "-"], "library-path=">, Alias<library_path>;
def: Flag<["-"], "M">, Alias<print_map>, HelpText<"Alias for --print-map">;
def: Flag<["-"], "r">, Alias<relocatable>;
def: Flag<["-"], "s">, Alias<strip_all>, HelpText<"Alias for --strip-all">;