Index: lld/trunk/include/lld/ReaderWriter/ELFLinkingContext.h =================================================================== --- lld/trunk/include/lld/ReaderWriter/ELFLinkingContext.h +++ lld/trunk/include/lld/ReaderWriter/ELFLinkingContext.h @@ -187,6 +187,10 @@ _absoluteSymbols[name] = addr; } + void addAlias(StringRef sym, StringRef target) { + _aliasSymbols[sym] = target; + } + /// Return the list of initializer symbols that are specified in the /// linker command line, using the -init option. range initFunctions() const { @@ -228,6 +232,10 @@ return _absoluteSymbols; } + const std::map getAliases() const { + return _aliasSymbols; + } + /// \brief Helper function to allocate strings. StringRef allocateString(StringRef ref) const { char *x = _allocator.Allocate(ref.size() + 1); @@ -275,6 +283,7 @@ StringRefVector _rpathList; StringRefVector _rpathLinkList; std::map _absoluteSymbols; + std::map _aliasSymbols; }; } // end namespace lld Index: lld/trunk/lib/Driver/GnuLdDriver.cpp =================================================================== --- lld/trunk/lib/Driver/GnuLdDriver.cpp +++ lld/trunk/lib/Driver/GnuLdDriver.cpp @@ -129,10 +129,9 @@ 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) { +// Parses an argument of --defsym== +static bool parseDefsymAsAbsolute(StringRef opt, StringRef &sym, + uint64_t &addr) { size_t equalPos = opt.find('='); if (equalPos == 0 || equalPos == StringRef::npos) return false; @@ -142,6 +141,17 @@ return true; } +// Parses an argument of --defsym== +static bool parseDefsymAsAlias(StringRef opt, StringRef &sym, + StringRef &target) { + size_t equalPos = opt.find('='); + if (equalPos == 0 || equalPos == StringRef::npos) + return false; + sym = opt.substr(0, equalPos); + target = opt.substr(equalPos + 1); + return !target.empty(); +} + llvm::ErrorOr ELFFileNode::getPath(const LinkingContext &) const { if (!_attributes._isDashlPrefix) return _path; @@ -416,13 +426,16 @@ break; case OPT_defsym: { - StringRef sym; + StringRef sym, target; uint64_t addr; - if (!parseDefsymOption(inputArg->getValue(), sym, addr)) { + if (parseDefsymAsAbsolute(inputArg->getValue(), sym, addr)) { + ctx->addInitialAbsoluteSymbol(sym, addr); + } else if (parseDefsymAsAlias(inputArg->getValue(), sym, target)) { + ctx->addAlias(sym, target); + } else { diagnostics << "invalid --defsym: " << inputArg->getValue() << "\n"; return false; } - ctx->addInitialAbsoluteSymbol(sym, addr); break; } Index: lld/trunk/lib/ReaderWriter/ELF/ELFLinkingContext.cpp =================================================================== --- lld/trunk/lib/ReaderWriter/ELF/ELFLinkingContext.cpp +++ lld/trunk/lib/ReaderWriter/ELF/ELFLinkingContext.cpp @@ -41,6 +41,70 @@ uint64_t _value; }; +// An AliasAtom is a zero-size atom representing an alias for other atom. It has +// a LayoutAfter reference to the target atom, so that this atom and the target +// atom will be layed out at the same location in the final result. Initially +// the target atom is an undefined atom. Resolver will replace it with a defined +// one. +// +// It does not have attributes itself. Most member function calls are forwarded +// to the target atom. +class AliasAtom : public SimpleDefinedAtom { +public: + AliasAtom(const File &file, StringRef name) + : SimpleDefinedAtom(file), _target(nullptr), _name(name) {} + + StringRef name() const override { return _name; } + uint64_t size() const override { return 0; } + ArrayRef rawContent() const override { return ArrayRef(); } + + Scope scope() const override { + getTarget(); + return _target ? _target->scope() : scopeLinkageUnit; + } + + Merge merge() const override { + getTarget(); + return _target ? _target->merge() : mergeNo; + } + + ContentType contentType() const override { + getTarget(); + return _target ? _target->contentType() : typeUnknown; + } + + Interposable interposable() const override { + getTarget(); + return _target ? _target->interposable() : interposeNo; + } + + SectionChoice sectionChoice() const override { + getTarget(); + return _target ? _target->sectionChoice() : sectionBasedOnContent; + } + + StringRef customSectionName() const override { + getTarget(); + return _target ? _target->customSectionName() : StringRef(""); + } + +private: + void getTarget() const { + if (_target) + return; + for (const Reference *r : *this) { + if (r->kindNamespace() == lld::Reference::KindNamespace::all && + r->kindValue() == lld::Reference::kindLayoutAfter) { + _target = dyn_cast(r->target()); + return; + } + } + } + + mutable const DefinedAtom *_target; + StringRef _name; +}; + class CommandLineUndefinedAtom : public SimpleUndefinedAtom { public: CommandLineUndefinedAtom(const File &f, StringRef name) @@ -204,6 +268,17 @@ uint64_t val = i.second; file->addAtom(*(new (_allocator) CommandLineAbsoluteAtom(*file, sym, val))); } + for (auto &i : getAliases()) { + StringRef from = i.first; + StringRef to = i.second; + SimpleDefinedAtom *fromAtom = new (_allocator) AliasAtom(*file, from); + UndefinedAtom *toAtom = new (_allocator) SimpleUndefinedAtom(*file, to); + fromAtom->addReference(Reference::KindNamespace::all, + Reference::KindArch::all, Reference::kindLayoutAfter, + 0, toAtom, 0); + file->addAtom(*fromAtom); + file->addAtom(*toAtom); + } files.push_back(std::move(file)); LinkingContext::createInternalFiles(files); } Index: lld/trunk/test/elf/X86_64/defsym.test =================================================================== --- lld/trunk/test/elf/X86_64/defsym.test +++ lld/trunk/test/elf/X86_64/defsym.test @@ -0,0 +1,21 @@ +RUN: lld -flavor gnu -target x86_64 --defsym=_start=fn %p/Inputs/fn.o -o %t +RUN: llvm-readobj -symbols %t | FileCheck %s + +CHECK: Symbol { +CHECK: Name: _start (1) +CHECK: Value: 0x4001E0 +CHECK: Size: 0 +CHECK: Binding: Global (0x1) +CHECK: Type: Function (0x2) +CHECK: Other: 0 +CHECK: Section: .text (0x5) +CHECK: } +CHECK: Symbol { +CHECK: Name: fn (8) +CHECK: Value: 0x4001E0 +CHECK: Size: 6 +CHECK: Binding: Global (0x1) +CHECK: Type: Function (0x2) +CHECK: Other: 0 +CHECK: Section: .text (0x5) +CHECK: } Index: lld/trunk/test/elf/defsym.objtxt =================================================================== --- lld/trunk/test/elf/defsym.objtxt +++ lld/trunk/test/elf/defsym.objtxt @@ -1,9 +1,28 @@ # RUN: lld -flavor gnu -target x86_64 --defsym=foo=0x1234 -r %s \ -# RUN: --output-filetype=yaml | FileCheck %s +# RUN: --output-filetype=yaml | FileCheck -check-prefix=ABS %s -absolute-atoms: +# RUN: lld -flavor gnu -target x86_64 --defsym=foo=main -r %s \ +# RUN: --output-filetype=yaml | FileCheck -check-prefix=ALIAS %s -# CHECK: absolute-atoms: -# CHECK: - name: foo -# CHECK: scope: global -# CHECK: value: 0x0000000000001234 +defined-atoms: + - name: main + scope: global + content: [ B8, 00, 00, 00, 00, C7, 44, 24, FC, 00, 00, 00, 00, C3 ] + alignment: 2^4 + section-choice: custom-required + section-name: .text + +# ABS: absolute-atoms: +# ABS: - name: foo +# ABS: scope: global +# ABS: value: 0x0000000000001234 + +# ALIAS: defined-atoms: +# ALIAS: - name: foo +# ALIAS: scope: global +# ALIAS: section-choice: custom-required +# ALIAS: section-name: .text +# ALIAS: references: +# ALIAS: - kind: layout-after +# ALIAS: offset: 0 +# ALIAS: target: main Index: lld/trunk/unittests/DriverTests/GnuLdDriverTest.cpp =================================================================== --- lld/trunk/unittests/DriverTests/GnuLdDriverTest.cpp +++ lld/trunk/unittests/DriverTests/GnuLdDriverTest.cpp @@ -149,6 +149,14 @@ EXPECT_EQ((uint64_t)0x1000, map["sym"]); } +TEST_F(GnuLdParserTest, DefsymAlias) { + EXPECT_TRUE( + parse("ld", "--start-group", "--end-group", "--defsym=sym=abc", nullptr)); + auto map = _context->getAliases(); + EXPECT_EQ((size_t)1, map.size()); + EXPECT_EQ("abc", map["sym"]); +} + TEST_F(GnuLdParserTest, DefsymOctal) { EXPECT_TRUE(parse("ld", "--start-group", "--end-group", "--defsym=sym=0777", nullptr)); @@ -157,11 +165,6 @@ EXPECT_EQ((uint64_t)0777, map["sym"]); } -TEST_F(GnuLdParserTest, DefsymFail) { - EXPECT_FALSE( - parse("ld", "--start-group", "--end-group", "--defsym=sym=abc", nullptr)); -} - TEST_F(GnuLdParserTest, DefsymMisssingSymbol) { EXPECT_FALSE( parse("ld", "--start-group", "--end-group", "--defsym==0", nullptr));