]>
Commit | Line | Data |
---|---|---|
a645023d A |
1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* |
2 | * | |
3 | * Copyright (c) 2009-2010 Apple Inc. All rights reserved. | |
4 | * | |
5 | * @APPLE_LICENSE_HEADER_START@ | |
6 | * | |
7 | * This file contains Original Code and/or Modifications of Original Code | |
8 | * as defined in and that are subject to the Apple Public Source License | |
9 | * Version 2.0 (the 'License'). You may not use this file except in | |
10 | * compliance with the License. Please obtain a copy of the License at | |
11 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
12 | * file. | |
13 | * | |
14 | * The Original Code and all software distributed under the License are | |
15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
19 | * Please see the License for the specific language governing rights and | |
20 | * limitations under the License. | |
21 | * | |
22 | * @APPLE_LICENSE_HEADER_END@ | |
23 | */ | |
24 | ||
25 | ||
26 | #include <stdlib.h> | |
27 | #include <sys/types.h> | |
28 | #include <sys/stat.h> | |
29 | #include <sys/mman.h> | |
30 | #include <sys/sysctl.h> | |
31 | #include <fcntl.h> | |
32 | #include <errno.h> | |
33 | #include <limits.h> | |
34 | #include <unistd.h> | |
35 | #include <assert.h> | |
36 | ||
ebf6f434 A |
37 | #include <iostream> |
38 | #include <sstream> | |
a645023d A |
39 | #include <string> |
40 | #include <map> | |
41 | #include <set> | |
42 | #include <vector> | |
43 | #include <algorithm> | |
a645023d A |
44 | |
45 | #include "Options.h" | |
46 | ||
47 | #include "ld.hpp" | |
48 | #include "InputFiles.h" | |
49 | #include "SymbolTable.h" | |
50 | ||
51 | ||
52 | ||
53 | namespace ld { | |
54 | namespace tool { | |
55 | ||
56 | ||
57 | // HACK, I can't find a way to pass values in the compare classes (e.g. ContentFuncs) | |
58 | // so use global variable to pass info. | |
59 | static ld::IndirectBindingTable* _s_indirectBindingTable = NULL; | |
a645023d A |
60 | |
61 | ||
62 | SymbolTable::SymbolTable(const Options& opts, std::vector<const ld::Atom*>& ibt) | |
ebf6f434 | 63 | : _options(opts), _cstringTable(6151), _indirectBindingTable(ibt), _hasExternalTentativeDefinitions(false) |
a645023d A |
64 | { |
65 | _s_indirectBindingTable = this; | |
a645023d A |
66 | } |
67 | ||
68 | ||
69 | size_t SymbolTable::ContentFuncs::operator()(const ld::Atom* atom) const | |
70 | { | |
71 | return atom->contentHash(*_s_indirectBindingTable); | |
72 | } | |
73 | ||
74 | bool SymbolTable::ContentFuncs::operator()(const ld::Atom* left, const ld::Atom* right) const | |
75 | { | |
76 | return (memcmp(left->rawContentPointer(), right->rawContentPointer(), left->size()) == 0); | |
77 | } | |
78 | ||
79 | ||
80 | ||
81 | size_t SymbolTable::CStringHashFuncs::operator()(const ld::Atom* atom) const | |
82 | { | |
83 | return atom->contentHash(*_s_indirectBindingTable); | |
84 | } | |
85 | ||
86 | bool SymbolTable::CStringHashFuncs::operator()(const ld::Atom* left, const ld::Atom* right) const | |
87 | { | |
88 | return (strcmp((char*)left->rawContentPointer(), (char*)right->rawContentPointer()) == 0); | |
89 | } | |
90 | ||
91 | ||
92 | size_t SymbolTable::UTF16StringHashFuncs::operator()(const ld::Atom* atom) const | |
93 | { | |
94 | return atom->contentHash(*_s_indirectBindingTable); | |
95 | } | |
96 | ||
97 | bool SymbolTable::UTF16StringHashFuncs::operator()(const ld::Atom* left, const ld::Atom* right) const | |
98 | { | |
99 | if ( left == right ) | |
100 | return true; | |
101 | const void* leftContent = left->rawContentPointer(); | |
102 | const void* rightContent = right->rawContentPointer(); | |
103 | unsigned int amount = left->size()-2; | |
104 | bool result = (memcmp(leftContent, rightContent, amount) == 0); | |
105 | return result; | |
106 | } | |
107 | ||
108 | ||
109 | size_t SymbolTable::ReferencesHashFuncs::operator()(const ld::Atom* atom) const | |
110 | { | |
111 | return atom->contentHash(*_s_indirectBindingTable); | |
112 | } | |
113 | ||
114 | bool SymbolTable::ReferencesHashFuncs::operator()(const ld::Atom* left, const ld::Atom* right) const | |
115 | { | |
116 | return left->canCoalesceWith(*right, *_s_indirectBindingTable); | |
117 | } | |
118 | ||
119 | ||
ebf6f434 A |
120 | void SymbolTable::addDuplicateSymbol(const char *name, const ld::Atom *atom) |
121 | { | |
122 | // Look up or create the file list for name. | |
123 | DuplicateSymbols::iterator symbolsIterator = _duplicateSymbols.find(name); | |
124 | DuplicatedSymbolAtomList *atoms = NULL; | |
125 | if (symbolsIterator != _duplicateSymbols.end()) { | |
126 | atoms = symbolsIterator->second; | |
127 | } else { | |
128 | atoms = new std::vector<const ld::Atom *>; | |
129 | _duplicateSymbols.insert(std::pair<const char *, DuplicatedSymbolAtomList *>(name, atoms)); | |
130 | } | |
131 | ||
132 | // check if file is already in the list, add it if not | |
133 | bool found = false; | |
134 | for (DuplicatedSymbolAtomList::iterator it = atoms->begin(); !found && it != atoms->end(); it++) | |
82b4b32b | 135 | if (strcmp((*it)->safeFilePath(), atom->safeFilePath()) == 0) |
ebf6f434 A |
136 | found = true; |
137 | if (!found) | |
138 | atoms->push_back(atom); | |
139 | } | |
a645023d | 140 | |
ebf6f434 | 141 | void SymbolTable::checkDuplicateSymbols() const |
a645023d | 142 | { |
ebf6f434 A |
143 | bool foundDuplicate = false; |
144 | for (DuplicateSymbols::const_iterator symbolIt = _duplicateSymbols.begin(); symbolIt != _duplicateSymbols.end(); symbolIt++) { | |
145 | DuplicatedSymbolAtomList *atoms = symbolIt->second; | |
146 | bool reportDuplicate; | |
147 | if (_options.deadCodeStrip()) { | |
148 | // search for a live atom | |
149 | reportDuplicate = false; | |
150 | for (DuplicatedSymbolAtomList::iterator it = atoms->begin(); !reportDuplicate && it != atoms->end(); it++) { | |
151 | if ((*it)->live()) | |
152 | reportDuplicate = true; | |
153 | } | |
154 | } else { | |
155 | reportDuplicate = true; | |
156 | } | |
157 | if (reportDuplicate) { | |
158 | foundDuplicate = true; | |
159 | fprintf(stderr, "duplicate symbol %s in:\n", symbolIt->first); | |
160 | for (DuplicatedSymbolAtomList::iterator atomIt = atoms->begin(); atomIt != atoms->end(); atomIt++) { | |
82b4b32b | 161 | fprintf(stderr, " %s\n", (*atomIt)->safeFilePath()); |
ebf6f434 A |
162 | } |
163 | } | |
164 | } | |
165 | if (foundDuplicate) | |
166 | throwf("%d duplicate symbol%s", (int)_duplicateSymbols.size(), _duplicateSymbols.size()==1?"":"s"); | |
167 | } | |
168 | ||
169 | // AtomPicker encapsulates the logic for picking which atom to use when adding an atom by name results in a collision | |
170 | class NameCollisionResolution { | |
171 | public: | |
172 | NameCollisionResolution(const ld::Atom& a, const ld::Atom& b, bool ignoreDuplicates, const Options& options) : _atomA(a), _atomB(b), _options(options), _reportDuplicate(false), _ignoreDuplicates(ignoreDuplicates) { | |
173 | pickAtom(); | |
174 | } | |
175 | ||
176 | // Returns which atom to use | |
177 | const ld::Atom& chosen() { return *_chosen; } | |
178 | bool choseAtom(const ld::Atom& atom) { return _chosen == &atom; } | |
179 | ||
180 | // Returns true if the two atoms should be reported as a duplicate symbol | |
181 | bool reportDuplicate() { return _reportDuplicate; } | |
182 | ||
183 | private: | |
184 | const ld::Atom& _atomA; | |
185 | const ld::Atom& _atomB; | |
186 | const Options& _options; | |
187 | const ld::Atom* _chosen; | |
188 | bool _reportDuplicate; | |
189 | bool _ignoreDuplicates; | |
190 | ||
191 | void pickAtom(const ld::Atom& atom) { _chosen = &atom; } // primitive to set which atom is picked | |
192 | void pickAtomA() { pickAtom(_atomA); } // primitive to pick atom A | |
193 | void pickAtomB() { pickAtom(_atomB); } // primitive to pick atom B | |
194 | ||
195 | // use atom A if pickA, otherwise use atom B | |
196 | void pickAOrB(bool pickA) { if (pickA) pickAtomA(); else pickAtomB(); } | |
197 | ||
198 | void pickHigherOrdinal() { | |
199 | pickAOrB(_atomA.file()->ordinal() < _atomB.file()->ordinal()); | |
200 | } | |
201 | ||
202 | void pickLowerOrdinal() { | |
203 | pickAOrB(_atomA.file()->ordinal() > _atomB.file()->ordinal()); | |
204 | } | |
205 | ||
206 | void pickLargerSize() { | |
207 | if (_atomA.size() == _atomB.size()) | |
208 | pickLowerOrdinal(); | |
209 | else | |
210 | pickAOrB(_atomA.size() > _atomB.size()); | |
211 | } | |
212 | ||
213 | void pickGreaterAlignment() { | |
214 | pickAOrB(_atomA.alignment().trailingZeros() > _atomB.alignment().trailingZeros()); | |
215 | } | |
216 | ||
217 | void pickBetweenRegularAtoms() { | |
218 | if ( _atomA.combine() == ld::Atom::combineByName ) { | |
219 | if ( _atomB.combine() == ld::Atom::combineByName ) { | |
220 | // <rdar://problem/9183821> always choose mach-o over llvm bit code, otherwise LTO may eliminate the llvm atom | |
221 | const bool aIsLTO = (_atomA.contentType() == ld::Atom::typeLTOtemporary); | |
222 | const bool bIsLTO = (_atomB.contentType() == ld::Atom::typeLTOtemporary); | |
223 | // <rdar://problem/9183821> always choose mach-o over llvm bit code, otherwise LTO may eliminate the llvm atom | |
224 | if ( aIsLTO != bIsLTO ) { | |
225 | pickAOrB(!aIsLTO); | |
226 | } | |
227 | else { | |
228 | // both weak, prefer non-auto-hide one | |
229 | if ( _atomA.autoHide() != _atomB.autoHide() ) { | |
230 | // <rdar://problem/6783167> support auto hidden weak symbols: .weak_def_can_be_hidden | |
231 | pickAOrB(!_atomA.autoHide()); | |
232 | } | |
233 | else if ( _atomA.autoHide() && _atomB.autoHide() ) { | |
234 | // both have auto-hide, so use one with greater alignment | |
235 | pickGreaterAlignment(); | |
236 | } | |
237 | else { | |
238 | // neither auto-hide, check visibility | |
239 | if ( _atomA.scope() != _atomB.scope() ) { | |
240 | // <rdar://problem/8304984> use more visible weak def symbol | |
241 | pickAOrB(_atomA.scope() == ld::Atom::scopeGlobal); | |
a645023d A |
242 | } |
243 | else { | |
ebf6f434 A |
244 | // both have same visibility, use one with greater alignment |
245 | pickGreaterAlignment(); | |
a645023d | 246 | } |
ebf6f434 A |
247 | } |
248 | } | |
249 | } | |
250 | else { | |
251 | pickAtomB(); // pick not-weak | |
252 | ||
253 | } | |
254 | } | |
255 | else { | |
256 | if ( _atomB.combine() == ld::Atom::combineByName ) { | |
257 | pickAtomA(); // pick not-weak | |
258 | ||
259 | } | |
260 | else { | |
261 | // both are not-weak | |
262 | if ( _atomA.section().type() == ld::Section::typeMachHeader ) { | |
263 | pickAtomA(); | |
264 | } | |
265 | else if ( _atomB.section().type() == ld::Section::typeMachHeader ) { | |
266 | pickAtomB(); | |
267 | } | |
268 | else { | |
269 | if ( _ignoreDuplicates ) { | |
270 | pickLowerOrdinal(); | |
271 | } | |
272 | else { | |
273 | _reportDuplicate = true; | |
274 | } | |
275 | } | |
276 | } | |
277 | } | |
278 | } | |
279 | ||
280 | void pickCommonsMode(const ld::Atom& dylib, const ld::Atom& proxy) { | |
281 | assert(dylib.definition() == ld::Atom::definitionTentative); | |
282 | assert(proxy.definition() == ld::Atom::definitionProxy); | |
283 | switch ( _options.commonsMode() ) { | |
284 | case Options::kCommonsIgnoreDylibs: | |
285 | if ( _options.warnCommons() ) | |
286 | warning("using common symbol %s from %s and ignoring defintion from dylib %s", | |
82b4b32b | 287 | proxy.name(), proxy.safeFilePath(), dylib.safeFilePath()); |
ebf6f434 A |
288 | pickAtom(dylib); |
289 | break; | |
290 | case Options::kCommonsOverriddenByDylibs: | |
291 | if ( _options.warnCommons() ) | |
292 | warning("replacing common symbol %s from %s with true definition from dylib %s", | |
82b4b32b | 293 | proxy.name(), proxy.safeFilePath(), dylib.safeFilePath()); |
ebf6f434 A |
294 | pickAtom(proxy); |
295 | break; | |
296 | case Options::kCommonsConflictsDylibsError: | |
297 | throwf("common symbol %s from %s conflicts with defintion from dylib %s", | |
82b4b32b | 298 | proxy.name(), proxy.safeFilePath(), dylib.safeFilePath()); |
ebf6f434 A |
299 | } |
300 | } | |
301 | ||
302 | void pickProxyAtom() { | |
303 | // both atoms are definitionProxy | |
304 | // <rdar://problem/5137732> ld should keep looking when it finds a weak definition in a dylib | |
305 | if ( _atomA.combine() == ld::Atom::combineByName ) { | |
306 | pickAtomB(); | |
307 | } else if ( _atomB.combine() == ld::Atom::combineByName ) { | |
308 | pickAtomA(); | |
309 | } else { | |
82b4b32b | 310 | throwf("symbol %s exported from both %s and %s\n", _atomA.name(), _atomA.safeFilePath(), _atomB.safeFilePath()); |
ebf6f434 A |
311 | } |
312 | } | |
313 | ||
314 | void pickAtom() { | |
f80fe69f | 315 | //fprintf(stderr, "pickAtom(), a=%p, def=%d, b=%p, def=%d\n", &_atomA, _atomA.definition(), &_atomB, _atomB.definition()); |
ebf6f434 A |
316 | // First, discriminate by definition |
317 | switch (_atomA.definition()) { | |
318 | case ld::Atom::definitionRegular: | |
319 | switch (_atomB.definition()) { | |
320 | case ld::Atom::definitionRegular: | |
321 | pickBetweenRegularAtoms(); | |
a645023d A |
322 | break; |
323 | case ld::Atom::definitionTentative: | |
f80fe69f | 324 | if ( _atomB.size() > _atomA.size() ) { |
f80fe69f | 325 | warning("tentative definition of '%s' with size %llu from '%s' is being replaced by real definition of smaller size %llu from '%s'", |
82b4b32b | 326 | _atomA.name(), _atomB.size(), _atomB.safeFilePath(), _atomA.size(), _atomA.safeFilePath()); |
f80fe69f | 327 | } |
ebf6f434 | 328 | pickAtomA(); |
a645023d A |
329 | break; |
330 | case ld::Atom::definitionAbsolute: | |
ebf6f434 A |
331 | _reportDuplicate = true; |
332 | pickHigherOrdinal(); | |
333 | break; | |
a645023d | 334 | case ld::Atom::definitionProxy: |
ebf6f434 | 335 | pickAtomA(); |
a645023d A |
336 | break; |
337 | } | |
338 | break; | |
339 | case ld::Atom::definitionTentative: | |
ebf6f434 | 340 | switch (_atomB.definition()) { |
a645023d | 341 | case ld::Atom::definitionRegular: |
f80fe69f | 342 | if ( _atomA.size() > _atomB.size() ) { |
f80fe69f | 343 | warning("tentative definition of '%s' with size %llu from '%s' is being replaced by real definition of smaller size %llu from '%s'", |
82b4b32b | 344 | _atomA.name(), _atomA.size(),_atomA.safeFilePath(), _atomB.size(), _atomB.safeFilePath()); |
f80fe69f | 345 | } |
ebf6f434 | 346 | pickAtomB(); |
a645023d A |
347 | break; |
348 | case ld::Atom::definitionTentative: | |
ebf6f434 | 349 | pickLargerSize(); |
a645023d A |
350 | break; |
351 | case ld::Atom::definitionAbsolute: | |
ebf6f434 | 352 | pickHigherOrdinal(); |
a645023d A |
353 | break; |
354 | case ld::Atom::definitionProxy: | |
ebf6f434 | 355 | pickCommonsMode(_atomA, _atomB); |
a645023d A |
356 | break; |
357 | } | |
358 | break; | |
359 | case ld::Atom::definitionAbsolute: | |
ebf6f434 | 360 | switch (_atomB.definition()) { |
a645023d | 361 | case ld::Atom::definitionRegular: |
ebf6f434 A |
362 | _reportDuplicate = true; |
363 | pickHigherOrdinal(); | |
364 | break; | |
a645023d | 365 | case ld::Atom::definitionTentative: |
ebf6f434 | 366 | pickAtomA(); |
a645023d A |
367 | break; |
368 | case ld::Atom::definitionAbsolute: | |
ebf6f434 A |
369 | _reportDuplicate = true; |
370 | pickHigherOrdinal(); | |
371 | break; | |
a645023d | 372 | case ld::Atom::definitionProxy: |
ebf6f434 | 373 | pickAtomA(); |
a645023d A |
374 | break; |
375 | } | |
376 | break; | |
377 | case ld::Atom::definitionProxy: | |
ebf6f434 | 378 | switch (_atomB.definition()) { |
a645023d | 379 | case ld::Atom::definitionRegular: |
ebf6f434 | 380 | pickAtomB(); |
a645023d A |
381 | break; |
382 | case ld::Atom::definitionTentative: | |
ebf6f434 | 383 | pickCommonsMode(_atomB, _atomA); |
a645023d A |
384 | break; |
385 | case ld::Atom::definitionAbsolute: | |
ebf6f434 | 386 | pickAtomB(); |
a645023d A |
387 | break; |
388 | case ld::Atom::definitionProxy: | |
ebf6f434 | 389 | pickProxyAtom(); |
a645023d A |
390 | break; |
391 | } | |
392 | break; | |
ebf6f434 | 393 | } |
a645023d | 394 | } |
ebf6f434 A |
395 | }; |
396 | ||
397 | bool SymbolTable::addByName(const ld::Atom& newAtom, bool ignoreDuplicates) | |
398 | { | |
399 | bool useNew = true; | |
400 | assert(newAtom.name() != NULL); | |
401 | const char* name = newAtom.name(); | |
402 | IndirectBindingSlot slot = this->findSlotForName(name); | |
403 | const ld::Atom* existingAtom = _indirectBindingTable[slot]; | |
404 | //fprintf(stderr, "addByName(%p) name=%s, slot=%u, existing=%p\n", &newAtom, newAtom.name(), slot, existingAtom); | |
405 | if ( existingAtom != NULL ) { | |
406 | assert(&newAtom != existingAtom); | |
407 | NameCollisionResolution picker(newAtom, *existingAtom, ignoreDuplicates, _options); | |
408 | if (picker.reportDuplicate()) { | |
409 | addDuplicateSymbol(name, existingAtom); | |
410 | addDuplicateSymbol(name, &newAtom); | |
411 | } | |
412 | useNew = picker.choseAtom(newAtom); | |
a645023d A |
413 | } |
414 | if ( useNew ) { | |
415 | _indirectBindingTable[slot] = &newAtom; | |
416 | if ( existingAtom != NULL ) { | |
417 | markCoalescedAway(existingAtom); | |
a645023d A |
418 | } |
419 | if ( newAtom.scope() == ld::Atom::scopeGlobal ) { | |
420 | if ( newAtom.definition() == ld::Atom::definitionTentative ) { | |
421 | _hasExternalTentativeDefinitions = true; | |
422 | } | |
423 | } | |
424 | } | |
425 | else { | |
426 | markCoalescedAway(&newAtom); | |
427 | } | |
428 | // return if existing atom in symbol table was replaced | |
429 | return useNew && (existingAtom != NULL); | |
430 | } | |
431 | ||
432 | ||
433 | bool SymbolTable::addByContent(const ld::Atom& newAtom) | |
434 | { | |
435 | bool useNew = true; | |
436 | const ld::Atom* existingAtom; | |
437 | IndirectBindingSlot slot = this->findSlotForContent(&newAtom, &existingAtom); | |
438 | //fprintf(stderr, "addByContent(%p) name=%s, slot=%u, existing=%p\n", &newAtom, newAtom.name(), slot, existingAtom); | |
439 | if ( existingAtom != NULL ) { | |
440 | // use existing unless new one has greater alignment requirements | |
441 | useNew = ( newAtom.alignment().trailingZeros() > existingAtom->alignment().trailingZeros() ); | |
442 | } | |
443 | if ( useNew ) { | |
444 | _indirectBindingTable[slot] = &newAtom; | |
445 | if ( existingAtom != NULL ) | |
446 | markCoalescedAway(existingAtom); | |
447 | } | |
448 | else { | |
449 | _indirectBindingTable[slot] = existingAtom; | |
450 | if ( existingAtom != &newAtom ) | |
451 | markCoalescedAway(&newAtom); | |
452 | } | |
453 | // return if existing atom in symbol table was replaced | |
454 | return useNew && (existingAtom != NULL); | |
455 | } | |
456 | ||
457 | bool SymbolTable::addByReferences(const ld::Atom& newAtom) | |
458 | { | |
459 | bool useNew = true; | |
460 | const ld::Atom* existingAtom; | |
461 | IndirectBindingSlot slot = this->findSlotForReferences(&newAtom, &existingAtom); | |
462 | //fprintf(stderr, "addByReferences(%p) name=%s, slot=%u, existing=%p\n", &newAtom, newAtom.name(), slot, existingAtom); | |
463 | if ( existingAtom != NULL ) { | |
464 | // use existing unless new one has greater alignment requirements | |
465 | useNew = ( newAtom.alignment().trailingZeros() > existingAtom->alignment().trailingZeros() ); | |
466 | } | |
467 | if ( useNew ) { | |
468 | _indirectBindingTable[slot] = &newAtom; | |
469 | if ( existingAtom != NULL ) | |
470 | markCoalescedAway(existingAtom); | |
471 | } | |
472 | else { | |
473 | if ( existingAtom != &newAtom ) | |
474 | markCoalescedAway(&newAtom); | |
475 | } | |
476 | // return if existing atom in symbol table was replaced | |
477 | return useNew && (existingAtom != NULL); | |
478 | } | |
479 | ||
480 | ||
481 | bool SymbolTable::add(const ld::Atom& atom, bool ignoreDuplicates) | |
482 | { | |
483 | //fprintf(stderr, "SymbolTable::add(%p), name=%s\n", &atom, atom.name()); | |
484 | assert(atom.scope() != ld::Atom::scopeTranslationUnit); | |
485 | switch ( atom.combine() ) { | |
486 | case ld::Atom::combineNever: | |
487 | case ld::Atom::combineByName: | |
488 | return this->addByName(atom, ignoreDuplicates); | |
489 | break; | |
490 | case ld::Atom::combineByNameAndContent: | |
491 | return this->addByContent(atom); | |
492 | break; | |
493 | case ld::Atom::combineByNameAndReferences: | |
494 | return this->addByReferences(atom); | |
495 | break; | |
496 | } | |
497 | ||
498 | return false; | |
499 | } | |
500 | ||
501 | void SymbolTable::markCoalescedAway(const ld::Atom* atom) | |
502 | { | |
503 | // remove this from list of all atoms used | |
82b4b32b | 504 | //fprintf(stderr, "markCoalescedAway(%p) from %s\n", atom, atom->safeFilePath()); |
a645023d A |
505 | (const_cast<ld::Atom*>(atom))->setCoalescedAway(); |
506 | ||
507 | // | |
508 | // The fixupNoneGroupSubordinate* fixup kind is used to model group comdat. | |
509 | // The "signature" atom in the group has a fixupNoneGroupSubordinate* fixup to | |
510 | // all other members of the group. So, if the signature atom is | |
511 | // coalesced away, all other atoms in the group should also be removed. | |
512 | // | |
513 | for (ld::Fixup::iterator fit=atom->fixupsBegin(), fend=atom->fixupsEnd(); fit != fend; ++fit) { | |
514 | switch ( fit->kind ) { | |
515 | case ld::Fixup::kindNoneGroupSubordinate: | |
516 | case ld::Fixup::kindNoneGroupSubordinateFDE: | |
517 | case ld::Fixup::kindNoneGroupSubordinateLSDA: | |
518 | assert(fit->binding == ld::Fixup::bindingDirectlyBound); | |
519 | this->markCoalescedAway(fit->u.target); | |
520 | break; | |
521 | default: | |
522 | break; | |
523 | } | |
524 | } | |
525 | ||
526 | } | |
527 | ||
ebf6f434 A |
528 | |
529 | struct StrcmpSorter { | |
530 | bool operator() (const char* i,const char* j) { | |
531 | if (i==NULL) | |
532 | return true; | |
533 | if (j==NULL) | |
534 | return false; | |
535 | return strcmp(i, j)<0;} | |
536 | }; | |
537 | ||
a645023d A |
538 | void SymbolTable::undefines(std::vector<const char*>& undefs) |
539 | { | |
540 | // return all names in _byNameTable that have no associated atom | |
541 | for (NameToSlot::iterator it=_byNameTable.begin(); it != _byNameTable.end(); ++it) { | |
542 | //fprintf(stderr, " _byNameTable[%s] = slot %d which has atom %p\n", it->first, it->second, _indirectBindingTable[it->second]); | |
543 | if ( _indirectBindingTable[it->second] == NULL ) | |
544 | undefs.push_back(it->first); | |
545 | } | |
546 | // sort so that undefines are in a stable order (not dependent on hashing functions) | |
ebf6f434 A |
547 | struct StrcmpSorter strcmpSorter; |
548 | std::sort(undefs.begin(), undefs.end(), strcmpSorter); | |
a645023d A |
549 | } |
550 | ||
551 | ||
552 | void SymbolTable::tentativeDefs(std::vector<const char*>& tents) | |
553 | { | |
554 | // return all names in _byNameTable that have no associated atom | |
555 | for (NameToSlot::iterator it=_byNameTable.begin(); it != _byNameTable.end(); ++it) { | |
556 | const char* name = it->first; | |
557 | const ld::Atom* atom = _indirectBindingTable[it->second]; | |
558 | if ( (atom != NULL) && (atom->definition() == ld::Atom::definitionTentative) ) | |
559 | tents.push_back(name); | |
560 | } | |
561 | std::sort(tents.begin(), tents.end()); | |
562 | } | |
563 | ||
564 | ||
ec29ba20 A |
565 | void SymbolTable::mustPreserveForBitcode(std::unordered_set<const char*>& syms) |
566 | { | |
567 | // return all names in _byNameTable that have no associated atom | |
568 | for (const auto &entry: _byNameTable) { | |
569 | const char* name = entry.first; | |
570 | const ld::Atom* atom = _indirectBindingTable[entry.second]; | |
571 | if ( (atom == NULL) || (atom->definition() == ld::Atom::definitionProxy) ) | |
572 | syms.insert(name); | |
573 | } | |
574 | } | |
575 | ||
576 | ||
a645023d A |
577 | bool SymbolTable::hasName(const char* name) |
578 | { | |
579 | NameToSlot::iterator pos = _byNameTable.find(name); | |
580 | if ( pos == _byNameTable.end() ) | |
581 | return false; | |
582 | return (_indirectBindingTable[pos->second] != NULL); | |
583 | } | |
584 | ||
585 | // find existing or create new slot | |
586 | SymbolTable::IndirectBindingSlot SymbolTable::findSlotForName(const char* name) | |
587 | { | |
588 | NameToSlot::iterator pos = _byNameTable.find(name); | |
589 | if ( pos != _byNameTable.end() ) | |
590 | return pos->second; | |
591 | // create new slot for this name | |
592 | SymbolTable::IndirectBindingSlot slot = _indirectBindingTable.size(); | |
593 | _indirectBindingTable.push_back(NULL); | |
594 | _byNameTable[name] = slot; | |
595 | _byNameReverseTable[slot] = name; | |
596 | return slot; | |
597 | } | |
598 | ||
9543cb2f A |
599 | void SymbolTable::removeDeadAtoms() |
600 | { | |
599556ff A |
601 | // remove dead atoms from: _byNameTable, _byNameReverseTable, and _indirectBindingTable |
602 | std::vector<const char*> namesToRemove; | |
9543cb2f A |
603 | for (NameToSlot::iterator it=_byNameTable.begin(); it != _byNameTable.end(); ++it) { |
604 | IndirectBindingSlot slot = it->second; | |
605 | const ld::Atom* atom = _indirectBindingTable[slot]; | |
606 | if ( atom != NULL ) { | |
607 | if ( !atom->live() && !atom->dontDeadStrip() ) { | |
608 | //fprintf(stderr, "removing from symbolTable[%u] %s\n", slot, atom->name()); | |
609 | _indirectBindingTable[slot] = NULL; | |
599556ff A |
610 | // <rdar://problem/16025786> need to completely remove dead atoms from symbol table |
611 | _byNameReverseTable.erase(slot); | |
612 | // can't remove while iterating, do it after iteration | |
613 | namesToRemove.push_back(it->first); | |
9543cb2f A |
614 | } |
615 | } | |
616 | } | |
599556ff A |
617 | for (std::vector<const char*>::iterator it = namesToRemove.begin(); it != namesToRemove.end(); ++it) { |
618 | _byNameTable.erase(*it); | |
619 | } | |
620 | ||
621 | // remove dead atoms from _nonLazyPointerTable | |
622 | for (ReferencesToSlot::iterator it=_nonLazyPointerTable.begin(); it != _nonLazyPointerTable.end(); ) { | |
623 | const ld::Atom* atom = it->first; | |
624 | assert(atom != NULL); | |
625 | if ( !atom->live() && !atom->dontDeadStrip() ) | |
626 | it = _nonLazyPointerTable.erase(it); | |
627 | else | |
628 | ++it; | |
629 | } | |
630 | ||
631 | // remove dead atoms from _cstringTable | |
632 | for (CStringToSlot::iterator it=_cstringTable.begin(); it != _cstringTable.end(); ) { | |
633 | const ld::Atom* atom = it->first; | |
634 | assert(atom != NULL); | |
635 | if ( !atom->live() && !atom->dontDeadStrip() ) | |
636 | it = _cstringTable.erase(it); | |
637 | else | |
638 | ++it; | |
639 | } | |
640 | ||
641 | // remove dead atoms from _utf16Table | |
642 | for (UTF16StringToSlot::iterator it=_utf16Table.begin(); it != _utf16Table.end(); ) { | |
643 | const ld::Atom* atom = it->first; | |
644 | assert(atom != NULL); | |
645 | if ( !atom->live() && !atom->dontDeadStrip() ) | |
646 | it = _utf16Table.erase(it); | |
647 | else | |
648 | ++it; | |
649 | } | |
650 | ||
651 | // remove dead atoms from _cfStringTable | |
652 | for (ReferencesToSlot::iterator it=_cfStringTable.begin(); it != _cfStringTable.end(); ) { | |
653 | const ld::Atom* atom = it->first; | |
654 | assert(atom != NULL); | |
655 | if ( !atom->live() && !atom->dontDeadStrip() ) | |
656 | it = _cfStringTable.erase(it); | |
657 | else | |
658 | ++it; | |
659 | } | |
660 | ||
661 | // remove dead atoms from _literal4Table | |
662 | for (ContentToSlot::iterator it=_literal4Table.begin(); it != _literal4Table.end(); ) { | |
663 | const ld::Atom* atom = it->first; | |
664 | assert(atom != NULL); | |
665 | if ( !atom->live() && !atom->dontDeadStrip() ) | |
666 | it = _literal4Table.erase(it); | |
667 | else | |
668 | ++it; | |
669 | } | |
670 | ||
671 | // remove dead atoms from _literal8Table | |
672 | for (ContentToSlot::iterator it=_literal8Table.begin(); it != _literal8Table.end(); ) { | |
673 | const ld::Atom* atom = it->first; | |
674 | assert(atom != NULL); | |
675 | if ( !atom->live() && !atom->dontDeadStrip() ) | |
676 | it = _literal8Table.erase(it); | |
677 | else | |
678 | ++it; | |
679 | } | |
680 | ||
681 | // remove dead atoms from _literal16Table | |
682 | for (ContentToSlot::iterator it=_literal16Table.begin(); it != _literal16Table.end(); ) { | |
683 | const ld::Atom* atom = it->first; | |
684 | assert(atom != NULL); | |
685 | if ( !atom->live() && !atom->dontDeadStrip() ) | |
686 | it = _literal16Table.erase(it); | |
687 | else | |
688 | ++it; | |
689 | } | |
9543cb2f | 690 | } |
a645023d | 691 | |
599556ff | 692 | |
a645023d A |
693 | // find existing or create new slot |
694 | SymbolTable::IndirectBindingSlot SymbolTable::findSlotForContent(const ld::Atom* atom, const ld::Atom** existingAtom) | |
695 | { | |
696 | //fprintf(stderr, "findSlotForContent(%p)\n", atom); | |
697 | SymbolTable::IndirectBindingSlot slot = 0; | |
698 | UTF16StringToSlot::iterator upos; | |
699 | CStringToSlot::iterator cspos; | |
700 | ContentToSlot::iterator pos; | |
701 | switch ( atom->section().type() ) { | |
702 | case ld::Section::typeCString: | |
703 | cspos = _cstringTable.find(atom); | |
704 | if ( cspos != _cstringTable.end() ) { | |
705 | *existingAtom = _indirectBindingTable[cspos->second]; | |
706 | return cspos->second; | |
707 | } | |
708 | slot = _indirectBindingTable.size(); | |
709 | _cstringTable[atom] = slot; | |
710 | break; | |
711 | case ld::Section::typeNonStdCString: | |
712 | { | |
713 | // use seg/sect name is key to map to avoid coalescing across segments and sections | |
714 | char segsect[64]; | |
715 | sprintf(segsect, "%s/%s", atom->section().segmentName(), atom->section().sectionName()); | |
716 | NameToMap::iterator mpos = _nonStdCStringSectionToMap.find(segsect); | |
717 | CStringToSlot* map = NULL; | |
718 | if ( mpos == _nonStdCStringSectionToMap.end() ) { | |
719 | map = new CStringToSlot(); | |
720 | _nonStdCStringSectionToMap[strdup(segsect)] = map; | |
721 | } | |
722 | else { | |
723 | map = mpos->second; | |
724 | } | |
725 | cspos = map->find(atom); | |
726 | if ( cspos != map->end() ) { | |
727 | *existingAtom = _indirectBindingTable[cspos->second]; | |
728 | return cspos->second; | |
729 | } | |
730 | slot = _indirectBindingTable.size(); | |
731 | map->operator[](atom) = slot; | |
732 | } | |
733 | break; | |
734 | case ld::Section::typeUTF16Strings: | |
735 | upos = _utf16Table.find(atom); | |
736 | if ( upos != _utf16Table.end() ) { | |
737 | *existingAtom = _indirectBindingTable[upos->second]; | |
738 | return upos->second; | |
739 | } | |
740 | slot = _indirectBindingTable.size(); | |
741 | _utf16Table[atom] = slot; | |
742 | break; | |
743 | case ld::Section::typeLiteral4: | |
744 | pos = _literal4Table.find(atom); | |
745 | if ( pos != _literal4Table.end() ) { | |
746 | *existingAtom = _indirectBindingTable[pos->second]; | |
747 | return pos->second; | |
748 | } | |
749 | slot = _indirectBindingTable.size(); | |
750 | _literal4Table[atom] = slot; | |
751 | break; | |
752 | case ld::Section::typeLiteral8: | |
753 | pos = _literal8Table.find(atom); | |
754 | if ( pos != _literal8Table.end() ) { | |
755 | *existingAtom = _indirectBindingTable[pos->second]; | |
756 | return pos->second; | |
757 | } | |
758 | slot = _indirectBindingTable.size(); | |
759 | _literal8Table[atom] = slot; | |
760 | break; | |
761 | case ld::Section::typeLiteral16: | |
762 | pos = _literal16Table.find(atom); | |
763 | if ( pos != _literal16Table.end() ) { | |
764 | *existingAtom = _indirectBindingTable[pos->second]; | |
765 | return pos->second; | |
766 | } | |
767 | slot = _indirectBindingTable.size(); | |
768 | _literal16Table[atom] = slot; | |
769 | break; | |
770 | default: | |
771 | assert(0 && "section type does not support coalescing by content"); | |
772 | } | |
773 | _indirectBindingTable.push_back(atom); | |
774 | *existingAtom = NULL; | |
775 | return slot; | |
776 | } | |
777 | ||
778 | ||
779 | ||
780 | // find existing or create new slot | |
781 | SymbolTable::IndirectBindingSlot SymbolTable::findSlotForReferences(const ld::Atom* atom, const ld::Atom** existingAtom) | |
782 | { | |
783 | //fprintf(stderr, "findSlotForReferences(%p)\n", atom); | |
784 | ||
785 | SymbolTable::IndirectBindingSlot slot = 0; | |
786 | ReferencesToSlot::iterator pos; | |
787 | switch ( atom->section().type() ) { | |
788 | case ld::Section::typeNonLazyPointer: | |
789 | pos = _nonLazyPointerTable.find(atom); | |
790 | if ( pos != _nonLazyPointerTable.end() ) { | |
791 | *existingAtom = _indirectBindingTable[pos->second]; | |
792 | return pos->second; | |
793 | } | |
794 | slot = _indirectBindingTable.size(); | |
795 | _nonLazyPointerTable[atom] = slot; | |
796 | break; | |
797 | case ld::Section::typeCFString: | |
798 | pos = _cfStringTable.find(atom); | |
799 | if ( pos != _cfStringTable.end() ) { | |
800 | *existingAtom = _indirectBindingTable[pos->second]; | |
801 | return pos->second; | |
802 | } | |
803 | slot = _indirectBindingTable.size(); | |
804 | _cfStringTable[atom] = slot; | |
805 | break; | |
806 | case ld::Section::typeObjCClassRefs: | |
807 | pos = _objc2ClassRefTable.find(atom); | |
808 | if ( pos != _objc2ClassRefTable.end() ) { | |
809 | *existingAtom = _indirectBindingTable[pos->second]; | |
810 | return pos->second; | |
811 | } | |
812 | slot = _indirectBindingTable.size(); | |
813 | _objc2ClassRefTable[atom] = slot; | |
814 | break; | |
815 | case ld::Section::typeCStringPointer: | |
816 | pos = _pointerToCStringTable.find(atom); | |
817 | if ( pos != _pointerToCStringTable.end() ) { | |
818 | *existingAtom = _indirectBindingTable[pos->second]; | |
819 | return pos->second; | |
820 | } | |
821 | slot = _indirectBindingTable.size(); | |
822 | _pointerToCStringTable[atom] = slot; | |
823 | break; | |
eaf282aa A |
824 | case ld::Section::typeTLVPointers: |
825 | pos = _threadPointerTable.find(atom); | |
826 | if ( pos != _threadPointerTable.end() ) { | |
827 | *existingAtom = _indirectBindingTable[pos->second]; | |
828 | return pos->second; | |
829 | } | |
830 | slot = _indirectBindingTable.size(); | |
831 | _threadPointerTable[atom] = slot; | |
832 | break; | |
a645023d A |
833 | default: |
834 | assert(0 && "section type does not support coalescing by references"); | |
835 | } | |
836 | _indirectBindingTable.push_back(atom); | |
837 | *existingAtom = NULL; | |
838 | return slot; | |
839 | } | |
840 | ||
841 | ||
842 | const char* SymbolTable::indirectName(IndirectBindingSlot slot) const | |
843 | { | |
844 | assert(slot < _indirectBindingTable.size()); | |
845 | const ld::Atom* target = _indirectBindingTable[slot]; | |
846 | if ( target != NULL ) { | |
847 | return target->name(); | |
848 | } | |
849 | // handle case when by-name reference is indirected and no atom yet in _byNameTable | |
850 | SlotToName::const_iterator pos = _byNameReverseTable.find(slot); | |
851 | if ( pos != _byNameReverseTable.end() ) | |
852 | return pos->second; | |
853 | assert(0); | |
854 | return NULL; | |
855 | } | |
856 | ||
857 | const ld::Atom* SymbolTable::indirectAtom(IndirectBindingSlot slot) const | |
858 | { | |
859 | assert(slot < _indirectBindingTable.size()); | |
860 | return _indirectBindingTable[slot]; | |
861 | } | |
862 | ||
a645023d A |
863 | void SymbolTable::printStatistics() |
864 | { | |
865 | // fprintf(stderr, "cstring table size: %lu, bucket count: %lu, hash func called %u times\n", | |
866 | // _cstringTable.size(), _cstringTable.bucket_count(), cstringHashCount); | |
867 | int count[11]; | |
868 | for(unsigned int b=0; b < 11; ++b) { | |
869 | count[b] = 0; | |
870 | } | |
871 | for(unsigned int i=0; i < _cstringTable.bucket_count(); ++i) { | |
d425e388 | 872 | unsigned int n = _cstringTable.bucket_size(i); |
a645023d A |
873 | if ( n < 10 ) |
874 | count[n] += 1; | |
875 | else | |
876 | count[10] += 1; | |
877 | } | |
878 | fprintf(stderr, "cstring table distribution\n"); | |
879 | for(unsigned int b=0; b < 11; ++b) { | |
880 | fprintf(stderr, "%u buckets have %u elements\n", count[b], b); | |
881 | } | |
882 | fprintf(stderr, "indirect table size: %lu\n", _indirectBindingTable.size()); | |
883 | fprintf(stderr, "by-name table size: %lu\n", _byNameTable.size()); | |
884 | // fprintf(stderr, "by-content table size: %lu, hash count: %u, equals count: %u, lookup count: %u\n", | |
885 | // _byContentTable.size(), contentHashCount, contentEqualCount, contentLookupCount); | |
886 | // fprintf(stderr, "by-ref table size: %lu, hashed count: %u, equals count: %u, lookup count: %u, insert count: %u\n", | |
887 | // _byReferencesTable.size(), refHashCount, refEqualsCount, refLookupCount, refInsertCount); | |
888 | ||
889 | //ReferencesHash obj; | |
890 | //for(ReferencesHashToSlot::iterator it=_byReferencesTable.begin(); it != _byReferencesTable.end(); ++it) { | |
891 | // if ( obj.operator()(it->first) == 0x2F3AC0EAC744EA70 ) { | |
82b4b32b | 892 | // fprintf(stderr, "hash=0x2F3AC0EAC744EA70 for %p %s from %s\n", it->first, it->first->name(), it->first->safeFilePath()); |
a645023d A |
893 | // |
894 | // } | |
895 | //} | |
896 | ||
897 | } | |
898 | ||
899 | } // namespace tool | |
900 | } // namespace ld | |
901 |