diff --git a/lld/include/lld/Driver/WinLinkInputGraph.h b/lld/include/lld/Driver/WinLinkInputGraph.h index eb698415bafa..78c60b23ecef 100644 --- a/lld/include/lld/Driver/WinLinkInputGraph.h +++ b/lld/include/lld/Driver/WinLinkInputGraph.h @@ -61,6 +61,27 @@ public: virtual ErrorOr getPath(const LinkingContext &ctx) const; }; +/// \brief Represents a ELF control node +class PECOFFGroup : public Group { +public: + PECOFFGroup() : Group(0) {} + + static inline bool classof(const InputElement *a) { + return a->kind() == InputElement::Kind::Control; + } + + virtual bool validate() { return true; } + virtual bool dump(raw_ostream &) { return true; } + + /// \brief Parse the group members. + error_code parse(const LinkingContext &ctx, raw_ostream &diagnostics) { + for (auto &elem : _elements) + if (error_code ec = elem->parse(ctx, diagnostics)) + return ec; + return error_code::success(); + } +}; + } // namespace lld #endif diff --git a/lld/include/lld/ReaderWriter/PECOFFLinkingContext.h b/lld/include/lld/ReaderWriter/PECOFFLinkingContext.h index 5e8fc297f5b4..2fad8c6a1f9d 100644 --- a/lld/include/lld/ReaderWriter/PECOFFLinkingContext.h +++ b/lld/include/lld/ReaderWriter/PECOFFLinkingContext.h @@ -30,6 +30,7 @@ using llvm::COFF::WindowsSubsystem; static const uint8_t DEFAULT_DOS_STUB[128] = {'M', 'Z'}; namespace lld { +class Group; class PECOFFLinkingContext : public LinkingContext { public: @@ -235,6 +236,9 @@ public: return false; } + void setLibraryGroup(Group *group) { _libraryGroup = group; } + Group *getLibraryGroup() const { return _libraryGroup; } + protected: /// Method to create a internal file for the entry symbol virtual std::unique_ptr createEntrySymbolFile() const; @@ -299,6 +303,9 @@ private: // a small DOS program that prints out a message "This program requires // Microsoft Windows." This feature was somewhat useful before Windows 95. ArrayRef _dosStub; + + // The PECOFFGroup that contains all the .lib files. + Group *_libraryGroup; }; } // end namespace lld diff --git a/lld/lib/Driver/WinLinkDriver.cpp b/lld/lib/Driver/WinLinkDriver.cpp index c9d113031f55..1a3fd51d50bb 100644 --- a/lld/lib/Driver/WinLinkDriver.cpp +++ b/lld/lib/Driver/WinLinkDriver.cpp @@ -564,6 +564,19 @@ parseArgs(int argc, const char *argv[], raw_ostream &diagnostics, return parsedArgs; } +// Returns true if the given file node has already been added to the input +// graph. +bool hasLibrary(const PECOFFLinkingContext &ctx, FileNode *fileNode) { + ErrorOr path = fileNode->getPath(ctx); + if (!path) + return false; + for (std::unique_ptr &p : ctx.getLibraryGroup()->elements()) + if (auto *f = dyn_cast(p.get())) + if (*path == *f->getPath(ctx)) + return true; + return false; +} + } // namespace // @@ -597,7 +610,8 @@ WinLinkDriver::parse(int argc, const char *argv[], PECOFFLinkingContext &ctx, return false; // The list of input files. - std::vector > inputElements; + std::vector > files; + std::vector > libraries; // Handle /help if (parsedArgs->getLastArg(OPT_help)) { @@ -899,8 +913,7 @@ WinLinkDriver::parse(int argc, const char *argv[], PECOFFLinkingContext &ctx, }; std::stable_sort(inputFiles.begin(), inputFiles.end(), compfn); for (StringRef path : inputFiles) - inputElements.push_back(std::unique_ptr( - new PECOFFFileNode(ctx, path))); + files.push_back(std::unique_ptr(new PECOFFFileNode(ctx, path))); // Use the default entry name if /entry option is not given. if (ctx.entrySymbolName().empty() && !parsedArgs->getLastArg(OPT_noentry)) @@ -933,9 +946,9 @@ WinLinkDriver::parse(int argc, const char *argv[], PECOFFLinkingContext &ctx, // but useful for us to test lld on Unix. if (llvm::opt::Arg *dashdash = parsedArgs->getLastArg(OPT_DASH_DASH)) { for (const StringRef value : dashdash->getValues()) { - std::unique_ptr elem( + std::unique_ptr elem( new PECOFFFileNode(ctx, ctx.allocate(value))); - inputElements.push_back(std::move(elem)); + files.push_back(std::move(elem)); } } @@ -944,10 +957,10 @@ WinLinkDriver::parse(int argc, const char *argv[], PECOFFLinkingContext &ctx, if (!ctx.getNoDefaultLibAll()) for (const StringRef path : defaultLibs) if (!ctx.hasNoDefaultLib(path)) - inputElements.push_back(std::unique_ptr( - new PECOFFLibraryNode(ctx, path))); + libraries.push_back(std::unique_ptr( + new PECOFFLibraryNode(ctx, ctx.allocate(path.lower())))); - if (inputElements.empty() && !isReadingDirectiveSection) { + if (files.empty() && !isReadingDirectiveSection) { diagnostics << "No input files\n"; return false; } @@ -956,7 +969,7 @@ WinLinkDriver::parse(int argc, const char *argv[], PECOFFLinkingContext &ctx, // constructed by replacing an extension of the first input file // with ".exe". if (ctx.outputPath().empty()) { - StringRef path = *dyn_cast(&*inputElements[0])->getPath(ctx); + StringRef path = *dyn_cast(&*files[0])->getPath(ctx); ctx.setOutputPath(replaceExtension(ctx, path, ".exe")); } @@ -968,19 +981,32 @@ WinLinkDriver::parse(int argc, const char *argv[], PECOFFLinkingContext &ctx, ctx.setManifestOutputPath(ctx.allocate(path)); } - // If the core linker already started, we need to explicitly call parse() for - // each input element, because the pass to parse input files in Driver::link - // has already done. - if (isReadingDirectiveSection) - for (auto &e : inputElements) - if (e->parse(ctx, diagnostics)) - return false; - // Add the input files to the input graph. if (!ctx.hasInputGraph()) ctx.setInputGraph(std::unique_ptr(new InputGraph())); - for (auto &e : inputElements) - ctx.inputGraph().addInputElement(std::move(e)); + for (auto &file : files) { + if (isReadingDirectiveSection) + if (file->parse(ctx, diagnostics)) + return false; + ctx.inputGraph().addInputElement(std::move(file)); + } + + // Add the library group to the input graph. + if (!isReadingDirectiveSection) { + auto group = std::unique_ptr(new PECOFFGroup()); + ctx.setLibraryGroup(group.get()); + ctx.inputGraph().addInputElement(std::move(group)); + } + + // Add the library files to the library group. + for (std::unique_ptr &lib : libraries) { + if (!hasLibrary(ctx, lib.get())) { + if (isReadingDirectiveSection) + if (lib->parse(ctx, diagnostics)) + return false; + ctx.getLibraryGroup()->processInputElement(std::move(lib)); + } + } // Validate the combination of options used. return ctx.validate(diagnostics); diff --git a/lld/unittests/DriverTests/DriverTest.h b/lld/unittests/DriverTests/DriverTest.h index 2901c01c1bc1..69217e205103 100644 --- a/lld/unittests/DriverTests/DriverTest.h +++ b/lld/unittests/DriverTests/DriverTest.h @@ -32,13 +32,24 @@ protected: int inputFileCount() { return linkingContext()->inputGraph().size(); } // Convenience method for getting i'th input files name. - std::string inputFile(unsigned index) { + std::string inputFile(int index) { const InputElement &inputElement = linkingContext()->inputGraph()[index]; if (inputElement.kind() == InputElement::Kind::File) return *dyn_cast(&inputElement)->getPath(*linkingContext()); llvm_unreachable("not handling other types of input files"); } + // Convenience method for getting i'th input files name. + std::string inputFile(int index1, int index2) { + Group *group = dyn_cast(&linkingContext()->inputGraph()[index1]); + if (!group) + llvm_unreachable("not handling other types of input files"); + FileNode *file = dyn_cast(group->elements()[index2].get()); + if (!file) + llvm_unreachable("not handling other types of input files"); + return *file->getPath(*linkingContext()); + } + // For unit tests to call driver with various command lines. bool parse(const char *args, ...) { // Construct command line options from varargs. diff --git a/lld/unittests/DriverTests/WinLinkDriverTest.cpp b/lld/unittests/DriverTests/WinLinkDriverTest.cpp index bba80ab964f5..ae108e286555 100644 --- a/lld/unittests/DriverTests/WinLinkDriverTest.cpp +++ b/lld/unittests/DriverTests/WinLinkDriverTest.cpp @@ -38,7 +38,7 @@ TEST_F(WinLinkParserTest, Basic) { EXPECT_EQ(llvm::COFF::IMAGE_FILE_MACHINE_I386, _context.getMachineType()); EXPECT_EQ("a.exe", _context.outputPath()); EXPECT_EQ("_start", _context.entrySymbolName()); - EXPECT_EQ(3, inputFileCount()); + EXPECT_EQ(4, inputFileCount()); EXPECT_EQ("a.obj", inputFile(0)); EXPECT_EQ("b.obj", inputFile(1)); EXPECT_EQ("c.obj", inputFile(2)); @@ -77,7 +77,7 @@ TEST_F(WinLinkParserTest, StartsWithHyphen) { parse("link.exe", "-subsystem:console", "-out:a.exe", "a.obj", nullptr)); EXPECT_EQ(llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_CUI, _context.getSubsystem()); EXPECT_EQ("a.exe", _context.outputPath()); - EXPECT_EQ(1, inputFileCount()); + EXPECT_EQ(2, inputFileCount()); EXPECT_EQ("a.obj", inputFile(0)); } @@ -86,7 +86,7 @@ TEST_F(WinLinkParserTest, UppercaseOption) { parse("link.exe", "/SUBSYSTEM:CONSOLE", "/OUT:a.exe", "a.obj", nullptr)); EXPECT_EQ(llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_CUI, _context.getSubsystem()); EXPECT_EQ("a.exe", _context.outputPath()); - EXPECT_EQ(1, inputFileCount()); + EXPECT_EQ(2, inputFileCount()); EXPECT_EQ("a.obj", inputFile(0)); } @@ -109,7 +109,7 @@ TEST_F(WinLinkParserTest, NoInputFiles) { TEST_F(WinLinkParserTest, NoFileExtension) { EXPECT_TRUE(parse("link.exe", "foo", "bar", nullptr)); EXPECT_EQ("foo.exe", _context.outputPath()); - EXPECT_EQ(2, inputFileCount()); + EXPECT_EQ(3, inputFileCount()); EXPECT_EQ("foo.obj", inputFile(0)); EXPECT_EQ("bar.obj", inputFile(1)); } @@ -117,7 +117,7 @@ TEST_F(WinLinkParserTest, NoFileExtension) { TEST_F(WinLinkParserTest, NonStandardFileExtension) { EXPECT_TRUE(parse("link.exe", "foo.o", nullptr)); EXPECT_EQ("foo.exe", _context.outputPath()); - EXPECT_EQ(1, inputFileCount()); + EXPECT_EQ(2, inputFileCount()); EXPECT_EQ("foo.o", inputFile(0)); } @@ -137,7 +137,7 @@ TEST_F(WinLinkParserTest, Libpath) { TEST_F(WinLinkParserTest, InputOrder) { EXPECT_TRUE(parse("link.exe", "b.lib", "b.obj", "c.obj", "a.lib", "a.obj", nullptr)); - EXPECT_EQ(5, inputFileCount()); + EXPECT_EQ(6, inputFileCount()); EXPECT_EQ("b.obj", inputFile(0)); EXPECT_EQ("c.obj", inputFile(1)); EXPECT_EQ("a.obj", inputFile(2)); @@ -319,19 +319,18 @@ TEST_F(WinLinkParserTest, SectionMultiple) { TEST_F(WinLinkParserTest, DefaultLib) { EXPECT_TRUE(parse("link.exe", "/defaultlib:user32.lib", "/defaultlib:kernel32", "a.obj", nullptr)); - EXPECT_EQ(3, inputFileCount()); + EXPECT_EQ(2, inputFileCount()); EXPECT_EQ("a.obj", inputFile(0)); - EXPECT_EQ("user32.lib", inputFile(1)); - EXPECT_EQ("kernel32.lib", inputFile(2)); + EXPECT_EQ("user32.lib", inputFile(1, 0)); + EXPECT_EQ("kernel32.lib", inputFile(1, 1)); } TEST_F(WinLinkParserTest, DefaultLibDuplicates) { EXPECT_TRUE(parse("link.exe", "/defaultlib:user32.lib", "/defaultlib:user32.lib", "a.obj", nullptr)); - EXPECT_EQ(3, inputFileCount()); + EXPECT_EQ(2, inputFileCount()); EXPECT_EQ("a.obj", inputFile(0)); - EXPECT_EQ("user32.lib", inputFile(1)); - EXPECT_EQ("user32.lib", inputFile(2)); + EXPECT_EQ("user32.lib", inputFile(1, 0)); } TEST_F(WinLinkParserTest, NoDefaultLib) { @@ -340,13 +339,13 @@ TEST_F(WinLinkParserTest, NoDefaultLib) { nullptr)); EXPECT_EQ(2, inputFileCount()); EXPECT_EQ("a.obj", inputFile(0)); - EXPECT_EQ("kernel32.lib", inputFile(1)); + EXPECT_EQ("kernel32.lib", inputFile(1, 0)); } TEST_F(WinLinkParserTest, NoDefaultLibAll) { EXPECT_TRUE(parse("link.exe", "/defaultlib:user32.lib", "/defaultlib:kernel32", "/nodefaultlib", "a.obj", nullptr)); - EXPECT_EQ(1, inputFileCount()); + EXPECT_EQ(2, inputFileCount()); EXPECT_EQ("a.obj", inputFile(0)); } @@ -356,7 +355,7 @@ TEST_F(WinLinkParserTest, DisallowLib) { nullptr)); EXPECT_EQ(2, inputFileCount()); EXPECT_EQ("a.obj", inputFile(0)); - EXPECT_EQ("kernel32.lib", inputFile(1)); + EXPECT_EQ("kernel32.lib", inputFile(1, 0)); } // @@ -567,7 +566,7 @@ TEST_F(WinLinkParserTest, Ignore) { "/ignoreidl", "/implib:foo", "/safeseh", "/safeseh:no", "/functionpadmin", "a.obj", nullptr)); EXPECT_EQ("", errorMessage()); - EXPECT_EQ(1, inputFileCount()); + EXPECT_EQ(2, inputFileCount()); EXPECT_EQ("a.obj", inputFile(0)); } @@ -580,7 +579,7 @@ TEST_F(WinLinkParserTest, DashDash) { "--", "b.obj", "-c.obj", nullptr)); EXPECT_EQ(llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_CUI, _context.getSubsystem()); EXPECT_EQ("a.exe", _context.outputPath()); - EXPECT_EQ(3, inputFileCount()); + EXPECT_EQ(4, inputFileCount()); EXPECT_EQ("a.obj", inputFile(0)); EXPECT_EQ("b.obj", inputFile(1)); EXPECT_EQ("-c.obj", inputFile(2));