1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-*
3 * Copyright (c) 2005-2010 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@
25 // start temp HACK for cross builds
26 extern "C" double log2 ( double );
28 // end temp HACK for cross builds
32 #include <sys/types.h>
35 #include <sys/sysctl.h>
41 #include <mach/mach_time.h>
42 #include <mach/vm_statistics.h>
43 #include <mach/mach_init.h>
44 #include <mach/mach_host.h>
46 #include <mach-o/dyld.h>
48 #include <AvailabilityMacros.h>
57 #include <ext/hash_map>
58 #include <ext/hash_set>
63 #include "MachOFileAbstraction.hpp"
64 #include "Architectures.hpp"
67 #include "InputFiles.h"
69 #include "OutputFile.h"
71 #include "passes/stubs/make_stubs.h"
72 #include "passes/dtrace_dof.h"
73 #include "passes/got.h"
74 #include "passes/tlvp.h"
75 #include "passes/huge.h"
76 #include "passes/compact_unwind.h"
77 #include "passes/order_file.h"
78 #include "passes/branch_island.h"
79 #include "passes/branch_shim.h"
80 #include "passes/objc.h"
81 #include "passes/dylibs.h"
83 #include "parsers/archive_file.h"
84 #include "parsers/macho_relocatable_file.h"
85 #include "parsers/macho_dylib_file.h"
86 #include "parsers/lto_file.h"
87 #include "parsers/opaque_section_file.h"
90 class InternalState
: public ld::Internal
93 InternalState(const Options
& opts
) : _options(opts
) { }
94 virtual ld::Internal::FinalSection
* addAtom(const ld::Atom
& atom
);
95 virtual ld::Internal::FinalSection
* getFinalSection(const ld::Section
&);
98 virtual ~InternalState() {}
101 class FinalSection
: public ld::Internal::FinalSection
104 FinalSection(const ld::Section
& sect
, uint32_t sectionsSeen
, bool objFile
);
105 static int sectionComparer(const void* l
, const void* r
);
106 static const ld::Section
& outputSection(const ld::Section
& sect
);
107 static const ld::Section
& objectOutputSection(const ld::Section
& sect
, bool makeTentativeDefsReal
);
109 friend class InternalState
;
110 static uint32_t sectionOrder(const ld::Section
& sect
, uint32_t sectionsSeen
);
111 static uint32_t segmentOrder(const ld::Section
& sect
, bool objFile
);
112 uint32_t _segmentOrder
;
113 uint32_t _sectionOrder
;
115 static std::vector
<const char*> _s_segmentsSeen
;
116 static ld::Section _s_DATA_data
;
117 static ld::Section _s_DATA_const
;
118 static ld::Section _s_TEXT_text
;
119 static ld::Section _s_TEXT_const
;
120 static ld::Section _s_DATA_nl_symbol_ptr
;
121 static ld::Section _s_DATA_common
;
126 size_t operator()(const ld::Section
*) const;
128 struct SectionEquals
{
129 bool operator()(const ld::Section
* left
, const ld::Section
* right
) const;
131 typedef __gnu_cxx::hash_map
<const ld::Section
*, FinalSection
*, SectionHash
, SectionEquals
> SectionInToOut
;
134 SectionInToOut _sectionInToFinalMap
;
135 const Options
& _options
;
138 ld::Section
InternalState::FinalSection::_s_DATA_data( "__DATA", "__data", ld::Section::typeUnclassified
);
139 ld::Section
InternalState::FinalSection::_s_DATA_const("__DATA", "__const", ld::Section::typeUnclassified
);
140 ld::Section
InternalState::FinalSection::_s_TEXT_text( "__TEXT", "__text", ld::Section::typeCode
);
141 ld::Section
InternalState::FinalSection::_s_TEXT_const("__TEXT", "__const", ld::Section::typeUnclassified
);
142 ld::Section
InternalState::FinalSection::_s_DATA_nl_symbol_ptr("__DATA", "__nl_symbol_ptr", ld::Section::typeNonLazyPointer
);
143 ld::Section
InternalState::FinalSection::_s_DATA_common("__DATA", "__common", ld::Section::typeZeroFill
);
144 std::vector
<const char*> InternalState::FinalSection::_s_segmentsSeen
;
147 size_t InternalState::SectionHash::operator()(const ld::Section
* sect
) const
150 __gnu_cxx::hash
<const char*> temp
;
151 hash
+= temp
.operator()(sect
->segmentName());
152 hash
+= temp
.operator()(sect
->sectionName());
156 bool InternalState::SectionEquals::operator()(const ld::Section
* left
, const ld::Section
* right
) const
158 return (*left
== *right
);
162 InternalState::FinalSection::FinalSection(const ld::Section
& sect
, uint32_t sectionsSeen
, bool objFile
)
163 : ld::Internal::FinalSection(sect
),
164 _segmentOrder(segmentOrder(sect
, objFile
)),
165 _sectionOrder(sectionOrder(sect
, sectionsSeen
))
167 //fprintf(stderr, "FinalSection(%s, %s) _segmentOrder=%d, _sectionOrder=%d\n",
168 // this->segmentName(), this->sectionName(), _segmentOrder, _sectionOrder);
171 const ld::Section
& InternalState::FinalSection::outputSection(const ld::Section
& sect
)
173 // merge sections in final linked image
174 switch ( sect
.type() ) {
175 case ld::Section::typeLiteral4
:
176 case ld::Section::typeLiteral8
:
177 case ld::Section::typeLiteral16
:
178 return _s_TEXT_const
;
179 case ld::Section::typeUnclassified
:
180 if ( strcmp(sect
.segmentName(), "__DATA") == 0 ) {
181 if ( strcmp(sect
.sectionName(), "__datacoal_nt") == 0 )
183 if ( strcmp(sect
.sectionName(), "__const_coal") == 0 )
184 return _s_DATA_const
;
186 else if ( strcmp(sect
.segmentName(), "__TEXT") == 0 ) {
187 if ( strcmp(sect
.sectionName(), "__const_coal") == 0 )
188 return _s_TEXT_const
;
191 case ld::Section::typeCode
:
192 if ( strcmp(sect
.segmentName(), "__TEXT") == 0 ) {
193 if ( strcmp(sect
.sectionName(), "__textcoal_nt") == 0 )
195 else if ( strcmp(sect
.sectionName(), "__StaticInit") == 0 )
199 case ld::Section::typeNonLazyPointer
:
200 if ( strcmp(sect
.segmentName(), "__DATA") == 0 ) {
201 if ( strcmp(sect
.sectionName(), "__nl_symbol_ptr") == 0 )
202 return _s_DATA_nl_symbol_ptr
;
204 else if ( strcmp(sect
.segmentName(), "__IMPORT") == 0 ) {
205 if ( strcmp(sect
.sectionName(), "__pointers") == 0 )
206 return _s_DATA_nl_symbol_ptr
;
209 case ld::Section::typeTentativeDefs
:
210 return _s_DATA_common
;
219 const ld::Section
& InternalState::FinalSection::objectOutputSection(const ld::Section
& sect
, bool makeTentativeDefsReal
)
221 // in -r mode the only section that ever changes is __tenative -> __common with -d option
222 if ( (sect
.type() == ld::Section::typeTentativeDefs
) && makeTentativeDefsReal
)
223 return _s_DATA_common
;
227 uint32_t InternalState::FinalSection::segmentOrder(const ld::Section
& sect
, bool objFile
)
229 if ( strcmp(sect
.segmentName(), "__PAGEZERO") == 0 )
231 if ( strcmp(sect
.segmentName(), "__HEADER") == 0 ) // only used with -preload
233 if ( strcmp(sect
.segmentName(), "__TEXT") == 0 )
235 // in -r mode, want __DATA last so zerofill sections are at end
236 if ( strcmp(sect
.segmentName(), "__DATA") == 0 )
237 return (objFile
? 5 : 2);
238 if ( strcmp(sect
.segmentName(), "__OBJC") == 0 )
240 if ( strcmp(sect
.segmentName(), "__IMPORT") == 0 )
243 // layout non-standard segments in order seen (+10 to shift beyond standard segments)
244 for (uint32_t i
=0; i
< _s_segmentsSeen
.size(); ++i
) {
245 if ( strcmp(_s_segmentsSeen
[i
], sect
.segmentName()) == 0 )
248 _s_segmentsSeen
.push_back(sect
.segmentName());
249 return _s_segmentsSeen
.size()-1+10;
252 uint32_t InternalState::FinalSection::sectionOrder(const ld::Section
& sect
, uint32_t sectionsSeen
)
254 if ( sect
.type() == ld::Section::typeFirstSection
)
256 if ( sect
.type() == ld::Section::typeMachHeader
)
258 if ( sect
.type() == ld::Section::typeLastSection
)
260 if ( strcmp(sect
.segmentName(), "__TEXT") == 0 ) {
261 switch ( sect
.type() ) {
262 case ld::Section::typeCode
:
263 // <rdar://problem/8346444> make __text always be first "code" section
264 if ( strcmp(sect
.sectionName(), "__text") == 0 )
268 case ld::Section::typeStub
:
270 case ld::Section::typeStubHelper
:
272 case ld::Section::typeLSDA
:
274 case ld::Section::typeUnwindInfo
:
276 case ld::Section::typeCFI
:
278 case ld::Section::typeStubClose
:
281 return sectionsSeen
+20;
284 else if ( strcmp(sect
.segmentName(), "__DATA") == 0 ) {
285 switch ( sect
.type() ) {
286 case ld::Section::typeLazyPointerClose
:
288 case ld::Section::typeDyldInfo
:
290 case ld::Section::typeNonLazyPointer
:
292 case ld::Section::typeLazyPointer
:
294 case ld::Section::typeInitializerPointers
:
296 case ld::Section::typeTerminatorPointers
:
298 case ld::Section::typeTLVInitialValues
:
299 return INT_MAX
-4; // need TLV zero-fill to follow TLV init values
300 case ld::Section::typeTLVZeroFill
:
302 case ld::Section::typeZeroFill
:
303 // make sure __huge is always last zerofill section
304 if ( strcmp(sect
.sectionName(), "__huge") == 0 )
309 // <rdar://problem/7435296> Reorder sections to reduce page faults in object files
310 if ( strcmp(sect
.sectionName(), "__objc_classlist") == 0 )
312 else if ( strcmp(sect
.sectionName(), "__objc_nlclslist") == 0 )
314 else if ( strcmp(sect
.sectionName(), "__objc_catlist") == 0 )
316 else if ( strcmp(sect
.sectionName(), "__objc_protolist") == 0 )
318 else if ( strcmp(sect
.sectionName(), "__objc_imageinfo") == 0 )
320 else if ( strcmp(sect
.sectionName(), "__objc_const") == 0 )
322 else if ( strcmp(sect
.sectionName(), "__objc_selrefs") == 0 )
324 else if ( strcmp(sect
.sectionName(), "__objc_msgrefs") == 0 )
326 else if ( strcmp(sect
.sectionName(), "__objc_protorefs") == 0 )
328 else if ( strcmp(sect
.sectionName(), "__objc_classrefs") == 0 )
330 else if ( strcmp(sect
.sectionName(), "__objc_superrefs") == 0 )
332 else if ( strcmp(sect
.sectionName(), "__objc_data") == 0 )
335 return sectionsSeen
+40;
338 // make sure zerofill in any other section is at end of segment
339 if ( sect
.type() == ld::Section::typeZeroFill
)
341 return sectionsSeen
+20;
345 static void validateFixups(const ld::Atom
& atom
)
347 //fprintf(stderr, "validateFixups %s\n", atom.name());
348 bool lastWasClusterEnd
= true;
349 ld::Fixup::Cluster lastClusterSize
= ld::Fixup::k1of1
;
350 uint32_t curClusterOffsetInAtom
= 0;
351 for (ld::Fixup::iterator fit
=atom
.fixupsBegin(); fit
!= atom
.fixupsEnd(); ++fit
) {
352 //fprintf(stderr, " fixup offset=%d, cluster=%d\n", fit->offsetInAtom, fit->clusterSize);
353 assert((fit
->offsetInAtom
< atom
.size()) || (fit
->offsetInAtom
== 0));
354 if ( fit
->firstInCluster() ) {
355 assert(lastWasClusterEnd
);
356 curClusterOffsetInAtom
= fit
->offsetInAtom
;
357 lastWasClusterEnd
= (fit
->clusterSize
== ld::Fixup::k1of1
);
360 assert(!lastWasClusterEnd
);
361 assert(fit
->offsetInAtom
== curClusterOffsetInAtom
);
362 switch ((ld::Fixup::Cluster
)fit
->clusterSize
) {
363 case ld::Fixup::k1of1
:
364 case ld::Fixup::k1of2
:
365 case ld::Fixup::k1of3
:
366 case ld::Fixup::k1of4
:
367 case ld::Fixup::k1of5
:
368 lastWasClusterEnd
= false;
370 case ld::Fixup::k2of2
:
371 assert(lastClusterSize
= ld::Fixup::k1of2
);
372 lastWasClusterEnd
= true;
374 case ld::Fixup::k2of3
:
375 assert(lastClusterSize
= ld::Fixup::k1of3
);
376 lastWasClusterEnd
= false;
378 case ld::Fixup::k2of4
:
379 assert(lastClusterSize
= ld::Fixup::k1of4
);
380 lastWasClusterEnd
= false;
382 case ld::Fixup::k2of5
:
383 assert(lastClusterSize
= ld::Fixup::k1of5
);
384 lastWasClusterEnd
= false;
386 case ld::Fixup::k3of3
:
387 assert(lastClusterSize
= ld::Fixup::k2of3
);
388 lastWasClusterEnd
= true;
390 case ld::Fixup::k3of4
:
391 assert(lastClusterSize
= ld::Fixup::k2of4
);
392 lastWasClusterEnd
= false;
394 case ld::Fixup::k3of5
:
395 assert(lastClusterSize
= ld::Fixup::k2of5
);
396 lastWasClusterEnd
= false;
398 case ld::Fixup::k4of4
:
399 assert(lastClusterSize
= ld::Fixup::k3of4
);
400 lastWasClusterEnd
= true;
402 case ld::Fixup::k4of5
:
403 assert(lastClusterSize
= ld::Fixup::k3of5
);
404 lastWasClusterEnd
= false;
406 case ld::Fixup::k5of5
:
407 assert(lastClusterSize
= ld::Fixup::k4of5
);
408 lastWasClusterEnd
= true;
412 lastClusterSize
= fit
->clusterSize
;
413 if ( fit
->binding
== ld::Fixup::bindingDirectlyBound
) {
414 assert(fit
->u
.target
!= NULL
);
417 switch (lastClusterSize
) {
418 case ld::Fixup::k1of1
:
419 case ld::Fixup::k2of2
:
420 case ld::Fixup::k3of3
:
421 case ld::Fixup::k4of4
:
422 case ld::Fixup::k5of5
:
425 assert(0 && "last fixup was not end of cluster");
431 ld::Internal::FinalSection
* InternalState::addAtom(const ld::Atom
& atom
)
433 ld::Internal::FinalSection
* fs
= this->getFinalSection(atom
.section());
435 // <rdar://problem/8612550> When order file used on data, turn ordered zero fill symbols into zero data
436 switch ( atom
.section().type() ) {
437 case ld::Section::typeZeroFill
:
438 case ld::Section::typeTentativeDefs
:
439 if ( (_options
.outputKind() == Options::kDyld
) && (atom
.symbolTableInclusion() == ld::Atom::symbolTableIn
)
440 && (atom
.size() <= 512) && (_options
.orderedSymbolsCount() != 0) ) {
441 for(Options::OrderedSymbolsIterator it
= _options
.orderedSymbolsBegin(); it
!= _options
.orderedSymbolsEnd(); ++it
) {
442 if ( (it
->objectFileName
== NULL
) && (strcmp(it
->symbolName
, atom
.name()) == 0) ) {
443 // found in order file, move to __data section
444 fs
= getFinalSection(InternalState::FinalSection::_s_DATA_data
);\
445 //fprintf(stderr, "moved %s to __data section\n", atom.name());
455 //fprintf(stderr, "InternalState::doAtom(%p), name=%s, sect=%s, finalsect=%p\n", &atom, atom.name(), atom.section().sectionName(), fs);
457 validateFixups(atom
);
459 fs
->atoms
.push_back(&atom
);
463 ld::Internal::FinalSection
* InternalState::getFinalSection(const ld::Section
& inputSection
)
465 const ld::Section
* baseForFinalSection
= &inputSection
;
467 // see if input section already has a FinalSection
468 SectionInToOut::iterator pos
= _sectionInToFinalMap
.find(&inputSection
);
469 if ( pos
!= _sectionInToFinalMap
.end() ) {
473 // otherwise, create a new final section
474 bool objFile
= false;
475 switch ( _options
.outputKind() ) {
476 case Options::kStaticExecutable
:
477 case Options::kDynamicExecutable
:
478 case Options::kDynamicLibrary
:
479 case Options::kDynamicBundle
:
481 case Options::kKextBundle
:
482 case Options::kPreload
:
484 // coalesce some sections
485 const ld::Section
& outSect
= FinalSection::outputSection(inputSection
);
486 pos
= _sectionInToFinalMap
.find(&outSect
);
487 if ( pos
!= _sectionInToFinalMap
.end() ) {
488 _sectionInToFinalMap
[&inputSection
] = pos
->second
;
489 //fprintf(stderr, "_sectionInToFinalMap[%p] = %p\n", &inputSection, pos->second);
492 else if ( outSect
!= inputSection
) {
493 // new output section created, but not in map
494 baseForFinalSection
= &outSect
;
498 case Options::kObjectFile
:
499 baseForFinalSection
= &FinalSection::objectOutputSection(inputSection
, _options
.makeTentativeDefinitionsReal());
500 pos
= _sectionInToFinalMap
.find(baseForFinalSection
);
501 if ( pos
!= _sectionInToFinalMap
.end() ) {
502 _sectionInToFinalMap
[&inputSection
] = pos
->second
;
503 //fprintf(stderr, "_sectionInToFinalMap[%p] = %p\n", &inputSection, pos->second);
510 InternalState::FinalSection
* result
= new InternalState::FinalSection(*baseForFinalSection
,
511 _sectionInToFinalMap
.size(), objFile
);
512 _sectionInToFinalMap
[baseForFinalSection
] = result
;
513 //fprintf(stderr, "_sectionInToFinalMap[%p] = %p\n", baseForFinalSection, result);
514 sections
.push_back(result
);
519 int InternalState::FinalSection::sectionComparer(const void* l
, const void* r
)
521 const FinalSection
* left
= *(FinalSection
**)l
;
522 const FinalSection
* right
= *(FinalSection
**)r
;
523 if ( left
->_segmentOrder
!= right
->_segmentOrder
)
524 return (left
->_segmentOrder
- right
->_segmentOrder
);
525 return (left
->_sectionOrder
- right
->_sectionOrder
);
528 void InternalState::sortSections()
530 //fprintf(stderr, "UNSORTED final sections:\n");
531 //for (std::vector<ld::Internal::FinalSection*>::iterator it = sections.begin(); it != sections.end(); ++it) {
532 // fprintf(stderr, "final section %p %s/%s\n", (*it), (*it)->segmentName(), (*it)->sectionName());
534 qsort(§ions
[0], sections
.size(), sizeof(FinalSection
*), &InternalState::FinalSection::sectionComparer
);
535 //fprintf(stderr, "SORTED final sections:\n");
536 //for (std::vector<ld::Internal::FinalSection*>::iterator it = sections.begin(); it != sections.end(); ++it) {
537 // fprintf(stderr, "final section %p %s/%s\n", (*it), (*it)->segmentName(), (*it)->sectionName());
539 assert((sections
[0]->type() == ld::Section::typeMachHeader
)
540 || ((sections
[0]->type() == ld::Section::typeFirstSection
) && (sections
[1]->type() == ld::Section::typeMachHeader
))
541 || ((sections
[0]->type() == ld::Section::typePageZero
) && (sections
[1]->type() == ld::Section::typeMachHeader
))
542 || ((sections
[0]->type() == ld::Section::typePageZero
) && (sections
[1]->type() == ld::Section::typeFirstSection
) && (sections
[2]->type() == ld::Section::typeMachHeader
)) );
546 static void getVMInfo(vm_statistics_data_t
& info
)
548 mach_msg_type_number_t count
= sizeof(vm_statistics_data_t
) / sizeof(natural_t
);
549 kern_return_t error
= host_statistics(mach_host_self(), HOST_VM_INFO
,
550 (host_info_t
)&info
, &count
);
551 if (error
!= KERN_SUCCESS
) {
552 bzero(&info
, sizeof(vm_statistics_data_t
));
556 int main(int argc
, const char* argv
[])
561 const char* archName
= NULL
;
562 bool showArch
= false;
563 bool archInferred
= false;
565 vm_statistics_data_t vmStart
;
566 vm_statistics_data_t vmEnd
;
569 // create object to track command line arguments
570 Options
options(argc
, argv
);
573 if ( options
.printStatistics() )
576 // update strings for error messages
577 showArch
= options
.printArchPrefix();
578 archName
= options
.architectureName();
579 archInferred
= (options
.architecture() == 0);
581 // open and parse input files
582 ld::tool::InputFiles
inputFiles(options
, &archName
);
584 // load and resolve all references
585 InternalState
state(options
);
586 ld::tool::Resolver
resolver(options
, inputFiles
, state
);
590 inputFiles
.dylibs(state
);
592 // do initial section sorting so passes have rough idea of the layout
593 state
.sortSections();
596 ld::passes::objc::doPass(options
, state
);
597 ld::passes::stubs::doPass(options
, state
);
598 ld::passes::huge::doPass(options
, state
);
599 ld::passes::got::doPass(options
, state
);
600 ld::passes::tlvp::doPass(options
, state
);
601 ld::passes::dylibs::doPass(options
, state
); // must be after stubs and GOT passes
602 ld::passes::order_file::doPass(options
, state
);
603 ld::passes::branch_shim::doPass(options
, state
); // must be after stubs
604 ld::passes::branch_island::doPass(options
, state
); // must be after stubs and order_file pass
605 ld::passes::dtrace::doPass(options
, state
);
606 ld::passes::compact_unwind::doPass(options
, state
); // must be after order-file pass
608 // sort final sections
609 state
.sortSections();
612 ld::tool::OutputFile
out(options
);
616 //mach_o::relocatable::printCounts();
617 if ( options
.printStatistics() ) {
619 fprintf(stderr
, "pageins=%u, pageouts=%u, faults=%u\n", vmEnd
.pageins
-vmStart
.pageins
,
620 vmEnd
.pageouts
-vmStart
.pageouts
, vmEnd
.faults
-vmStart
.faults
);
624 catch (const char* msg
) {
626 fprintf(stderr
, "ld: %s for inferred architecture %s\n", msg
, archName
);
628 fprintf(stderr
, "ld: %s for architecture %s\n", msg
, archName
);
630 fprintf(stderr
, "ld: %s\n", msg
);
639 // implement assert() function to print out a backtrace before aborting
640 void __assert_rtn(const char* func
, const char* file
, int line
, const char* failedexpr
)
642 fprintf(stderr
, "Assertion failed: (%s), function %s, file %s, line %d.\n", failedexpr
, func
, file
, line
);
644 void* callStack
[128];
645 int depth
= ::backtrace(callStack
, 128);
646 char* buffer
= (char*)malloc(1024);
647 for(int i
=0; i
< depth
-1; ++i
) {
649 dladdr(callStack
[i
], &info
);
650 const char* symboName
= info
.dli_sname
;
651 if ( (symboName
!= NULL
) && (strncmp(symboName
, "_Z", 2) == 0) ) {
652 size_t bufLen
= 1024;
654 char* unmangled
= abi::__cxa_demangle(symboName
, buffer
, &bufLen
, &result
);
655 if ( unmangled
!= NULL
)
656 symboName
= unmangled
;
658 long offset
= (uintptr_t)callStack
[i
] - (uintptr_t)info
.dli_saddr
;
659 fprintf(stderr
, "%d %p %s + %ld\n", i
, callStack
[i
], symboName
, offset
);