1 //===-- sanitizer_symbolizer_posix_libcdep.cc -----------------------------===//
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
6 //===----------------------------------------------------------------------===//
8 // This file is shared between AddressSanitizer and ThreadSanitizer
10 // POSIX-specific implementation of symbolizer parts.
11 //===----------------------------------------------------------------------===//
13 #include "sanitizer_platform.h"
15 #include "sanitizer_allocator_internal.h"
16 #include "sanitizer_common.h"
17 #include "sanitizer_flags.h"
18 #include "sanitizer_internal_defs.h"
19 #include "sanitizer_linux.h"
20 #include "sanitizer_placement_new.h"
21 #include "sanitizer_procmaps.h"
22 #include "sanitizer_symbolizer.h"
23 #include "sanitizer_symbolizer_libbacktrace.h"
30 // C++ demangling function, as required by Itanium C++ ABI. This is weak,
31 // because we do not require a C++ ABI library to be linked to a program
32 // using sanitizers; if it's not present, we'll just use the mangled name.
33 namespace __cxxabiv1
{
34 extern "C" SANITIZER_WEAK_ATTRIBUTE
35 char *__cxa_demangle(const char *mangled
, char *buffer
,
36 size_t *length
, int *status
);
39 namespace __sanitizer
{
41 // Attempts to demangle the name via __cxa_demangle from __cxxabiv1.
42 static const char *DemangleCXXABI(const char *name
) {
43 // FIXME: __cxa_demangle aggressively insists on allocating memory.
44 // There's not much we can do about that, short of providing our
45 // own demangler (libc++abi's implementation could be adapted so that
46 // it does not allocate). For now, we just call it anyway, and we leak
47 // the returned value.
48 if (__cxxabiv1::__cxa_demangle
)
49 if (const char *demangled_name
=
50 __cxxabiv1::__cxa_demangle(name
, 0, 0, 0))
51 return demangled_name
;
56 // Extracts the prefix of "str" that consists of any characters not
57 // present in "delims" string, and copies this prefix to "result", allocating
59 // Returns a pointer to "str" after skipping extracted prefix and first
61 static const char *ExtractToken(const char *str
, const char *delims
,
63 uptr prefix_len
= internal_strcspn(str
, delims
);
64 *result
= (char*)InternalAlloc(prefix_len
+ 1);
65 internal_memcpy(*result
, str
, prefix_len
);
66 (*result
)[prefix_len
] = '\0';
67 const char *prefix_end
= str
+ prefix_len
;
68 if (*prefix_end
!= '\0') prefix_end
++;
72 // Same as ExtractToken, but converts extracted token to integer.
73 static const char *ExtractInt(const char *str
, const char *delims
,
76 const char *ret
= ExtractToken(str
, delims
, &buff
);
78 *result
= (int)internal_atoll(buff
);
84 static const char *ExtractUptr(const char *str
, const char *delims
,
87 const char *ret
= ExtractToken(str
, delims
, &buff
);
89 *result
= (uptr
)internal_atoll(buff
);
95 class ExternalSymbolizerInterface
{
97 // Can't declare pure virtual functions in sanitizer runtimes:
98 // __cxa_pure_virtual might be unavailable.
99 virtual char *SendCommand(bool is_data
, const char *module_name
,
100 uptr module_offset
) {
105 // SymbolizerProcess encapsulates communication between the tool and
106 // external symbolizer program, running in a different subprocess.
107 // SymbolizerProcess may not be used from two threads simultaneously.
108 class SymbolizerProcess
: public ExternalSymbolizerInterface
{
110 explicit SymbolizerProcess(const char *path
)
112 input_fd_(kInvalidFd
),
113 output_fd_(kInvalidFd
),
115 failed_to_start_(false),
116 reported_invalid_path_(false) {
118 CHECK_NE(path_
[0], '\0');
121 char *SendCommand(bool is_data
, const char *module_name
, uptr module_offset
) {
122 for (; times_restarted_
< kMaxTimesRestarted
; times_restarted_
++) {
123 // Start or restart symbolizer if we failed to send command to it.
124 if (char *res
= SendCommandImpl(is_data
, module_name
, module_offset
))
128 if (!failed_to_start_
) {
129 Report("WARNING: Failed to use and restart external symbolizer!\n");
130 failed_to_start_
= true;
137 if (input_fd_
!= kInvalidFd
)
138 internal_close(input_fd_
);
139 if (output_fd_
!= kInvalidFd
)
140 internal_close(output_fd_
);
141 return StartSymbolizerSubprocess();
144 char *SendCommandImpl(bool is_data
, const char *module_name
,
145 uptr module_offset
) {
146 if (input_fd_
== kInvalidFd
|| output_fd_
== kInvalidFd
)
149 if (!RenderInputCommand(buffer_
, kBufferSize
, is_data
, module_name
,
152 if (!writeToSymbolizer(buffer_
, internal_strlen(buffer_
)))
154 if (!readFromSymbolizer(buffer_
, kBufferSize
))
159 bool readFromSymbolizer(char *buffer
, uptr max_length
) {
164 uptr just_read
= internal_read(input_fd_
, buffer
+ read_len
,
165 max_length
- read_len
- 1);
166 // We can't read 0 bytes, as we don't expect external symbolizer to close
168 if (just_read
== 0 || just_read
== (uptr
)-1) {
169 Report("WARNING: Can't read from symbolizer at fd %d\n", input_fd_
);
172 read_len
+= just_read
;
173 if (ReachedEndOfOutput(buffer
, read_len
))
176 buffer
[read_len
] = '\0';
180 bool writeToSymbolizer(const char *buffer
, uptr length
) {
183 uptr write_len
= internal_write(output_fd_
, buffer
, length
);
184 if (write_len
== 0 || write_len
== (uptr
)-1) {
185 Report("WARNING: Can't write to symbolizer at fd %d\n", output_fd_
);
191 bool StartSymbolizerSubprocess() {
192 if (!FileExists(path_
)) {
193 if (!reported_invalid_path_
) {
194 Report("WARNING: invalid path to external symbolizer!\n");
195 reported_invalid_path_
= true;
202 // The client program may close its stdin and/or stdout and/or stderr
203 // thus allowing socketpair to reuse file descriptors 0, 1 or 2.
204 // In this case the communication between the forked processes may be
205 // broken if either the parent or the child tries to close or duplicate
206 // these descriptors. The loop below produces two pairs of file
207 // descriptors, each greater than 2 (stderr).
209 for (int i
= 0; i
< 5; i
++) {
210 if (pipe(sock_pair
[i
]) == -1) {
211 for (int j
= 0; j
< i
; j
++) {
212 internal_close(sock_pair
[j
][0]);
213 internal_close(sock_pair
[j
][1]);
215 Report("WARNING: Can't create a socket pair to start "
216 "external symbolizer (errno: %d)\n", errno
);
218 } else if (sock_pair
[i
][0] > 2 && sock_pair
[i
][1] > 2) {
222 outfd
= sock_pair
[i
];
223 for (int j
= 0; j
< i
; j
++) {
224 if (sock_pair
[j
] == infd
) continue;
225 internal_close(sock_pair
[j
][0]);
226 internal_close(sock_pair
[j
][1]);
235 // Real fork() may call user callbacks registered with pthread_atfork().
236 int pid
= internal_fork();
239 internal_close(infd
[0]);
240 internal_close(infd
[1]);
241 internal_close(outfd
[0]);
242 internal_close(outfd
[1]);
243 Report("WARNING: failed to fork external symbolizer "
244 " (errno: %d)\n", errno
);
246 } else if (pid
== 0) {
248 internal_close(STDOUT_FILENO
);
249 internal_close(STDIN_FILENO
);
250 internal_dup2(outfd
[0], STDIN_FILENO
);
251 internal_dup2(infd
[1], STDOUT_FILENO
);
252 internal_close(outfd
[0]);
253 internal_close(outfd
[1]);
254 internal_close(infd
[0]);
255 internal_close(infd
[1]);
256 for (int fd
= sysconf(_SC_OPEN_MAX
); fd
> 2; fd
--)
258 ExecuteWithDefaultArgs(path_
);
262 // Continue execution in parent process.
263 internal_close(outfd
[0]);
264 internal_close(infd
[1]);
266 output_fd_
= outfd
[1];
268 // Check that symbolizer subprocess started successfully.
270 SleepForMillis(kSymbolizerStartupTimeMillis
);
271 int exited_pid
= waitpid(pid
, &pid_status
, WNOHANG
);
272 if (exited_pid
!= 0) {
273 // Either waitpid failed, or child has already exited.
274 Report("WARNING: external symbolizer didn't start up correctly!\n");
281 virtual bool RenderInputCommand(char *buffer
, uptr max_length
, bool is_data
,
282 const char *module_name
,
283 uptr module_offset
) const {
287 virtual bool ReachedEndOfOutput(const char *buffer
, uptr length
) const {
291 virtual void ExecuteWithDefaultArgs(const char *path_to_binary
) const {
299 static const uptr kBufferSize
= 16 * 1024;
300 char buffer_
[kBufferSize
];
302 static const uptr kMaxTimesRestarted
= 5;
303 static const int kSymbolizerStartupTimeMillis
= 10;
304 uptr times_restarted_
;
305 bool failed_to_start_
;
306 bool reported_invalid_path_
;
309 // For now we assume the following protocol:
310 // For each request of the form
311 // <module_name> <module_offset>
312 // passed to STDIN, external symbolizer prints to STDOUT response:
314 // <file_name>:<line_number>:<column_number>
316 // <file_name>:<line_number>:<column_number>
319 class LLVMSymbolizerProcess
: public SymbolizerProcess
{
321 explicit LLVMSymbolizerProcess(const char *path
) : SymbolizerProcess(path
) {}
324 bool RenderInputCommand(char *buffer
, uptr max_length
, bool is_data
,
325 const char *module_name
, uptr module_offset
) const {
326 internal_snprintf(buffer
, max_length
, "%s\"%s\" 0x%zx\n",
327 is_data
? "DATA " : "", module_name
, module_offset
);
331 bool ReachedEndOfOutput(const char *buffer
, uptr length
) const {
332 // Empty line marks the end of llvm-symbolizer output.
333 return length
>= 2 && buffer
[length
- 1] == '\n' &&
334 buffer
[length
- 2] == '\n';
337 void ExecuteWithDefaultArgs(const char *path_to_binary
) const {
338 #if defined(__x86_64__)
339 const char* const kSymbolizerArch
= "--default-arch=x86_64";
340 #elif defined(__i386__)
341 const char* const kSymbolizerArch
= "--default-arch=i386";
342 #elif defined(__powerpc64__) && defined(__BIG_ENDIAN__)
343 const char* const kSymbolizerArch
= "--default-arch=powerpc64";
344 #elif defined(__powerpc64__) && defined(__LITTLE_ENDIAN__)
345 const char* const kSymbolizerArch
= "--default-arch=powerpc64le";
347 const char* const kSymbolizerArch
= "--default-arch=unknown";
350 const char *const inline_flag
= common_flags()->symbolize_inline_frames
352 : "--inlining=false";
353 execl(path_to_binary
, path_to_binary
, inline_flag
, kSymbolizerArch
,
358 class Addr2LineProcess
: public SymbolizerProcess
{
360 Addr2LineProcess(const char *path
, const char *module_name
)
361 : SymbolizerProcess(path
), module_name_(internal_strdup(module_name
)) {}
363 const char *module_name() const { return module_name_
; }
366 bool RenderInputCommand(char *buffer
, uptr max_length
, bool is_data
,
367 const char *module_name
, uptr module_offset
) const {
370 CHECK_EQ(0, internal_strcmp(module_name
, module_name_
));
371 internal_snprintf(buffer
, max_length
, "0x%zx\n", module_offset
);
375 bool ReachedEndOfOutput(const char *buffer
, uptr length
) const {
376 // Output should consist of two lines.
378 for (uptr i
= 0; i
< length
; ++i
) {
379 if (buffer
[i
] == '\n')
387 void ExecuteWithDefaultArgs(const char *path_to_binary
) const {
388 execl(path_to_binary
, path_to_binary
, "-Cfe", module_name_
, (char *)0);
391 const char *module_name_
; // Owned, leaked.
394 class Addr2LinePool
: public ExternalSymbolizerInterface
{
396 explicit Addr2LinePool(const char *addr2line_path
,
397 LowLevelAllocator
*allocator
)
398 : addr2line_path_(addr2line_path
), allocator_(allocator
),
399 addr2line_pool_(16) {}
401 char *SendCommand(bool is_data
, const char *module_name
, uptr module_offset
) {
404 Addr2LineProcess
*addr2line
= 0;
405 for (uptr i
= 0; i
< addr2line_pool_
.size(); ++i
) {
407 internal_strcmp(module_name
, addr2line_pool_
[i
]->module_name())) {
408 addr2line
= addr2line_pool_
[i
];
414 new(*allocator_
) Addr2LineProcess(addr2line_path_
, module_name
);
415 addr2line_pool_
.push_back(addr2line
);
417 return addr2line
->SendCommand(is_data
, module_name
, module_offset
);
421 const char *addr2line_path_
;
422 LowLevelAllocator
*allocator_
;
423 InternalMmapVector
<Addr2LineProcess
*> addr2line_pool_
;
426 #if SANITIZER_SUPPORTS_WEAK_HOOKS
428 SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
429 bool __sanitizer_symbolize_code(const char *ModuleName
, u64 ModuleOffset
,
430 char *Buffer
, int MaxLength
);
431 SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
432 bool __sanitizer_symbolize_data(const char *ModuleName
, u64 ModuleOffset
,
433 char *Buffer
, int MaxLength
);
434 SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
435 void __sanitizer_symbolize_flush();
436 SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
437 int __sanitizer_symbolize_demangle(const char *Name
, char *Buffer
,
441 class InternalSymbolizer
{
443 typedef bool (*SanitizerSymbolizeFn
)(const char*, u64
, char*, int);
445 static InternalSymbolizer
*get(LowLevelAllocator
*alloc
) {
446 if (__sanitizer_symbolize_code
!= 0 &&
447 __sanitizer_symbolize_data
!= 0) {
448 return new(*alloc
) InternalSymbolizer();
453 char *SendCommand(bool is_data
, const char *module_name
, uptr module_offset
) {
454 SanitizerSymbolizeFn symbolize_fn
= is_data
? __sanitizer_symbolize_data
455 : __sanitizer_symbolize_code
;
456 if (symbolize_fn(module_name
, module_offset
, buffer_
, kBufferSize
))
462 if (__sanitizer_symbolize_flush
)
463 __sanitizer_symbolize_flush();
466 const char *Demangle(const char *name
) {
467 if (__sanitizer_symbolize_demangle
) {
468 for (uptr res_length
= 1024;
469 res_length
<= InternalSizeClassMap::kMaxSize
;) {
470 char *res_buff
= static_cast<char*>(InternalAlloc(res_length
));
472 __sanitizer_symbolize_demangle(name
, res_buff
, res_length
);
473 if (req_length
> res_length
) {
474 res_length
= req_length
+ 1;
475 InternalFree(res_buff
);
485 InternalSymbolizer() { }
487 static const int kBufferSize
= 16 * 1024;
488 static const int kMaxDemangledNameSize
= 1024;
489 char buffer_
[kBufferSize
];
491 #else // SANITIZER_SUPPORTS_WEAK_HOOKS
493 class InternalSymbolizer
{
495 static InternalSymbolizer
*get(LowLevelAllocator
*alloc
) { return 0; }
496 char *SendCommand(bool is_data
, const char *module_name
, uptr module_offset
) {
500 const char *Demangle(const char *name
) { return name
; }
503 #endif // SANITIZER_SUPPORTS_WEAK_HOOKS
505 class POSIXSymbolizer
: public Symbolizer
{
507 POSIXSymbolizer(ExternalSymbolizerInterface
*external_symbolizer
,
508 InternalSymbolizer
*internal_symbolizer
,
509 LibbacktraceSymbolizer
*libbacktrace_symbolizer
)
511 external_symbolizer_(external_symbolizer
),
512 internal_symbolizer_(internal_symbolizer
),
513 libbacktrace_symbolizer_(libbacktrace_symbolizer
) {}
515 uptr
SymbolizePC(uptr addr
, AddressInfo
*frames
, uptr max_frames
) {
516 BlockingMutexLock
l(&mu_
);
519 const char *module_name
;
521 if (!FindModuleNameAndOffsetForAddress(addr
, &module_name
, &module_offset
))
523 // First, try to use libbacktrace symbolizer (if it's available).
524 if (libbacktrace_symbolizer_
!= 0) {
526 uptr res
= libbacktrace_symbolizer_
->SymbolizeCode(
527 addr
, frames
, max_frames
, module_name
, module_offset
);
531 const char *str
= SendCommand(false, module_name
, module_offset
);
533 // Symbolizer was not initialized or failed. Fill only data
534 // about module name and offset.
535 AddressInfo
*info
= &frames
[0];
537 info
->FillAddressAndModuleInfo(addr
, module_name
, module_offset
);
541 for (frame_id
= 0; frame_id
< max_frames
; frame_id
++) {
542 AddressInfo
*info
= &frames
[frame_id
];
543 char *function_name
= 0;
544 str
= ExtractToken(str
, "\n", &function_name
);
545 CHECK(function_name
);
546 if (function_name
[0] == '\0') {
547 // There are no more frames.
551 info
->FillAddressAndModuleInfo(addr
, module_name
, module_offset
);
552 info
->function
= function_name
;
553 // Parse <file>:<line>:<column> buffer.
554 char *file_line_info
= 0;
555 str
= ExtractToken(str
, "\n", &file_line_info
);
556 CHECK(file_line_info
);
557 const char *line_info
= ExtractToken(file_line_info
, ":", &info
->file
);
558 line_info
= ExtractInt(line_info
, ":", &info
->line
);
559 line_info
= ExtractInt(line_info
, "", &info
->column
);
560 InternalFree(file_line_info
);
562 // Functions and filenames can be "??", in which case we write 0
563 // to address info to mark that names are unknown.
564 if (0 == internal_strcmp(info
->function
, "??")) {
565 InternalFree(info
->function
);
568 if (0 == internal_strcmp(info
->file
, "??")) {
569 InternalFree(info
->file
);
574 // Make sure we return at least one frame.
575 AddressInfo
*info
= &frames
[0];
577 info
->FillAddressAndModuleInfo(addr
, module_name
, module_offset
);
583 bool SymbolizeData(uptr addr
, DataInfo
*info
) {
584 BlockingMutexLock
l(&mu_
);
585 LoadedModule
*module
= FindModuleForAddress(addr
);
588 const char *module_name
= module
->full_name();
589 uptr module_offset
= addr
- module
->base_address();
591 info
->module
= internal_strdup(module_name
);
592 info
->module_offset
= module_offset
;
593 // First, try to use libbacktrace symbolizer (if it's available).
594 if (libbacktrace_symbolizer_
!= 0) {
596 if (libbacktrace_symbolizer_
->SymbolizeData(addr
, info
))
599 const char *str
= SendCommand(true, module_name
, module_offset
);
602 str
= ExtractToken(str
, "\n", &info
->name
);
603 str
= ExtractUptr(str
, " ", &info
->start
);
604 str
= ExtractUptr(str
, "\n", &info
->size
);
605 info
->start
+= module
->base_address();
609 bool GetModuleNameAndOffsetForPC(uptr pc
, const char **module_name
,
610 uptr
*module_address
) {
611 BlockingMutexLock
l(&mu_
);
612 return FindModuleNameAndOffsetForAddress(pc
, module_name
, module_address
);
615 bool CanReturnFileLineInfo() {
616 return internal_symbolizer_
!= 0 || external_symbolizer_
!= 0 ||
617 libbacktrace_symbolizer_
!= 0;
621 BlockingMutexLock
l(&mu_
);
622 if (internal_symbolizer_
!= 0) {
623 SymbolizerScope
sym_scope(this);
624 internal_symbolizer_
->Flush();
628 const char *Demangle(const char *name
) {
629 BlockingMutexLock
l(&mu_
);
630 // Run hooks even if we don't use internal symbolizer, as cxxabi
631 // demangle may call system functions.
632 SymbolizerScope
sym_scope(this);
633 // Try to use libbacktrace demangler (if available).
634 if (libbacktrace_symbolizer_
!= 0) {
635 if (const char *demangled
= libbacktrace_symbolizer_
->Demangle(name
))
638 if (internal_symbolizer_
!= 0)
639 return internal_symbolizer_
->Demangle(name
);
640 return DemangleCXXABI(name
);
643 void PrepareForSandboxing() {
644 #if SANITIZER_LINUX && !SANITIZER_ANDROID
645 BlockingMutexLock
l(&mu_
);
646 // Cache /proc/self/exe on Linux.
652 char *SendCommand(bool is_data
, const char *module_name
, uptr module_offset
) {
654 // First, try to use internal symbolizer.
655 if (internal_symbolizer_
) {
656 SymbolizerScope
sym_scope(this);
657 return internal_symbolizer_
->SendCommand(is_data
, module_name
,
660 // Otherwise, fall back to external symbolizer.
661 if (external_symbolizer_
) {
662 SymbolizerScope
sym_scope(this);
663 return external_symbolizer_
->SendCommand(is_data
, module_name
,
669 LoadedModule
*FindModuleForAddress(uptr address
) {
671 bool modules_were_reloaded
= false;
672 if (modules_
== 0 || !modules_fresh_
) {
673 modules_
= (LoadedModule
*)(symbolizer_allocator_
.Allocate(
674 kMaxNumberOfModuleContexts
* sizeof(LoadedModule
)));
676 n_modules_
= GetListOfModules(modules_
, kMaxNumberOfModuleContexts
,
678 CHECK_GT(n_modules_
, 0);
679 CHECK_LT(n_modules_
, kMaxNumberOfModuleContexts
);
680 modules_fresh_
= true;
681 modules_were_reloaded
= true;
683 for (uptr i
= 0; i
< n_modules_
; i
++) {
684 if (modules_
[i
].containsAddress(address
)) {
688 // Reload the modules and look up again, if we haven't tried it yet.
689 if (!modules_were_reloaded
) {
690 // FIXME: set modules_fresh_ from dlopen()/dlclose() interceptors.
691 // It's too aggressive to reload the list of modules each time we fail
692 // to find a module for a given address.
693 modules_fresh_
= false;
694 return FindModuleForAddress(address
);
699 bool FindModuleNameAndOffsetForAddress(uptr address
, const char **module_name
,
700 uptr
*module_offset
) {
702 LoadedModule
*module
= FindModuleForAddress(address
);
705 *module_name
= module
->full_name();
706 *module_offset
= address
- module
->base_address();
710 // 16K loaded modules should be enough for everyone.
711 static const uptr kMaxNumberOfModuleContexts
= 1 << 14;
712 LoadedModule
*modules_
; // Array of module descriptions is leaked.
714 // If stale, need to reload the modules before looking up addresses.
718 ExternalSymbolizerInterface
*external_symbolizer_
; // Leaked.
719 InternalSymbolizer
*const internal_symbolizer_
; // Leaked.
720 LibbacktraceSymbolizer
*libbacktrace_symbolizer_
; // Leaked.
723 Symbolizer
*Symbolizer::PlatformInit() {
724 if (!common_flags()->symbolize
) {
725 return new(symbolizer_allocator_
) POSIXSymbolizer(0, 0, 0);
727 InternalSymbolizer
* internal_symbolizer
=
728 InternalSymbolizer::get(&symbolizer_allocator_
);
729 ExternalSymbolizerInterface
*external_symbolizer
= 0;
730 LibbacktraceSymbolizer
*libbacktrace_symbolizer
= 0;
732 if (!internal_symbolizer
) {
733 libbacktrace_symbolizer
=
734 LibbacktraceSymbolizer::get(&symbolizer_allocator_
);
735 if (!libbacktrace_symbolizer
) {
736 const char *path_to_external
= common_flags()->external_symbolizer_path
;
737 if (path_to_external
&& path_to_external
[0] == '\0') {
738 // External symbolizer is explicitly disabled. Do nothing.
740 // Find path to llvm-symbolizer if it's not provided.
741 if (!path_to_external
)
742 path_to_external
= FindPathToBinary("llvm-symbolizer");
743 if (path_to_external
) {
744 external_symbolizer
= new(symbolizer_allocator_
)
745 LLVMSymbolizerProcess(path_to_external
);
746 } else if (common_flags()->allow_addr2line
) {
747 // If llvm-symbolizer is not found, try to use addr2line.
748 if (const char *addr2line_path
= FindPathToBinary("addr2line")) {
749 external_symbolizer
= new(symbolizer_allocator_
)
750 Addr2LinePool(addr2line_path
, &symbolizer_allocator_
);
757 return new(symbolizer_allocator_
) POSIXSymbolizer(
758 external_symbolizer
, internal_symbolizer
, libbacktrace_symbolizer
);
761 } // namespace __sanitizer
763 #endif // SANITIZER_POSIX