Index: include/llvm/MC/MCStreamer.h =================================================================== --- include/llvm/MC/MCStreamer.h +++ include/llvm/MC/MCStreamer.h @@ -489,6 +489,14 @@ /// .size symbol, expression virtual void emitELFSize(MCSymbol *Symbol, const MCExpr *Value); + /// \brief Emit an ELF .symver directive. + /// + /// This corresponds to an assembler statement such as: + /// .symver _start, foo@@SOME_VERSION + /// \param Alias - The versioned alias (i.e. "foo@@SOME_VERSION") + /// \param Aliasee - The aliased symbol (i.e. "_start") + virtual void emitELFSymverDirective(MCSymbol *Alias, const MCSymbol *Aliasee); + /// \brief Emit a Linker Optimization Hint (LOH) directive. /// \param Args - Arguments of the LOH. virtual void EmitLOHDirective(MCLOHType Kind, const MCLOHArgs &Args) {} Index: include/llvm/Object/ModuleSymbolTable.h =================================================================== --- include/llvm/Object/ModuleSymbolTable.h +++ include/llvm/Object/ModuleSymbolTable.h @@ -26,6 +26,7 @@ namespace llvm { class GlobalValue; +class RecordStreamer; class ModuleSymbolTable { public: @@ -52,7 +53,7 @@ /// For each found symbol, call \p AsmSymbol with the name of the symbol found /// and the associated flags. static void CollectAsmSymbols( - const Triple &TheTriple, StringRef InlineAsm, + const Module &M, function_ref AsmSymbol); }; Index: lib/Analysis/ModuleSummaryAnalysis.cpp =================================================================== --- lib/Analysis/ModuleSummaryAnalysis.cpp +++ lib/Analysis/ModuleSummaryAnalysis.cpp @@ -410,9 +410,8 @@ // be listed on the llvm.used or llvm.compiler.used global and marked as // referenced from there. ModuleSymbolTable::CollectAsmSymbols( - Triple(M.getTargetTriple()), M.getModuleInlineAsm(), - [&M, &Index, &CantBePromoted](StringRef Name, - object::BasicSymbolRef::Flags Flags) { + M, [&M, &Index, &CantBePromoted](StringRef Name, + object::BasicSymbolRef::Flags Flags) { // Symbols not marked as Weak or Global are local definitions. if (Flags & (object::BasicSymbolRef::SF_Weak | object::BasicSymbolRef::SF_Global)) Index: lib/LTO/LTOBackend.cpp =================================================================== --- lib/LTO/LTOBackend.cpp +++ lib/LTO/LTOBackend.cpp @@ -354,7 +354,7 @@ // llvm.compiler.used to prevent optimization to drop these from the output. StringSet<> AsmUndefinedRefs; ModuleSymbolTable::CollectAsmSymbols( - Triple(Mod.getTargetTriple()), Mod.getModuleInlineAsm(), + Mod, [&AsmUndefinedRefs](StringRef Name, object::BasicSymbolRef::Flags Flags) { if (Flags & object::BasicSymbolRef::SF_Undefined) AsmUndefinedRefs.insert(Name); Index: lib/MC/MCParser/ELFAsmParser.cpp =================================================================== --- lib/MC/MCParser/ELFAsmParser.cpp +++ lib/MC/MCParser/ELFAsmParser.cpp @@ -734,6 +734,7 @@ const MCExpr *Value = MCSymbolRefExpr::create(Sym, getContext()); getStreamer().EmitAssignment(Alias, Value); + getStreamer().emitELFSymverDirective(Alias, Sym); return false; } Index: lib/MC/MCStreamer.cpp =================================================================== --- lib/MC/MCStreamer.cpp +++ lib/MC/MCStreamer.cpp @@ -821,6 +821,8 @@ llvm_unreachable("this directive only supported on COFF targets"); } void MCStreamer::emitELFSize(MCSymbol *Symbol, const MCExpr *Value) {} +void MCStreamer::emitELFSymverDirective(MCSymbol *Alias, + const MCSymbol *Aliasee) {} void MCStreamer::EmitLocalCommonSymbol(MCSymbol *Symbol, uint64_t Size, unsigned ByteAlignment) {} void MCStreamer::EmitTBSSSymbol(MCSection *Section, MCSymbol *Symbol, Index: lib/Object/ModuleSymbolTable.cpp =================================================================== --- lib/Object/ModuleSymbolTable.cpp +++ lib/Object/ModuleSymbolTable.cpp @@ -50,20 +50,95 @@ for (GlobalAlias &GA : M->aliases()) SymTab.push_back(&GA); - CollectAsmSymbols(Triple(M->getTargetTriple()), M->getModuleInlineAsm(), - [this](StringRef Name, BasicSymbolRef::Flags Flags) { - SymTab.push_back(new (AsmSymbols.Allocate()) - AsmSymbol(Name, Flags)); - }); + CollectAsmSymbols(*M, [this](StringRef Name, BasicSymbolRef::Flags Flags) { + SymTab.push_back(new (AsmSymbols.Allocate()) AsmSymbol(Name, Flags)); + }); +} + +// Ensure ELF .symver aliases get the same binding as the defined symbol +// they alias with. +static void handleSymverAliases(const Module &M, RecordStreamer &Streamer) { + if (Streamer.symverAliases().empty()) + return; + + // The name in the assembler will be mangled, but the name in the IR + // might not, so we first compute a mapping from mangled name to GV. + Mangler Mang; + SmallString<64> MangledName; + StringMap MangledNameMap; + auto GetMangledName = [&](const GlobalValue &GV) { + if (!GV.hasName()) + return; + + MangledName.clear(); + MangledName.reserve(GV.getName().size() + 1); + Mang.getNameWithPrefix(MangledName, &GV, /*CannotUsePrivateLabel=*/false); + MangledNameMap[MangledName] = &GV; + }; + for (const Function &F : M) + GetMangledName(F); + for (const GlobalVariable &GV : M.globals()) + GetMangledName(GV); + for (const GlobalAlias &GA : M.aliases()) + GetMangledName(GA); + + // Walk all the recorded .symver aliases, and set up the binding + // for each alias. + for (auto &Symver : Streamer.symverAliases()) { + const MCSymbol *Aliasee = Symver.first; + MCSymbolAttr Attr = MCSA_Invalid; + + // First check if the aliasee binding was recorded in the asm. + RecordStreamer::State state = Streamer.getSymbolState(Aliasee); + switch (state) { + case RecordStreamer::Global: + case RecordStreamer::DefinedGlobal: + Attr = MCSA_Global; + break; + case RecordStreamer::UndefinedWeak: + case RecordStreamer::DefinedWeak: + Attr = MCSA_Weak; + break; + default: + break; + } + + // If we don't have a symbol attribute from assembly, then check if + // the aliasee was defined in the IR. + if (Attr == MCSA_Invalid) { + const auto *GV = M.getNamedValue(Aliasee->getName()); + if (!GV) { + auto MI = MangledNameMap.find(Aliasee->getName()); + if (MI != MangledNameMap.end()) + GV = MI->second; + else + continue; + } + if (GV->hasExternalLinkage()) + Attr = MCSA_Global; + else if (GV->hasLocalLinkage()) + Attr = MCSA_Local; + else if (GV->isWeakForLinker()) + Attr = MCSA_Weak; + } + if (Attr == MCSA_Invalid) + continue; + + // Set the detected binding on each alias with this aliasee. + for (auto &Alias : Symver.second) + Streamer.EmitSymbolAttribute(Alias, Attr); + } } void ModuleSymbolTable::CollectAsmSymbols( - const Triple &TT, StringRef InlineAsm, + const Module &M, function_ref AsmSymbol) { + StringRef InlineAsm = M.getModuleInlineAsm(); if (InlineAsm.empty()) return; std::string Err; + const Triple TT(M.getTargetTriple()); const Target *T = TargetRegistry::lookupTarget(TT.str(), Err); assert(T && T->hasMCAsmParser()); @@ -106,6 +181,8 @@ if (Parser->Run(false)) return; + handleSymverAliases(M, Streamer); + for (auto &KV : Streamer) { StringRef Key = KV.first(); RecordStreamer::State Value = KV.second; Index: lib/Object/RecordStreamer.h =================================================================== --- lib/Object/RecordStreamer.h +++ lib/Object/RecordStreamer.h @@ -20,6 +20,10 @@ private: StringMap Symbols; + // Map of aliases created by .symver directives, saved so we can update + // their symbol binding after parsing complete. This maps from each + // aliasee to its list of aliases. + DenseMap> SymverAliasMap; void markDefined(const MCSymbol &Symbol); void markGlobal(const MCSymbol &Symbol, MCSymbolAttr Attribute); void markUsed(const MCSymbol &Symbol); @@ -38,6 +42,20 @@ unsigned ByteAlignment) override; void EmitCommonSymbol(MCSymbol *Symbol, uint64_t Size, unsigned ByteAlignment) override; + /// Record .symver aliases for later processing. + void emitELFSymverDirective(MCSymbol *Alias, + const MCSymbol *Aliasee) override; + /// Return the map of .symver aliasee to associated aliases. + DenseMap> &symverAliases() { + return SymverAliasMap; + } + /// Get the state recorded for the given symbol. + State getSymbolState(const MCSymbol *Sym) { + auto SI = Symbols.find(Sym->getName()); + if (SI == Symbols.end()) + return NeverSeen; + return SI->second; + } }; } #endif Index: lib/Object/RecordStreamer.cpp =================================================================== --- lib/Object/RecordStreamer.cpp +++ lib/Object/RecordStreamer.cpp @@ -110,3 +110,8 @@ unsigned ByteAlignment) { markDefined(*Symbol); } + +void RecordStreamer::emitELFSymverDirective(MCSymbol *Alias, + const MCSymbol *Aliasee) { + SymverAliasMap[Aliasee].push_back(Alias); +} Index: lib/Transforms/IPO/FunctionImport.cpp =================================================================== --- lib/Transforms/IPO/FunctionImport.cpp +++ lib/Transforms/IPO/FunctionImport.cpp @@ -598,7 +598,7 @@ // the current module. StringSet<> AsmUndefinedRefs; ModuleSymbolTable::CollectAsmSymbols( - Triple(TheModule.getTargetTriple()), TheModule.getModuleInlineAsm(), + TheModule, [&AsmUndefinedRefs](StringRef Name, object::BasicSymbolRef::Flags Flags) { if (Flags & object::BasicSymbolRef::SF_Undefined) AsmUndefinedRefs.insert(Name); Index: test/LTO/X86/symver-asm.ll =================================================================== --- test/LTO/X86/symver-asm.ll +++ test/LTO/X86/symver-asm.ll @@ -1,16 +1,47 @@ ; RUN: llvm-as < %s >%t1 -; RUN: llvm-lto -o %t2 %t1 +; RUN: llvm-lto -exported-symbol=io_cancel_0_4 -exported-symbol=io_cancel_weak_0_4 -exported-symbol=foo -o %t2 %t1 ; RUN: llvm-nm %t2 | FileCheck %s +; RUN: llvm-lto2 -r %t1,io_cancel_0_4,plx -r %t1,io_cancel_0_4,plx -r %t1,io_cancel_local_0_4,plx -r %t1,io_cancel_weak_0_4,plx -r %t1,io_cancel_weak_0_4,plx -r %t1,io_cancel@@LIBAIO_0.4,plx -r %t1,io_cancel_weak@@LIBAIO_0.4,plx -r %t1,io_cancel_weak@@LIBAIO_0.4.1,plx -r %t1,foo,plx -r %t1,foo,plx -r %t1,foo@@VER1,plx -o %t3 %t1 -save-temps +; RUN: llvm-nm %t3.0 | FileCheck %s +; RUN: llvm-dis %t3.0.2.internalize.bc -o - | FileCheck %s --check-prefix=INTERN +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" module asm ".symver io_cancel_0_4,io_cancel@@LIBAIO_0.4" +module asm ".symver io_cancel_local_0_4,io_cancel_local@@LIBAIO_0.4" +module asm ".symver io_cancel_weak_0_4,io_cancel_weak@@LIBAIO_0.4" +; Ensure we handle case of same aliasee with two version aliases. +module asm ".symver io_cancel_weak_0_4,io_cancel_weak@@LIBAIO_0.4.1" +module asm ".symver foo,foo@@VER1" + +; Local values used in inline assembly must be specified on the +; llvm.compiler.used so they aren't incorrectly DCE'd during module linking. +@llvm.compiler.used = appending global [1 x i8*] [i8* bitcast (i32 ()* @io_cancel_local_0_4 to i8*)], section "llvm.metadata" -; Even without -exported-symbol, io_cancel_0_4 should be noticed by LTOModule's -; RecordStreamer, so it shouldn't get eliminated. However, the object file will -; contain the aliased symver as well as the original. define i32 @io_cancel_0_4() { -; CHECK: io_cancel@@LIBAIO_0.4 -; CHECK: io_cancel_0_4 +; CHECK-DAG: T io_cancel@@LIBAIO_0.4 +; CHECK-DAG: T io_cancel_0_4 + ret i32 0 +} + +define internal i32 @io_cancel_local_0_4() { +; INTERN: llvm.compiler.used {{.*}} @io_cancel_local_0_4 +; INTERN: define internal i32 @io_cancel_local_0_4() +; CHECK-DAG: t io_cancel_local@@LIBAIO_0.4 +; CHECK-DAG: t io_cancel_local_0_4 + ret i32 0 +} + +define weak i32 @io_cancel_weak_0_4() { +; CHECK-DAG: W io_cancel_weak@@LIBAIO_0.4 +; CHECK-DAG: W io_cancel_weak@@LIBAIO_0.4.1 +; CHECK-DAG: W io_cancel_weak_0_4 +ret i32 0 +} + +define i32 @"\01foo"() { +; CHECK-DAG: T foo@@VER1 +; CHECK-DAG: T foo ret i32 0 } Index: test/LTO/X86/symver-asm2.ll =================================================================== --- /dev/null +++ test/LTO/X86/symver-asm2.ll @@ -0,0 +1,30 @@ +; Test to ensure symbol binding works correctly for symver directives, +; when the aliased symbols are defined in inline assembly, including +; cases when the symbol attributes are provided after the .symver +; directive. + +; RUN: llvm-as < %s >%t1 +; RUN: llvm-lto -o %t2 %t1 +; RUN: llvm-nm %t2 | FileCheck %s +; RUN: llvm-lto2 -r %t1,_start,plx -r %t1,_start3,plx -r %t1,foo@@SOME_VERSION -r %t1,foo@SOME_VERSION3 -o %t3 %t1 -save-temps +; RUN: llvm-nm %t3.0 | FileCheck %s + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +module asm ".global _start" +module asm "_start:" +module asm "_start2:" +module asm "_start3:" +module asm ".symver _start, foo@@SOME_VERSION" +module asm ".symver _start2, foo@SOME_VERSION2" +module asm ".symver _start3, foo@SOME_VERSION3" +module asm ".local _start2" +module asm ".weak _start3" + +; CHECK-DAG: T _start +; CHECK-DAG: t _start2 +; CHECK-DAG: W _start3 +; CHECK-DAG: T foo@@SOME_VERSION +; CHECK-DAG: t foo@SOME_VERSION2 +; CHECK-DAG: W foo@SOME_VERSION3