diff options
Diffstat (limited to 'unittests/DriverTests/GnuLdDriverTest.cpp')
-rw-r--r-- | unittests/DriverTests/GnuLdDriverTest.cpp | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/unittests/DriverTests/GnuLdDriverTest.cpp b/unittests/DriverTests/GnuLdDriverTest.cpp new file mode 100644 index 000000000000..92eb920f0180 --- /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("."))); +} + |