1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
3 * Copyright (c) 2009-2011 Apple Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
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
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.
22 * @APPLE_LICENSE_HEADER_END@
30 #include <mach/machine.h>
35 #include <unordered_map>
45 // The purpose of this pass is to take the graph of all Atoms and produce an ordered
46 // sequence of atoms. The constraints are that: 1) all Atoms of the same Segment must
47 // be contiguous, 2) all Atoms of the same Section must be contigous, 3) Atoms specified
48 // in an order are sequenced as in the order file and before Atoms not specified,
49 // 4) Atoms in the same section from the same .o file should be contiguous and sequenced
50 // in the same order they were in the .o file, 5) Atoms in the same Section but which came
51 // from different .o files should be sequenced in the same order that the .o files
52 // were passed to the linker (i.e. command line order).
54 // The way this is implemented is that the linker passes a "base ordinal" to each File
55 // as it is constructed. Add each atom has an objectAddress() method. Then
56 // sorting is just sorting by section, then by file ordinal, then by object address.
58 // If an -order_file is specified, it gets more complicated. First, an override-ordinal map
59 // is created. It causes the sort routine to ignore the value returned by ordinal() and objectAddress()
60 // and use the override value instead. Next some Atoms must be laid out consecutively
61 // (e.g. hand written assembly that does not end with return, but rather falls into
62 // the next label). This is modeled in via a kindNoneFollowOn fixup. The use of
63 // kindNoneFollowOn fixups produces "clusters" of atoms that must stay together.
64 // If an order_file tries to move one atom, it may need to move a whole cluster. The
65 // algorithm to do this models clusters using two maps. The "starts" maps maps any
66 // atom in a cluster to the first Atom in the cluster. The "nexts" maps an Atom in a
67 // cluster to the next Atom in the cluster. With this in place, while processing an
68 // order_file, if any entry is in a cluster (in "starts" map), then the entire cluster is
69 // given ordinal overrides.
75 Layout(const Options
& opts
, ld::Internal
& state
);
81 Comparer(const Layout
& l
, ld::Internal
& s
) : _layout(l
), _state(s
) {}
82 bool operator()(const ld::Atom
* left
, const ld::Atom
* right
);
84 const Layout
& _layout
;
88 typedef std::unordered_map
<const char*, const ld::Atom
*, CStringHash
, CStringEquals
> NameToAtom
;
90 typedef std::map
<const ld::Atom
*, const ld::Atom
*> AtomToAtom
;
92 typedef std::map
<const ld::Atom
*, uint32_t> AtomToOrdinal
;
94 const ld::Atom
* findAtom(const Options::OrderedSymbol
& orderedSymbol
);
95 void buildNameTable();
96 void buildFollowOnTables();
97 void buildOrdinalOverrideMap();
98 const ld::Atom
* follower(const ld::Atom
* atom
);
99 static bool matchesObjectFile(const ld::Atom
* atom
, const char* objectFileLeafName
);
100 bool possibleToOrder(const ld::Internal::FinalSection
*);
102 const Options
& _options
;
103 ld::Internal
& _state
;
104 AtomToAtom _followOnStarts
;
105 AtomToAtom _followOnNexts
;
106 NameToAtom _nameTable
;
107 std::vector
<const ld::Atom
*> _nameCollisionAtoms
;
108 AtomToOrdinal _ordinalOverrideMap
;
115 bool Layout::_s_log
= false;
117 Layout::Layout(const Options
& opts
, ld::Internal
& state
)
118 : _options(opts
), _state(state
), _comparer(*this, state
), _haveOrderFile(opts
.orderedSymbolsCount() != 0)
123 bool Layout::Comparer::operator()(const ld::Atom
* left
, const ld::Atom
* right
)
128 // magic section$start symbol always sorts to the start of its section
129 if ( left
->contentType() == ld::Atom::typeSectionStart
)
131 if ( right
->contentType() == ld::Atom::typeSectionStart
)
134 // if an -order_file is specified, then sorting is altered to sort those symbols first
135 if ( _layout
._haveOrderFile
) {
136 AtomToOrdinal::const_iterator leftPos
= _layout
._ordinalOverrideMap
.find(left
);
137 AtomToOrdinal::const_iterator rightPos
= _layout
._ordinalOverrideMap
.find(right
);
138 AtomToOrdinal::const_iterator end
= _layout
._ordinalOverrideMap
.end();
139 if ( leftPos
!= end
) {
140 if ( rightPos
!= end
) {
141 // both left and right are overridden, so compare overridden ordinals
142 return leftPos
->second
< rightPos
->second
;
145 // left is overridden and right is not, so left < right
150 if ( rightPos
!= end
) {
151 // right is overridden and left is not, so right < left
155 // neither are overridden,
156 // fall into default sorting below
161 // magic section$end symbol always sorts to the end of its section
162 if ( left
->contentType() == ld::Atom::typeSectionEnd
)
164 if ( right
->contentType() == ld::Atom::typeSectionEnd
)
167 // aliases sort before their target
168 bool leftIsAlias
= left
->isAlias();
170 for (ld::Fixup::iterator fit
=left
->fixupsBegin(); fit
!= left
->fixupsEnd(); ++fit
) {
171 const ld::Atom
* target
= NULL
;
172 if ( fit
->kind
== ld::Fixup::kindNoneFollowOn
) {
173 switch ( fit
->binding
) {
174 case ld::Fixup::bindingsIndirectlyBound
:
175 target
= _state
.indirectBindingTable
[fit
->u
.bindingIndex
];
177 case ld::Fixup::bindingDirectlyBound
:
178 target
= fit
->u
.target
;
183 if ( target
== right
)
184 return true; // left already before right
185 left
= target
; // sort as if alias was its target
190 bool rightIsAlias
= right
->isAlias();
191 if ( rightIsAlias
) {
192 for (ld::Fixup::iterator fit
=right
->fixupsBegin(); fit
!= right
->fixupsEnd(); ++fit
) {
193 const ld::Atom
* target
= NULL
;
194 if ( fit
->kind
== ld::Fixup::kindNoneFollowOn
) {
195 switch ( fit
->binding
) {
196 case ld::Fixup::bindingsIndirectlyBound
:
197 target
= _state
.indirectBindingTable
[fit
->u
.bindingIndex
];
199 case ld::Fixup::bindingDirectlyBound
:
200 target
= fit
->u
.target
;
205 if ( target
== left
)
206 return false; // need to swap, alias is after target
207 right
= target
; // continue with sort as if right was target
213 // the __common section can have real or tentative definitions
214 // we want the real ones to sort before tentative ones
215 bool leftIsTent
= (left
->definition() == ld::Atom::definitionTentative
);
216 bool rightIsTent
= (right
->definition() == ld::Atom::definitionTentative
);
217 if ( leftIsTent
!= rightIsTent
)
221 // initializers are auto sorted to start of section
222 if ( !fInitializerSet
.empty() ) {
223 bool leftFirst
= (fInitializerSet
.count(left
) != 0);
224 bool rightFirst
= (fInitializerSet
.count(right
) != 0);
225 if ( leftFirst
!= rightFirst
)
229 // terminators are auto sorted to end of section
230 if ( !fTerminatorSet
.empty() ) {
231 bool leftLast
= (fTerminatorSet
.count(left
) != 0);
232 bool rightLast
= (fTerminatorSet
.count(right
) != 0);
233 if ( leftLast
!= rightLast
)
239 const ld::File
* leftFile
= left
->file();
240 const ld::File
* rightFile
= right
->file();
241 // <rdar://problem/10830126> properly sort if on file is NULL and the other is not
242 ld::File::Ordinal leftFileOrdinal
= (leftFile
!= NULL
) ? leftFile
->ordinal() : ld::File::Ordinal::NullOrdinal();
243 ld::File::Ordinal rightFileOrdinal
= (rightFile
!= NULL
) ? rightFile
->ordinal() : ld::File::Ordinal::NullOrdinal();
244 if ( leftFileOrdinal
!= rightFileOrdinal
)
245 return leftFileOrdinal
< rightFileOrdinal
;
247 // tentative defintions have no address in .o file, they are traditionally laid out by name
248 if ( leftIsTent
&& rightIsTent
)
249 return (strcmp(left
->name(), right
->name()) < 0);
251 // lastly sort by atom address
252 int64_t addrDiff
= left
->objectAddress() - right
->objectAddress();
253 if ( addrDiff
== 0 ) {
254 // have same address so one might be an alias, and aliases need to sort before target
255 if ( leftIsAlias
!= rightIsAlias
)
258 // both at same address, sort by name
259 return (strcmp(left
->name(), right
->name()) < 0);
261 return (addrDiff
< 0);
264 bool Layout::matchesObjectFile(const ld::Atom
* atom
, const char* objectFileLeafName
)
266 if ( objectFileLeafName
== NULL
)
268 const char* atomFullPath
= atom
->file()->path();
269 const char* lastSlash
= strrchr(atomFullPath
, '/');
270 if ( lastSlash
!= NULL
) {
271 if ( strcmp(&lastSlash
[1], objectFileLeafName
) == 0 )
275 if ( strcmp(atomFullPath
, objectFileLeafName
) == 0 )
282 bool Layout::possibleToOrder(const ld::Internal::FinalSection
* sect
)
284 // atoms in only some sections can have order_file applied
285 switch ( sect
->type() ) {
286 case ld::Section::typeUnclassified
:
287 case ld::Section::typeCode
:
288 case ld::Section::typeZeroFill
:
290 case ld::Section::typeImportProxies
:
293 // if section has command line aliases, then we must apply ordering so aliases layout before targets
294 if ( _options
.haveCmdLineAliases() ) {
295 for (std::vector
<const ld::Atom
*>::const_iterator ait
=sect
->atoms
.begin(); ait
!= sect
->atoms
.end(); ++ait
) {
296 const ld::Atom
* atom
= *ait
;
297 if ( atom
->isAlias() )
306 void Layout::buildNameTable()
308 for (std::vector
<ld::Internal::FinalSection
*>::iterator sit
=_state
.sections
.begin(); sit
!= _state
.sections
.end(); ++sit
) {
309 ld::Internal::FinalSection
* sect
= *sit
;
310 // some sections are not worth scanning for names
311 if ( ! possibleToOrder(sect
) )
313 for (std::vector
<const ld::Atom
*>::iterator ait
=sect
->atoms
.begin(); ait
!= sect
->atoms
.end(); ++ait
) {
314 const ld::Atom
* atom
= *ait
;
315 if ( atom
->symbolTableInclusion() == ld::Atom::symbolTableIn
) {
316 const char* name
= atom
->name();
318 // static function or data
319 NameToAtom::iterator pos
= _nameTable
.find(name
);
320 if ( pos
== _nameTable
.end() )
321 _nameTable
[name
] = atom
;
323 const ld::Atom
* existing
= _nameTable
[name
];
324 if ( existing
!= NULL
) {
325 _nameCollisionAtoms
.push_back(existing
);
326 _nameTable
[name
] = NULL
; // collision, denote with NULL
328 _nameCollisionAtoms
.push_back(atom
);
335 fprintf(stderr
, "buildNameTable() _nameTable:\n");
336 for(NameToAtom::iterator it
=_nameTable
.begin(); it
!= _nameTable
.end(); ++it
)
337 fprintf(stderr
, " %p <- %s\n", it
->second
, it
->first
);
338 fprintf(stderr
, "buildNameTable() _nameCollisionAtoms:\n");
339 for(std::vector
<const ld::Atom
*>::iterator it
=_nameCollisionAtoms
.begin(); it
!= _nameCollisionAtoms
.end(); ++it
)
340 fprintf(stderr
, " %p, %s\n", *it
, (*it
)->name());
345 const ld::Atom
* Layout::findAtom(const Options::OrderedSymbol
& orderedSymbol
)
347 // look for name in _nameTable
348 NameToAtom::iterator pos
= _nameTable
.find(orderedSymbol
.symbolName
);
349 if ( pos
!= _nameTable
.end() ) {
350 if ( (pos
->second
!= NULL
) && matchesObjectFile(pos
->second
, orderedSymbol
.objectFileName
) ) {
351 //fprintf(stderr, "found %s in hash table\n", orderedSymbol.symbolName);
354 if ( pos
->second
== NULL
) {
355 // name is in hash table, but atom is NULL, so that means there are duplicates, so we use super slow way
356 if ( ( orderedSymbol
.objectFileName
== NULL
) && _options
.printOrderFileStatistics() ) {
357 warning("%s specified in order_file but it exists in multiple .o files. "
358 "Prefix symbol with .o filename in order_file to disambiguate", orderedSymbol
.symbolName
);
360 for (std::vector
<const ld::Atom
*>::iterator it
=_nameCollisionAtoms
.begin(); it
!= _nameCollisionAtoms
.end(); it
++) {
361 const ld::Atom
* atom
= *it
;
362 if ( strcmp(atom
->name(), orderedSymbol
.symbolName
) == 0 ) {
363 if ( matchesObjectFile(atom
, orderedSymbol
.objectFileName
) ) {
374 const ld::Atom
* Layout::follower(const ld::Atom
* atom
)
376 for (const ld::Atom
* a
= _followOnStarts
[atom
]; a
!= NULL
; a
= _followOnNexts
[a
]) {
378 if ( _followOnNexts
[a
] == atom
) {
382 // no follower, first in chain
386 void Layout::buildFollowOnTables()
388 // if no -order_file, then skip building follow on table
389 if ( ! _haveOrderFile
)
392 // first make a pass to find all follow-on references and build start/next maps
393 // which are a way to represent clusters of atoms that must layout together
394 for (std::vector
<ld::Internal::FinalSection
*>::iterator sit
=_state
.sections
.begin(); sit
!= _state
.sections
.end(); ++sit
) {
395 ld::Internal::FinalSection
* sect
= *sit
;
396 if ( !possibleToOrder(sect
) )
398 for (std::vector
<const ld::Atom
*>::iterator ait
=sect
->atoms
.begin(); ait
!= sect
->atoms
.end(); ++ait
) {
399 const ld::Atom
* atom
= *ait
;
400 for (ld::Fixup::iterator fit
= atom
->fixupsBegin(), end
=atom
->fixupsEnd(); fit
!= end
; ++fit
) {
401 if ( fit
->kind
== ld::Fixup::kindNoneFollowOn
) {
402 assert(fit
->binding
== ld::Fixup::bindingDirectlyBound
);
403 const ld::Atom
* followOnAtom
= fit
->u
.target
;
404 if ( _s_log
) fprintf(stderr
, "ref %p %s -> %p %s\n", atom
, atom
->name(), followOnAtom
, followOnAtom
->name());
405 assert(_followOnNexts
.count(atom
) == 0);
406 _followOnNexts
[atom
] = followOnAtom
;
407 if ( _followOnStarts
.count(atom
) == 0 ) {
408 // first time atom has been seen, make it start of chain
409 _followOnStarts
[atom
] = atom
;
410 if ( _s_log
) fprintf(stderr
, " start %s -> %s\n", atom
->name(), atom
->name());
412 if ( _followOnStarts
.count(followOnAtom
) == 0 ) {
413 // first time followOnAtom has been seen, make atom start of chain
414 _followOnStarts
[followOnAtom
] = _followOnStarts
[atom
];
415 if ( _s_log
) fprintf(stderr
, " start %s -> %s\n", followOnAtom
->name(), _followOnStarts
[atom
]->name());
418 if ( _followOnStarts
[followOnAtom
] == followOnAtom
) {
419 // followOnAtom atom already start of another chain, hook together
420 // and change all to use atom as start
421 const ld::Atom
* a
= followOnAtom
;
423 assert(_followOnStarts
[a
] == followOnAtom
);
424 _followOnStarts
[a
] = _followOnStarts
[atom
];
425 if ( _s_log
) fprintf(stderr
, " adjust start for %s -> %s\n", a
->name(), _followOnStarts
[atom
]->name());
426 AtomToAtom::iterator pos
= _followOnNexts
.find(a
);
427 if ( pos
!= _followOnNexts
.end() )
434 // attempt to insert atom into existing followOn chain
435 const ld::Atom
* curPrevToFollowOnAtom
= this->follower(followOnAtom
);
436 assert(curPrevToFollowOnAtom
!= NULL
);
437 assert((atom
->size() == 0) || (curPrevToFollowOnAtom
->size() == 0));
438 if ( atom
->size() == 0 ) {
439 // insert alias into existing chain right before followOnAtom
440 _followOnNexts
[curPrevToFollowOnAtom
] = atom
;
441 _followOnNexts
[atom
] = followOnAtom
;
442 _followOnStarts
[atom
] = _followOnStarts
[followOnAtom
];
445 // insert real atom into existing chain right before alias of followOnAtom
446 const ld::Atom
* curPrevPrevToFollowOn
= this->follower(curPrevToFollowOnAtom
);
447 if ( curPrevPrevToFollowOn
== NULL
) {
448 // nothing previous, so make this a start of a new chain
449 _followOnNexts
[atom
] = curPrevToFollowOnAtom
;
450 for (const ld::Atom
* a
= atom
; a
!= NULL
; a
= _followOnNexts
[a
]) {
451 if ( _s_log
) fprintf(stderr
, " adjust start for %s -> %s\n", a
->name(), atom
->name());
452 _followOnStarts
[a
] = atom
;
456 // is previous, insert into existing chain before previous
457 _followOnNexts
[curPrevPrevToFollowOn
] = atom
;
458 _followOnNexts
[atom
] = curPrevToFollowOnAtom
;
459 _followOnStarts
[atom
] = _followOnStarts
[curPrevToFollowOnAtom
];
470 for(AtomToAtom::iterator it
= _followOnStarts
.begin(); it
!= _followOnStarts
.end(); ++it
)
471 fprintf(stderr
, "start %s -> %s\n", it
->first
->name(), it
->second
->name());
473 for(AtomToAtom::iterator it
= _followOnNexts
.begin(); it
!= _followOnNexts
.end(); ++it
)
474 fprintf(stderr
, "next %s -> %s\n", it
->first
->name(), (it
->second
!= NULL
) ? it
->second
->name() : "null");
482 InSet(const std::set
<const ld::Atom
*>& theSet
) : _set(theSet
) {}
484 bool operator()(const ld::Atom
* atom
) const {
485 return ( _set
.count(atom
) != 0 );
488 const std::set
<const ld::Atom
*>& _set
;
492 void Layout::buildOrdinalOverrideMap()
494 // if no -order_file, then skip building override map
495 if ( ! _haveOrderFile
)
498 // build fast name->atom table
499 this->buildNameTable();
501 // handle .o files that cannot have their atoms rearranged
502 // with the start/next maps of follow-on atoms we can process the order file and produce override ordinals
504 uint32_t matchCount
= 0;
505 std::set
<const ld::Atom
*> moveToData
;
506 for(Options::OrderedSymbolsIterator it
= _options
.orderedSymbolsBegin(); it
!= _options
.orderedSymbolsEnd(); ++it
) {
507 const ld::Atom
* atom
= this->findAtom(*it
);
508 if ( atom
!= NULL
) {
509 // <rdar://problem/8612550> When order file used on data, turn ordered zero fill symbols into zero data
510 switch ( atom
->section().type() ) {
511 case ld::Section::typeZeroFill
:
512 case ld::Section::typeTentativeDefs
:
513 if ( atom
->size() <= 512 )
514 moveToData
.insert(atom
);
520 AtomToAtom::iterator start
= _followOnStarts
.find(atom
);
521 if ( start
!= _followOnStarts
.end() ) {
522 // this symbol for the order file corresponds to an atom that is in a cluster that must lay out together
523 for(const ld::Atom
* nextAtom
= start
->second
; nextAtom
!= NULL
; nextAtom
= _followOnNexts
[nextAtom
]) {
524 AtomToOrdinal::iterator pos
= _ordinalOverrideMap
.find(nextAtom
);
525 if ( pos
== _ordinalOverrideMap
.end() ) {
526 _ordinalOverrideMap
[nextAtom
] = index
++;
527 if (_s_log
) fprintf(stderr
, "override ordinal %u assigned to %s in cluster from %s\n", index
, nextAtom
->name(), nextAtom
->file()->path());
530 if (_s_log
) fprintf(stderr
, "could not order %s as %u because it was already laid out earlier by %s as %u\n",
531 atom
->name(), index
, _followOnStarts
[atom
]->name(), _ordinalOverrideMap
[atom
] );
536 _ordinalOverrideMap
[atom
] = index
;
537 if (_s_log
) fprintf(stderr
, "override ordinal %u assigned to %s from %s\n", index
, atom
->name(), atom
->file()->path());
542 if ( _options
.printOrderFileStatistics() ) {
543 if ( it
->objectFileName
== NULL
)
544 warning("can't find match for order_file entry: %s", it
->symbolName
);
546 warning("can't find match for order_file entry: %s/%s", it
->objectFileName
, it
->symbolName
);
551 if ( _options
.printOrderFileStatistics() && (_options
.orderedSymbolsCount() != matchCount
) ) {
552 warning("only %u out of %lu order_file symbols were applicable", matchCount
, _options
.orderedSymbolsCount() );
555 // <rdar://problem/8612550> When order file used on data, turn ordered zero fill symbols into zeroed data
556 if ( ! moveToData
.empty() ) {
557 // <rdar://problem/14919139> only move zero fill symbols to __data if there is a __data section
558 ld::Internal::FinalSection
* dataSect
= NULL
;
559 for (std::vector
<ld::Internal::FinalSection
*>::iterator sit
=_state
.sections
.begin(); sit
!= _state
.sections
.end(); ++sit
) {
560 ld::Internal::FinalSection
* sect
= *sit
;
561 if ( sect
->type() == ld::Section::typeUnclassified
) {
562 if ( (strcmp(sect
->sectionName(), "__data") == 0) && (strcmp(sect
->segmentName(), "__DATA") == 0) )
567 if ( dataSect
!= NULL
) {
568 // add atoms to __data
569 dataSect
->atoms
.insert(dataSect
->atoms
.end(), moveToData
.begin(), moveToData
.end());
570 // remove atoms from original sections
571 for (std::vector
<ld::Internal::FinalSection
*>::iterator sit
=_state
.sections
.begin(); sit
!= _state
.sections
.end(); ++sit
) {
572 ld::Internal::FinalSection
* sect
= *sit
;
573 switch ( sect
->type() ) {
574 case ld::Section::typeZeroFill
:
575 case ld::Section::typeTentativeDefs
:
576 sect
->atoms
.erase(std::remove_if(sect
->atoms
.begin(), sect
->atoms
.end(), InSet(moveToData
)), sect
->atoms
.end());
587 void Layout::doPass()
589 const bool log
= false;
591 fprintf(stderr
, "Unordered atoms:\n");
592 for (std::vector
<ld::Internal::FinalSection
*>::iterator sit
=_state
.sections
.begin(); sit
!= _state
.sections
.end(); ++sit
) {
593 ld::Internal::FinalSection
* sect
= *sit
;
594 for (std::vector
<const ld::Atom
*>::iterator ait
=sect
->atoms
.begin(); ait
!= sect
->atoms
.end(); ++ait
) {
595 const ld::Atom
* atom
= *ait
;
596 fprintf(stderr
, "\t%p\t%s\t%s\n", atom
, sect
->sectionName(), atom
->name());
601 // handle .o files that cannot have their atoms rearranged
602 this->buildFollowOnTables();
604 // assign new ordinal value to all ordered atoms
605 this->buildOrdinalOverrideMap();
607 // sort atoms in each section
608 for (std::vector
<ld::Internal::FinalSection
*>::iterator sit
=_state
.sections
.begin(); sit
!= _state
.sections
.end(); ++sit
) {
609 ld::Internal::FinalSection
* sect
= *sit
;
610 if ( sect
->type() == ld::Section::typeTempAlias
)
612 if ( log
) fprintf(stderr
, "sorting section %s\n", sect
->sectionName());
613 std::sort(sect
->atoms
.begin(), sect
->atoms
.end(), _comparer
);
617 fprintf(stderr
, "Sorted atoms:\n");
618 for (std::vector
<ld::Internal::FinalSection
*>::iterator sit
=_state
.sections
.begin(); sit
!= _state
.sections
.end(); ++sit
) {
619 ld::Internal::FinalSection
* sect
= *sit
;
620 for (std::vector
<const ld::Atom
*>::iterator ait
=sect
->atoms
.begin(); ait
!= sect
->atoms
.end(); ++ait
) {
621 const ld::Atom
* atom
= *ait
;
622 fprintf(stderr
, "\t%p\t%s\t%s\n", atom
, sect
->sectionName(), atom
->name());
629 void doPass(const Options
& opts
, ld::Internal
& state
)
631 Layout
layout(opts
, state
);
636 } // namespace order_file
637 } // namespace passes