diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2015-03-24 21:31:36 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2015-03-24 21:31:36 +0000 |
commit | fb911942f1434f3d1750f83f25f5e42c80e60638 (patch) | |
tree | 1678c4a4f0182e4029a86d135aa4a1b7d09e3c41 /lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.h |
Notes
Diffstat (limited to 'lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.h')
-rw-r--r-- | lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.h | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.h b/lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.h new file mode 100644 index 0000000000000..b9764d70bb3bf --- /dev/null +++ b/lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.h @@ -0,0 +1,309 @@ +//===- lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.h ----------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Atoms.h" +#include "lld/Core/Simple.h" +#include "lld/ReaderWriter/PECOFFLinkingContext.h" +#include "llvm/Support/Allocator.h" +#include <algorithm> +#include <mutex> + +using llvm::COFF::WindowsSubsystem; + +namespace lld { +namespace pecoff { + +bool findDecoratedSymbol(PECOFFLinkingContext *ctx, + std::string sym, std::string &res); + +namespace impl { + +/// The defined atom for dllexported symbols with __imp_ prefix. +class ImpPointerAtom : public COFFLinkerInternalAtom { +public: + ImpPointerAtom(const File &file, StringRef symbolName, uint64_t ordinal) + : COFFLinkerInternalAtom(file, /*oridnal*/ 0, std::vector<uint8_t>(4), + symbolName), + _ordinal(ordinal) {} + + uint64_t ordinal() const override { return _ordinal; } + Scope scope() const override { return scopeGlobal; } + ContentType contentType() const override { return typeData; } + Alignment alignment() const override { return Alignment(4); } + ContentPermissions permissions() const override { return permR__; } + +private: + uint64_t _ordinal; +}; + +class ImpSymbolFile : public SimpleFile { +public: + ImpSymbolFile(StringRef defsym, StringRef undefsym, uint64_t ordinal, + bool is64) + : SimpleFile(defsym), _undefined(*this, undefsym), + _defined(*this, defsym, ordinal) { + SimpleReference *ref; + if (is64) { + ref = new SimpleReference(Reference::KindNamespace::COFF, + Reference::KindArch::x86_64, + llvm::COFF::IMAGE_REL_AMD64_ADDR32, + 0, &_undefined, 0); + } else { + ref = new SimpleReference(Reference::KindNamespace::COFF, + Reference::KindArch::x86, + llvm::COFF::IMAGE_REL_I386_DIR32, + 0, &_undefined, 0); + } + _defined.addReference(std::unique_ptr<SimpleReference>(ref)); + addAtom(_defined); + addAtom(_undefined); + }; + +private: + SimpleUndefinedAtom _undefined; + ImpPointerAtom _defined; +}; + +// A file to make Resolver to resolve a symbol TO instead of a symbol FROM, +// using fallback mechanism for an undefined symbol. One can virtually rename an +// undefined symbol using this file. +class SymbolRenameFile : public SimpleFile { +public: + SymbolRenameFile(StringRef from, StringRef to) + : SimpleFile("<symbol-rename>"), _fromSym(from), _toSym(to), + _from(*this, _fromSym, &_to), _to(*this, _toSym) { + addAtom(_from); + }; + +private: + std::string _fromSym; + std::string _toSym; + COFFUndefinedAtom _from; + COFFUndefinedAtom _to; +}; + +} // namespace impl + +// A virtual file containing absolute symbol __ImageBase. __ImageBase (or +// ___ImageBase on x86) is a linker-generated symbol whose address is the same +// as the image base address. +class LinkerGeneratedSymbolFile : public SimpleFile { +public: + LinkerGeneratedSymbolFile(const PECOFFLinkingContext &ctx) + : SimpleFile("<linker-internal-file>"), + _imageBaseAtom(*this, ctx.decorateSymbol("__ImageBase"), + Atom::scopeGlobal, ctx.getBaseAddress()) { + addAtom(_imageBaseAtom); + }; + +private: + SimpleAbsoluteAtom _imageBaseAtom; +}; + +// A LocallyImporteSymbolFile is an archive file containing __imp_ +// symbols for local use. +// +// For each defined symbol, linker creates an implicit defined symbol +// by appending "__imp_" prefix to the original name. The content of +// the implicit symbol is a pointer to the original symbol +// content. This feature allows one to compile and link the following +// code without error, although _imp__hello is not defined in the +// code. (the leading "_" in this example is automatically appended, +// assuming it's x86.) +// +// void hello() { printf("Hello\n"); } +// extern void (*_imp__hello)(); +// int main() { +// _imp__hello(); +// return 0; +// } +// +// This odd feature is for the compatibility with MSVC link.exe. +class LocallyImportedSymbolFile : public SimpleArchiveLibraryFile { +public: + LocallyImportedSymbolFile(const PECOFFLinkingContext &ctx) + : SimpleArchiveLibraryFile("__imp_"), _is64(ctx.is64Bit()), + _ordinal(0) {} + + File *find(StringRef sym, bool dataSymbolOnly) override { + std::string prefix = "__imp_"; + if (!sym.startswith(prefix)) + return nullptr; + StringRef undef = sym.substr(prefix.size()); + return new (_alloc) impl::ImpSymbolFile(sym, undef, _ordinal++, _is64); + } + +private: + bool _is64; + uint64_t _ordinal; + llvm::BumpPtrAllocator _alloc; +}; + +// A ExportedSymbolRenameFile is a virtual archive file for dllexported symbols. +// +// One usually has to specify the exact symbol name to resolve it. That's true +// in most cases for PE/COFF, except the one described below. +// +// DLLExported symbols can be specified using a module definition file. In a +// file, one can write an EXPORT directive followed by symbol names. Such +// symbols may not be fully decorated. +// +// If a symbol FOO is specified to be dllexported by a module definition file, +// linker has to search not only for /FOO/ but also for /FOO@[0-9]+/ for stdcall +// and for /\?FOO@@.+/ for C++. This ambiguous matching semantics does not fit +// well with Resolver. +// +// We could probably modify Resolver to resolve ambiguous symbols, but I think +// we don't want to do that because it'd be rarely used, and only this Windows +// specific feature would use it. It's probably not a good idea to make the core +// linker to be able to deal with it. +// +// So, instead of tweaking Resolver, we chose to do some hack here. An +// ExportedSymbolRenameFile maintains a set containing all possibly defined +// symbol names. That set would be a union of (1) all the defined symbols that +// are already parsed and read and (2) all the defined symbols in archive files +// that are not yet be parsed. +// +// If Resolver asks this file to return an atom for a dllexported symbol, find() +// looks up the set, doing ambiguous matching. If there's a symbol with @ +// prefix, it returns an atom to rename the dllexported symbol, hoping that +// Resolver will find the new symbol with atsign from an archive file at the +// next visit. +class ExportedSymbolRenameFile : public SimpleArchiveLibraryFile { +public: + ExportedSymbolRenameFile(const PECOFFLinkingContext &ctx) + : SimpleArchiveLibraryFile("<export>"), + _ctx(const_cast<PECOFFLinkingContext *>(&ctx)) { + for (PECOFFLinkingContext::ExportDesc &desc : _ctx->getDllExports()) + _exportedSyms.insert(desc.name); + } + + File *find(StringRef sym, bool dataSymbolOnly) override { + typedef PECOFFLinkingContext::ExportDesc ExportDesc; + if (_exportedSyms.count(sym) == 0) + return nullptr; + std::string replace; + if (!findDecoratedSymbol(_ctx, sym.str(), replace)) + return nullptr; + + for (ExportDesc &exp : _ctx->getDllExports()) + if (exp.name == sym) + exp.mangledName = replace; + if (_ctx->deadStrip()) + _ctx->addDeadStripRoot(_ctx->allocate(replace)); + return new (_alloc) impl::SymbolRenameFile(sym, replace); + } + +private: + std::set<std::string> _exportedSyms; + llvm::BumpPtrAllocator _alloc; + PECOFFLinkingContext *_ctx; +}; + +// Windows has not only one but many entry point functions. The +// appropriate one is automatically selected based on the subsystem +// setting and the user-supplied entry point function. +// +// http://msdn.microsoft.com/en-us/library/f9t8842e.aspx +class EntryPointFile : public SimpleFile { +public: + EntryPointFile(const PECOFFLinkingContext &ctx) + : SimpleFile("<entry>"), _ctx(const_cast<PECOFFLinkingContext *>(&ctx)), + _firstTime(true) {} + + const atom_collection<UndefinedAtom> &undefined() const override { + return const_cast<EntryPointFile *>(this)->getUndefinedAtoms(); + } + +private: + const atom_collection<UndefinedAtom> &getUndefinedAtoms() { + std::lock_guard<std::mutex> lock(_mutex); + if (!_firstTime) + return _undefinedAtoms; + _firstTime = false; + + if (_ctx->hasEntry()) { + StringRef entrySym = _ctx->allocate(getEntry()); + _undefinedAtoms._atoms.push_back( + new (_alloc) SimpleUndefinedAtom(*this, entrySym)); + _ctx->setHasEntry(true); + _ctx->setEntrySymbolName(entrySym); + if (_ctx->deadStrip()) + _ctx->addDeadStripRoot(entrySym); + } + return _undefinedAtoms; + } + + // Returns the entry point function name. + std::string getEntry() const { + StringRef opt = _ctx->getEntrySymbolName(); + if (!opt.empty()) { + std::string mangled; + if (findDecoratedSymbol(_ctx, opt, mangled)) + return mangled; + return _ctx->decorateSymbol(opt); + } + return _ctx->decorateSymbol(getDefaultEntry()); + } + + std::string getDefaultEntry() const { + const std::string wWinMainCRTStartup = "wWinMainCRTStartup"; + const std::string WinMainCRTStartup = "WinMainCRTStartup"; + const std::string wmainCRTStartup = "wmainCRTStartup"; + const std::string mainCRTStartup = "mainCRTStartup"; + + if (_ctx->isDll()) { + if (_ctx->getMachineType() == llvm::COFF::IMAGE_FILE_MACHINE_I386) + return "_DllMainCRTStartup@12"; + return "_DllMainCRTStartup"; + } + + // Returns true if a given name exists in an input object file. + auto defined = [&](StringRef name) -> bool { + StringRef sym = _ctx->decorateSymbol(name); + if (_ctx->definedSymbols().count(sym)) + return true; + std::string ignore; + return findDecoratedSymbol(_ctx, sym, ignore); + }; + + switch (_ctx->getSubsystem()) { + case WindowsSubsystem::IMAGE_SUBSYSTEM_UNKNOWN: { + if (defined("wWinMain")) + return wWinMainCRTStartup; + if (defined("WinMain")) + return WinMainCRTStartup; + if (defined("wmain")) + return wmainCRTStartup; + if (!defined("main")) + llvm::errs() << "Cannot infer subsystem; assuming /subsystem:console\n"; + return mainCRTStartup; + } + case WindowsSubsystem::IMAGE_SUBSYSTEM_WINDOWS_GUI: + if (defined("WinMain")) + return WinMainCRTStartup; + return wWinMainCRTStartup; + case WindowsSubsystem::IMAGE_SUBSYSTEM_WINDOWS_CUI: + if (defined("wmain")) + return wmainCRTStartup; + return mainCRTStartup; + default: + return mainCRTStartup; + } + } + + PECOFFLinkingContext *_ctx; + atom_collection_vector<UndefinedAtom> _undefinedAtoms; + std::mutex _mutex; + llvm::BumpPtrAllocator _alloc; + bool _firstTime; +}; + +} // end namespace pecoff +} // end namespace lld |