forked from OSchip/llvm-project
382 lines
13 KiB
C++
382 lines
13 KiB
C++
//===- 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/ArchiveLibraryFile.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 {
|
|
|
|
class ResolvableSymbols;
|
|
bool findDecoratedSymbol(PECOFFLinkingContext *ctx, ResolvableSymbols *syms,
|
|
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) {
|
|
auto *ref = is64 ? new COFFReference(&_undefined, 0,
|
|
llvm::COFF::IMAGE_REL_AMD64_ADDR32,
|
|
Reference::KindArch::x86_64)
|
|
: new COFFReference(&_undefined, 0,
|
|
llvm::COFF::IMAGE_REL_I386_DIR32,
|
|
Reference::KindArch::x86);
|
|
_defined.addReference(std::unique_ptr<COFFReference>(ref));
|
|
addAtom(_defined);
|
|
addAtom(_undefined);
|
|
};
|
|
|
|
private:
|
|
SimpleUndefinedAtom _undefined;
|
|
ImpPointerAtom _defined;
|
|
};
|
|
|
|
class VirtualArchiveLibraryFile : public ArchiveLibraryFile {
|
|
public:
|
|
VirtualArchiveLibraryFile(StringRef filename)
|
|
: ArchiveLibraryFile(filename) {}
|
|
|
|
const atom_collection<DefinedAtom> &defined() const override {
|
|
return _definedAtoms;
|
|
}
|
|
|
|
const atom_collection<UndefinedAtom> &undefined() const override {
|
|
return _undefinedAtoms;
|
|
}
|
|
|
|
const atom_collection<SharedLibraryAtom> &sharedLibrary() const override {
|
|
return _sharedLibraryAtoms;
|
|
}
|
|
|
|
const atom_collection<AbsoluteAtom> &absolute() const override {
|
|
return _absoluteAtoms;
|
|
}
|
|
|
|
std::error_code
|
|
parseAllMembers(std::vector<std::unique_ptr<File>> &result) const override {
|
|
return std::error_code();
|
|
}
|
|
|
|
private:
|
|
atom_collection_vector<DefinedAtom> _definedAtoms;
|
|
atom_collection_vector<UndefinedAtom> _undefinedAtoms;
|
|
atom_collection_vector<SharedLibraryAtom> _sharedLibraryAtoms;
|
|
atom_collection_vector<AbsoluteAtom> _absoluteAtoms;
|
|
};
|
|
|
|
// 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:
|
|
COFFAbsoluteAtom _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 impl::VirtualArchiveLibraryFile {
|
|
public:
|
|
LocallyImportedSymbolFile(const PECOFFLinkingContext &ctx)
|
|
: VirtualArchiveLibraryFile("__imp_"), _is64(ctx.is64Bit()),
|
|
_ordinal(0) {}
|
|
|
|
const File *find(StringRef sym, bool dataSymbolOnly) const 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;
|
|
mutable uint64_t _ordinal;
|
|
mutable llvm::BumpPtrAllocator _alloc;
|
|
};
|
|
|
|
class ResolvableSymbols {
|
|
public:
|
|
void add(File *file) {
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
if (_seen.count(file) > 0)
|
|
return;
|
|
_seen.insert(file);
|
|
_queue.insert(file);
|
|
}
|
|
|
|
const std::set<std::string> &defined() {
|
|
readAllSymbols();
|
|
return _defined;
|
|
}
|
|
|
|
private:
|
|
// Files are read lazily, so that it has no runtime overhead if
|
|
// no one accesses this class.
|
|
void readAllSymbols() {
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
for (File *file : _queue) {
|
|
if (auto *archive = dyn_cast<ArchiveLibraryFile>(file)) {
|
|
for (const std::string &sym : archive->getDefinedSymbols())
|
|
_defined.insert(sym);
|
|
continue;
|
|
}
|
|
for (const DefinedAtom *atom : file->defined())
|
|
if (!atom->name().empty())
|
|
_defined.insert(atom->name());
|
|
}
|
|
_queue.clear();
|
|
}
|
|
|
|
std::set<std::string> _defined;
|
|
std::set<File *> _seen;
|
|
std::set<File *> _queue;
|
|
std::mutex _mutex;
|
|
};
|
|
|
|
// 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 impl::VirtualArchiveLibraryFile {
|
|
public:
|
|
ExportedSymbolRenameFile(const PECOFFLinkingContext &ctx,
|
|
std::shared_ptr<ResolvableSymbols> syms)
|
|
: VirtualArchiveLibraryFile("<export>"), _syms(syms),
|
|
_ctx(const_cast<PECOFFLinkingContext *>(&ctx)) {
|
|
for (PECOFFLinkingContext::ExportDesc &desc : _ctx->getDllExports())
|
|
_exportedSyms.insert(desc.name);
|
|
}
|
|
|
|
const File *find(StringRef sym, bool dataSymbolOnly) const override {
|
|
typedef PECOFFLinkingContext::ExportDesc ExportDesc;
|
|
if (_exportedSyms.count(sym) == 0)
|
|
return nullptr;
|
|
std::string replace;
|
|
if (!findDecoratedSymbol(_ctx, _syms.get(), 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;
|
|
std::shared_ptr<ResolvableSymbols> _syms;
|
|
mutable llvm::BumpPtrAllocator _alloc;
|
|
mutable 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,
|
|
std::shared_ptr<ResolvableSymbols> syms)
|
|
: SimpleFile("<entry>"), _ctx(const_cast<PECOFFLinkingContext *>(&ctx)),
|
|
_syms(syms), _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, _syms.get(), 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 (_syms->defined().count(sym))
|
|
return true;
|
|
std::string ignore;
|
|
return findDecoratedSymbol(_ctx, _syms.get(), 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;
|
|
std::shared_ptr<ResolvableSymbols> _syms;
|
|
llvm::BumpPtrAllocator _alloc;
|
|
bool _firstTime;
|
|
};
|
|
|
|
} // end namespace pecoff
|
|
} // end namespace lld
|