Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -23,6 +23,7 @@ namespace lld { namespace elf { +class DefinedRegular; class InputFile; struct Symbol; @@ -67,6 +68,13 @@ size_t NameOff = 0; // Offset in the string table }; +// For --defsym=symbol=expression +struct Defsym { + llvm::StringRef Name; + llvm::StringRef Expr; + DefinedRegular *Sym; +}; + // This struct contains the global configuration for the linker. // Most fields are direct mapping from the command line options // and such fields have the same name as the corresponding options. @@ -90,6 +98,7 @@ llvm::StringRef Sysroot; llvm::StringRef ThinLTOCacheDir; std::string RPath; + std::vector Defsym; std::vector VersionDefinitions; std::vector AuxiliaryList; std::vector SearchPaths; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -564,6 +564,16 @@ return Ret; } +static std::vector getDefsym(opt::InputArgList &Args) { + std::vector Ret; + for (auto *Arg : Args.filtered(OPT_defsym)) { + Defsym Sym; + std::tie(Sym.Name, Sym.Expr) = StringRef(Arg->getValue()).split('='); + Ret.push_back(Sym); + } + return Ret; +} + // Initializes Config members by the command line options. void LinkerDriver::readConfigs(opt::InputArgList &Args) { Config->AllowMultipleDefinition = Args.hasArg(OPT_allow_multiple_definition); @@ -572,6 +582,7 @@ Config->BsymbolicFunctions = Args.hasArg(OPT_Bsymbolic_functions); Config->DefineCommon = getArg(Args, OPT_define_common, OPT_no_define_common, !Args.hasArg(OPT_relocatable)); + Config->Defsym = getDefsym(Args); Config->Demangle = getArg(Args, OPT_demangle, OPT_no_demangle, true); Config->DisableVerify = Args.hasArg(OPT_disable_verify); Config->Discard = getDiscard(Args); Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -48,6 +48,8 @@ def define_common: F<"define-common">, HelpText<"Assign space to common symbols">; +def defsym: J<"defsym=">, HelpText<"Create a global symbol using expression">; + def demangle: F<"demangle">, HelpText<"Demangle symbol names">; def disable_new_dtags: F<"disable-new-dtags">, Index: ELF/ScriptParser.h =================================================================== --- ELF/ScriptParser.h +++ ELF/ScriptParser.h @@ -16,6 +16,8 @@ namespace lld { namespace elf { +uint64_t calcDefsym(MemoryBufferRef MB); + // Parses a linker script. Calling this function updates // Config and ScriptConfig. void readLinkerScript(MemoryBufferRef MB); Index: ELF/ScriptParser.cpp =================================================================== --- ELF/ScriptParser.cpp +++ ELF/ScriptParser.cpp @@ -43,7 +43,7 @@ static bool isUnderSysroot(StringRef Path); namespace { -class ScriptParser final : ScriptLexer { +class ScriptParser : ScriptLexer { public: ScriptParser(MemoryBufferRef MB) : ScriptLexer(MB), @@ -53,6 +53,11 @@ void readVersionScript(); void readDynamicList(); +protected: + Expr readExpr(); + + bool DefsymMode = false; + private: void addFile(StringRef Path); @@ -92,10 +97,10 @@ uint64_t readMemoryAssignment(StringRef, StringRef, StringRef); std::pair readMemoryAttributes(); - Expr readExpr(); Expr readExpr1(Expr Lhs, int MinPrec); StringRef readParenLiteral(); Expr readPrimary(); + llvm::Optional readPrimaryOpt(StringRef Tok, std::string &Location); Expr readTernary(Expr Cond); Expr readParenExpr(); @@ -109,6 +114,13 @@ bool IsUnderSysroot; }; + +class DefsymParser : public ScriptParser { +public: + DefsymParser(MemoryBufferRef MB) : ScriptParser(MB) { DefsymMode = true; } + Expr DefsymParser::readExpression() { return readExpr(); } +}; + } // namespace static bool isUnderSysroot(StringRef Path) { @@ -819,22 +831,8 @@ return Tok; } -Expr ScriptParser::readPrimary() { - if (peek() == "(") - return readParenExpr(); - - if (consume("~")) { - Expr E = readPrimary(); - return [=] { return ~E().getValue(); }; - } - if (consume("-")) { - Expr E = readPrimary(); - return [=] { return -E().getValue(); }; - } - - StringRef Tok = next(); - std::string Location = getCurrentLocation(); - +llvm::Optional ScriptParser::readPrimaryOpt(StringRef Tok, + std::string &Location) { // Built-in functions are parsed here. // https://sourceware.org/binutils/docs/ld/Builtin-Functions.html. if (Tok == "ABSOLUTE") { @@ -922,15 +920,45 @@ // Tok is the dot. if (Tok == ".") return [=] { return Script->getSymbolValue(Location, Tok); }; + return None; +} + +Expr ScriptParser::readPrimary() { + if (peek() == "(") + return readParenExpr(); + + if (consume("~")) { + Expr E = readPrimary(); + return [=] { return ~E().getValue(); }; + } + if (consume("-")) { + Expr E = readPrimary(); + return [=] { return -E().getValue(); }; + } + + StringRef Tok = next(); + std::string Location = getCurrentLocation(); + + // Expression parser also can be used for --defsym option. + // In that case only arithmetic operations are supported, in compare + // with regular linkerscript expressions. Here we avoid proccessing + // of optional commands if parser is in defsym evaluation mode. + if (!DefsymMode) + if (Optional Opt = readPrimaryOpt(Tok, Location)) + return *Opt; // Tok is a literal number. if (Optional Val = parseInt(Tok)) return [=] { return *Val; }; // Tok is a symbol name. - if (!isValidCIdentifier(Tok)) + if (!isValidCIdentifier(Tok) || (DefsymMode && Tok == ".")) setError("malformed number: " + Tok); - Script->Opt.ReferencedSymbols.push_back(Tok); + if (Error) + return {}; + + if (!DefsymMode) + Script->Opt.ReferencedSymbols.push_back(Tok); return [=] { return Script->getSymbolValue(Location, Tok); }; } @@ -1164,6 +1192,10 @@ return {Flags, NegFlags}; } +uint64_t elf::calcDefsym(MemoryBufferRef MB) { + return DefsymParser(MB).readExpression()().getValue(); +} + void elf::readLinkerScript(MemoryBufferRef MB) { ScriptParser(MB).readLinkerScript(); } Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -15,6 +15,7 @@ #include "Memory.h" #include "OutputSections.h" #include "Relocations.h" +#include "ScriptParser.h" #include "Strings.h" #include "SymbolTable.h" #include "SyntheticSections.h" @@ -885,6 +886,9 @@ ElfSym::Etext2 = Add("_etext"); ElfSym::Edata1 = Add("edata"); ElfSym::Edata2 = Add("_edata"); + + for (Defsym &DS : Config->Defsym) + DS.Sym = Symtab::X->addAbsolute(DS.Name, STV_DEFAULT, STB_GLOBAL); } // Sort input sections by section name suffixes for @@ -1701,6 +1705,9 @@ if (ElfSym::Bss) ElfSym::Bss->Section = findSection(".bss"); + for (Defsym &DS : Config->Defsym) + DS.Sym->Value = calcDefsym({DS.Expr, Saver.save("--defsym=" + DS.Name)}); + // Setup MIPS _gp_disp/__gnu_local_gp symbols which should // be equal to the _gp symbol's value. if (Config->EMachine == EM_MIPS) { Index: test/ELF/defsym.s =================================================================== --- test/ELF/defsym.s +++ test/ELF/defsym.s @@ -0,0 +1,29 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +# RUN: ld.lld %t.o -o %t --defsym=val1=1+0x2 --defsym=val2=foo+1 +# RUN: llvm-readobj -t -s %t | FileCheck %s + +# CHECK: Symbol { +# CHECK: Name: foo +# CHECK-NEXT: Value: 0x201000 +# CHECK: Symbol { +# CHECK: Name: val1 +# CHECK-NEXT: Value: 0x3 +# CHECK: Symbol { +# CHECK: Name: val2 +# CHECK-NEXT: Value: 0x201001 + +# RUN: not ld.lld %t.o -o %t --defsym=val1=1[ 2>&1 | \ +# RUN: FileCheck --check-prefix=ERR1 %s +# ERR1: --defsym=val1:1: malformed number: 1[ + +# RUN: not ld.lld %t.o -o %t --defsym=val2=. 2>&1 | \ +# RUN: FileCheck --check-prefix=ERR2 %s +# ERR2: --defsym=val2:1: malformed number: . + +.globl foo +.type foo, @function +foo: + nop + +_start: