diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2015-03-24 21:31:36 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2015-03-24 21:31:36 +0000 |
commit | fb911942f1434f3d1750f83f25f5e42c80e60638 (patch) | |
tree | 1678c4a4f0182e4029a86d135aa4a1b7d09e3c41 /unittests/DriverTests |
Notes
Diffstat (limited to 'unittests/DriverTests')
-rw-r--r-- | unittests/DriverTests/CMakeLists.txt | 14 | ||||
-rw-r--r-- | unittests/DriverTests/DarwinLdDriverTest.cpp | 240 | ||||
-rw-r--r-- | unittests/DriverTests/DriverTest.h | 61 | ||||
-rw-r--r-- | unittests/DriverTests/GnuLdDriverTest.cpp | 284 | ||||
-rw-r--r-- | unittests/DriverTests/Makefile | 20 | ||||
-rw-r--r-- | unittests/DriverTests/UniversalDriverTest.cpp | 33 | ||||
-rw-r--r-- | unittests/DriverTests/WinLinkDriverTest.cpp | 728 | ||||
-rw-r--r-- | unittests/DriverTests/WinLinkModuleDefTest.cpp | 155 |
8 files changed, 1535 insertions, 0 deletions
diff --git a/unittests/DriverTests/CMakeLists.txt b/unittests/DriverTests/CMakeLists.txt new file mode 100644 index 0000000000000..59d56d459582f --- /dev/null +++ b/unittests/DriverTests/CMakeLists.txt @@ -0,0 +1,14 @@ +add_lld_unittest(DriverTests + UniversalDriverTest.cpp + GnuLdDriverTest.cpp + DarwinLdDriverTest.cpp + WinLinkDriverTest.cpp + WinLinkModuleDefTest.cpp + ) + +target_link_libraries(DriverTests + lldDriver + lldCore + lldPECOFF + lldMachO + ) diff --git a/unittests/DriverTests/DarwinLdDriverTest.cpp b/unittests/DriverTests/DarwinLdDriverTest.cpp new file mode 100644 index 0000000000000..1c77a05f58567 --- /dev/null +++ b/unittests/DriverTests/DarwinLdDriverTest.cpp @@ -0,0 +1,240 @@ +//===- lld/unittest/DarwinLdDriverTest.cpp --------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Darwin's ld driver tests. +/// +//===----------------------------------------------------------------------===// + +#include "DriverTest.h" +#include "lld/ReaderWriter/MachOLinkingContext.h" +#include "llvm/Support/MachO.h" + +using namespace llvm; +using namespace lld; + +namespace { +class DarwinLdParserTest + : public ParserTest<DarwinLdDriver, MachOLinkingContext> { +protected: + const LinkingContext *linkingContext() override { return &_ctx; } +}; +} + +TEST_F(DarwinLdParserTest, Basic) { + EXPECT_TRUE(parse("ld", "foo.o", "bar.o", "-arch", "i386", nullptr)); + EXPECT_FALSE(_ctx.allowRemainingUndefines()); + EXPECT_FALSE(_ctx.deadStrip()); + EXPECT_EQ(2, inputFileCount()); + EXPECT_EQ("foo.o", inputFile(0)); + EXPECT_EQ("bar.o", inputFile(1)); +} + +TEST_F(DarwinLdParserTest, Output) { + EXPECT_TRUE(parse("ld", "-o", "my.out", "foo.o", "-arch", "i386", nullptr)); + EXPECT_EQ("my.out", _ctx.outputPath()); +} + +TEST_F(DarwinLdParserTest, Dylib) { + EXPECT_TRUE(parse("ld", "-dylib", "foo.o", "-arch", "i386", nullptr)); + EXPECT_EQ(llvm::MachO::MH_DYLIB, _ctx.outputMachOType()); +} + +TEST_F(DarwinLdParserTest, Relocatable) { + EXPECT_TRUE(parse("ld", "-r", "foo.o", "-arch", "i386", nullptr)); + EXPECT_EQ(llvm::MachO::MH_OBJECT, _ctx.outputMachOType()); +} + +TEST_F(DarwinLdParserTest, Bundle) { + EXPECT_TRUE(parse("ld", "-bundle", "foo.o", "-arch", "i386", nullptr)); + EXPECT_EQ(llvm::MachO::MH_BUNDLE, _ctx.outputMachOType()); +} + +TEST_F(DarwinLdParserTest, Preload) { + EXPECT_TRUE(parse("ld", "-preload", "foo.o", "-arch", "i386", nullptr)); + EXPECT_EQ(llvm::MachO::MH_PRELOAD, _ctx.outputMachOType()); +} + +TEST_F(DarwinLdParserTest, Static) { + EXPECT_TRUE(parse("ld", "-static", "foo.o", "-arch", "i386", nullptr)); + EXPECT_EQ(llvm::MachO::MH_EXECUTE, _ctx.outputMachOType()); +} + +TEST_F(DarwinLdParserTest, Entry) { + EXPECT_TRUE(parse("ld", "-e", "entryFunc", "foo.o", "-arch", "i386",nullptr)); + EXPECT_EQ("entryFunc", _ctx.entrySymbolName()); +} + +TEST_F(DarwinLdParserTest, DeadStrip) { + EXPECT_TRUE(parse("ld", "-arch", "x86_64", "-dead_strip", "foo.o", nullptr)); + EXPECT_TRUE(_ctx.deadStrip()); +} + +TEST_F(DarwinLdParserTest, DeadStripRootsExe) { + EXPECT_TRUE(parse("ld", "-arch", "x86_64", "-dead_strip", "foo.o", nullptr)); + EXPECT_FALSE(_ctx.globalsAreDeadStripRoots()); +} + +TEST_F(DarwinLdParserTest, DeadStripRootsDylib) { + EXPECT_TRUE(parse("ld", "-arch", "x86_64", "-dylib", "-dead_strip", "foo.o", + nullptr)); + EXPECT_TRUE(_ctx.globalsAreDeadStripRoots()); +} + +TEST_F(DarwinLdParserTest, Arch) { + EXPECT_TRUE(parse("ld", "-arch", "x86_64", "foo.o", nullptr)); + EXPECT_EQ(MachOLinkingContext::arch_x86_64, _ctx.arch()); + EXPECT_EQ((uint32_t)llvm::MachO::CPU_TYPE_X86_64, _ctx.getCPUType()); + EXPECT_EQ(llvm::MachO::CPU_SUBTYPE_X86_64_ALL, _ctx.getCPUSubType()); +} + +TEST_F(DarwinLdParserTest, Arch_x86) { + EXPECT_TRUE(parse("ld", "-arch", "i386", "foo.o", nullptr)); + EXPECT_EQ(MachOLinkingContext::arch_x86, _ctx.arch()); + EXPECT_EQ((uint32_t)llvm::MachO::CPU_TYPE_I386, _ctx.getCPUType()); + EXPECT_EQ(llvm::MachO::CPU_SUBTYPE_X86_ALL, _ctx.getCPUSubType()); +} + +TEST_F(DarwinLdParserTest, Arch_armv6) { + EXPECT_TRUE(parse("ld", "-arch", "armv6", "foo.o", nullptr)); + EXPECT_EQ(MachOLinkingContext::arch_armv6, _ctx.arch()); + EXPECT_EQ((uint32_t)llvm::MachO::CPU_TYPE_ARM, _ctx.getCPUType()); + EXPECT_EQ(llvm::MachO::CPU_SUBTYPE_ARM_V6, _ctx.getCPUSubType()); +} + +TEST_F(DarwinLdParserTest, Arch_armv7) { + EXPECT_TRUE(parse("ld", "-arch", "armv7", "foo.o", nullptr)); + EXPECT_EQ(MachOLinkingContext::arch_armv7, _ctx.arch()); + EXPECT_EQ((uint32_t)llvm::MachO::CPU_TYPE_ARM, _ctx.getCPUType()); + EXPECT_EQ(llvm::MachO::CPU_SUBTYPE_ARM_V7, _ctx.getCPUSubType()); +} + +TEST_F(DarwinLdParserTest, Arch_armv7s) { + EXPECT_TRUE(parse("ld", "-arch", "armv7s", "foo.o", nullptr)); + EXPECT_EQ(MachOLinkingContext::arch_armv7s, _ctx.arch()); + EXPECT_EQ((uint32_t)llvm::MachO::CPU_TYPE_ARM, _ctx.getCPUType()); + EXPECT_EQ(llvm::MachO::CPU_SUBTYPE_ARM_V7S, _ctx.getCPUSubType()); +} + +TEST_F(DarwinLdParserTest, MinMacOSX10_7) { + EXPECT_TRUE(parse("ld", "-macosx_version_min", "10.7", "foo.o", + "-arch", "x86_64", nullptr)); + EXPECT_EQ(MachOLinkingContext::OS::macOSX, _ctx.os()); + EXPECT_TRUE(_ctx.minOS("10.7", "")); + EXPECT_FALSE(_ctx.minOS("10.8", "")); +} + +TEST_F(DarwinLdParserTest, MinMacOSX10_8) { + EXPECT_TRUE(parse("ld", "-macosx_version_min", "10.8.3", "foo.o", + "-arch", "x86_64", nullptr)); + EXPECT_EQ(MachOLinkingContext::OS::macOSX, _ctx.os()); + EXPECT_TRUE(_ctx.minOS("10.7", "")); + EXPECT_TRUE(_ctx.minOS("10.8", "")); +} + +TEST_F(DarwinLdParserTest, iOS5) { + EXPECT_TRUE(parse("ld", "-ios_version_min", "5.0", "foo.o", + "-arch", "armv7", nullptr)); + EXPECT_EQ(MachOLinkingContext::OS::iOS, _ctx.os()); + EXPECT_TRUE(_ctx.minOS("", "5.0")); + EXPECT_FALSE(_ctx.minOS("", "6.0")); +} + +TEST_F(DarwinLdParserTest, iOS6) { + EXPECT_TRUE(parse("ld", "-ios_version_min", "6.0", "foo.o", "-arch", "armv7", + nullptr)); + EXPECT_EQ(MachOLinkingContext::OS::iOS, _ctx.os()); + EXPECT_TRUE(_ctx.minOS("", "5.0")); + EXPECT_TRUE(_ctx.minOS("", "6.0")); +} + +TEST_F(DarwinLdParserTest, iOS_Simulator5) { + EXPECT_TRUE(parse("ld", "-ios_simulator_version_min", "5.0", "a.o", + "-arch", "i386", nullptr)); + EXPECT_EQ(MachOLinkingContext::OS::iOS_simulator, _ctx.os()); + EXPECT_TRUE(_ctx.minOS("", "5.0")); + EXPECT_FALSE(_ctx.minOS("", "6.0")); +} + +TEST_F(DarwinLdParserTest, iOS_Simulator6) { + EXPECT_TRUE(parse("ld", "-ios_simulator_version_min", "6.0", "a.o", + "-arch", "i386", nullptr)); + EXPECT_EQ(MachOLinkingContext::OS::iOS_simulator, _ctx.os()); + EXPECT_TRUE(_ctx.minOS("", "5.0")); + EXPECT_TRUE(_ctx.minOS("", "6.0")); +} + +TEST_F(DarwinLdParserTest, compatibilityVersion) { + EXPECT_TRUE( + parse("ld", "-dylib", "-compatibility_version", "1.2.3", "a.o", + "-arch", "i386",nullptr)); + EXPECT_EQ(_ctx.compatibilityVersion(), 0x10203U); +} + +TEST_F(DarwinLdParserTest, compatibilityVersionInvalidType) { + EXPECT_FALSE(parse("ld", "-bundle", "-compatibility_version", "1.2.3", "a.o", + "-arch", "i386",nullptr)); +} + +TEST_F(DarwinLdParserTest, compatibilityVersionInvalidValue) { + EXPECT_FALSE(parse("ld", "-bundle", "-compatibility_version", "1,2,3", "a.o", + "-arch", "i386", nullptr)); +} + +TEST_F(DarwinLdParserTest, currentVersion) { + EXPECT_TRUE( + parse("ld", "-dylib", "-current_version", "1.2.3", "a.o", "-arch", "i386", + nullptr)); + EXPECT_EQ(_ctx.currentVersion(), 0x10203U); +} + +TEST_F(DarwinLdParserTest, currentVersionInvalidType) { + EXPECT_FALSE( + parse("ld", "-bundle", "-current_version", "1.2.3", "a.o", + "-arch", "i386", nullptr)); +} + +TEST_F(DarwinLdParserTest, currentVersionInvalidValue) { + EXPECT_FALSE( + parse("ld", "-bundle", "-current_version", "1,2,3", "a.o", + "-arch", "i386", nullptr)); +} + +TEST_F(DarwinLdParserTest, bundleLoader) { + EXPECT_TRUE( + parse("ld", "-bundle", "-bundle_loader", "/bin/ls", "a.o", + "-arch", "i386", nullptr)); + EXPECT_EQ(_ctx.bundleLoader(), "/bin/ls"); +} + +TEST_F(DarwinLdParserTest, bundleLoaderInvalidType) { + EXPECT_FALSE(parse("ld", "-bundle_loader", "/bin/ls", "a.o", "-arch", "i386", + nullptr)); +} + +TEST_F(DarwinLdParserTest, deadStrippableDylib) { + EXPECT_TRUE( + parse("ld", "-dylib", "-mark_dead_strippable_dylib", "a.o", + "-arch", "i386", nullptr)); + EXPECT_EQ(true, _ctx.deadStrippableDylib()); +} + +TEST_F(DarwinLdParserTest, deadStrippableDylibInvalidType) { + EXPECT_FALSE(parse("ld", "-mark_dead_strippable_dylib", "a.o", + "-arch", "i386", nullptr)); +} + +TEST_F(DarwinLdParserTest, llvmOptions) { + EXPECT_TRUE(parse("ld", "-mllvm", "-debug-only", "-mllvm", "foo", "a.o", + "-arch", "i386", nullptr)); + const std::vector<const char *> &options = _ctx.llvmOptions(); + EXPECT_EQ(options.size(), 2UL); + EXPECT_EQ(strcmp(options[0],"-debug-only"), 0); + EXPECT_EQ(strcmp(options[1],"foo"), 0); +} diff --git a/unittests/DriverTests/DriverTest.h b/unittests/DriverTests/DriverTest.h new file mode 100644 index 0000000000000..2349132ee2ce7 --- /dev/null +++ b/unittests/DriverTests/DriverTest.h @@ -0,0 +1,61 @@ +//===- lld/unittest/DriverTest.h ------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lld/Driver/Driver.h" +#include "llvm/Support/raw_ostream.h" +#include "gtest/gtest.h" +#include <stdarg.h> + +namespace { + +using namespace llvm; +using namespace lld; + +template<typename D, typename T> +class ParserTest : public testing::Test { +protected: + + virtual const LinkingContext *linkingContext() = 0; + + std::string &errorMessage() { return _errorMessage; } + + // Convenience method for getting number of input files. + int inputFileCount() { + return linkingContext()->getNodes().size(); + } + + // Convenience method for getting i'th input files name. + std::string inputFile(int index) { + Node &node = *linkingContext()->getNodes()[index]; + if (node.kind() == Node::Kind::File) + return cast<FileNode>(&node)->getFile()->path(); + llvm_unreachable("not handling other types of input files"); + } + + // For unit tests to call driver with various command lines. + bool parse(const char *args, ...) { + // Construct command line options from varargs. + std::vector<const char *> vec; + vec.push_back(args); + va_list ap; + va_start(ap, args); + while (const char *arg = va_arg(ap, const char *)) + vec.push_back(arg); + va_end(ap); + + // Call the parser. + raw_string_ostream os(_errorMessage); + return D::parse(vec.size(), &vec[0], _ctx, os); + } + + T _ctx; + std::string _errorMessage; +}; + +} diff --git a/unittests/DriverTests/GnuLdDriverTest.cpp b/unittests/DriverTests/GnuLdDriverTest.cpp new file mode 100644 index 0000000000000..92eb920f01801 --- /dev/null +++ b/unittests/DriverTests/GnuLdDriverTest.cpp @@ -0,0 +1,284 @@ +//===- lld/unittest/GnuLdDriverTest.cpp -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief GNU ld driver tests. +/// +//===----------------------------------------------------------------------===// + +#include "DriverTest.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "llvm/Support/MemoryBuffer.h" + +using namespace llvm; +using namespace lld; + +namespace { + +class GnuLdParserTest + : public ParserTest<GnuLdDriver, std::unique_ptr<ELFLinkingContext>> { +protected: + const LinkingContext *linkingContext() override { return _ctx.get(); } +}; + +class LinkerScriptTest : public testing::Test { +protected: + void SetUp() override { + llvm::Triple triple(llvm::sys::getDefaultTargetTriple()); + _ctx = std::move(GnuLdDriver::createELFLinkingContext(triple)); + } + + void parse(StringRef script, bool nostdlib = false) { + std::unique_ptr<MemoryBuffer> mb = MemoryBuffer::getMemBuffer( + script, "foo.so"); + std::string s; + raw_string_ostream out(s); + std::error_code ec = + GnuLdDriver::evalLinkerScript(*_ctx, std::move(mb), out, nostdlib); + EXPECT_FALSE(ec); + }; + + std::unique_ptr<ELFLinkingContext> _ctx; +}; + +} // anonymous namespace + +TEST_F(GnuLdParserTest, Empty) { + EXPECT_FALSE(parse("ld", nullptr)); + EXPECT_EQ(linkingContext(), nullptr); + EXPECT_EQ("No input files\n", errorMessage()); +} + +// -o + +TEST_F(GnuLdParserTest, Output) { + EXPECT_TRUE(parse("ld", "a.o", "-o", "foo", nullptr)); + EXPECT_EQ("foo", _ctx->outputPath()); +} + +TEST_F(GnuLdParserTest, OutputDefault) { + EXPECT_TRUE(parse("ld", "abc.o", nullptr)); + EXPECT_EQ("a.out", _ctx->outputPath()); +} + +// --noinhibit-exec + +TEST_F(GnuLdParserTest, NoinhibitExec) { + EXPECT_TRUE(parse("ld", "a.o", "--noinhibit-exec", nullptr)); + EXPECT_TRUE(_ctx->allowRemainingUndefines()); +} + +// --entry + +TEST_F(GnuLdParserTest, Entry) { + EXPECT_TRUE(parse("ld", "a.o", "--entry", "foo", nullptr)); + EXPECT_EQ("foo", _ctx->entrySymbolName()); +} + +TEST_F(GnuLdParserTest, EntryShort) { + EXPECT_TRUE(parse("ld", "a.o", "-e", "foo", nullptr)); + EXPECT_EQ("foo", _ctx->entrySymbolName()); +} + +TEST_F(GnuLdParserTest, EntryJoined) { + EXPECT_TRUE(parse("ld", "a.o", "--entry=foo", nullptr)); + EXPECT_EQ("foo", _ctx->entrySymbolName()); +} + +// --export-dynamic + +TEST_F(GnuLdParserTest, ExportDynamic) { + EXPECT_TRUE(parse("ld", "a.o", "--export-dynamic", nullptr)); + EXPECT_TRUE(_ctx->shouldExportDynamic()); +} + +TEST_F(GnuLdParserTest, NoExportDynamic) { + EXPECT_TRUE(parse("ld", "a.o", "--no-export-dynamic", nullptr)); + EXPECT_FALSE(_ctx->shouldExportDynamic()); +} + +// --init + +TEST_F(GnuLdParserTest, Init) { + EXPECT_TRUE(parse("ld", "a.o", "-init", "foo", "-init", "bar", nullptr)); + EXPECT_EQ("bar", _ctx->initFunction()); +} + +TEST_F(GnuLdParserTest, InitJoined) { + EXPECT_TRUE(parse("ld", "a.o", "-init=foo", nullptr)); + EXPECT_EQ("foo", _ctx->initFunction()); +} + +// --soname + +TEST_F(GnuLdParserTest, SOName) { + EXPECT_TRUE(parse("ld", "a.o", "--soname=foo", nullptr)); + EXPECT_EQ("foo", _ctx->sharedObjectName()); +} + +TEST_F(GnuLdParserTest, SONameSingleDash) { + EXPECT_TRUE(parse("ld", "a.o", "-soname=foo", nullptr)); + EXPECT_EQ("foo", _ctx->sharedObjectName()); +} + +TEST_F(GnuLdParserTest, SONameH) { + EXPECT_TRUE(parse("ld", "a.o", "-h", "foo", nullptr)); + EXPECT_EQ("foo", _ctx->sharedObjectName()); +} + +// -rpath + +TEST_F(GnuLdParserTest, Rpath) { + EXPECT_TRUE(parse("ld", "a.o", "-rpath", "foo:bar", nullptr)); + EXPECT_EQ(2, _ctx->getRpathList().size()); + EXPECT_EQ("foo", _ctx->getRpathList()[0]); + EXPECT_EQ("bar", _ctx->getRpathList()[1]); +} + +TEST_F(GnuLdParserTest, RpathEq) { + EXPECT_TRUE(parse("ld", "a.o", "-rpath=foo", nullptr)); + EXPECT_EQ(1, _ctx->getRpathList().size()); + EXPECT_EQ("foo", _ctx->getRpathList()[0]); +} + +// --defsym + +TEST_F(GnuLdParserTest, DefsymDecimal) { + EXPECT_TRUE(parse("ld", "a.o", "--defsym=sym=1000", nullptr)); + assert(_ctx.get()); + auto map = _ctx->getAbsoluteSymbols(); + EXPECT_EQ((size_t)1, map.size()); + EXPECT_EQ((uint64_t)1000, map["sym"]); +} + +TEST_F(GnuLdParserTest, DefsymHexadecimal) { + EXPECT_TRUE(parse("ld", "a.o", "--defsym=sym=0x1000", nullptr)); + auto map = _ctx->getAbsoluteSymbols(); + EXPECT_EQ((size_t)1, map.size()); + EXPECT_EQ((uint64_t)0x1000, map["sym"]); +} + +TEST_F(GnuLdParserTest, DefsymAlias) { + EXPECT_TRUE(parse("ld", "a.o", "--defsym=sym=abc", nullptr)); + auto map = _ctx->getAliases(); + EXPECT_EQ((size_t)1, map.size()); + EXPECT_EQ("abc", map["sym"]); +} + +TEST_F(GnuLdParserTest, DefsymOctal) { + EXPECT_TRUE(parse("ld", "a.o", "--defsym=sym=0777", nullptr)); + auto map = _ctx->getAbsoluteSymbols(); + EXPECT_EQ((size_t)1, map.size()); + EXPECT_EQ((uint64_t)0777, map["sym"]); +} + +TEST_F(GnuLdParserTest, DefsymMisssingSymbol) { + EXPECT_FALSE(parse("ld", "a.o", "--defsym==0", nullptr)); +} + +TEST_F(GnuLdParserTest, DefsymMisssingValue) { + EXPECT_FALSE(parse("ld", "a.o", "--defsym=sym=", nullptr)); +} + +// --as-needed + +TEST_F(GnuLdParserTest, AsNeeded) { + EXPECT_TRUE(parse("ld", "a.o", "--as-needed", "b.o", "c.o", + "--no-as-needed", "d.o", nullptr)); + std::vector<std::unique_ptr<Node>> &nodes = _ctx->getNodes(); + EXPECT_EQ((size_t)4, nodes.size()); + EXPECT_FALSE(cast<FileNode>(nodes[0].get())->asNeeded()); + EXPECT_TRUE(cast<FileNode>(nodes[1].get())->asNeeded()); + EXPECT_TRUE(cast<FileNode>(nodes[2].get())->asNeeded()); + EXPECT_FALSE(cast<FileNode>(nodes[3].get())->asNeeded()); +} + +// Linker script + +TEST_F(LinkerScriptTest, Input) { + parse("INPUT(/x /y)"); + std::vector<std::unique_ptr<Node>> &nodes = _ctx->getNodes(); + EXPECT_EQ((size_t)2, nodes.size()); + EXPECT_EQ("/x", cast<FileNode>(nodes[0].get())->getFile()->path()); + EXPECT_EQ("/y", cast<FileNode>(nodes[1].get())->getFile()->path()); +} + +TEST_F(LinkerScriptTest, Group) { + parse("GROUP(/x /y)"); + std::vector<std::unique_ptr<Node>> &nodes = _ctx->getNodes(); + EXPECT_EQ((size_t)3, nodes.size()); + EXPECT_EQ("/x", cast<FileNode>(nodes[0].get())->getFile()->path()); + EXPECT_EQ("/y", cast<FileNode>(nodes[1].get())->getFile()->path()); + EXPECT_EQ(2, cast<GroupEnd>(nodes[2].get())->getSize()); +} + +TEST_F(LinkerScriptTest, SearchDir) { + parse("SEARCH_DIR(\"/foo/bar\")"); + std::vector<StringRef> paths = _ctx->getSearchPaths(); + EXPECT_EQ((size_t)1, paths.size()); + EXPECT_EQ("/foo/bar", paths[0]); +} + +TEST_F(LinkerScriptTest, Entry) { + parse("ENTRY(blah)"); + EXPECT_EQ("blah", _ctx->entrySymbolName()); +} + +TEST_F(LinkerScriptTest, Output) { + parse("OUTPUT(\"/path/to/output\")"); + EXPECT_EQ("/path/to/output", _ctx->outputPath()); +} + +// Test that search paths are ignored when nostdlib is set. +TEST_F(LinkerScriptTest, IgnoreSearchDirNoStdLib) { + parse("SEARCH_DIR(\"/foo/bar\")", true /*nostdlib*/); + std::vector<StringRef> paths = _ctx->getSearchPaths(); + EXPECT_EQ((size_t)0, paths.size()); +} + +TEST_F(LinkerScriptTest, ExprEval) { + parse("SECTIONS { symbol = 0x4000 + 0x40; \n" + ". = (symbol >= 0x4040)? (0x5001 * 2 & 0xFFF0) << 1 : 0}"); + + EXPECT_EQ((size_t)1, _ctx->linkerScriptSema().getLinkerScripts().size()); + + script::LinkerScript *ls = + _ctx->linkerScriptSema().getLinkerScripts()[0]->get(); + EXPECT_EQ((size_t)1, ls->_commands.size()); + + auto *secs = dyn_cast<const script::Sections>(*ls->_commands.begin()); + EXPECT_TRUE(secs != nullptr); + EXPECT_EQ(2, secs->end() - secs->begin()); + + auto command = secs->begin(); + auto *sa1 = dyn_cast<const script::SymbolAssignment>(*command); + EXPECT_TRUE(sa1 != nullptr); + EXPECT_EQ(script::SymbolAssignment::Simple, sa1->assignmentKind()); + EXPECT_EQ(script::SymbolAssignment::Default, sa1->assignmentVisibility()); + + ++command; + auto *sa2 = dyn_cast<const script::SymbolAssignment>(*command); + EXPECT_TRUE(sa2 != nullptr); + EXPECT_EQ(script::SymbolAssignment::Simple, sa2->assignmentKind()); + EXPECT_EQ(script::SymbolAssignment::Default, sa2->assignmentVisibility()); + + script::Expression::SymbolTableTy mySymbolTable; + auto ans = sa1->expr()->evalExpr(mySymbolTable); + EXPECT_FALSE(ans.getError()); + int64_t result = *ans; + EXPECT_EQ(0x4040, result); + mySymbolTable[sa1->symbol()] = result; + + auto ans2 = sa2->expr()->evalExpr(mySymbolTable); + EXPECT_FALSE(ans2.getError()); + result = *ans2; + EXPECT_EQ(0x14000, result); + EXPECT_EQ(0, sa2->symbol().compare(StringRef("."))); +} + diff --git a/unittests/DriverTests/Makefile b/unittests/DriverTests/Makefile new file mode 100644 index 0000000000000..ae97fb01adbf3 --- /dev/null +++ b/unittests/DriverTests/Makefile @@ -0,0 +1,20 @@ +##===---- unittests/DriverTests/Makefile ----------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===-------------------------------------------------------------------===## + +LLD_LEVEL = ../.. +TESTNAME = DriverTests +USEDLIBS = lldDriver.a lldConfig.a \ + lldELF.a lldMachO.a lldPECOFF.a \ + lldCore.a lldNative.a lldReaderWriter.a \ + lldHexagonELFTarget.a lldMipsELFTarget.a \ + lldX86ELFTarget.a lldExampleSubTarget.a lldX86_64ELFTarget.a \ + lldYAML.a lldAArch64ELFTarget.a lldARMELFTarget.a \ + LLVMObject.a LLVMMCParser.a LLVMMC.a LLVMBitReader.a \ + LLVMCore.a LLVMOption.a LLVMSupport.a +include $(LLD_LEVEL)/unittests/Makefile diff --git a/unittests/DriverTests/UniversalDriverTest.cpp b/unittests/DriverTests/UniversalDriverTest.cpp new file mode 100644 index 0000000000000..8e90ca4d58673 --- /dev/null +++ b/unittests/DriverTests/UniversalDriverTest.cpp @@ -0,0 +1,33 @@ +//===- lld/unittest/UniversalDriverTest.cpp -------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Universal driver tests that depend on the value of argv[0]. +/// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" +#include "lld/Driver/Driver.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace lld; + +TEST(UniversalDriver, flavor) { + const char *args[] = { "gnu-ld" }; + + std::string diags; + raw_string_ostream os(diags); + UniversalDriver::link(array_lengthof(args), args, os); + EXPECT_EQ(os.str().find("failed to determine driver flavor"), + std::string::npos); + EXPECT_NE(os.str().find("No input files"), + std::string::npos); +} diff --git a/unittests/DriverTests/WinLinkDriverTest.cpp b/unittests/DriverTests/WinLinkDriverTest.cpp new file mode 100644 index 0000000000000..c2bc455aa81f5 --- /dev/null +++ b/unittests/DriverTests/WinLinkDriverTest.cpp @@ -0,0 +1,728 @@ +//===- lld/unittest/WinLinkDriverTest.cpp ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Windows link.exe driver tests. +/// +//===----------------------------------------------------------------------===// + +#include "DriverTest.h" +#include "lld/ReaderWriter/PECOFFLinkingContext.h" +#include "llvm/ADT/Optional.h" +#include "llvm/Support/COFF.h" +#include <set> +#include <vector> + +using namespace llvm; +using namespace lld; + +namespace { +class WinLinkParserTest + : public ParserTest<WinLinkDriver, PECOFFLinkingContext> { +protected: + const LinkingContext *linkingContext() override { return &_ctx; } +}; +} + +TEST_F(WinLinkParserTest, Basic) { + EXPECT_TRUE(parse("link.exe", "/subsystem:console", "/out:a.exe", + "-entry:start", "a.obj", "b.obj", "c.obj", nullptr)); + EXPECT_EQ(llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_CUI, _ctx.getSubsystem()); + EXPECT_EQ(llvm::COFF::IMAGE_FILE_MACHINE_I386, _ctx.getMachineType()); + EXPECT_EQ("a.exe", _ctx.outputPath()); + EXPECT_EQ("start", _ctx.getEntrySymbolName()); + EXPECT_EQ(4, inputFileCount()); + EXPECT_EQ("a.obj", inputFile(0)); + EXPECT_EQ("b.obj", inputFile(1)); + EXPECT_EQ("c.obj", inputFile(2)); + EXPECT_TRUE(_ctx.getInputSearchPaths().empty()); + + // Unspecified flags will have default values. + EXPECT_FALSE(_ctx.isDll()); + EXPECT_EQ(6, _ctx.getMinOSVersion().majorVersion); + EXPECT_EQ(0, _ctx.getMinOSVersion().minorVersion); + EXPECT_EQ(0x400000U, _ctx.getBaseAddress()); + EXPECT_EQ(1024 * 1024U, _ctx.getStackReserve()); + EXPECT_EQ(4096U, _ctx.getStackCommit()); + EXPECT_EQ(4096U, _ctx.getSectionDefaultAlignment()); + EXPECT_FALSE(_ctx.allowRemainingUndefines()); + EXPECT_TRUE(_ctx.isNxCompat()); + EXPECT_FALSE(_ctx.getLargeAddressAware()); + EXPECT_TRUE(_ctx.getAllowBind()); + EXPECT_TRUE(_ctx.getAllowIsolation()); + EXPECT_FALSE(_ctx.getSwapRunFromCD()); + EXPECT_FALSE(_ctx.getSwapRunFromNet()); + EXPECT_TRUE(_ctx.getBaseRelocationEnabled()); + EXPECT_TRUE(_ctx.isTerminalServerAware()); + EXPECT_TRUE(_ctx.getDynamicBaseEnabled()); + EXPECT_TRUE(_ctx.getCreateManifest()); + EXPECT_EQ("", _ctx.getManifestDependency()); + EXPECT_FALSE(_ctx.getEmbedManifest()); + EXPECT_EQ(1, _ctx.getManifestId()); + EXPECT_TRUE(_ctx.getManifestUAC()); + EXPECT_EQ("'asInvoker'", _ctx.getManifestLevel()); + EXPECT_EQ("'false'", _ctx.getManifestUiAccess()); + EXPECT_TRUE(_ctx.deadStrip()); + EXPECT_FALSE(_ctx.logInputFiles()); +} + +TEST_F(WinLinkParserTest, StartsWithHyphen) { + EXPECT_TRUE( + parse("link.exe", "-subsystem:console", "-out:a.exe", "a.obj", nullptr)); + EXPECT_EQ(llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_CUI, _ctx.getSubsystem()); + EXPECT_EQ("a.exe", _ctx.outputPath()); + EXPECT_EQ(2, inputFileCount()); + EXPECT_EQ("a.obj", inputFile(0)); +} + +TEST_F(WinLinkParserTest, UppercaseOption) { + EXPECT_TRUE( + parse("link.exe", "/SUBSYSTEM:CONSOLE", "/OUT:a.exe", "a.obj", nullptr)); + EXPECT_EQ(llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_CUI, _ctx.getSubsystem()); + EXPECT_EQ("a.exe", _ctx.outputPath()); + EXPECT_EQ(2, inputFileCount()); + EXPECT_EQ("a.obj", inputFile(0)); +} + +TEST_F(WinLinkParserTest, Mllvm) { + EXPECT_TRUE(parse("link.exe", "/mllvm:-debug", "a.obj", nullptr)); + const std::vector<const char *> &options = _ctx.llvmOptions(); + EXPECT_EQ(1U, options.size()); + EXPECT_STREQ("-debug", options[0]); +} + +TEST_F(WinLinkParserTest, NoInputFiles) { + EXPECT_FALSE(parse("link.exe", nullptr)); + EXPECT_EQ("No input files\n", errorMessage()); +} + +// +// Tests for implicit file extension interpolation. +// + +TEST_F(WinLinkParserTest, NoFileExtension) { + EXPECT_TRUE(parse("link.exe", "foo", "bar", nullptr)); + EXPECT_EQ("foo.exe", _ctx.outputPath()); + EXPECT_EQ(3, inputFileCount()); + EXPECT_EQ("foo.obj", inputFile(0)); + EXPECT_EQ("bar.obj", inputFile(1)); +} + +TEST_F(WinLinkParserTest, NonStandardFileExtension) { + EXPECT_TRUE(parse("link.exe", "foo.o", nullptr)); + EXPECT_EQ("foo.exe", _ctx.outputPath()); + EXPECT_EQ(2, inputFileCount()); + EXPECT_EQ("foo.o", inputFile(0)); +} + +TEST_F(WinLinkParserTest, Libpath) { + EXPECT_TRUE( + parse("link.exe", "/libpath:dir1", "/libpath:dir2", "a.obj", nullptr)); + const std::vector<StringRef> &paths = _ctx.getInputSearchPaths(); + EXPECT_EQ(2U, paths.size()); + EXPECT_EQ("dir1", paths[0]); + EXPECT_EQ("dir2", paths[1]); +} + +// +// Tests for input file order +// + +TEST_F(WinLinkParserTest, InputOrder) { + EXPECT_TRUE(parse("link.exe", "a.lib", "b.obj", "c.obj", "a.lib", "d.obj", + nullptr)); + EXPECT_EQ(5, inputFileCount()); + EXPECT_EQ("b.obj", inputFile(0)); + EXPECT_EQ("c.obj", inputFile(1)); + EXPECT_EQ("d.obj", inputFile(2)); + EXPECT_EQ("a.lib", inputFile(3)); +} + +// +// Tests for command line options that take values. +// + +TEST_F(WinLinkParserTest, AlternateName) { + EXPECT_TRUE(parse("link.exe", "/alternatename:sym1=sym", + "/alternatename:sym2=sym", "a.out", nullptr)); + const std::set<std::string> &aliases = _ctx.getAlternateNames("sym"); + EXPECT_EQ(2U, aliases.size()); + auto it = aliases.begin(); + EXPECT_EQ("sym1", *it++); + EXPECT_EQ("sym2", *it++); +} + +TEST_F(WinLinkParserTest, Export) { + EXPECT_TRUE(parse("link.exe", "/export:foo", "a.out", nullptr)); + const std::vector<PECOFFLinkingContext::ExportDesc> &exports = + _ctx.getDllExports(); + EXPECT_EQ(1U, exports.size()); + EXPECT_EQ("_foo", exports[0].name); + EXPECT_EQ(-1, exports[0].ordinal); + EXPECT_FALSE(exports[0].noname); + EXPECT_FALSE(exports[0].isData); +} + +TEST_F(WinLinkParserTest, ExportWithOptions) { + EXPECT_TRUE(parse("link.exe", "/export:foo,@8,noname,data", + "/export:bar,@10,data", "a.out", nullptr)); + const std::vector<PECOFFLinkingContext::ExportDesc> &exports = + _ctx.getDllExports(); + EXPECT_EQ(2U, exports.size()); + EXPECT_EQ("_foo", exports[0].name); + EXPECT_EQ(8, exports[0].ordinal); + EXPECT_TRUE(exports[0].noname); + EXPECT_TRUE(exports[0].isData); + EXPECT_EQ("_bar", exports[1].name); + EXPECT_EQ(10, exports[1].ordinal); + EXPECT_FALSE(exports[1].noname); + EXPECT_TRUE(exports[1].isData); +} + +TEST_F(WinLinkParserTest, ExportDuplicateExports) { + EXPECT_TRUE( + parse("link.exe", "/export:foo", "/export:foo,@2", "a.out", nullptr)); + const std::vector<PECOFFLinkingContext::ExportDesc> &exports = + _ctx.getDllExports(); + EXPECT_EQ(1U, exports.size()); + EXPECT_EQ("_foo", exports[0].name); + EXPECT_EQ(-1, exports[0].ordinal); +} + +TEST_F(WinLinkParserTest, ExportDuplicateOrdinals) { + EXPECT_FALSE( + parse("link.exe", "/export:foo,@1", "/export:bar,@1", "a.out", nullptr)); +} + +TEST_F(WinLinkParserTest, ExportInvalid1) { + EXPECT_FALSE(parse("link.exe", "/export:foo,@0", "a.out", nullptr)); +} + +TEST_F(WinLinkParserTest, ExportInvalid2) { + EXPECT_FALSE(parse("link.exe", "/export:foo,@65536", "a.out", nullptr)); +} + +TEST_F(WinLinkParserTest, MachineX86) { + EXPECT_TRUE(parse("link.exe", "/machine:x86", "a.obj", nullptr)); + EXPECT_EQ(llvm::COFF::IMAGE_FILE_MACHINE_I386, _ctx.getMachineType()); +} + +TEST_F(WinLinkParserTest, MachineX64) { + EXPECT_TRUE(parse("link.exe", "/machine:x64", "a.obj", nullptr)); + EXPECT_EQ(llvm::COFF::IMAGE_FILE_MACHINE_AMD64, _ctx.getMachineType()); +} + +TEST_F(WinLinkParserTest, MachineArm) { + EXPECT_TRUE(parse("link.exe", "/machine:arm", "a.obj", nullptr)); + EXPECT_EQ(llvm::COFF::IMAGE_FILE_MACHINE_ARMNT, _ctx.getMachineType()); +} + +TEST_F(WinLinkParserTest, MachineUnknown) { + EXPECT_FALSE(parse("link.exe", "/machine:nosucharch", "a.obj", nullptr)); + EXPECT_EQ("error: unknown machine type: nosucharch\n", errorMessage()); +} + +TEST_F(WinLinkParserTest, MajorImageVersion) { + EXPECT_TRUE(parse("link.exe", "/version:7", "foo.o", nullptr)); + EXPECT_EQ(7, _ctx.getImageVersion().majorVersion); + EXPECT_EQ(0, _ctx.getImageVersion().minorVersion); +} + +TEST_F(WinLinkParserTest, MajorMinorImageVersion) { + EXPECT_TRUE(parse("link.exe", "/version:72.35", "foo.o", nullptr)); + EXPECT_EQ(72, _ctx.getImageVersion().majorVersion); + EXPECT_EQ(35, _ctx.getImageVersion().minorVersion); +} + +TEST_F(WinLinkParserTest, MinMajorOSVersion) { + EXPECT_TRUE(parse("link.exe", "/subsystem:windows,3", "foo.o", nullptr)); + EXPECT_EQ(llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_GUI, _ctx.getSubsystem()); + EXPECT_EQ(3, _ctx.getMinOSVersion().majorVersion); + EXPECT_EQ(0, _ctx.getMinOSVersion().minorVersion); +} + +TEST_F(WinLinkParserTest, MinMajorMinorOSVersion) { + EXPECT_TRUE(parse("link.exe", "/subsystem:windows,3.1", "foo.o", nullptr)); + EXPECT_EQ(llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_GUI, _ctx.getSubsystem()); + EXPECT_EQ(3, _ctx.getMinOSVersion().majorVersion); + EXPECT_EQ(1, _ctx.getMinOSVersion().minorVersion); +} + +TEST_F(WinLinkParserTest, Base) { + EXPECT_TRUE(parse("link.exe", "/base:8388608", "a.obj", nullptr)); + EXPECT_EQ(0x800000U, _ctx.getBaseAddress()); +} + +TEST_F(WinLinkParserTest, InvalidBase) { + EXPECT_FALSE(parse("link.exe", "/base:1234", "a.obj", nullptr)); + EXPECT_TRUE(StringRef(errorMessage()) + .startswith("Base address have to be multiple of 64K")); +} + +TEST_F(WinLinkParserTest, StackReserve) { + EXPECT_TRUE(parse("link.exe", "/stack:8192", "a.obj", nullptr)); + EXPECT_EQ(8192U, _ctx.getStackReserve()); + EXPECT_EQ(4096U, _ctx.getStackCommit()); +} + +TEST_F(WinLinkParserTest, StackReserveAndCommit) { + EXPECT_TRUE(parse("link.exe", "/stack:16384,8192", "a.obj", nullptr)); + EXPECT_EQ(16384U, _ctx.getStackReserve()); + EXPECT_EQ(8192U, _ctx.getStackCommit()); +} + +TEST_F(WinLinkParserTest, InvalidStackSize) { + EXPECT_FALSE(parse("link.exe", "/stack:8192,16384", "a.obj", nullptr)); + EXPECT_TRUE(StringRef(errorMessage()).startswith("Invalid stack size")); +} + +TEST_F(WinLinkParserTest, HeapReserve) { + EXPECT_TRUE(parse("link.exe", "/heap:8192", "a.obj", nullptr)); + EXPECT_EQ(8192U, _ctx.getHeapReserve()); + EXPECT_EQ(4096U, _ctx.getHeapCommit()); +} + +TEST_F(WinLinkParserTest, HeapReserveAndCommit) { + EXPECT_TRUE(parse("link.exe", "/heap:16384,8192", "a.obj", nullptr)); + EXPECT_EQ(16384U, _ctx.getHeapReserve()); + EXPECT_EQ(8192U, _ctx.getHeapCommit()); +} + +TEST_F(WinLinkParserTest, InvalidHeapSize) { + EXPECT_FALSE(parse("link.exe", "/heap:8192,16384", "a.obj", nullptr)); + EXPECT_TRUE(StringRef(errorMessage()).startswith("Invalid heap size")); +} + +TEST_F(WinLinkParserTest, SectionAlignment) { + EXPECT_TRUE(parse("link.exe", "/align:8192", "a.obj", nullptr)); + EXPECT_EQ(8192U, _ctx.getSectionDefaultAlignment()); +} + +TEST_F(WinLinkParserTest, InvalidAlignment) { + EXPECT_FALSE(parse("link.exe", "/align:1000", "a.obj", nullptr)); + EXPECT_EQ("Section alignment must be a power of 2, but got 1000\n", + errorMessage()); +} + +TEST_F(WinLinkParserTest, Include) { + EXPECT_TRUE(parse("link.exe", "/include:foo", "a.out", nullptr)); + auto symbols = _ctx.initialUndefinedSymbols(); + EXPECT_FALSE(symbols.empty()); + EXPECT_EQ("foo", symbols[0]); +} + +TEST_F(WinLinkParserTest, Merge) { + EXPECT_TRUE(parse("link.exe", "/merge:.foo=.bar", "/merge:.bar=.baz", + "a.out", nullptr)); + EXPECT_EQ(".baz", _ctx.getOutputSectionName(".foo")); + EXPECT_EQ(".baz", _ctx.getOutputSectionName(".bar")); + EXPECT_EQ(".abc", _ctx.getOutputSectionName(".abc")); +} + +TEST_F(WinLinkParserTest, Merge_Circular) { + EXPECT_FALSE(parse("link.exe", "/merge:.foo=.bar", "/merge:.bar=.foo", + "a.out", nullptr)); +} + +TEST_F(WinLinkParserTest, Implib) { + EXPECT_TRUE(parse("link.exe", "/implib:foo.dll.lib", "a.out", nullptr)); + EXPECT_EQ("foo.dll.lib", _ctx.getOutputImportLibraryPath()); +} + +TEST_F(WinLinkParserTest, ImplibDefault) { + EXPECT_TRUE(parse("link.exe", "/out:foobar.dll", "a.out", nullptr)); + EXPECT_EQ("foobar.lib", _ctx.getOutputImportLibraryPath()); +} + +// +// Tests for /section +// + +namespace { +const uint32_t discardable = llvm::COFF::IMAGE_SCN_MEM_DISCARDABLE; +const uint32_t not_cached = llvm::COFF::IMAGE_SCN_MEM_NOT_CACHED; +const uint32_t not_paged = llvm::COFF::IMAGE_SCN_MEM_NOT_PAGED; +const uint32_t shared = llvm::COFF::IMAGE_SCN_MEM_SHARED; +const uint32_t execute = llvm::COFF::IMAGE_SCN_MEM_EXECUTE; +const uint32_t read = llvm::COFF::IMAGE_SCN_MEM_READ; +const uint32_t write = llvm::COFF::IMAGE_SCN_MEM_WRITE; + +#define TEST_SECTION(testname, arg, expect) \ + TEST_F(WinLinkParserTest, testname) { \ + EXPECT_TRUE(parse("link.exe", "/section:.text," arg, "a.obj", nullptr)); \ + EXPECT_EQ(expect, _ctx.getSectionAttributes(".text", execute | read)); \ + } + +TEST_SECTION(SectionD, "d", execute | read | discardable) +TEST_SECTION(SectionE, "e", execute) +TEST_SECTION(SectionK, "k", execute | read | not_cached) +TEST_SECTION(SectionP, "p", execute | read | not_paged) +TEST_SECTION(SectionR, "r", read) +TEST_SECTION(SectionS, "s", execute | read | shared) +TEST_SECTION(SectionW, "w", write) + +#undef TEST_SECTION + +TEST_F(WinLinkParserTest, Section) { + EXPECT_TRUE(parse("link.exe", "/section:.text,dekprsw", + "/section:.text,!dekprsw", "a.obj", nullptr)); + EXPECT_EQ(0U, _ctx.getSectionAttributes(".text", execute | read)); +} + +TEST_F(WinLinkParserTest, SectionNegate) { + EXPECT_TRUE(parse("link.exe", "/section:.text,!e", "a.obj", nullptr)); + EXPECT_EQ(read, _ctx.getSectionAttributes(".text", execute | read)); +} + +TEST_F(WinLinkParserTest, SectionMultiple) { + EXPECT_TRUE(parse("link.exe", "/section:.foo,e", "/section:.foo,rw", + "/section:.foo,!d", "a.obj", nullptr)); + uint32_t flags = execute | read | not_paged | discardable; + uint32_t expected = execute | read | write | not_paged; + EXPECT_EQ(expected, _ctx.getSectionAttributes(".foo", flags)); +} + +} // end anonymous namespace + +// +// Tests for /defaultlib and /nodefaultlib. +// + +TEST_F(WinLinkParserTest, DefaultLib) { + EXPECT_TRUE(parse("link.exe", "/defaultlib:user32.lib", + "/defaultlib:kernel32", "a.obj", nullptr)); + EXPECT_EQ(4, inputFileCount()); + EXPECT_EQ("a.obj", inputFile(0)); + EXPECT_EQ("user32.lib", inputFile(1)); + EXPECT_EQ("kernel32.lib", inputFile(2)); +} + +TEST_F(WinLinkParserTest, DefaultLibDuplicates) { + EXPECT_TRUE(parse("link.exe", "/defaultlib:user32.lib", + "/defaultlib:user32.lib", "a.obj", nullptr)); + EXPECT_EQ(3, inputFileCount()); + EXPECT_EQ("a.obj", inputFile(0)); + EXPECT_EQ("user32.lib", inputFile(1)); +} + +TEST_F(WinLinkParserTest, NoDefaultLib) { + EXPECT_TRUE(parse("link.exe", "/defaultlib:user32.lib", + "/defaultlib:kernel32", "/nodefaultlib:user32.lib", "a.obj", + nullptr)); + EXPECT_EQ(3, inputFileCount()); + EXPECT_EQ("a.obj", inputFile(0)); + EXPECT_EQ("kernel32.lib", inputFile(1)); +} + +TEST_F(WinLinkParserTest, NoDefaultLibCase) { + EXPECT_TRUE(parse("link.exe", "/defaultlib:user32", + "/defaultlib:kernel32", "/nodefaultlib:USER32.LIB", "a.obj", + nullptr)); + EXPECT_EQ(3, inputFileCount()); + EXPECT_EQ("a.obj", inputFile(0)); + EXPECT_EQ("kernel32.lib", inputFile(1)); +} + +TEST_F(WinLinkParserTest, NoDefaultLibAll) { + EXPECT_TRUE(parse("link.exe", "/defaultlib:user32.lib", + "/defaultlib:kernel32", "/nodefaultlib", "a.obj", nullptr)); + EXPECT_EQ(2, inputFileCount()); + EXPECT_EQ("a.obj", inputFile(0)); +} + +TEST_F(WinLinkParserTest, DisallowLib) { + EXPECT_TRUE(parse("link.exe", "/defaultlib:user32.lib", + "/defaultlib:kernel32", "/disallowlib:user32.lib", "a.obj", + nullptr)); + EXPECT_EQ(3, inputFileCount()); + EXPECT_EQ("a.obj", inputFile(0)); + EXPECT_EQ("kernel32.lib", inputFile(1)); +} + +// +// Tests for DLL. +// + +TEST_F(WinLinkParserTest, NoEntry) { + EXPECT_TRUE(parse("link.exe", "/noentry", "/dll", "a.obj", nullptr)); + EXPECT_TRUE(_ctx.isDll()); + EXPECT_EQ(0x10000000U, _ctx.getBaseAddress()); + EXPECT_EQ("", _ctx.entrySymbolName()); +} + +TEST_F(WinLinkParserTest, NoEntryError) { + // /noentry without /dll is an error. + EXPECT_FALSE(parse("link.exe", "/noentry", "a.obj", nullptr)); + EXPECT_EQ("/noentry must be specified with /dll\n", errorMessage()); +} + +// +// Tests for DELAYLOAD. +// + +TEST_F(WinLinkParserTest, DelayLoad) { + EXPECT_TRUE(parse("link.exe", "/delayload:abc.dll", "/delayload:def.dll", + "a.obj", nullptr)); + EXPECT_TRUE(_ctx.isDelayLoadDLL("abc.dll")); + EXPECT_TRUE(_ctx.isDelayLoadDLL("DEF.DLL")); + EXPECT_FALSE(_ctx.isDelayLoadDLL("xyz.dll")); +} + +// +// Tests for SEH. +// + +TEST_F(WinLinkParserTest, SafeSEH) { + EXPECT_TRUE(parse("link.exe", "/safeseh", "a.obj", nullptr)); + EXPECT_TRUE(_ctx.requireSEH()); + EXPECT_FALSE(_ctx.noSEH()); +} + +TEST_F(WinLinkParserTest, NoSafeSEH) { + EXPECT_TRUE(parse("link.exe", "/safeseh:no", "a.obj", nullptr)); + EXPECT_FALSE(_ctx.requireSEH()); + EXPECT_TRUE(_ctx.noSEH()); +} + +// +// Tests for boolean flags. +// + +TEST_F(WinLinkParserTest, Force) { + EXPECT_TRUE(parse("link.exe", "/force", "a.obj", nullptr)); + EXPECT_TRUE(_ctx.allowRemainingUndefines()); +} + +TEST_F(WinLinkParserTest, ForceUnresolved) { + EXPECT_TRUE(parse("link.exe", "/force:unresolved", "a.obj", nullptr)); + EXPECT_TRUE(_ctx.allowRemainingUndefines()); +} + +TEST_F(WinLinkParserTest, NoNxCompat) { + EXPECT_TRUE(parse("link.exe", "/nxcompat:no", "a.obj", nullptr)); + EXPECT_FALSE(_ctx.isNxCompat()); +} + +TEST_F(WinLinkParserTest, LargeAddressAware) { + EXPECT_TRUE(parse("link.exe", "/largeaddressaware", "a.obj", nullptr)); + EXPECT_TRUE(_ctx.getLargeAddressAware()); +} + +TEST_F(WinLinkParserTest, NoLargeAddressAware) { + EXPECT_TRUE(parse("link.exe", "/largeaddressaware:no", "a.obj", nullptr)); + EXPECT_FALSE(_ctx.getLargeAddressAware()); +} + +TEST_F(WinLinkParserTest, AllowBind) { + EXPECT_TRUE(parse("link.exe", "/allowbind", "a.obj", nullptr)); + EXPECT_TRUE(_ctx.getAllowBind()); +} + +TEST_F(WinLinkParserTest, NoAllowBind) { + EXPECT_TRUE(parse("link.exe", "/allowbind:no", "a.obj", nullptr)); + EXPECT_FALSE(_ctx.getAllowBind()); +} + +TEST_F(WinLinkParserTest, AllowIsolation) { + EXPECT_TRUE(parse("link.exe", "/allowisolation", "a.obj", nullptr)); + EXPECT_TRUE(_ctx.getAllowIsolation()); +} + +TEST_F(WinLinkParserTest, NoAllowIsolation) { + EXPECT_TRUE(parse("link.exe", "/allowisolation:no", "a.obj", nullptr)); + EXPECT_FALSE(_ctx.getAllowIsolation()); +} + +TEST_F(WinLinkParserTest, SwapRunFromCD) { + EXPECT_TRUE(parse("link.exe", "/swaprun:cd", "a.obj", nullptr)); + EXPECT_TRUE(_ctx.getSwapRunFromCD()); +} + +TEST_F(WinLinkParserTest, SwapRunFromNet) { + EXPECT_TRUE(parse("link.exe", "/swaprun:net", "a.obj", nullptr)); + EXPECT_TRUE(_ctx.getSwapRunFromNet()); +} + +TEST_F(WinLinkParserTest, Debug) { + EXPECT_TRUE(parse("link.exe", "/debug", "a.obj", nullptr)); + EXPECT_TRUE(_ctx.deadStrip()); + EXPECT_TRUE(_ctx.getDebug()); + EXPECT_EQ("a.pdb", _ctx.getPDBFilePath()); +} + +TEST_F(WinLinkParserTest, PDB) { + EXPECT_TRUE(parse("link.exe", "/debug", "/pdb:foo.pdb", "a.obj", nullptr)); + EXPECT_TRUE(_ctx.getDebug()); + EXPECT_EQ("foo.pdb", _ctx.getPDBFilePath()); +} + +TEST_F(WinLinkParserTest, Fixed) { + EXPECT_TRUE(parse("link.exe", "/fixed", "a.out", nullptr)); + EXPECT_FALSE(_ctx.getBaseRelocationEnabled()); + EXPECT_FALSE(_ctx.getDynamicBaseEnabled()); +} + +TEST_F(WinLinkParserTest, NoFixed) { + EXPECT_TRUE(parse("link.exe", "/fixed:no", "a.out", nullptr)); + EXPECT_TRUE(_ctx.getBaseRelocationEnabled()); +} + +TEST_F(WinLinkParserTest, TerminalServerAware) { + EXPECT_TRUE(parse("link.exe", "/tsaware", "a.out", nullptr)); + EXPECT_TRUE(_ctx.isTerminalServerAware()); +} + +TEST_F(WinLinkParserTest, NoTerminalServerAware) { + EXPECT_TRUE(parse("link.exe", "/tsaware:no", "a.out", nullptr)); + EXPECT_FALSE(_ctx.isTerminalServerAware()); +} + +TEST_F(WinLinkParserTest, DynamicBase) { + EXPECT_TRUE(parse("link.exe", "/dynamicbase", "a.out", nullptr)); + EXPECT_TRUE(_ctx.getDynamicBaseEnabled()); +} + +TEST_F(WinLinkParserTest, NoDynamicBase) { + EXPECT_TRUE(parse("link.exe", "/dynamicbase:no", "a.out", nullptr)); + EXPECT_FALSE(_ctx.getDynamicBaseEnabled()); +} + +// +// Test for /failifmismatch +// + +TEST_F(WinLinkParserTest, FailIfMismatch_Match) { + EXPECT_TRUE(parse("link.exe", "/failifmismatch:foo=bar", + "/failifmismatch:foo=bar", "/failifmismatch:abc=def", + "a.out", nullptr)); +} + +TEST_F(WinLinkParserTest, FailIfMismatch_Mismatch) { + EXPECT_FALSE(parse("link.exe", "/failifmismatch:foo=bar", + "/failifmismatch:foo=baz", "a.out", nullptr)); +} + +// +// Tests for /manifest, /manifestuac, /manifestfile, and /manifestdependency. +// +TEST_F(WinLinkParserTest, Manifest_Default) { + EXPECT_TRUE(parse("link.exe", "/manifest", "a.out", nullptr)); + EXPECT_TRUE(_ctx.getCreateManifest()); + EXPECT_FALSE(_ctx.getEmbedManifest()); + EXPECT_EQ(1, _ctx.getManifestId()); + EXPECT_EQ("'asInvoker'", _ctx.getManifestLevel()); + EXPECT_EQ("'false'", _ctx.getManifestUiAccess()); +} + +TEST_F(WinLinkParserTest, Manifest_No) { + EXPECT_TRUE(parse("link.exe", "/manifest:no", "a.out", nullptr)); + EXPECT_FALSE(_ctx.getCreateManifest()); +} + +TEST_F(WinLinkParserTest, Manifestuac_no) { + EXPECT_TRUE(parse("link.exe", "/manifestuac:NO", "a.out", nullptr)); + EXPECT_FALSE(_ctx.getManifestUAC()); +} + +TEST_F(WinLinkParserTest, Manifestuac_Level) { + EXPECT_TRUE(parse("link.exe", "/manifestuac:level='requireAdministrator'", + "a.out", nullptr)); + EXPECT_EQ("'requireAdministrator'", _ctx.getManifestLevel()); + EXPECT_EQ("'false'", _ctx.getManifestUiAccess()); +} + +TEST_F(WinLinkParserTest, Manifestuac_UiAccess) { + EXPECT_TRUE(parse("link.exe", "/manifestuac:uiAccess='true'", "a.out", nullptr)); + EXPECT_EQ("'asInvoker'", _ctx.getManifestLevel()); + EXPECT_EQ("'true'", _ctx.getManifestUiAccess()); +} + +TEST_F(WinLinkParserTest, Manifestuac_LevelAndUiAccess) { + EXPECT_TRUE(parse("link.exe", + "/manifestuac:level='requireAdministrator' uiAccess='true'", + "a.out", nullptr)); + EXPECT_EQ("'requireAdministrator'", _ctx.getManifestLevel()); + EXPECT_EQ("'true'", _ctx.getManifestUiAccess()); +} + +TEST_F(WinLinkParserTest, Manifestfile) { + EXPECT_TRUE(parse("link.exe", "/manifestfile:bar.manifest", + "a.out", nullptr)); + EXPECT_EQ("bar.manifest", _ctx.getManifestOutputPath()); +} + +TEST_F(WinLinkParserTest, Manifestdependency) { + EXPECT_TRUE(parse("link.exe", "/manifestdependency:foo bar", "a.out", + nullptr)); + EXPECT_EQ("foo bar", _ctx.getManifestDependency()); +} + +// +// Test for /OPT +// + +TEST_F(WinLinkParserTest, OptNoRef) { + EXPECT_TRUE(parse("link.exe", "/opt:noref", "a.obj", nullptr)); + EXPECT_FALSE(_ctx.deadStrip()); +} + +TEST_F(WinLinkParserTest, OptIgnore) { + EXPECT_TRUE(parse("link.exe", "/opt:ref", "/opt:icf", "/opt:noicf", + "/opt:icf=foo", "/opt:lbr", "/opt:nolbr", "a.obj", + nullptr)); +} + +TEST_F(WinLinkParserTest, OptUnknown) { + EXPECT_FALSE(parse("link.exe", "/opt:foo", "a.obj", nullptr)); +} + +// +// Test for /PROFILE +// + +TEST_F(WinLinkParserTest, Profile) { + EXPECT_TRUE(parse("link.exe", "/profile", "a.obj", nullptr)); + EXPECT_TRUE(_ctx.deadStrip()); + EXPECT_TRUE(_ctx.getBaseRelocationEnabled()); + EXPECT_TRUE(_ctx.getDynamicBaseEnabled()); +} + +// +// Test for command line flags that are ignored. +// + +TEST_F(WinLinkParserTest, Ignore) { + // There are some no-op command line options that are recognized for + // compatibility with link.exe. + EXPECT_TRUE(parse("link.exe", "/nologo", "/errorreport:prompt", + "/incremental", "/incremental:no", "/delay:unload", + "/disallowlib:foo", "/pdbaltpath:bar", + "/wx", "/wx:no", "/tlbid:1", "/tlbout:foo", "/idlout:foo", + "/ignore:4000", "/ignoreidl", "/implib:foo", "/safeseh", + "/safeseh:no", "/functionpadmin", "/maxilksize:1024", + "a.obj", nullptr)); + EXPECT_EQ("", errorMessage()); + EXPECT_EQ(2, inputFileCount()); + EXPECT_EQ("a.obj", inputFile(0)); +} + +// +// Test for "--" +// + +TEST_F(WinLinkParserTest, DashDash) { + EXPECT_TRUE(parse("link.exe", "/subsystem:console", "/out:a.exe", "a.obj", + "--", "b.obj", "-c.obj", nullptr)); + EXPECT_EQ(llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_CUI, _ctx.getSubsystem()); + EXPECT_EQ("a.exe", _ctx.outputPath()); + EXPECT_EQ(4, inputFileCount()); + EXPECT_EQ("a.obj", inputFile(0)); + EXPECT_EQ("b.obj", inputFile(1)); + EXPECT_EQ("-c.obj", inputFile(2)); +} diff --git a/unittests/DriverTests/WinLinkModuleDefTest.cpp b/unittests/DriverTests/WinLinkModuleDefTest.cpp new file mode 100644 index 0000000000000..6762fd4b2ea66 --- /dev/null +++ b/unittests/DriverTests/WinLinkModuleDefTest.cpp @@ -0,0 +1,155 @@ +//===- lld/unittest/WinLinkModuleDefTest.cpp ------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" +#include "lld/Driver/WinLinkModuleDef.h" +#include "llvm/Support/MemoryBuffer.h" +#include <memory> + +using namespace llvm; +using namespace lld; + +class ParserTest : public testing::Test { +protected: + std::vector<moduledef::Directive *> _dirs; + + void parse(const char *contents) { + auto membuf = + std::unique_ptr<MemoryBuffer>(MemoryBuffer::getMemBuffer(contents)); + moduledef::Lexer lexer(std::move(membuf)); + moduledef::Parser parser(lexer, _alloc); + EXPECT_TRUE(parser.parse(_dirs)); + EXPECT_TRUE(!_dirs.empty()); + } + + void verifyExportDesc(const PECOFFLinkingContext::ExportDesc &exp, + StringRef sym, int ordinal, bool noname, bool isData) { + EXPECT_EQ(sym, exp.name); + EXPECT_EQ(ordinal, exp.ordinal); + EXPECT_EQ(noname, exp.noname); + EXPECT_EQ(isData, exp.isData); + } + +private: + llvm::BumpPtrAllocator _alloc; +}; + +TEST_F(ParserTest, Exports) { + parse("EXPORTS\n" + " sym1\n" + " sym2 @5\n" + " sym3 @8 NONAME\n" + " sym4 DATA\n" + " sym5 @10 NONAME DATA\n"); + EXPECT_EQ(1U, _dirs.size()); + const std::vector<PECOFFLinkingContext::ExportDesc> &exports = + cast<moduledef::Exports>(_dirs[0])->getExports(); + EXPECT_EQ(5U, exports.size()); + verifyExportDesc(exports[0], "sym1", -1, false, false); + verifyExportDesc(exports[1], "sym2", 5, false, false); + verifyExportDesc(exports[2], "sym3", 8, true, false); + verifyExportDesc(exports[3], "sym4", -1, false, true); + verifyExportDesc(exports[4], "sym5", 10, true, true); +} + +TEST_F(ParserTest, Heapsize) { + parse("HEAPSIZE 65536"); + EXPECT_EQ(1U, _dirs.size()); + auto *heapsize = cast<moduledef::Heapsize>(_dirs[0]); + EXPECT_EQ(65536U, heapsize->getReserve()); + EXPECT_EQ(0U, heapsize->getCommit()); +} + +TEST_F(ParserTest, HeapsizeWithCommit) { + parse("HEAPSIZE 65536, 8192"); + EXPECT_EQ(1U, _dirs.size()); + auto *heapsize = cast<moduledef::Heapsize>(_dirs[0]); + EXPECT_EQ(65536U, heapsize->getReserve()); + EXPECT_EQ(8192U, heapsize->getCommit()); +} + +TEST_F(ParserTest, StacksizeBasic) { + parse("STACKSIZE 65536"); + EXPECT_EQ(1U, _dirs.size()); + auto *stacksize = cast<moduledef::Stacksize>(_dirs[0]); + EXPECT_EQ(65536U, stacksize->getReserve()); + EXPECT_EQ(0U, stacksize->getCommit()); +} + +TEST_F(ParserTest, StacksizeWithCommit) { + parse("STACKSIZE 65536, 8192"); + EXPECT_EQ(1U, _dirs.size()); + auto *stacksize = cast<moduledef::Stacksize>(_dirs[0]); + EXPECT_EQ(65536U, stacksize->getReserve()); + EXPECT_EQ(8192U, stacksize->getCommit()); +} + +TEST_F(ParserTest, Library) { + parse("LIBRARY foo.dll"); + EXPECT_EQ(1U, _dirs.size()); + auto *lib = cast<moduledef::Library>(_dirs[0]); + EXPECT_EQ("foo.dll", lib->getName()); +} + +TEST_F(ParserTest, NameBasic) { + parse("NAME foo.exe"); + EXPECT_EQ(1U, _dirs.size()); + auto *name = cast<moduledef::Name>(_dirs[0]); + EXPECT_EQ("foo.exe", name->getOutputPath()); + EXPECT_EQ(0U, name->getBaseAddress()); +} + +TEST_F(ParserTest, NameWithBase) { + parse("NAME foo.exe BASE=4096"); + EXPECT_EQ(1U, _dirs.size()); + auto *name = cast<moduledef::Name>(_dirs[0]); + EXPECT_EQ("foo.exe", name->getOutputPath()); + EXPECT_EQ(4096U, name->getBaseAddress()); +} + +TEST_F(ParserTest, NameLongFileName) { + parse("NAME \"a long file name.exe\""); + EXPECT_EQ(1U, _dirs.size()); + auto *name = cast<moduledef::Name>(_dirs[0]); + EXPECT_EQ("a long file name.exe", name->getOutputPath()); + EXPECT_EQ(0U, name->getBaseAddress()); +} + +TEST_F(ParserTest, VersionMajor) { + parse("VERSION 12"); + EXPECT_EQ(1U, _dirs.size()); + auto *ver = cast<moduledef::Version>(_dirs[0]); + EXPECT_EQ(12, ver->getMajorVersion()); + EXPECT_EQ(0, ver->getMinorVersion()); +} + +TEST_F(ParserTest, VersionMajorMinor) { + parse("VERSION 12.34"); + EXPECT_EQ(1U, _dirs.size()); + auto *ver = cast<moduledef::Version>(_dirs[0]); + EXPECT_EQ(12, ver->getMajorVersion()); + EXPECT_EQ(34, ver->getMinorVersion()); +} + +TEST_F(ParserTest, Multiple) { + parse("LIBRARY foo\n" + "EXPORTS sym\n" + "VERSION 12"); + EXPECT_EQ(3U, _dirs.size()); + auto *lib = cast<moduledef::Library>(_dirs[0]); + EXPECT_EQ("foo.dll", lib->getName()); + + const std::vector<PECOFFLinkingContext::ExportDesc> &exports = + cast<moduledef::Exports>(_dirs[1])->getExports(); + EXPECT_EQ(1U, exports.size()); + verifyExportDesc(exports[0], "sym", -1, false, false); + + auto *ver = cast<moduledef::Version>(_dirs[2]); + EXPECT_EQ(12, ver->getMajorVersion()); +} |