diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b64301d1ad8..7458de08fc16 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,5 @@ set(LLD_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(LLD_INCLUDE_DIR ${LLD_SOURCE_DIR}/include ) set(LLD_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) # Compute the LLD version from the LLVM version. @@ -86,6 +87,12 @@ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) ) endif() +macro(add_lld_library name) + add_llvm_library(${name} ${ARGN}) + set_target_properties(${name} PROPERTIES FOLDER "lld libraries") +endmacro(add_lld_library) + + add_subdirectory(lib) add_subdirectory(tools) diff --git a/COFF/CMakeLists.txt b/COFF/CMakeLists.txt index 78dc34eff96e..3f31ba9ba1fb 100644 --- a/COFF/CMakeLists.txt +++ b/COFF/CMakeLists.txt @@ -2,7 +2,7 @@ set(LLVM_TARGET_DEFINITIONS Options.td) tablegen(LLVM Options.inc -gen-opt-parser-defs) add_public_tablegen_target(COFFOptionsTableGen) -add_llvm_library(lldCOFF +add_lld_library(lldCOFF Chunks.cpp DLL.cpp Driver.cpp diff --git a/COFF/Chunks.cpp b/COFF/Chunks.cpp index 50bf55be269b..f9f768d69866 100644 --- a/COFF/Chunks.cpp +++ b/COFF/Chunks.cpp @@ -310,7 +310,7 @@ void SEHTableChunk::writeTo(uint8_t *Buf) const { BaserelChunk::BaserelChunk(uint32_t Page, Baserel *Begin, Baserel *End) { // Block header consists of 4 byte page RVA and 4 byte block size. // Each entry is 2 byte. Last entry may be padding. - Data.resize(RoundUpToAlignment((End - Begin) * 2 + 8, 4)); + Data.resize(align((End - Begin) * 2 + 8, 4)); uint8_t *P = Data.data(); write32le(P, Page); write32le(P + 4, Data.size()); diff --git a/COFF/Chunks.h b/COFF/Chunks.h index 60b8e76f8230..274135516eb9 100644 --- a/COFF/Chunks.h +++ b/COFF/Chunks.h @@ -326,6 +326,10 @@ public: uint8_t Type; }; +inline uint64_t align(uint64_t Value, uint64_t Align) { + return llvm::RoundUpToAlignment(Value, Align); +} + } // namespace coff } // namespace lld diff --git a/COFF/Config.h b/COFF/Config.h index 409ede648636..9cfccadba5fa 100644 --- a/COFF/Config.h +++ b/COFF/Config.h @@ -25,6 +25,7 @@ using llvm::COFF::WindowsSubsystem; using llvm::StringRef; class DefinedAbsolute; class DefinedRelative; +class StringChunk; class Undefined; // Short aliases. @@ -42,6 +43,12 @@ struct Export { bool Data = false; bool Private = false; + // If an export is a form of /export:foo=dllname.bar, that means + // that foo should be exported as an alias to bar in the DLL. + // ForwardTo is set to "dllname.bar" part. Usually empty. + StringRef ForwardTo; + StringChunk *ForwardChunk = nullptr; + // True if this /export option was in .drectves section. bool Directives = false; StringRef SymbolName; diff --git a/COFF/DLL.cpp b/COFF/DLL.cpp index 40ca5cf61dc2..8f3383d75c7b 100644 --- a/COFF/DLL.cpp +++ b/COFF/DLL.cpp @@ -45,7 +45,7 @@ public: size_t getSize() const override { // Starts with 2 byte Hint field, followed by a null-terminated string, // ends with 0 or 1 byte padding. - return RoundUpToAlignment(Name.size() + 3, 2); + return align(Name.size() + 3, 2); } void writeTo(uint8_t *Buf) const override { @@ -320,8 +320,12 @@ public: void writeTo(uint8_t *Buf) const override { for (Export &E : Config->Exports) { - auto *D = cast(E.Sym->repl()); - write32le(Buf + OutputSectionOff + E.Ordinal * 4, D->getRVA()); + uint8_t *P = Buf + OutputSectionOff + E.Ordinal * 4; + if (E.ForwardChunk) { + write32le(P, E.ForwardChunk->getRVA()); + } else { + write32le(P, cast(E.Sym->repl())->getRVA()); + } } } @@ -539,6 +543,15 @@ EdataContents::EdataContents() { for (Export &E : Config->Exports) if (!E.Noname) Names.push_back(new StringChunk(E.ExportName)); + + std::vector Forwards; + for (Export &E : Config->Exports) { + if (E.ForwardTo.empty()) + continue; + E.ForwardChunk = new StringChunk(E.ForwardTo); + Forwards.push_back(E.ForwardChunk); + } + auto *NameTab = new NamePointersChunk(Names); auto *OrdinalTab = new ExportOrdinalChunk(Names.size()); auto *Dir = new ExportDirectoryChunk(MaxOrdinal, Names.size(), DLLName, @@ -550,6 +563,8 @@ EdataContents::EdataContents() { Chunks.push_back(std::unique_ptr(OrdinalTab)); for (Chunk *C : Names) Chunks.push_back(std::unique_ptr(C)); + for (Chunk *C : Forwards) + Chunks.push_back(std::unique_ptr(C)); } } // namespace coff diff --git a/COFF/Driver.cpp b/COFF/Driver.cpp index f528dafd9857..4cacf0ff552a 100644 --- a/COFF/Driver.cpp +++ b/COFF/Driver.cpp @@ -586,6 +586,8 @@ void LinkerDriver::link(llvm::ArrayRef ArgsArr) { // Windows specific -- Make sure we resolve all dllexported symbols. for (Export &E : Config->Exports) { + if (!E.ForwardTo.empty()) + continue; E.Sym = addUndefined(E.Name); if (!E.Directives) Symtab.mangleMaybe(E.Sym); diff --git a/COFF/DriverUtils.cpp b/COFF/DriverUtils.cpp index 391a8ab66420..014fee7fefd7 100644 --- a/COFF/DriverUtils.cpp +++ b/COFF/DriverUtils.cpp @@ -321,7 +321,8 @@ void createSideBySideManifest() { } // Parse a string in the form of -// "[=][,@ordinal[,NONAME]][,DATA][,PRIVATE]". +// "[=][,@ordinal[,NONAME]][,DATA][,PRIVATE]" +// or "=.". // Used for parsing /export arguments. Export parseExport(StringRef Arg) { Export E; @@ -329,12 +330,25 @@ Export parseExport(StringRef Arg) { std::tie(E.Name, Rest) = Arg.split(","); if (E.Name.empty()) goto err; + if (E.Name.find('=') != StringRef::npos) { - std::tie(E.ExtName, E.Name) = E.Name.split("="); + StringRef X, Y; + std::tie(X, Y) = E.Name.split("="); + + // If "=.". + if (Y.find(".") != StringRef::npos) { + E.Name = X; + E.ForwardTo = Y; + return E; + } + + E.ExtName = X; + E.Name = Y; if (E.Name.empty()) goto err; } + // If "=[,@ordinal[,NONAME]][,DATA][,PRIVATE]" while (!Rest.empty()) { StringRef Tok; std::tie(Tok, Rest) = Rest.split(","); @@ -388,15 +402,22 @@ void fixupExports() { } for (Export &E : Config->Exports) { - if (Undefined *U = cast_or_null(E.Sym->WeakAlias)) { + if (!E.ForwardTo.empty()) { + E.SymbolName = E.Name; + } else if (Undefined *U = cast_or_null(E.Sym->WeakAlias)) { E.SymbolName = U->getName(); } else { E.SymbolName = E.Sym->getName(); } } - for (Export &E : Config->Exports) - E.ExportName = undecorate(E.ExtName.empty() ? E.Name : E.ExtName); + for (Export &E : Config->Exports) { + if (!E.ForwardTo.empty()) { + E.ExportName = undecorate(E.Name); + } else { + E.ExportName = undecorate(E.ExtName.empty() ? E.Name : E.ExtName); + } + } // Uniquefy by name. std::map Map; diff --git a/COFF/InputFiles.cpp b/COFF/InputFiles.cpp index 23fc2a085122..23af1e89c34d 100644 --- a/COFF/InputFiles.cpp +++ b/COFF/InputFiles.cpp @@ -23,7 +23,6 @@ using namespace llvm::COFF; using namespace llvm::object; using namespace llvm::support::endian; -using llvm::RoundUpToAlignment; using llvm::Triple; using llvm::support::ulittle32_t; using llvm::sys::fs::file_magic; diff --git a/COFF/Writer.cpp b/COFF/Writer.cpp index a74b316b87a4..5575c8d6b320 100644 --- a/COFF/Writer.cpp +++ b/COFF/Writer.cpp @@ -163,13 +163,13 @@ void OutputSection::addChunk(Chunk *C) { Chunks.push_back(C); C->setOutputSection(this); uint64_t Off = Header.VirtualSize; - Off = RoundUpToAlignment(Off, C->getAlign()); + Off = align(Off, C->getAlign()); C->setRVA(Off); C->setOutputSectionOff(Off); Off += C->getSize(); Header.VirtualSize = Off; if (C->hasData()) - Header.SizeOfRawData = RoundUpToAlignment(Off, SectorSize); + Header.SizeOfRawData = align(Off, SectorSize); } void OutputSection::addPermissions(uint32_t C) { @@ -448,15 +448,14 @@ void Writer::createSymbolAndStringTable() { OutputSection *LastSection = OutputSections.back(); // We position the symbol table to be adjacent to the end of the last section. uint64_t FileOff = - LastSection->getFileOff() + - RoundUpToAlignment(LastSection->getRawSize(), SectorSize); + LastSection->getFileOff() + align(LastSection->getRawSize(), SectorSize); if (!OutputSymtab.empty()) { PointerToSymbolTable = FileOff; FileOff += OutputSymtab.size() * sizeof(coff_symbol16); } if (!Strtab.empty()) FileOff += Strtab.size() + 4; - FileSize = RoundUpToAlignment(FileOff, SectorSize); + FileSize = align(FileOff, SectorSize); } // Visits all sections to assign incremental, non-overlapping RVAs and @@ -467,7 +466,7 @@ void Writer::assignAddresses() { sizeof(coff_section) * OutputSections.size(); SizeOfHeaders += Config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header); - SizeOfHeaders = RoundUpToAlignment(SizeOfHeaders, SectorSize); + SizeOfHeaders = align(SizeOfHeaders, SectorSize); uint64_t RVA = 0x1000; // The first page is kept unmapped. FileSize = SizeOfHeaders; // Move DISCARDABLE (or non-memory-mapped) sections to the end of file because @@ -481,10 +480,10 @@ void Writer::assignAddresses() { addBaserels(Sec); Sec->setRVA(RVA); Sec->setFileOffset(FileSize); - RVA += RoundUpToAlignment(Sec->getVirtualSize(), PageSize); - FileSize += RoundUpToAlignment(Sec->getRawSize(), SectorSize); + RVA += align(Sec->getVirtualSize(), PageSize); + FileSize += align(Sec->getRawSize(), SectorSize); } - SizeOfImage = SizeOfHeaders + RoundUpToAlignment(RVA - 0x1000, PageSize); + SizeOfImage = SizeOfHeaders + align(RVA - 0x1000, PageSize); } template void Writer::writeHeader() { diff --git a/ELF/CMakeLists.txt b/ELF/CMakeLists.txt index 763275e30caa..3dcb65ff8957 100644 --- a/ELF/CMakeLists.txt +++ b/ELF/CMakeLists.txt @@ -2,7 +2,7 @@ set(LLVM_TARGET_DEFINITIONS Options.td) tablegen(LLVM Options.inc -gen-opt-parser-defs) add_public_tablegen_target(ELFOptionsTableGen) -add_llvm_library(lldELF2 +add_lld_library(lldELF2 Driver.cpp DriverUtils.cpp Error.cpp diff --git a/ELF/Driver.cpp b/ELF/Driver.cpp index 2a3ecfa61586..f00d97851e4a 100644 --- a/ELF/Driver.cpp +++ b/ELF/Driver.cpp @@ -26,10 +26,10 @@ using namespace llvm::object; using namespace lld; using namespace lld::elf2; -Configuration *lld::elf2::Config; -LinkerDriver *lld::elf2::Driver; +Configuration *elf2::Config; +LinkerDriver *elf2::Driver; -void lld::elf2::link(ArrayRef Args) { +void elf2::link(ArrayRef Args) { Configuration C; LinkerDriver D; Config = &C; @@ -42,9 +42,9 @@ static std::pair parseEmulation(StringRef S) { return {ELF32BEKind, EM_MIPS}; if (S == "elf32ltsmip") return {ELF32LEKind, EM_MIPS}; - if (S == "elf32ppc") + if (S == "elf32ppc" || S == "elf32ppc_fbsd") return {ELF32BEKind, EM_PPC}; - if (S == "elf64ppc") + if (S == "elf64ppc" || S == "elf64ppc_fbsd") return {ELF64BEKind, EM_PPC64}; if (S == "elf_i386") return {ELF32LEKind, EM_386}; @@ -107,6 +107,24 @@ void LinkerDriver::addFile(StringRef Path) { } } +// Some command line options or some combinations of them are not allowed. +// This function checks for such errors. +static void checkOptions(opt::InputArgList &Args) { + // Traditional linkers can generate re-linkable object files instead + // of executables or DSOs. We don't support that since the feature + // does not seem to provide more value than the static archiver. + if (Args.hasArg(OPT_relocatable)) + error("-r option is not supported. Use 'ar' command instead."); + + // The MIPS ABI as of 2016 does not support the GNU-style symbol lookup + // table which is a relatively new feature. + if (Config->EMachine == EM_MIPS && Config->GnuHash) + error("The .gnu.hash section is not compatible with the MIPS target."); + + if (Config->EMachine == EM_AMDGPU && !Config->Entry.empty()) + error("-e option is not valid for AMDGPU."); +} + static StringRef getString(opt::InputArgList &Args, unsigned Key, StringRef Default = "") { if (auto *Arg = Args.getLastArg(Key)) @@ -125,13 +143,9 @@ void LinkerDriver::main(ArrayRef ArgsArr) { initSymbols(); opt::InputArgList Args = parseArgs(&Alloc, ArgsArr); + readConfigs(Args); createFiles(Args); - - // Traditional linkers can generate re-linkable object files instead - // of executables or DSOs. We don't support that since the feature - // does not seem to provide more value than the static archiver. - if (Args.hasArg(OPT_relocatable)) - error("-r option is not supported. Use 'ar' command instead."); + checkOptions(Args); switch (Config->EKind) { case ELF32LEKind: @@ -151,7 +165,8 @@ void LinkerDriver::main(ArrayRef ArgsArr) { } } -void LinkerDriver::createFiles(opt::InputArgList &Args) { +// Initializes Config members by the command line options. +void LinkerDriver::readConfigs(opt::InputArgList &Args) { for (auto *Arg : Args.filtered(OPT_L)) Config->SearchPaths.push_back(Arg->getValue()); @@ -162,10 +177,9 @@ void LinkerDriver::createFiles(opt::InputArgList &Args) { Config->RPath = llvm::join(RPaths.begin(), RPaths.end(), ":"); if (auto *Arg = Args.getLastArg(OPT_m)) { + // Parse ELF{32,64}{LE,BE} and CPU type. StringRef S = Arg->getValue(); - std::pair P = parseEmulation(S); - Config->EKind = P.first; - Config->EMachine = P.second; + std::tie(Config->EKind, Config->EMachine) = parseEmulation(S); Config->Emulation = S; } @@ -217,7 +231,9 @@ void LinkerDriver::createFiles(opt::InputArgList &Args) { for (auto *Arg : Args.filtered(OPT_undefined)) Config->Undefined.push_back(Arg->getValue()); +} +void LinkerDriver::createFiles(opt::InputArgList &Args) { for (auto *Arg : Args) { switch (Arg->getOption().getID()) { case OPT_l: @@ -250,9 +266,6 @@ void LinkerDriver::createFiles(opt::InputArgList &Args) { if (Files.empty()) error("no input files."); - - if (Config->GnuHash && Config->EMachine == EM_MIPS) - error("The .gnu.hash section is not compatible with the MIPS target."); } template void LinkerDriver::link(opt::InputArgList &Args) { @@ -261,7 +274,10 @@ template void LinkerDriver::link(opt::InputArgList &Args) { if (!Config->Shared) { // Add entry symbol. - if (Config->Entry.empty()) + // + // There is no entry symbol for AMDGPU binaries, so skip adding one to avoid + // having and undefined symbol. + if (Config->Entry.empty() && Config->EMachine != EM_AMDGPU) Config->Entry = (Config->EMachine == EM_MIPS) ? "__start" : "_start"; // In the assembly for 32 bit x86 the _GLOBAL_OFFSET_TABLE_ symbol @@ -288,8 +304,9 @@ template void LinkerDriver::link(opt::InputArgList &Args) { if (Config->EMachine == EM_MIPS) { // On MIPS O32 ABI, _gp_disp is a magic symbol designates offset between - // start of function and gp pointer into GOT. - Config->MipsGpDisp = Symtab.addIgnored("_gp_disp"); + // start of function and gp pointer into GOT. Use 'strong' variant of + // the addIgnored to prevent '_gp_disp' substitution. + Config->MipsGpDisp = Symtab.addIgnoredStrong("_gp_disp"); // Define _gp for MIPS. st_value of _gp symbol will be updated by Writer // so that it points to an absolute address which is relative to GOT. @@ -304,6 +321,9 @@ template void LinkerDriver::link(opt::InputArgList &Args) { for (StringRef S : Config->Undefined) Symtab.addUndefinedOpt(S); + for (auto *Arg : Args.filtered(OPT_wrap)) + Symtab.wrap(Arg->getValue()); + if (Config->OutputFile.empty()) Config->OutputFile = "a.out"; diff --git a/ELF/Driver.h b/ELF/Driver.h index bfae2b3f4dfa..720ef46dc710 100644 --- a/ELF/Driver.h +++ b/ELF/Driver.h @@ -26,14 +26,12 @@ void link(ArrayRef Args); class LinkerDriver { public: void main(ArrayRef Args); - void createFiles(llvm::opt::InputArgList &Args); - template void link(llvm::opt::InputArgList &Args); - void addFile(StringRef Path); private: - template