From 3907f2a802673bd7a52a802a7fac67db2f831732 Mon Sep 17 00:00:00 2001 From: Rui Ueyama Date: Fri, 28 Mar 2014 19:02:06 +0000 Subject: [PATCH] [ELF] Support --defsym option to define an absolute symbol. This patch is to support --defsym option for ELF file format/GNU-compatible driver. Currently it takes a symbol name followed by '=' and a number. If such option is given, the driver sets up an absolute symbol with the specified address. You can specify multiple --defsym options to define multiple symbols. GNU LD's --defsym provides many more features. For example, it allows users to specify another symbol name instead of a number to define a symbol alias, or it even allows a symbol plus an offset (e.g. --defsym=foo+3) to define symbol- relative alias. This patch does not support that, but will be supported in subsequent patches. Differential Revision: http://llvm-reviews.chandlerc.com/D3208 llvm-svn: 205029 --- .../lld/ReaderWriter/ELFLinkingContext.h | 13 ++++++- lld/lib/Core/LinkingContext.cpp | 4 +- lld/lib/Driver/GnuLdDriver.cpp | 24 ++++++++++++ lld/lib/Driver/GnuLdOptions.td | 4 +- .../ReaderWriter/ELF/ELFLinkingContext.cpp | 31 ++++++++++++++- .../PECOFF/PECOFFLinkingContext.cpp | 5 ++- lld/test/elf/defsym.objtxt | 9 +++++ lld/test/pecoff/include.test | 4 +- lld/unittests/DriverTests/GnuLdDriverTest.cpp | 39 +++++++++++++++++++ 9 files changed, 124 insertions(+), 9 deletions(-) create mode 100644 lld/test/elf/defsym.objtxt diff --git a/lld/include/lld/ReaderWriter/ELFLinkingContext.h b/lld/include/lld/ReaderWriter/ELFLinkingContext.h index 031396c5866a..95949030ff72 100644 --- a/lld/include/lld/ReaderWriter/ELFLinkingContext.h +++ b/lld/include/lld/ReaderWriter/ELFLinkingContext.h @@ -151,9 +151,10 @@ public: void setIsStaticExecutable(bool v) { _isStaticExecutable = v; } void setMergeCommonStrings(bool v) { _mergeCommonStrings = v; } void setUseShlibUndefines(bool use) { _useShlibUndefines = use; } - void setOutputELFType(uint32_t type) { _outputELFType = type; } + void createInternalFiles(std::vector> &) const override; + /// \brief Set the dynamic linker path void setInterpreter(StringRef dynamicLinker) { _dynamicLinkerArg = true; @@ -181,6 +182,11 @@ public: /// add to the list of finalizer functions void addFiniFunction(StringRef name) { _finiFunctions.push_back(name); } + /// Add an absolute symbol. Used for --defsym. + void addInitialAbsoluteSymbol(StringRef name, uint64_t addr) { + _absoluteSymbols[name] = addr; + } + /// Return the list of initializer symbols that are specified in the /// linker command line, using the -init option. range initFunctions() const { @@ -225,6 +231,10 @@ public: return true; } + const std::map &getAbsoluteSymbols() const { + return _absoluteSymbols; + } + /// \brief Helper function to allocate strings. StringRef allocateString(StringRef ref) const { char *x = _allocator.Allocate(ref.size() + 1); @@ -272,6 +282,7 @@ protected: StringRefVector _rpathList; StringRefVector _rpathLinkList; std::map _undefinedAtomsFromFile; + std::map _absoluteSymbols; }; } // end namespace lld diff --git a/lld/lib/Core/LinkingContext.cpp b/lld/lib/Core/LinkingContext.cpp index 361bff22555e..d92ef27968c5 100644 --- a/lld/lib/Core/LinkingContext.cpp +++ b/lld/lib/Core/LinkingContext.cpp @@ -44,7 +44,7 @@ bool LinkingContext::createImplicitFiles( } std::unique_ptr LinkingContext::createEntrySymbolFile() const { - return createEntrySymbolFile("command line option -e"); + return createEntrySymbolFile(""); } std::unique_ptr @@ -58,7 +58,7 @@ LinkingContext::createEntrySymbolFile(StringRef filename) const { } std::unique_ptr LinkingContext::createUndefinedSymbolFile() const { - return createUndefinedSymbolFile("command line option -u"); + return createUndefinedSymbolFile(""); } std::unique_ptr diff --git a/lld/lib/Driver/GnuLdDriver.cpp b/lld/lib/Driver/GnuLdDriver.cpp index 1631c82304e3..fb57706674b5 100644 --- a/lld/lib/Driver/GnuLdDriver.cpp +++ b/lld/lib/Driver/GnuLdDriver.cpp @@ -88,6 +88,19 @@ static error_code getFileMagic(ELFLinkingContext &ctx, StringRef path, return make_error_code(ReaderError::unknown_file_format); } +// Parses an argument of --defsym. A given string must be in the form +// of =. Note that we don't support symbol-relative +// aliases yet. +static bool parseDefsymOption(StringRef opt, StringRef &sym, uint64_t &addr) { + size_t equalPos = opt.find('='); + if (equalPos == StringRef::npos) + return false; + sym = opt.substr(0, equalPos); + if (opt.substr(equalPos + 1).getAsInteger(0, addr)) + return false; + return true; +} + llvm::ErrorOr ELFFileNode::getPath(const LinkingContext &) const { if (!_isDashlPrefix) return _path; @@ -363,6 +376,17 @@ bool GnuLdDriver::parse(int argc, const char *argv[], asNeeded = false; break; + case OPT_defsym: { + StringRef sym; + uint64_t addr; + if (!parseDefsymOption(inputArg->getValue(), sym, addr)) { + diagnostics << "invalid --defsym: " << inputArg->getValue() << "\n"; + return false; + } + ctx->addInitialAbsoluteSymbol(sym, addr); + break; + } + case OPT_start_group: { std::unique_ptr controlStart(new ELFGroup(*ctx, index++)); controlNodeStack.push(controlStart.get()); diff --git a/lld/lib/Driver/GnuLdOptions.td b/lld/lib/Driver/GnuLdOptions.td index 81f26b584339..844327e1bc1e 100644 --- a/lld/lib/Driver/GnuLdOptions.td +++ b/lld/lib/Driver/GnuLdOptions.td @@ -190,6 +190,9 @@ def use_shlib_undefs: Flag<["--"], "use-shlib-undefines">, def allow_multiple_definition: Flag<["--"], "allow-multiple-definition">, HelpText<"Allow multiple definitions">, Group; +def defsym : Joined<["--"], "defsym=">, + HelpText<"Create a defined symbol">, + Group; //===----------------------------------------------------------------------===// /// Custom Options @@ -243,4 +246,3 @@ def alias_output_filetype: Joined<["--"], "output-filetype=">, //===----------------------------------------------------------------------===// def help : Flag<["--"], "help">, HelpText<"Display this help message">; - diff --git a/lld/lib/ReaderWriter/ELF/ELFLinkingContext.cpp b/lld/lib/ReaderWriter/ELF/ELFLinkingContext.cpp index 7bb8dc52b513..ebe2fb0f2ccd 100644 --- a/lld/lib/ReaderWriter/ELF/ELFLinkingContext.cpp +++ b/lld/lib/ReaderWriter/ELF/ELFLinkingContext.cpp @@ -25,6 +25,22 @@ namespace lld { +class CommandLineAbsoluteAtom : public AbsoluteAtom { +public: + CommandLineAbsoluteAtom(const File &file, StringRef name, uint64_t value) + : _file(file), _name(name), _value(value) {} + + const File &file() const override { return _file; } + StringRef name() const override { return _name; } + uint64_t value() const override { return _value; } + Scope scope() const override { return scopeGlobal; } + +private: + const File &_file; + StringRef _name; + uint64_t _value; +}; + class CommandLineUndefinedAtom : public SimpleUndefinedAtom { public: CommandLineUndefinedAtom(const File &f, StringRef name) @@ -179,6 +195,19 @@ ErrorOr ELFLinkingContext::searchLibrary(StringRef libName) const { return libName; } +void ELFLinkingContext::createInternalFiles( + std::vector> &files) const { + std::unique_ptr file( + new SimpleFile("")); + for (auto i : getAbsoluteSymbols()) { + StringRef sym = i.first; + uint64_t val = i.second; + file->addAtom(*(new (_allocator) CommandLineAbsoluteAtom(*file, sym, val))); + } + files.push_back(std::move(file)); + LinkingContext::createInternalFiles(files); +} + std::unique_ptr ELFLinkingContext::createUndefinedSymbolFile() const { if (_initialUndefinedSymbols.empty()) return nullptr; @@ -186,7 +215,7 @@ std::unique_ptr ELFLinkingContext::createUndefinedSymbolFile() const { new SimpleFile("command line option -u")); for (auto undefSymStr : _initialUndefinedSymbols) undefinedSymFile->addAtom(*(new (_allocator) CommandLineUndefinedAtom( - *undefinedSymFile, undefSymStr))); + *undefinedSymFile, undefSymStr))); return std::move(undefinedSymFile); } diff --git a/lld/lib/ReaderWriter/PECOFF/PECOFFLinkingContext.cpp b/lld/lib/ReaderWriter/PECOFF/PECOFFLinkingContext.cpp index 9b91cfaccace..849fc65b5dcd 100644 --- a/lld/lib/ReaderWriter/PECOFF/PECOFFLinkingContext.cpp +++ b/lld/lib/ReaderWriter/PECOFF/PECOFFLinkingContext.cpp @@ -93,11 +93,12 @@ bool PECOFFLinkingContext::validateImpl(raw_ostream &diagnostics) { } std::unique_ptr PECOFFLinkingContext::createEntrySymbolFile() const { - return LinkingContext::createEntrySymbolFile("command line option /entry"); + return LinkingContext::createEntrySymbolFile(""); } std::unique_ptr PECOFFLinkingContext::createUndefinedSymbolFile() const { - return LinkingContext::createUndefinedSymbolFile("command line option /include"); + return LinkingContext::createUndefinedSymbolFile( + ""); } bool PECOFFLinkingContext::createImplicitFiles( diff --git a/lld/test/elf/defsym.objtxt b/lld/test/elf/defsym.objtxt new file mode 100644 index 000000000000..fa99c5ed87c3 --- /dev/null +++ b/lld/test/elf/defsym.objtxt @@ -0,0 +1,9 @@ +# RUN: lld -flavor gnu -target x86_64 --defsym=foo=0x1234 -r %s \ +# RUN: --output-filetype=yaml | FileCheck %s + +absolute-atoms: + +# CHECK: absolute-atoms: +# CHECK: - name: foo +# CHECK: scope: global +# CHECK: value: 0x0000000000001234 diff --git a/lld/test/pecoff/include.test b/lld/test/pecoff/include.test index 514497aaf588..bee1f48bf1a7 100644 --- a/lld/test/pecoff/include.test +++ b/lld/test/pecoff/include.test @@ -4,5 +4,5 @@ # RUN: /subsystem:console -- %t.obj 2> %t.log # RUN: FileCheck %s < %t.log -CHECK: Undefined symbol: command line option /include: sym1 -CHECK: Undefined symbol: command line option /include: sym2 +CHECK: Undefined symbol: : sym1 +CHECK: Undefined symbol: : sym2 diff --git a/lld/unittests/DriverTests/GnuLdDriverTest.cpp b/lld/unittests/DriverTests/GnuLdDriverTest.cpp index 63b898d67aef..8e2fadcee6bc 100644 --- a/lld/unittests/DriverTests/GnuLdDriverTest.cpp +++ b/lld/unittests/DriverTests/GnuLdDriverTest.cpp @@ -27,8 +27,47 @@ protected: }; } +// All calls of parse() in this file has empty "--start-group" and "--end-group" +// options. This is a workaround for the current GNU-compatible driver. The +// driver complains if no input file is given, but if we give a file, it tries +// to read it to get magic bytes. It's not suitable for unit tests. +// +// TODO: Modify the driver to make it more test friendly. + TEST_F(GnuLdParserTest, Empty) { EXPECT_FALSE(parse("ld", nullptr)); EXPECT_EQ(linkingContext(), nullptr); EXPECT_EQ("No input files\n", errorMessage()); } + +// Tests for --defsym + +TEST_F(GnuLdParserTest, DefsymDecimal) { + EXPECT_TRUE(parse("ld", "--start-group", "--end-group", "--defsym=sym=1000", + nullptr)); + assert(_context.get()); + auto map = _context->getAbsoluteSymbols(); + EXPECT_EQ((size_t)1, map.size()); + EXPECT_EQ((uint64_t)1000, map["sym"]); +} + +TEST_F(GnuLdParserTest, DefsymHexadecimal) { + EXPECT_TRUE(parse("ld", "--start-group", "--end-group", "--defsym=sym=0x1000", + nullptr)); + auto map = _context->getAbsoluteSymbols(); + EXPECT_EQ((size_t)1, map.size()); + EXPECT_EQ((uint64_t)0x1000, map["sym"]); +} + +TEST_F(GnuLdParserTest, DefsymOctal) { + EXPECT_TRUE(parse("ld", "--start-group", "--end-group", "--defsym=sym=0777", + nullptr)); + auto map = _context->getAbsoluteSymbols(); + EXPECT_EQ((size_t)1, map.size()); + EXPECT_EQ((uint64_t)0777, map["sym"]); +} + +TEST_F(GnuLdParserTest, DefsymFail) { + EXPECT_FALSE( + parse("ld", "--start-group", "--end-group", "--defsym=sym=abc", nullptr)); +}