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
, bool mergeZeroFill
);
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
;
122 static ld::Section _s_DATA_zerofill
;
127 size_t operator()(const ld::Section
*) const;
129 struct SectionEquals
{
130 bool operator()(const ld::Section
* left
, const ld::Section
* right
) const;
132 typedef __gnu_cxx::hash_map
<const ld::Section
*, FinalSection
*, SectionHash
, SectionEquals
> SectionInToOut
;
135 SectionInToOut _sectionInToFinalMap
;
136 const Options
& _options
;
139 ld::Section
InternalState::FinalSection::_s_DATA_data( "__DATA", "__data", ld::Section::typeUnclassified
);
140 ld::Section
InternalState::FinalSection::_s_DATA_const("__DATA", "__const", ld::Section::typeUnclassified
);
141 ld::Section
InternalState::FinalSection::_s_TEXT_text( "__TEXT", "__text", ld::Section::typeCode
);
142 ld::Section
InternalState::FinalSection::_s_TEXT_const("__TEXT", "__const", ld::Section::typeUnclassified
);
143 ld::Section
InternalState::FinalSection::_s_DATA_nl_symbol_ptr("__DATA", "__nl_symbol_ptr", ld::Section::typeNonLazyPointer
);
144 ld::Section
InternalState::FinalSection::_s_DATA_common("__DATA", "__common", ld::Section::typeZeroFill
);
145 ld::Section
InternalState::FinalSection::_s_DATA_zerofill("__DATA", "__zerofill", ld::Section::typeZeroFill
);
146 std::vector
<const char*> InternalState::FinalSection::_s_segmentsSeen
;
149 size_t InternalState::SectionHash::operator()(const ld::Section
* sect
) const
152 __gnu_cxx::hash
<const char*> temp
;
153 hash
+= temp
.operator()(sect
->segmentName());
154 hash
+= temp
.operator()(sect
->sectionName());
158 bool InternalState::SectionEquals::operator()(const ld::Section
* left
, const ld::Section
* right
) const
160 return (*left
== *right
);
164 InternalState::FinalSection::FinalSection(const ld::Section
& sect
, uint32_t sectionsSeen
, bool objFile
)
165 : ld::Internal::FinalSection(sect
),
166 _segmentOrder(segmentOrder(sect
, objFile
)),
167 _sectionOrder(sectionOrder(sect
, sectionsSeen
))
169 //fprintf(stderr, "FinalSection(%s, %s) _segmentOrder=%d, _sectionOrder=%d\n",
170 // this->segmentName(), this->sectionName(), _segmentOrder, _sectionOrder);
173 const ld::Section
& InternalState::FinalSection::outputSection(const ld::Section
& sect
, bool mergeZeroFill
)
175 // merge sections in final linked image
176 switch ( sect
.type() ) {
177 case ld::Section::typeLiteral4
:
178 case ld::Section::typeLiteral8
:
179 case ld::Section::typeLiteral16
:
180 return _s_TEXT_const
;
181 case ld::Section::typeUnclassified
:
182 if ( strcmp(sect
.segmentName(), "__DATA") == 0 ) {
183 if ( strcmp(sect
.sectionName(), "__datacoal_nt") == 0 )
185 if ( strcmp(sect
.sectionName(), "__const_coal") == 0 )
186 return _s_DATA_const
;
188 else if ( strcmp(sect
.segmentName(), "__TEXT") == 0 ) {
189 if ( strcmp(sect
.sectionName(), "__const_coal") == 0 )
190 return _s_TEXT_const
;
193 case ld::Section::typeZeroFill
:
195 return _s_DATA_zerofill
;
197 case ld::Section::typeCode
:
198 if ( strcmp(sect
.segmentName(), "__TEXT") == 0 ) {
199 if ( strcmp(sect
.sectionName(), "__textcoal_nt") == 0 )
201 else if ( strcmp(sect
.sectionName(), "__StaticInit") == 0 )
205 case ld::Section::typeNonLazyPointer
:
206 if ( strcmp(sect
.segmentName(), "__DATA") == 0 ) {
207 if ( strcmp(sect
.sectionName(), "__nl_symbol_ptr") == 0 )
208 return _s_DATA_nl_symbol_ptr
;
210 else if ( strcmp(sect
.segmentName(), "__IMPORT") == 0 ) {
211 if ( strcmp(sect
.sectionName(), "__pointers") == 0 )
212 return _s_DATA_nl_symbol_ptr
;
215 case ld::Section::typeTentativeDefs
:
217 return _s_DATA_zerofill
;
219 return _s_DATA_common
;
228 const ld::Section
& InternalState::FinalSection::objectOutputSection(const ld::Section
& sect
, bool makeTentativeDefsReal
)
230 // in -r mode the only section that ever changes is __tenative -> __common with -d option
231 if ( (sect
.type() == ld::Section::typeTentativeDefs
) && makeTentativeDefsReal
)
232 return _s_DATA_common
;
236 uint32_t InternalState::FinalSection::segmentOrder(const ld::Section
& sect
, bool objFile
)
238 if ( strcmp(sect
.segmentName(), "__PAGEZERO") == 0 )
240 if ( strcmp(sect
.segmentName(), "__HEADER") == 0 ) // only used with -preload
242 if ( strcmp(sect
.segmentName(), "__TEXT") == 0 )
244 // in -r mode, want __DATA last so zerofill sections are at end
245 if ( strcmp(sect
.segmentName(), "__DATA") == 0 )
246 return (objFile
? 5 : 2);
247 if ( strcmp(sect
.segmentName(), "__OBJC") == 0 )
249 if ( strcmp(sect
.segmentName(), "__IMPORT") == 0 )
252 // layout non-standard segments in order seen (+10 to shift beyond standard segments)
253 for (uint32_t i
=0; i
< _s_segmentsSeen
.size(); ++i
) {
254 if ( strcmp(_s_segmentsSeen
[i
], sect
.segmentName()) == 0 )
257 _s_segmentsSeen
.push_back(sect
.segmentName());
258 return _s_segmentsSeen
.size()-1+10;
261 uint32_t InternalState::FinalSection::sectionOrder(const ld::Section
& sect
, uint32_t sectionsSeen
)
263 if ( sect
.type() == ld::Section::typeFirstSection
)
265 if ( sect
.type() == ld::Section::typeMachHeader
)
267 if ( sect
.type() == ld::Section::typeLastSection
)
269 if ( strcmp(sect
.segmentName(), "__TEXT") == 0 ) {
270 switch ( sect
.type() ) {
271 case ld::Section::typeCode
:
272 // <rdar://problem/8346444> make __text always be first "code" section
273 if ( strcmp(sect
.sectionName(), "__text") == 0 )
277 case ld::Section::typeStub
:
279 case ld::Section::typeStubHelper
:
281 case ld::Section::typeLSDA
:
283 case ld::Section::typeUnwindInfo
:
285 case ld::Section::typeCFI
:
287 case ld::Section::typeStubClose
:
290 return sectionsSeen
+20;
293 else if ( strcmp(sect
.segmentName(), "__DATA") == 0 ) {
294 switch ( sect
.type() ) {
295 case ld::Section::typeLazyPointerClose
:
297 case ld::Section::typeDyldInfo
:
299 case ld::Section::typeNonLazyPointer
:
301 case ld::Section::typeLazyPointer
:
303 case ld::Section::typeInitializerPointers
:
305 case ld::Section::typeTerminatorPointers
:
307 case ld::Section::typeTLVInitialValues
:
308 return INT_MAX
-4; // need TLV zero-fill to follow TLV init values
309 case ld::Section::typeTLVZeroFill
:
311 case ld::Section::typeZeroFill
:
312 // make sure __huge is always last zerofill section
313 if ( strcmp(sect
.sectionName(), "__huge") == 0 )
318 // <rdar://problem/7435296> Reorder sections to reduce page faults in object files
319 if ( strcmp(sect
.sectionName(), "__objc_classlist") == 0 )
321 else if ( strcmp(sect
.sectionName(), "__objc_nlclslist") == 0 )
323 else if ( strcmp(sect
.sectionName(), "__objc_catlist") == 0 )
325 else if ( strcmp(sect
.sectionName(), "__objc_protolist") == 0 )
327 else if ( strcmp(sect
.sectionName(), "__objc_imageinfo") == 0 )
329 else if ( strcmp(sect
.sectionName(), "__objc_const") == 0 )
331 else if ( strcmp(sect
.sectionName(), "__objc_selrefs") == 0 )
333 else if ( strcmp(sect
.sectionName(), "__objc_msgrefs") == 0 )
335 else if ( strcmp(sect
.sectionName(), "__objc_protorefs") == 0 )
337 else if ( strcmp(sect
.sectionName(), "__objc_classrefs") == 0 )
339 else if ( strcmp(sect
.sectionName(), "__objc_superrefs") == 0 )
341 else if ( strcmp(sect
.sectionName(), "__objc_data") == 0 )
344 return sectionsSeen
+40;
347 // make sure zerofill in any other section is at end of segment
348 if ( sect
.type() == ld::Section::typeZeroFill
)
350 return sectionsSeen
+20;
354 static void validateFixups(const ld::Atom
& atom
)
356 //fprintf(stderr, "validateFixups %s\n", atom.name());
357 bool lastWasClusterEnd
= true;
358 ld::Fixup::Cluster lastClusterSize
= ld::Fixup::k1of1
;
359 uint32_t curClusterOffsetInAtom
= 0;
360 for (ld::Fixup::iterator fit
=atom
.fixupsBegin(); fit
!= atom
.fixupsEnd(); ++fit
) {
361 //fprintf(stderr, " fixup offset=%d, cluster=%d\n", fit->offsetInAtom, fit->clusterSize);
362 assert((fit
->offsetInAtom
< atom
.size()) || (fit
->offsetInAtom
== 0));
363 if ( fit
->firstInCluster() ) {
364 assert(lastWasClusterEnd
);
365 curClusterOffsetInAtom
= fit
->offsetInAtom
;
366 lastWasClusterEnd
= (fit
->clusterSize
== ld::Fixup::k1of1
);
369 assert(!lastWasClusterEnd
);
370 assert(fit
->offsetInAtom
== curClusterOffsetInAtom
);
371 switch ((ld::Fixup::Cluster
)fit
->clusterSize
) {
372 case ld::Fixup::k1of1
:
373 case ld::Fixup::k1of2
:
374 case ld::Fixup::k1of3
:
375 case ld::Fixup::k1of4
:
376 case ld::Fixup::k1of5
:
377 lastWasClusterEnd
= false;
379 case ld::Fixup::k2of2
:
380 assert(lastClusterSize
= ld::Fixup::k1of2
);
381 lastWasClusterEnd
= true;
383 case ld::Fixup::k2of3
:
384 assert(lastClusterSize
= ld::Fixup::k1of3
);
385 lastWasClusterEnd
= false;
387 case ld::Fixup::k2of4
:
388 assert(lastClusterSize
= ld::Fixup::k1of4
);
389 lastWasClusterEnd
= false;
391 case ld::Fixup::k2of5
:
392 assert(lastClusterSize
= ld::Fixup::k1of5
);
393 lastWasClusterEnd
= false;
395 case ld::Fixup::k3of3
:
396 assert(lastClusterSize
= ld::Fixup::k2of3
);
397 lastWasClusterEnd
= true;
399 case ld::Fixup::k3of4
:
400 assert(lastClusterSize
= ld::Fixup::k2of4
);
401 lastWasClusterEnd
= false;
403 case ld::Fixup::k3of5
:
404 assert(lastClusterSize
= ld::Fixup::k2of5
);
405 lastWasClusterEnd
= false;
407 case ld::Fixup::k4of4
:
408 assert(lastClusterSize
= ld::Fixup::k3of4
);
409 lastWasClusterEnd
= true;
411 case ld::Fixup::k4of5
:
412 assert(lastClusterSize
= ld::Fixup::k3of5
);
413 lastWasClusterEnd
= false;
415 case ld::Fixup::k5of5
:
416 assert(lastClusterSize
= ld::Fixup::k4of5
);
417 lastWasClusterEnd
= true;
421 lastClusterSize
= fit
->clusterSize
;
422 if ( fit
->binding
== ld::Fixup::bindingDirectlyBound
) {
423 assert(fit
->u
.target
!= NULL
);
426 switch (lastClusterSize
) {
427 case ld::Fixup::k1of1
:
428 case ld::Fixup::k2of2
:
429 case ld::Fixup::k3of3
:
430 case ld::Fixup::k4of4
:
431 case ld::Fixup::k5of5
:
434 assert(0 && "last fixup was not end of cluster");
440 ld::Internal::FinalSection
* InternalState::addAtom(const ld::Atom
& atom
)
442 ld::Internal::FinalSection
* fs
= this->getFinalSection(atom
.section());
444 // <rdar://problem/8612550> When order file used on data, turn ordered zero fill symbols into zero data
445 switch ( atom
.section().type() ) {
446 case ld::Section::typeZeroFill
:
447 case ld::Section::typeTentativeDefs
:
448 if ( (atom
.symbolTableInclusion() == ld::Atom::symbolTableIn
)
449 && (atom
.size() <= 512) && (_options
.orderedSymbolsCount() != 0) ) {
450 for(Options::OrderedSymbolsIterator it
= _options
.orderedSymbolsBegin(); it
!= _options
.orderedSymbolsEnd(); ++it
) {
451 if ( (it
->objectFileName
== NULL
) && (strcmp(it
->symbolName
, atom
.name()) == 0) ) {
452 // found in order file, move to __data section
453 fs
= getFinalSection(InternalState::FinalSection::_s_DATA_data
);\
454 //fprintf(stderr, "moved %s to __data section\n", atom.name());
464 //fprintf(stderr, "InternalState::doAtom(%p), name=%s, sect=%s, finalsect=%p\n", &atom, atom.name(), atom.section().sectionName(), fs);
466 validateFixups(atom
);
468 fs
->atoms
.push_back(&atom
);
472 ld::Internal::FinalSection
* InternalState::getFinalSection(const ld::Section
& inputSection
)
474 const ld::Section
* baseForFinalSection
= &inputSection
;
476 // see if input section already has a FinalSection
477 SectionInToOut::iterator pos
= _sectionInToFinalMap
.find(&inputSection
);
478 if ( pos
!= _sectionInToFinalMap
.end() ) {
482 // otherwise, create a new final section
483 bool objFile
= false;
484 switch ( _options
.outputKind() ) {
485 case Options::kStaticExecutable
:
486 case Options::kDynamicExecutable
:
487 case Options::kDynamicLibrary
:
488 case Options::kDynamicBundle
:
490 case Options::kKextBundle
:
491 case Options::kPreload
:
493 // coalesce some sections
494 const ld::Section
& outSect
= FinalSection::outputSection(inputSection
, _options
.mergeZeroFill());
495 pos
= _sectionInToFinalMap
.find(&outSect
);
496 if ( pos
!= _sectionInToFinalMap
.end() ) {
497 _sectionInToFinalMap
[&inputSection
] = pos
->second
;
498 //fprintf(stderr, "_sectionInToFinalMap[%p] = %p\n", &inputSection, pos->second);
501 else if ( outSect
!= inputSection
) {
502 // new output section created, but not in map
503 baseForFinalSection
= &outSect
;
507 case Options::kObjectFile
:
508 baseForFinalSection
= &FinalSection::objectOutputSection(inputSection
, _options
.makeTentativeDefinitionsReal());
509 pos
= _sectionInToFinalMap
.find(baseForFinalSection
);
510 if ( pos
!= _sectionInToFinalMap
.end() ) {
511 _sectionInToFinalMap
[&inputSection
] = pos
->second
;
512 //fprintf(stderr, "_sectionInToFinalMap[%p] = %p\n", &inputSection, pos->second);
519 InternalState::FinalSection
* result
= new InternalState::FinalSection(*baseForFinalSection
,
520 _sectionInToFinalMap
.size(), objFile
);
521 _sectionInToFinalMap
[baseForFinalSection
] = result
;
522 //fprintf(stderr, "_sectionInToFinalMap[%p] = %p\n", baseForFinalSection, result);
523 sections
.push_back(result
);
528 int InternalState::FinalSection::sectionComparer(const void* l
, const void* r
)
530 const FinalSection
* left
= *(FinalSection
**)l
;
531 const FinalSection
* right
= *(FinalSection
**)r
;
532 if ( left
->_segmentOrder
!= right
->_segmentOrder
)
533 return (left
->_segmentOrder
- right
->_segmentOrder
);
534 return (left
->_sectionOrder
- right
->_sectionOrder
);
537 void InternalState::sortSections()
539 //fprintf(stderr, "UNSORTED final sections:\n");
540 //for (std::vector<ld::Internal::FinalSection*>::iterator it = sections.begin(); it != sections.end(); ++it) {
541 // fprintf(stderr, "final section %p %s/%s\n", (*it), (*it)->segmentName(), (*it)->sectionName());
543 qsort(§ions
[0], sections
.size(), sizeof(FinalSection
*), &InternalState::FinalSection::sectionComparer
);
544 //fprintf(stderr, "SORTED final sections:\n");
545 //for (std::vector<ld::Internal::FinalSection*>::iterator it = sections.begin(); it != sections.end(); ++it) {
546 // fprintf(stderr, "final section %p %s/%s\n", (*it), (*it)->segmentName(), (*it)->sectionName());
548 assert((sections
[0]->type() == ld::Section::typeMachHeader
)
549 || ((sections
[0]->type() == ld::Section::typeFirstSection
) && (sections
[1]->type() == ld::Section::typeMachHeader
))
550 || ((sections
[0]->type() == ld::Section::typePageZero
) && (sections
[1]->type() == ld::Section::typeMachHeader
))
551 || ((sections
[0]->type() == ld::Section::typePageZero
) && (sections
[1]->type() == ld::Section::typeFirstSection
) && (sections
[2]->type() == ld::Section::typeMachHeader
)) );
555 static void getVMInfo(vm_statistics_data_t
& info
)
557 mach_msg_type_number_t count
= sizeof(vm_statistics_data_t
) / sizeof(natural_t
);
558 kern_return_t error
= host_statistics(mach_host_self(), HOST_VM_INFO
,
559 (host_info_t
)&info
, &count
);
560 if (error
!= KERN_SUCCESS
) {
561 bzero(&info
, sizeof(vm_statistics_data_t
));
565 int main(int argc
, const char* argv
[])
570 const char* archName
= NULL
;
571 bool showArch
= false;
572 bool archInferred
= false;
574 vm_statistics_data_t vmStart
;
575 vm_statistics_data_t vmEnd
;
578 // create object to track command line arguments
579 Options
options(argc
, argv
);
582 if ( options
.printStatistics() )
585 // update strings for error messages
586 showArch
= options
.printArchPrefix();
587 archName
= options
.architectureName();
588 archInferred
= (options
.architecture() == 0);
590 // open and parse input files
591 ld::tool::InputFiles
inputFiles(options
, &archName
);
593 // load and resolve all references
594 InternalState
state(options
);
595 ld::tool::Resolver
resolver(options
, inputFiles
, state
);
599 inputFiles
.dylibs(state
);
601 // do initial section sorting so passes have rough idea of the layout
602 state
.sortSections();
605 ld::passes::objc::doPass(options
, state
);
606 ld::passes::stubs::doPass(options
, state
);
607 ld::passes::huge::doPass(options
, state
);
608 ld::passes::got::doPass(options
, state
);
609 ld::passes::tlvp::doPass(options
, state
);
610 ld::passes::dylibs::doPass(options
, state
); // must be after stubs and GOT passes
611 ld::passes::order_file::doPass(options
, state
);
612 ld::passes::branch_shim::doPass(options
, state
); // must be after stubs
613 ld::passes::branch_island::doPass(options
, state
); // must be after stubs and order_file pass
614 ld::passes::dtrace::doPass(options
, state
);
615 ld::passes::compact_unwind::doPass(options
, state
); // must be after order-file pass
617 // sort final sections
618 state
.sortSections();
621 ld::tool::OutputFile
out(options
);
625 //mach_o::relocatable::printCounts();
626 if ( options
.printStatistics() ) {
628 fprintf(stderr
, "pageins=%u, pageouts=%u, faults=%u\n", vmEnd
.pageins
-vmStart
.pageins
,
629 vmEnd
.pageouts
-vmStart
.pageouts
, vmEnd
.faults
-vmStart
.faults
);
633 catch (const char* msg
) {
635 fprintf(stderr
, "ld: %s for inferred architecture %s\n", msg
, archName
);
637 fprintf(stderr
, "ld: %s for architecture %s\n", msg
, archName
);
639 fprintf(stderr
, "ld: %s\n", msg
);
648 // implement assert() function to print out a backtrace before aborting
649 void __assert_rtn(const char* func
, const char* file
, int line
, const char* failedexpr
)
651 fprintf(stderr
, "Assertion failed: (%s), function %s, file %s, line %d.\n", failedexpr
, func
, file
, line
);
653 void* callStack
[128];
654 int depth
= ::backtrace(callStack
, 128);
655 char* buffer
= (char*)malloc(1024);
656 for(int i
=0; i
< depth
-1; ++i
) {
658 dladdr(callStack
[i
], &info
);
659 const char* symboName
= info
.dli_sname
;
660 if ( (symboName
!= NULL
) && (strncmp(symboName
, "_Z", 2) == 0) ) {
661 size_t bufLen
= 1024;
663 char* unmangled
= abi::__cxa_demangle(symboName
, buffer
, &bufLen
, &result
);
664 if ( unmangled
!= NULL
)
665 symboName
= unmangled
;
667 long offset
= (uintptr_t)callStack
[i
] - (uintptr_t)info
.dli_saddr
;
668 fprintf(stderr
, "%d %p %s + %ld\n", i
, callStack
[i
], symboName
, offset
);