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