]> git.saurik.com Git - apple/ld64.git/blob - src/ld/passes/order.cpp
b4be79fd0fc953ebeefa90d6f01d2532bc685a60
[apple/ld64.git] / src / ld / passes / order.cpp
1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
2 *
3 * Copyright (c) 2009-2011 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 <stdint.h>
27 #include <math.h>
28 #include <unistd.h>
29 #include <dlfcn.h>
30 #include <mach/machine.h>
31
32 #include <vector>
33 #include <map>
34 #include <unordered_map>
35
36 #include "ld.hpp"
37 #include "order.h"
38
39 namespace ld {
40 namespace passes {
41 namespace order {
42
43 //
44 // The purpose of this pass is to take the graph of all Atoms and produce an ordered
45 // sequence of atoms. The constraints are that: 1) all Atoms of the same Segment must
46 // be contiguous, 2) all Atoms of the same Section must be contigous, 3) Atoms specified
47 // in an order are sequenced as in the order file and before Atoms not specified,
48 // 4) Atoms in the same section from the same .o file should be contiguous and sequenced
49 // in the same order they were in the .o file, 5) Atoms in the same Section but which came
50 // from different .o files should be sequenced in the same order that the .o files
51 // were passed to the linker (i.e. command line order).
52 //
53 // The way this is implemented is that the linker passes a "base ordinal" to each File
54 // as it is constructed. Add each atom has an objectAddress() method. Then
55 // sorting is just sorting by section, then by file ordinal, then by object address.
56 //
57 // If an -order_file is specified, it gets more complicated. First, an override-ordinal map
58 // is created. It causes the sort routine to ignore the value returned by ordinal() and objectAddress()
59 // and use the override value instead. Next some Atoms must be laid out consecutively
60 // (e.g. hand written assembly that does not end with return, but rather falls into
61 // the next label). This is modeled in via a kindNoneFollowOn fixup. The use of
62 // kindNoneFollowOn fixups produces "clusters" of atoms that must stay together.
63 // If an order_file tries to move one atom, it may need to move a whole cluster. The
64 // algorithm to do this models clusters using two maps. The "starts" maps maps any
65 // atom in a cluster to the first Atom in the cluster. The "nexts" maps an Atom in a
66 // cluster to the next Atom in the cluster. With this in place, while processing an
67 // order_file, if any entry is in a cluster (in "starts" map), then the entire cluster is
68 // given ordinal overrides.
69 //
70
71 class Layout
72 {
73 public:
74 Layout(const Options& opts, ld::Internal& state);
75 void doPass();
76 private:
77
78 class Comparer {
79 public:
80 Comparer(const Layout& l) : _layout(l) {}
81 bool operator()(const ld::Atom* left, const ld::Atom* right);
82 private:
83 const Layout& _layout;
84 };
85
86 typedef std::unordered_map<const char*, const ld::Atom*, CStringHash, CStringEquals> NameToAtom;
87
88 typedef std::map<const ld::Atom*, const ld::Atom*> AtomToAtom;
89
90 typedef std::map<const ld::Atom*, uint32_t> AtomToOrdinal;
91
92 const ld::Atom* findAtom(const Options::OrderedSymbol& orderedSymbol);
93 void buildNameTable();
94 void buildFollowOnTables();
95 void buildOrdinalOverrideMap();
96 const ld::Atom* follower(const ld::Atom* atom);
97 static bool matchesObjectFile(const ld::Atom* atom, const char* objectFileLeafName);
98 bool possibleToOrder(const ld::Internal::FinalSection*);
99
100 const Options& _options;
101 ld::Internal& _state;
102 AtomToAtom _followOnStarts;
103 AtomToAtom _followOnNexts;
104 NameToAtom _nameTable;
105 std::vector<const ld::Atom*> _nameCollisionAtoms;
106 AtomToOrdinal _ordinalOverrideMap;
107 Comparer _comparer;
108 bool _haveOrderFile;
109
110 static bool _s_log;
111 };
112
113 bool Layout::_s_log = false;
114
115 Layout::Layout(const Options& opts, ld::Internal& state)
116 : _options(opts), _state(state), _comparer(*this), _haveOrderFile(opts.orderedSymbolsCount() != 0)
117 {
118 }
119
120
121 bool Layout::Comparer::operator()(const ld::Atom* left, const ld::Atom* right)
122 {
123 if ( left == right )
124 return false;
125
126 // magic section$start symbol always sorts to the start of its section
127 if ( left->contentType() == ld::Atom::typeSectionStart )
128 return true;
129 if ( right->contentType() == ld::Atom::typeSectionStart )
130 return false;
131
132 // if an -order_file is specified, then sorting is altered to sort those symbols first
133 if ( _layout._haveOrderFile ) {
134 AtomToOrdinal::const_iterator leftPos = _layout._ordinalOverrideMap.find(left);
135 AtomToOrdinal::const_iterator rightPos = _layout._ordinalOverrideMap.find(right);
136 AtomToOrdinal::const_iterator end = _layout._ordinalOverrideMap.end();
137 if ( leftPos != end ) {
138 if ( rightPos != end ) {
139 // both left and right are overridden, so compare overridden ordinals
140 return leftPos->second < rightPos->second;
141 }
142 else {
143 // left is overridden and right is not, so left < right
144 return true;
145 }
146 }
147 else {
148 if ( rightPos != end ) {
149 // right is overridden and left is not, so right < left
150 return false;
151 }
152 else {
153 // neither are overridden,
154 // fall into default sorting below
155 }
156 }
157 }
158
159 // magic section$end symbol always sorts to the end of its section
160 if ( left->contentType() == ld::Atom::typeSectionEnd )
161 return false;
162 if ( right->contentType() == ld::Atom::typeSectionEnd )
163 return true;
164
165 // the __common section can have real or tentative definitions
166 // we want the real ones to sort before tentative ones
167 bool leftIsTent = (left->definition() == ld::Atom::definitionTentative);
168 bool rightIsTent = (right->definition() == ld::Atom::definitionTentative);
169 if ( leftIsTent != rightIsTent )
170 return rightIsTent;
171
172 #if 0
173 // initializers are auto sorted to start of section
174 if ( !fInitializerSet.empty() ) {
175 bool leftFirst = (fInitializerSet.count(left) != 0);
176 bool rightFirst = (fInitializerSet.count(right) != 0);
177 if ( leftFirst != rightFirst )
178 return leftFirst;
179 }
180
181 // terminators are auto sorted to end of section
182 if ( !fTerminatorSet.empty() ) {
183 bool leftLast = (fTerminatorSet.count(left) != 0);
184 bool rightLast = (fTerminatorSet.count(right) != 0);
185 if ( leftLast != rightLast )
186 return rightLast;
187 }
188 #endif
189
190 // sort by .o order
191 const ld::File* leftFile = left->file();
192 const ld::File* rightFile = right->file();
193 // <rdar://problem/10830126> properly sort if on file is NULL and the other is not
194 ld::File::Ordinal leftFileOrdinal = (leftFile != NULL) ? leftFile->ordinal() : ld::File::Ordinal::NullOrdinal();
195 ld::File::Ordinal rightFileOrdinal = (rightFile != NULL) ? rightFile->ordinal() : ld::File::Ordinal::NullOrdinal();
196 if ( leftFileOrdinal != rightFileOrdinal )
197 return leftFileOrdinal< rightFileOrdinal;
198
199 // tentative defintions have no address in .o file, they are traditionally laid out by name
200 if ( leftIsTent && rightIsTent )
201 return (strcmp(left->name(), right->name()) < 0);
202
203 // lastly sort by atom address
204 int64_t addrDiff = left->objectAddress() - right->objectAddress();
205 if ( addrDiff == 0 ) {
206 // have same address so one might be an alias, and aliases need to sort before target
207 bool leftIsAlias = left->isAlias();
208 bool rightIsAlias = right->isAlias();
209 if ( leftIsAlias != rightIsAlias )
210 return leftIsAlias;
211
212 // both at same address, sort by name
213 return (strcmp(left->name(), right->name()) < 0);
214 }
215 return (addrDiff < 0);
216 }
217
218 bool Layout::matchesObjectFile(const ld::Atom* atom, const char* objectFileLeafName)
219 {
220 if ( objectFileLeafName == NULL )
221 return true;
222 const char* atomFullPath = atom->file()->path();
223 const char* lastSlash = strrchr(atomFullPath, '/');
224 if ( lastSlash != NULL ) {
225 if ( strcmp(&lastSlash[1], objectFileLeafName) == 0 )
226 return true;
227 }
228 else {
229 if ( strcmp(atomFullPath, objectFileLeafName) == 0 )
230 return true;
231 }
232 return false;
233 }
234
235
236 bool Layout::possibleToOrder(const ld::Internal::FinalSection* sect)
237 {
238 // atoms in only some sections can have order_file applied
239 switch ( sect->type() ) {
240 case ld::Section::typeUnclassified:
241 case ld::Section::typeCode:
242 case ld::Section::typeZeroFill:
243 return true;
244 case ld::Section::typeImportProxies:
245 return false;
246 default:
247 // if section has command line aliases, then we must apply ordering so aliases layout before targets
248 if ( _options.haveCmdLineAliases() ) {
249 for (std::vector<const ld::Atom*>::const_iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
250 const ld::Atom* atom = *ait;
251 if ( atom->isAlias() )
252 return true;
253 }
254 }
255 break;
256 }
257 return false;
258 }
259
260 void Layout::buildNameTable()
261 {
262 for (std::vector<ld::Internal::FinalSection*>::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) {
263 ld::Internal::FinalSection* sect = *sit;
264 // some sections are not worth scanning for names
265 if ( ! possibleToOrder(sect) )
266 continue;
267 for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
268 const ld::Atom* atom = *ait;
269 if ( atom->symbolTableInclusion() == ld::Atom::symbolTableIn ) {
270 const char* name = atom->name();
271 if ( name != NULL) {
272 // static function or data
273 NameToAtom::iterator pos = _nameTable.find(name);
274 if ( pos == _nameTable.end() )
275 _nameTable[name] = atom;
276 else {
277 const ld::Atom* existing = _nameTable[name];
278 if ( existing != NULL ) {
279 _nameCollisionAtoms.push_back(existing);
280 _nameTable[name] = NULL; // collision, denote with NULL
281 }
282 _nameCollisionAtoms.push_back(atom);
283 }
284 }
285 }
286 }
287 }
288 if ( _s_log ) {
289 fprintf(stderr, "buildNameTable() _nameTable:\n");
290 for(NameToAtom::iterator it=_nameTable.begin(); it != _nameTable.end(); ++it)
291 fprintf(stderr, " %p <- %s\n", it->second, it->first);
292 fprintf(stderr, "buildNameTable() _nameCollisionAtoms:\n");
293 for(std::vector<const ld::Atom*>::iterator it=_nameCollisionAtoms.begin(); it != _nameCollisionAtoms.end(); ++it)
294 fprintf(stderr, " %p, %s\n", *it, (*it)->name());
295 }
296 }
297
298
299 const ld::Atom* Layout::findAtom(const Options::OrderedSymbol& orderedSymbol)
300 {
301 // look for name in _nameTable
302 NameToAtom::iterator pos = _nameTable.find(orderedSymbol.symbolName);
303 if ( pos != _nameTable.end() ) {
304 if ( (pos->second != NULL) && matchesObjectFile(pos->second, orderedSymbol.objectFileName) ) {
305 //fprintf(stderr, "found %s in hash table\n", orderedSymbol.symbolName);
306 return pos->second;
307 }
308 if ( pos->second == NULL ) {
309 // name is in hash table, but atom is NULL, so that means there are duplicates, so we use super slow way
310 if ( ( orderedSymbol.objectFileName == NULL) && _options.printOrderFileStatistics() ) {
311 warning("%s specified in order_file but it exists in multiple .o files. "
312 "Prefix symbol with .o filename in order_file to disambiguate", orderedSymbol.symbolName);
313 }
314 for (std::vector<const ld::Atom*>::iterator it=_nameCollisionAtoms.begin(); it != _nameCollisionAtoms.end(); it++) {
315 const ld::Atom* atom = *it;
316 if ( strcmp(atom->name(), orderedSymbol.symbolName) == 0 ) {
317 if ( matchesObjectFile(atom, orderedSymbol.objectFileName) ) {
318 return atom;
319 }
320 }
321 }
322 }
323 }
324
325 return NULL;
326 }
327
328 const ld::Atom* Layout::follower(const ld::Atom* atom)
329 {
330 for (const ld::Atom* a = _followOnStarts[atom]; a != NULL; a = _followOnNexts[a]) {
331 assert(a != NULL);
332 if ( _followOnNexts[a] == atom ) {
333 return a;
334 }
335 }
336 // no follower, first in chain
337 return NULL;
338 }
339
340 void Layout::buildFollowOnTables()
341 {
342 // if no -order_file, then skip building follow on table
343 if ( ! _haveOrderFile )
344 return;
345
346 // first make a pass to find all follow-on references and build start/next maps
347 // which are a way to represent clusters of atoms that must layout together
348 for (std::vector<ld::Internal::FinalSection*>::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) {
349 ld::Internal::FinalSection* sect = *sit;
350 if ( !possibleToOrder(sect) )
351 continue;
352 for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
353 const ld::Atom* atom = *ait;
354 for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
355 if ( fit->kind == ld::Fixup::kindNoneFollowOn ) {
356 assert(fit->binding == ld::Fixup::bindingDirectlyBound);
357 const ld::Atom* followOnAtom = fit->u.target;
358 if ( _s_log ) fprintf(stderr, "ref %p %s -> %p %s\n", atom, atom->name(), followOnAtom, followOnAtom->name());
359 assert(_followOnNexts.count(atom) == 0);
360 _followOnNexts[atom] = followOnAtom;
361 if ( _followOnStarts.count(atom) == 0 ) {
362 // first time atom has been seen, make it start of chain
363 _followOnStarts[atom] = atom;
364 if ( _s_log ) fprintf(stderr, " start %s -> %s\n", atom->name(), atom->name());
365 }
366 if ( _followOnStarts.count(followOnAtom) == 0 ) {
367 // first time followOnAtom has been seen, make atom start of chain
368 _followOnStarts[followOnAtom] = _followOnStarts[atom];
369 if ( _s_log ) fprintf(stderr, " start %s -> %s\n", followOnAtom->name(), _followOnStarts[atom]->name());
370 }
371 else {
372 if ( _followOnStarts[followOnAtom] == followOnAtom ) {
373 // followOnAtom atom already start of another chain, hook together
374 // and change all to use atom as start
375 const ld::Atom* a = followOnAtom;
376 while ( true ) {
377 assert(_followOnStarts[a] == followOnAtom);
378 _followOnStarts[a] = _followOnStarts[atom];
379 if ( _s_log ) fprintf(stderr, " adjust start for %s -> %s\n", a->name(), _followOnStarts[atom]->name());
380 AtomToAtom::iterator pos = _followOnNexts.find(a);
381 if ( pos != _followOnNexts.end() )
382 a = pos->second;
383 else
384 break;
385 }
386 }
387 else {
388 // attempt to insert atom into existing followOn chain
389 const ld::Atom* curPrevToFollowOnAtom = this->follower(followOnAtom);
390 assert(curPrevToFollowOnAtom != NULL);
391 assert((atom->size() == 0) || (curPrevToFollowOnAtom->size() == 0));
392 if ( atom->size() == 0 ) {
393 // insert alias into existing chain right before followOnAtom
394 _followOnNexts[curPrevToFollowOnAtom] = atom;
395 _followOnNexts[atom] = followOnAtom;
396 _followOnStarts[atom] = _followOnStarts[followOnAtom];
397 }
398 else {
399 // insert real atom into existing chain right before alias of followOnAtom
400 const ld::Atom* curPrevPrevToFollowOn = this->follower(curPrevToFollowOnAtom);
401 if ( curPrevPrevToFollowOn == NULL ) {
402 // nothing previous, so make this a start of a new chain
403 _followOnNexts[atom] = curPrevToFollowOnAtom;
404 for (const ld::Atom* a = atom; a != NULL; a = _followOnNexts[a]) {
405 if ( _s_log ) fprintf(stderr, " adjust start for %s -> %s\n", a->name(), atom->name());
406 _followOnStarts[a] = atom;
407 }
408 }
409 else {
410 // is previous, insert into existing chain before previous
411 _followOnNexts[curPrevPrevToFollowOn] = atom;
412 _followOnNexts[atom] = curPrevToFollowOnAtom;
413 _followOnStarts[atom] = _followOnStarts[curPrevToFollowOnAtom];
414 }
415 }
416 }
417 }
418 }
419 }
420 }
421 }
422
423 if ( _s_log ) {
424 for(AtomToAtom::iterator it = _followOnStarts.begin(); it != _followOnStarts.end(); ++it)
425 fprintf(stderr, "start %s -> %s\n", it->first->name(), it->second->name());
426
427 for(AtomToAtom::iterator it = _followOnNexts.begin(); it != _followOnNexts.end(); ++it)
428 fprintf(stderr, "next %s -> %s\n", it->first->name(), (it->second != NULL) ? it->second->name() : "null");
429 }
430 }
431
432
433 class InSet
434 {
435 public:
436 InSet(const std::set<const ld::Atom*>& theSet) : _set(theSet) {}
437
438 bool operator()(const ld::Atom* atom) const {
439 return ( _set.count(atom) != 0 );
440 }
441 private:
442 const std::set<const ld::Atom*>& _set;
443 };
444
445
446 void Layout::buildOrdinalOverrideMap()
447 {
448 // if no -order_file, then skip building override map
449 if ( ! _haveOrderFile )
450 return;
451
452 // build fast name->atom table
453 this->buildNameTable();
454
455 // handle .o files that cannot have their atoms rearranged
456 // with the start/next maps of follow-on atoms we can process the order file and produce override ordinals
457 uint32_t index = 0;
458 uint32_t matchCount = 0;
459 std::set<const ld::Atom*> moveToData;
460 for(Options::OrderedSymbolsIterator it = _options.orderedSymbolsBegin(); it != _options.orderedSymbolsEnd(); ++it) {
461 const ld::Atom* atom = this->findAtom(*it);
462 if ( atom != NULL ) {
463 // <rdar://problem/8612550> When order file used on data, turn ordered zero fill symbols into zero data
464 switch ( atom->section().type() ) {
465 case ld::Section::typeZeroFill:
466 case ld::Section::typeTentativeDefs:
467 if ( atom->size() <= 512 )
468 moveToData.insert(atom);
469 break;
470 default:
471 break;
472 }
473
474 AtomToAtom::iterator start = _followOnStarts.find(atom);
475 if ( start != _followOnStarts.end() ) {
476 // this symbol for the order file corresponds to an atom that is in a cluster that must lay out together
477 for(const ld::Atom* nextAtom = start->second; nextAtom != NULL; nextAtom = _followOnNexts[nextAtom]) {
478 AtomToOrdinal::iterator pos = _ordinalOverrideMap.find(nextAtom);
479 if ( pos == _ordinalOverrideMap.end() ) {
480 _ordinalOverrideMap[nextAtom] = index++;
481 if (_s_log ) fprintf(stderr, "override ordinal %u assigned to %s in cluster from %s\n", index, nextAtom->name(), nextAtom->file()->path());
482 }
483 else {
484 if (_s_log ) fprintf(stderr, "could not order %s as %u because it was already laid out earlier by %s as %u\n",
485 atom->name(), index, _followOnStarts[atom]->name(), _ordinalOverrideMap[atom] );
486 }
487 }
488 }
489 else {
490 _ordinalOverrideMap[atom] = index;
491 if (_s_log ) fprintf(stderr, "override ordinal %u assigned to %s from %s\n", index, atom->name(), atom->file()->path());
492 }
493 ++matchCount;
494 }
495 else {
496 if ( _options.printOrderFileStatistics() ) {
497 if ( it->objectFileName == NULL )
498 warning("can't find match for order_file entry: %s", it->symbolName);
499 else
500 warning("can't find match for order_file entry: %s/%s", it->objectFileName, it->symbolName);
501 }
502 }
503 ++index;
504 }
505 if ( _options.printOrderFileStatistics() && (_options.orderedSymbolsCount() != matchCount) ) {
506 warning("only %u out of %lu order_file symbols were applicable", matchCount, _options.orderedSymbolsCount() );
507 }
508
509
510 // <rdar://problem/8612550> When order file used on data, turn ordered zero fill symbols into zeroed data
511 if ( ! moveToData.empty() ) {
512 for (std::vector<ld::Internal::FinalSection*>::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) {
513 ld::Internal::FinalSection* sect = *sit;
514 switch ( sect->type() ) {
515 case ld::Section::typeZeroFill:
516 case ld::Section::typeTentativeDefs:
517 sect->atoms.erase(std::remove_if(sect->atoms.begin(), sect->atoms.end(), InSet(moveToData)), sect->atoms.end());
518 break;
519 case ld::Section::typeUnclassified:
520 if ( (strcmp(sect->sectionName(), "__data") == 0) && (strcmp(sect->segmentName(), "__DATA") == 0) )
521 sect->atoms.insert(sect->atoms.end(), moveToData.begin(), moveToData.end());
522 break;
523 default:
524 break;
525 }
526 }
527 }
528
529 }
530
531 void Layout::doPass()
532 {
533 // handle .o files that cannot have their atoms rearranged
534 this->buildFollowOnTables();
535
536 // assign new ordinal value to all ordered atoms
537 this->buildOrdinalOverrideMap();
538
539 // sort atoms in each section
540 for (std::vector<ld::Internal::FinalSection*>::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) {
541 ld::Internal::FinalSection* sect = *sit;
542 std::sort(sect->atoms.begin(), sect->atoms.end(), _comparer);
543 }
544
545 //fprintf(stderr, "Sorted atoms:\n");
546 //for (std::vector<ld::Internal::FinalSection*>::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) {
547 // ld::Internal::FinalSection* sect = *sit;
548 // for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
549 // const ld::Atom* atom = *ait;
550 // fprintf(stderr, "\t%s\t%s\n", sect->sectionName(), atom->name());
551 // }
552 //}
553
554 }
555
556
557 void doPass(const Options& opts, ld::Internal& state)
558 {
559 Layout layout(opts, state);
560 layout.doPass();
561 }
562
563
564 } // namespace order_file
565 } // namespace passes
566 } // namespace ld