Index: ELF/Driver.h =================================================================== --- ELF/Driver.h +++ ELF/Driver.h @@ -20,9 +20,13 @@ extern class LinkerDriver *Driver; -// Entry point of the ELF linker. +// An entry point of the ELF linker. It is not guranteed that the +// function will return. void link(ArrayRef Args); +// The other entry point of the ELF linker which is guaranteed to return. +bool main(llvm::ArrayRef Args, raw_ostream &Diag); + class LinkerDriver { public: void main(ArrayRef Args); 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,100 @@ 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::main(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); + link(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 main(). Because fork is not available on Windows, +// we start a new process. +bool elf2::main(ArrayRef Args, raw_ostream &Diag) { + if (Args.size() < 2) { + link(Args); + exit(0); + } + + // "-lld-no-exec" is the marker that we are in a subprocess. + if (Args[1] == StringRef("-lld-no-exec")) { + std::vector Argv(Args.begin(), Args.end()); + Argv.erase(Argv.begin() + 1); + link(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 Exe = sys::findProgramByName("lld.exe", {Dir}); + if (!Exe) { + Exe = sys::findProgramByName("lld.exe"); + if (!Exe) + error("lld is not found"); + } + + // Add other parameters. + std::vector Argv; + Argv.push_back(Exe->data()); + Argv.push_back("-flavor"); + Argv.push_back("gnu"); + Argv.push_back("-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(Argv[0], 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 { +// An entry point for the internal use. It is not guaranteed to return. void link(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 main(llvm::ArrayRef args, raw_ostream &diag = llvm::errs()); } /// Driver for lld unit tests