Index: ELF/Driver.h =================================================================== --- ELF/Driver.h +++ ELF/Driver.h @@ -20,8 +20,12 @@ extern class LinkerDriver *Driver; -// Entry point of the ELF linker. -void link(ArrayRef Args); +// An entry point of the ELF linker. It is not guranteed that the +// function will return. +void linkOrFail(ArrayRef Args); + +// The other entry point of the ELF linker which is guaranteed to return. +bool link(llvm::ArrayRef Args, raw_ostream &Diag); class LinkerDriver { public: Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -29,7 +29,7 @@ Configuration *elf2::Config; LinkerDriver *elf2::Driver; -void elf2::link(ArrayRef Args) { +void elf2::linkOrFail(ArrayRef Args) { Configuration C; LinkerDriver D; Config = &C; Index: ELF/DriverUtils.cpp =================================================================== --- ELF/DriverUtils.cpp +++ ELF/DriverUtils.cpp @@ -16,10 +16,19 @@ #include "Driver.h" #include "Error.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/Config/config.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" #include "llvm/Support/StringSaver.h" +#include "llvm/Support/raw_ostream.h" + +#ifdef HAVE_UNISTD_H +#include +#include +#include +#endif using namespace llvm; @@ -118,3 +127,92 @@ sys::path::append(Path, Dir, File); return Path.str(); } + +#ifdef LLVM_ON_UNIX + +// This is a wrapper for link(). Unlike link(), this function always returns. +bool elf2::link(ArrayRef Args, raw_ostream &Diag) { + // Create a communication channel between this process and a child. + int Fd[2]; + pipe(Fd); + + // Start a subprocess. + pid_t P; + if ((P = fork()) == -1) + error("fork failed."); + + if (P == 0) { + // We are in the child. Plumb stdout and stderr to the pipe. + close(Fd[0]); + close(0); + dup2(Fd[1], 1); + dup2(Fd[1], 2); + linkOrFail(Args); + exit(0); + } + + // We are in the parent. Read from the pipe until EOF. + close(Fd[1]); + char Buf[4096]; + for (;;) { + size_t N = read(Fd[0], Buf, sizeof(Buf)); + if (N == size_t(-1)) { + if (errno == EAGAIN) + continue; + error("read failed."); + } + if (N == 0) + break; + Diag << StringRef(Buf, N); + } + + // Return true if the child exited normally. + for (;;) { + int Status; + if (waitpid(P, &Status, 0) == -1) + error("waitpid failed."); + if (WIFEXITED(Status)) + return !WEXITSTATUS(Status); + } +} + +#else + +// Windows version of link(). Because fork is not available on Windows, +// we start a new process. +bool elf2::link(ArrayRef Args, raw_ostream &Diag) { + // "-lld-no-exec" is the marker that we are in a subprocess. + if (Args.size() > 1 && Args[1] == StringRef("-lld-no-exec")) { + std::vector Argv(Args.begin(), Args.end()); + Argv.erase(Argv.begin() + 1); + linkOrFail(Argv); + exit(0); + } + + // We want to build arguments such that lld -flavor gnu Args[1]... + // First, look for lld.exe from the current executable directory or + // from %PATH%. + SmallString<128> Dir = sys::fs::getMainExecutable(nullptr, nullptr); + sys::path::remove_filename(Dir); + ErrorOr ExeOrErr = sys::findProgramByName("lld.exe", {Dir}); + if (!ExeOrErr) { + ExeOrErr = sys::findProgramByName("lld.exe"); + if (!ExeOrErr) + error("lld is not found"); + } + const char *Exe = ExeOrErr->data(); + + // Add other parameters. + std::vector Argv = {Exe, "-flavor", "gnu", "-lld-no-exec"}; + Argv.insert(Argv.end(), Args.begin() + 1, Args.end()); + Argv.push_back(nullptr); + + // Start a subprocess and return its result. + std::string ErrMsg; + int Status = + sys::ExecuteAndWait(Exe, Argv.data(), nullptr, nullptr, 0, 0, &ErrMsg); + Diag << ErrMsg; + return !Status; +} + +#endif Index: include/lld/Driver/Driver.h =================================================================== --- include/lld/Driver/Driver.h +++ include/lld/Driver/Driver.h @@ -125,7 +125,12 @@ } namespace elf2 { -void link(llvm::ArrayRef args); +// An entry point for the internal use. It is not guaranteed to return. +void linkOrFail(llvm::ArrayRef args); + +// If you want to call LLD ELF's main from your program, use this function. +// It is guaranteed to return. Returns true on success. +bool link(llvm::ArrayRef args, raw_ostream &diag = llvm::errs()); } /// Driver for lld unit tests Index: lib/Driver/UniversalDriver.cpp =================================================================== --- lib/Driver/UniversalDriver.cpp +++ lib/Driver/UniversalDriver.cpp @@ -205,7 +205,7 @@ case Flavor::old_gnu_ld: return GnuLdDriver::linkELF(args, diagnostics); case Flavor::gnu_ld: - elf2::link(args); + elf2::linkOrFail(args); return true; case Flavor::darwin_ld: return DarwinLdDriver::linkMachO(args, diagnostics);