1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
|
//===- lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.cpp --------------===//
//
// 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 <mutex>
namespace lld {
namespace pecoff {
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)
: SimpleFile(defsym), _undefined(*this, undefsym),
_defined(*this, defsym, ordinal) {
_defined.addReference(std::unique_ptr<COFFReference>(
new COFFReference(&_undefined, 0, llvm::COFF::IMAGE_REL_I386_DIR32)));
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.
//
// 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_"),
_prefix(ctx.decorateSymbol("_imp_")), _ordinal(0) {}
const File *find(StringRef sym, bool dataSymbolOnly) const override {
if (!sym.startswith(_prefix))
return nullptr;
StringRef undef = sym.substr(_prefix.size());
return new (_alloc) impl::ImpSymbolFile(sym, undef, _ordinal++);
}
private:
std::string _prefix;
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 -- one can omit "@" and the following
// number suffix for the stdcall function.
//
// 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]+. 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) {
for (const PECOFFLinkingContext::ExportDesc &desc : ctx.getDllExports())
_exportedSyms.insert(desc.name);
}
const File *find(StringRef sym, bool dataSymbolOnly) const override {
if (_exportedSyms.count(sym) == 0)
return nullptr;
std::string replace;
if (!findSymbolWithAtsignSuffix(sym.str(), replace))
return nullptr;
return new (_alloc) impl::SymbolRenameFile(sym, replace);
}
private:
// Find a symbol that starts with a given symbol name followed
// by @number suffix.
bool findSymbolWithAtsignSuffix(std::string sym, std::string &res) const {
sym.append("@");
const std::set<std::string> &defined = _syms->defined();
auto it = defined.lower_bound(sym);
for (auto e = defined.end(); it != e; ++it) {
if (!StringRef(*it).startswith(sym))
return false;
if (it->size() == sym.size())
continue;
StringRef suffix = StringRef(*it).substr(sym.size());
if (suffix.find_first_not_of("0123456789") != StringRef::npos)
continue;
res = *it;
return true;
}
return false;
}
std::set<std::string> _exportedSyms;
mutable std::shared_ptr<ResolvableSymbols> _syms;
mutable llvm::BumpPtrAllocator _alloc;
};
} // end namespace pecoff
} // end namespace lld
|