Index: ELF/LinkerScript.h =================================================================== --- ELF/LinkerScript.h +++ ELF/LinkerScript.h @@ -51,6 +51,10 @@ static bool classof(const BaseCommand *C); StringRef Name; std::vector Expr; + bool Provide = false; + // Hidden and Ignore can be true, only if Provide is true + bool Hidden = false; + bool Ignore = false; }; struct OutputSectionCommand : BaseCommand { Index: ELF/LinkerScript.cpp =================================================================== --- ELF/LinkerScript.cpp +++ ELF/LinkerScript.cpp @@ -321,7 +321,7 @@ uint64_t Val = evalExpr(Cmd->Expr, Dot); if (Cmd->Name == ".") { Dot = Val; - } else { + } else if (!Cmd->Ignore) { auto *D = cast>(Symtab::X->find(Cmd->Name)); D->Value = Val; } @@ -511,10 +511,19 @@ } template void LinkerScript::addScriptedSymbols() { - for (const std::unique_ptr &Base : Opt.Commands) - if (auto *Cmd = dyn_cast(Base.get())) - if (Cmd->Name != "." && Symtab::X->find(Cmd->Name) == nullptr) - Symtab::X->addAbsolute(Cmd->Name, STV_DEFAULT); + for (const std::unique_ptr &Base : Opt.Commands) { + auto *Cmd = dyn_cast(Base.get()); + if (!Cmd || Cmd->Name == ".") + continue; + + if (Symtab::X->find(Cmd->Name) == nullptr) + Symtab::X->addAbsolute(Cmd->Name, + Cmd->Hidden ? STV_HIDDEN : STV_DEFAULT); + else + // Symbol already exists in symbol table. If it is provided + // then we can't override its value. + Cmd->Ignore = Cmd->Provide; + } } template bool LinkerScript::hasPhdrsCommands() { @@ -575,7 +584,8 @@ void readOutputSectionDescription(StringRef OutSec); std::vector readOutputSectionPhdrs(); unsigned readPhdrType(); - void readSymbolAssignment(StringRef Name); + void readProvide(bool Hidden); + SymbolAssignment *readSymbolAssignment(StringRef Name); std::vector readSectionsCommandExpr(); const static StringMap Cmd; @@ -772,7 +782,11 @@ continue; } next(); - if (peek() == "=") + if (Tok == "PROVIDE") + readProvide(false); + else if (Tok == "PROVIDE_HIDDEN") + readProvide(true); + else if (peek() == "=") readSymbolAssignment(Tok); else readOutputSectionDescription(Tok); @@ -832,19 +846,39 @@ } } -void ScriptParser::readSymbolAssignment(StringRef Name) { +void ScriptParser::readProvide(bool Hidden) { + expect("("); + if (SymbolAssignment *Assignment = readSymbolAssignment(next())) { + Assignment->Provide = true; + Assignment->Hidden = Hidden; + } + expect(")"); + expect(";"); +} + +SymbolAssignment *ScriptParser::readSymbolAssignment(StringRef Name) { expect("="); std::vector Expr = readSectionsCommandExpr(); - if (Expr.empty()) + if (Expr.empty()) { error("error in symbol assignment expression"); - else + } else { Opt.Commands.push_back(llvm::make_unique(Name, Expr)); + return static_cast(Opt.Commands.back().get()); + } + return nullptr; } +// This function reads balanced expression until semicolon is seen. std::vector ScriptParser::readSectionsCommandExpr() { + int Braces = 0; std::vector Expr; while (!Error) { - StringRef Tok = next(); + StringRef Tok = peek(); + Braces += (Tok == "("); + Braces -= (Tok == ")"); + if (Braces < 0) + break; + next(); if (Tok == ";") break; Expr.push_back(Tok); Index: test/ELF/linkerscript-locationcounter.s =================================================================== --- test/ELF/linkerscript-locationcounter.s +++ test/ELF/linkerscript-locationcounter.s @@ -319,7 +319,7 @@ # RUN: }" > %t.script # RUN: not ld.lld %t --script %t.script -o %t2 2>&1 | \ # RUN: FileCheck --check-prefix=BRACKETERR2 %s -# BRACKETERR2: stray token: ) +# BRACKETERR2: expected, but got * ## Empty expression. # RUN: echo "SECTIONS { \ Index: test/ELF/linkerscript-symbols.s =================================================================== --- test/ELF/linkerscript-symbols.s +++ test/ELF/linkerscript-symbols.s @@ -1,11 +1,43 @@ # REQUIRES: x86 # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +# Simple symbol assignment. Should raise conflict in case we +# have duplicates in any input section, but currently simply +# replaces the value. # RUN: echo "SECTIONS {.text : {*(.text.*)} text_end = .;}" > %t.script # RUN: ld.lld -o %t1 --script %t.script %t -# RUN: llvm-objdump -t %t1 | FileCheck %s -# CHECK: 0000000000000121 *ABS* 00000000 text_end +# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=SIMPLE %s +# SIMPLE: 0000000000000121 *ABS* 00000000 text_end + +# Provide new symbol. The value should be 1, like set in PROVIDE() +# RUN: echo "SECTIONS { PROVIDE(newsym = 1);}" > %t.script +# RUN: ld.lld -o %t1 --script %t.script %t +# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=PROVIDE1 %s +# PROVIDE1: 0000000000000001 *ABS* 00000000 newsym + +# Provide new symbol (hidden). The value should be 1 +# RUN: echo "SECTIONS { PROVIDE_HIDDEN(newsym = 1);}" > %t.script +# RUN: ld.lld -o %t1 --script %t.script %t +# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=HIDDEN1 %s +# HIDDEN1: 0000000000000001 *ABS* 00000000 .hidden newsym + +# Provide existing symbol. The value should be 0, even though we +# have value of 1 in PROVIDE() +# RUN: echo "SECTIONS { PROVIDE(somesym = 1);}" > %t.script +# RUN: ld.lld -o %t1 --script %t.script %t +# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=PROVIDE2 %s +# PROVIDE2: 0000000000000000 *ABS* 00000000 somesym + +# Provide existing symbol. The value should be 0, even though we +# have value of 1 in PROVIDE(). Visibility should not change +# RUN: echo "SECTIONS { PROVIDE(somesym = 1);}" > %t.script +# RUN: ld.lld -o %t1 --script %t.script %t +# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=HIDDEN2 %s +# HIDDEN2: 0000000000000000 *ABS* 00000000 somesym .global _start _start: nop + +.global somesym +somesym = 0