//===- 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> { protected: const LinkingContext *linkingContext() override { return _ctx.get(); } }; class LinkerScriptTest : public testing::Test { protected: void SetUp() override { llvm::Triple triple(llvm::sys::getDefaultTargetTriple()); _ctx = GnuLdDriver::createELFLinkingContext(triple); } void parse(StringRef script, bool nostdlib = false) { std::unique_ptr 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 _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> &nodes = _ctx->getNodes(); EXPECT_EQ((size_t)4, nodes.size()); EXPECT_FALSE(cast(nodes[0].get())->asNeeded()); EXPECT_TRUE(cast(nodes[1].get())->asNeeded()); EXPECT_TRUE(cast(nodes[2].get())->asNeeded()); EXPECT_FALSE(cast(nodes[3].get())->asNeeded()); } // Emulation TEST_F(GnuLdParserTest, Emulation) { EXPECT_TRUE(parse("mips-linux-gnu-ld", "a.o", "-m", "elf64ltsmip", nullptr)); EXPECT_EQ(Triple::mips64el, _ctx->getTriple().getArch()); EXPECT_TRUE( parse("mips64el-linux-gnu-ld", "a.o", "-m", "elf32btsmip", nullptr)); EXPECT_EQ(Triple::mips, _ctx->getTriple().getArch()); EXPECT_TRUE( parse("mipsel-linux-gnu-ld", "a.o", "-m", "elf32btsmipn32", nullptr)); EXPECT_EQ(Triple::mips, _ctx->getTriple().getArch()); EXPECT_TRUE( parse("mips-linux-gnu-ld", "a.o", "-m", "elf32ltsmipn32", nullptr)); EXPECT_EQ(Triple::mipsel, _ctx->getTriple().getArch()); } // Linker script TEST_F(LinkerScriptTest, Input) { parse("INPUT(/x /y)"); std::vector> &nodes = _ctx->getNodes(); EXPECT_EQ((size_t)2, nodes.size()); EXPECT_EQ("/x", cast(nodes[0].get())->getFile()->path()); EXPECT_EQ("/y", cast(nodes[1].get())->getFile()->path()); } TEST_F(LinkerScriptTest, Group) { parse("GROUP(/x /y)"); std::vector> &nodes = _ctx->getNodes(); EXPECT_EQ((size_t)3, nodes.size()); EXPECT_EQ("/x", cast(nodes[0].get())->getFile()->path()); EXPECT_EQ("/y", cast(nodes[1].get())->getFile()->path()); EXPECT_EQ(2, cast(nodes[2].get())->getSize()); } TEST_F(LinkerScriptTest, SearchDir) { parse("SEARCH_DIR(\"/foo/bar\")"); std::vector 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 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(*ls->_commands.begin()); EXPECT_TRUE(secs != nullptr); EXPECT_EQ(2, secs->end() - secs->begin()); auto command = secs->begin(); auto *sa1 = dyn_cast(*command); EXPECT_TRUE(sa1 != nullptr); EXPECT_EQ(script::SymbolAssignment::Simple, sa1->assignmentKind()); EXPECT_EQ(script::SymbolAssignment::Default, sa1->assignmentVisibility()); ++command; auto *sa2 = dyn_cast(*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("."))); }