Index: include/lld/Core/DefinedAtom.h =================================================================== --- include/lld/Core/DefinedAtom.h +++ include/lld/Core/DefinedAtom.h @@ -229,6 +229,9 @@ /// not by the OS loader. virtual Scope scope() const = 0; + /// \brief Alter the visibility of this atom. + virtual void setScope(Scope); + /// \brief Whether the linker should use direct or indirect access to this /// atom. virtual Interposable interposable() const = 0; Index: include/lld/Core/LinkingContext.h =================================================================== --- include/lld/Core/LinkingContext.h +++ include/lld/Core/LinkingContext.h @@ -224,6 +224,21 @@ } InputGraph &getInputGraph() const { return *_inputGraph; } + /// Notify the LinkingContext when an atom is added to the symbol table. + /// This is an opportunity for platform specific work to be done such + /// as adjusting the atom's scope. + virtual void notifySymbolTableAdd(const Atom *atom) const { + } + + /// Notify the LinkingContext when the symbol table found a name collision. + /// The useNew parameter specifies which the symbol table plans to keep, + /// but that can be changed by the LinkingContext. This is also an + /// opportunity for the platform specific processing. + virtual void notifySymbolTableCoalesce(const Atom *existingAtom, + const Atom *newAtom, + bool &useNew) const { + } + /// This method adds undefined symbols specified by the -u option to the to /// the list of undefined symbols known to the linker. This option essentially /// forces an undefined symbol to be created. You may also need to call Index: include/lld/ReaderWriter/MachOLinkingContext.h =================================================================== --- include/lld/ReaderWriter/MachOLinkingContext.h +++ include/lld/ReaderWriter/MachOLinkingContext.h @@ -15,6 +15,7 @@ #include "lld/ReaderWriter/Writer.h" #include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringSet.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MachO.h" @@ -25,7 +26,7 @@ namespace lld { namespace mach_o { -class ArchHandler; +class ArchHandler; class MachODylibFile; } @@ -51,6 +52,12 @@ iOS_simulator }; + enum class ExportMode { + globals, // Default, all global symbols exported. + whiteList, // -exported_symbols_list, only listed symbols exported. + blackList // -unexported_symbols_list, no listed symbol exported. + }; + /// Initializes the context to sane default values given the specified output /// file type, arch, os, and minimum os version. This should be called before /// other setXXX() methods. @@ -59,6 +66,9 @@ void addPasses(PassManager &pm) override; bool validateImpl(raw_ostream &diagnostics) override; bool createImplicitFiles(std::vector> &) const override; + void notifySymbolTableAdd(const Atom *atom) const override; + void notifySymbolTableCoalesce(const Atom *existingAtom, const Atom *newAtom, + bool &useNew) const override; uint32_t getCPUType() const; uint32_t getCPUSubType() const; @@ -79,6 +89,10 @@ StringRef archName() const { return nameFromArch(_arch); } OS os() const { return _os; } + ExportMode exportMode() { return _exportMode; } + void setExportMode(ExportMode mode) { _exportMode = mode; } + void addExportedSymbol(StringRef sym); + bool minOS(StringRef mac, StringRef iOS) const; void setDoNothing(bool value) { _doNothing = value; } bool doNothing() const { return _doNothing; } @@ -222,6 +236,8 @@ private: Writer &writer() const override; mach_o::MachODylibFile* loadIndirectDylib(StringRef path) const; + void checkExportWhiteList(const DefinedAtom *atom) const; + void checkExportBlackList(const DefinedAtom *atom) const; struct ArchInfo { @@ -265,6 +281,8 @@ llvm::StringMap _pathToDylibMap; std::set _allDylibs; mutable std::vector> _indirectDylibs; + ExportMode _exportMode; + llvm::StringSet<> _exportedSymbols; }; } // end namespace lld Index: lib/Core/DefinedAtom.cpp =================================================================== --- lib/Core/DefinedAtom.cpp +++ lib/Core/DefinedAtom.cpp @@ -7,10 +7,12 @@ // //===----------------------------------------------------------------------===// -#include "llvm/Support/ErrorHandling.h" #include "lld/Core/DefinedAtom.h" +#include "llvm/Support/ErrorHandling.h" + +#include namespace lld { @@ -81,5 +83,10 @@ } +void DefinedAtom::setScope(Scope s) { + assert(s == scope() && "setScope() must be overridden to alter scope"); +} + + } // namespace Index: lib/Core/SymbolTable.cpp =================================================================== --- lib/Core/SymbolTable.cpp +++ lib/Core/SymbolTable.cpp @@ -171,6 +171,7 @@ const Atom *existing = findByName(name); if (existing == nullptr) { // Name is not in symbol table yet, add it associate with this atom. + _context.notifySymbolTableAdd(&newAtom); _nameTable[name] = &newAtom; return true; } @@ -299,6 +300,9 @@ break; } + // Give context a chance to change which is kept. + _context.notifySymbolTableCoalesce(existing, &newAtom, useNew); + if (useNew) { // Update name table to use new atom. _nameTable[name] = &newAtom; Index: lib/Driver/DarwinLdDriver.cpp =================================================================== --- lib/Driver/DarwinLdDriver.cpp +++ lib/Driver/DarwinLdDriver.cpp @@ -88,6 +88,31 @@ new MachOFileNode(path, forceLoad))); } +// Export lists are one symbol per line. Blank lines are ignored. +// Trailing comments start with #. +std::error_code parseExportsList(StringRef exportFilePath, + MachOLinkingContext &ctx, + raw_ostream &diagnostics) { + // Map in export list file. + ErrorOr> mb = + MemoryBuffer::getFileOrSTDIN(exportFilePath); + if (std::error_code ec = mb.getError()) + return ec; + StringRef buffer = mb->get()->getBuffer(); + while (!buffer.empty()) { + // Split off each line in the file. + std::pair lineAndRest = buffer.split('\n'); + StringRef line = lineAndRest.first; + // Ignore trailing # comments. + std::pair symAndComment = line.split('#'); + StringRef sym = symAndComment.first.trim(); + if (!sym.empty()) + ctx.addExportedSymbol(sym); + buffer = lineAndRest.second; + } + return std::error_code(); +} + // // There are two variants of the -filelist option: // @@ -349,7 +374,7 @@ ctx.appendLLVMOption(llvmArg->getValue()); } - // Handle -print_atoms a + // Handle -print_atoms if (parsedArgs->getLastArg(OPT_print_atoms)) ctx.setPrintAtoms(); @@ -427,6 +452,42 @@ } } + // Handle -exported_symbols_list + for (auto expFile : parsedArgs->filtered(OPT_exported_symbols_list)) { + if (ctx.exportMode() == MachOLinkingContext::ExportMode::blackList) { + diagnostics << "error: -exported_symbols_list cannot be combined " + << "with -unexported_symbols_list\n"; + return false; + } + ctx.setExportMode(MachOLinkingContext::ExportMode::whiteList); + if (std::error_code ec = parseExportsList(expFile->getValue(), ctx, + diagnostics)) { + diagnostics << "error: " << ec.message() + << ", processing '-exported_symbols_list " + << expFile->getValue() + << "'\n"; + return false; + } + } + + // Handle -unexported_symbols_list + for (auto expFile : parsedArgs->filtered(OPT_unexported_symbols_list)) { + if (ctx.exportMode() == MachOLinkingContext::ExportMode::whiteList) { + diagnostics << "error: -unexported_symbols_list cannot be combined " + << "with -exported_symbols_list\n"; + return false; + } + ctx.setExportMode(MachOLinkingContext::ExportMode::blackList); + if (std::error_code ec = parseExportsList(expFile->getValue(), ctx, + diagnostics)) { + diagnostics << "error: " << ec.message() + << ", processing '-unexported_symbols_list " + << expFile->getValue() + << "'\n"; + return false; + } + } + // Handle input files for (auto &arg : *parsedArgs) { ErrorOr resolvedPath = StringRef(); @@ -442,7 +503,8 @@ diagnostics << "Unable to find library -l" << arg->getValue() << "\n"; return false; } else if (ctx.testingFileUsage()) { - diagnostics << "Found library " << canonicalizePath(resolvedPath.get()) << '\n'; + diagnostics << "Found library " + << canonicalizePath(resolvedPath.get()) << '\n'; } addFile(resolvedPath.get(), inputGraph, globalWholeArchive); break; @@ -452,7 +514,8 @@ diagnostics << "Unable to find -framework " << arg->getValue() << "\n"; return false; } else if (ctx.testingFileUsage()) { - diagnostics << "Found framework " << canonicalizePath(resolvedPath.get()) << '\n'; + diagnostics << "Found framework " + << canonicalizePath(resolvedPath.get()) << '\n'; } addFile(resolvedPath.get(), inputGraph, globalWholeArchive); break; Index: lib/Driver/DarwinLdOptions.td =================================================================== --- lib/Driver/DarwinLdOptions.td +++ lib/Driver/DarwinLdOptions.td @@ -34,6 +34,12 @@ def mllvm : Separate<["-"], "mllvm">, MetaVarName<"