]> git.proxmox.com Git - rustc.git/blobdiff - src/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc
Imported Upstream version 1.6.0+dfsg1
[rustc.git] / src / compiler-rt / lib / sanitizer_common / sanitizer_symbolizer_posix_libcdep.cc
index a87bfbf1ad89b4f60114218f66da6f5c1d62930a..a19b0a678629dbd4da5c9ffb6886d9ba805b7871 100644 (file)
 #include "sanitizer_internal_defs.h"
 #include "sanitizer_linux.h"
 #include "sanitizer_placement_new.h"
+#include "sanitizer_posix.h"
 #include "sanitizer_procmaps.h"
-#include "sanitizer_symbolizer.h"
+#include "sanitizer_symbolizer_internal.h"
 #include "sanitizer_symbolizer_libbacktrace.h"
+#include "sanitizer_symbolizer_mac.h"
 
 #include <errno.h>
 #include <stdlib.h>
 #include <sys/wait.h>
 #include <unistd.h>
 
+#if SANITIZER_MAC
+#include <util.h>  // for forkpty()
+#endif  // SANITIZER_MAC
+
 // C++ demangling function, as required by Itanium C++ ABI. This is weak,
 // because we do not require a C++ ABI library to be linked to a program
 // using sanitizers; if it's not present, we'll just use the mangled name.
@@ -41,7 +47,7 @@ namespace __cxxabiv1 {
 namespace __sanitizer {
 
 // Attempts to demangle the name via __cxa_demangle from __cxxabiv1.
-static const char *DemangleCXXABI(const char *name) {
+const char *DemangleCXXABI(const char *name) {
   // FIXME: __cxa_demangle aggressively insists on allocating memory.
   // There's not much we can do about that, short of providing our
   // own demangler (libc++abi's implementation could be adapted so that
@@ -55,150 +61,47 @@ static const char *DemangleCXXABI(const char *name) {
   return name;
 }
 
-// Extracts the prefix of "str" that consists of any characters not
-// present in "delims" string, and copies this prefix to "result", allocating
-// space for it.
-// Returns a pointer to "str" after skipping extracted prefix and first
-// delimiter char.
-static const char *ExtractToken(const char *str, const char *delims,
-                                char **result) {
-  uptr prefix_len = internal_strcspn(str, delims);
-  *result = (char*)InternalAlloc(prefix_len + 1);
-  internal_memcpy(*result, str, prefix_len);
-  (*result)[prefix_len] = '\0';
-  const char *prefix_end = str + prefix_len;
-  if (*prefix_end != '\0') prefix_end++;
-  return prefix_end;
-}
-
-// Same as ExtractToken, but converts extracted token to integer.
-static const char *ExtractInt(const char *str, const char *delims,
-                              int *result) {
-  char *buff;
-  const char *ret = ExtractToken(str, delims, &buff);
-  if (buff != 0) {
-    *result = (int)internal_atoll(buff);
-  }
-  InternalFree(buff);
-  return ret;
-}
-
-static const char *ExtractUptr(const char *str, const char *delims,
-                               uptr *result) {
-  char *buff;
-  const char *ret = ExtractToken(str, delims, &buff);
-  if (buff != 0) {
-    *result = (uptr)internal_atoll(buff);
-  }
-  InternalFree(buff);
-  return ret;
-}
-
-class ExternalSymbolizerInterface {
- public:
-  // Can't declare pure virtual functions in sanitizer runtimes:
-  // __cxa_pure_virtual might be unavailable.
-  virtual char *SendCommand(bool is_data, const char *module_name,
-                            uptr module_offset) {
-    UNIMPLEMENTED();
-  }
-};
-
-// SymbolizerProcess encapsulates communication between the tool and
-// external symbolizer program, running in a different subprocess.
-// SymbolizerProcess may not be used from two threads simultaneously.
-class SymbolizerProcess : public ExternalSymbolizerInterface {
- public:
-  explicit SymbolizerProcess(const char *path)
-      : path_(path),
-        input_fd_(kInvalidFd),
-        output_fd_(kInvalidFd),
-        times_restarted_(0),
-        failed_to_start_(false),
-        reported_invalid_path_(false) {
-    CHECK(path_);
-    CHECK_NE(path_[0], '\0');
-  }
-
-  char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {
-    for (; times_restarted_ < kMaxTimesRestarted; times_restarted_++) {
-      // Start or restart symbolizer if we failed to send command to it.
-      if (char *res = SendCommandImpl(is_data, module_name, module_offset))
-        return res;
-      Restart();
-    }
-    if (!failed_to_start_) {
-      Report("WARNING: Failed to use and restart external symbolizer!\n");
-      failed_to_start_ = true;
-    }
-    return 0;
-  }
-
- private:
-  bool Restart() {
-    if (input_fd_ != kInvalidFd)
-      internal_close(input_fd_);
-    if (output_fd_ != kInvalidFd)
-      internal_close(output_fd_);
-    return StartSymbolizerSubprocess();
-  }
-
-  char *SendCommandImpl(bool is_data, const char *module_name,
-                        uptr module_offset) {
-    if (input_fd_ == kInvalidFd || output_fd_ == kInvalidFd)
-      return 0;
-    CHECK(module_name);
-    if (!RenderInputCommand(buffer_, kBufferSize, is_data, module_name,
-                            module_offset))
-      return 0;
-    if (!writeToSymbolizer(buffer_, internal_strlen(buffer_)))
-      return 0;
-    if (!readFromSymbolizer(buffer_, kBufferSize))
-      return 0;
-    return buffer_;
-  }
-
-  bool readFromSymbolizer(char *buffer, uptr max_length) {
-    if (max_length == 0)
-      return true;
-    uptr read_len = 0;
-    while (true) {
-      uptr just_read = internal_read(input_fd_, buffer + read_len,
-                                     max_length - read_len - 1);
-      // We can't read 0 bytes, as we don't expect external symbolizer to close
-      // its stdout.
-      if (just_read == 0 || just_read == (uptr)-1) {
-        Report("WARNING: Can't read from symbolizer at fd %d\n", input_fd_);
-        return false;
-      }
-      read_len += just_read;
-      if (ReachedEndOfOutput(buffer, read_len))
-        break;
-    }
-    buffer[read_len] = '\0';
-    return true;
-  }
-
-  bool writeToSymbolizer(const char *buffer, uptr length) {
-    if (length == 0)
-      return true;
-    uptr write_len = internal_write(output_fd_, buffer, length);
-    if (write_len == 0 || write_len == (uptr)-1) {
-      Report("WARNING: Can't write to symbolizer at fd %d\n", output_fd_);
-      return false;
+bool SymbolizerProcess::StartSymbolizerSubprocess() {
+  if (!FileExists(path_)) {
+    if (!reported_invalid_path_) {
+      Report("WARNING: invalid path to external symbolizer!\n");
+      reported_invalid_path_ = true;
     }
-    return true;
+    return false;
   }
 
-  bool StartSymbolizerSubprocess() {
-    if (!FileExists(path_)) {
-      if (!reported_invalid_path_) {
-        Report("WARNING: invalid path to external symbolizer!\n");
-        reported_invalid_path_ = true;
-      }
+  int pid;
+  if (use_forkpty_) {
+#if SANITIZER_MAC
+    fd_t fd = kInvalidFd;
+    // Use forkpty to disable buffering in the new terminal.
+    pid = forkpty(&fd, 0, 0, 0);
+    if (pid == -1) {
+      // forkpty() failed.
+      Report("WARNING: failed to fork external symbolizer (errno: %d)\n",
+             errno);
       return false;
+    } else if (pid == 0) {
+      // Child subprocess.
+      const char *argv[kArgVMax];
+      GetArgV(path_, argv);
+      execv(path_, const_cast<char **>(&argv[0]));
+      internal__exit(1);
     }
 
+    // Continue execution in parent process.
+    input_fd_ = output_fd_ = fd;
+
+    // Disable echo in the new terminal, disable CR.
+    struct termios termflags;
+    tcgetattr(fd, &termflags);
+    termflags.c_oflag &= ~ONLCR;
+    termflags.c_lflag &= ~ECHO;
+    tcsetattr(fd, TCSANOW, &termflags);
+#else  // SANITIZER_MAC
+    UNIMPLEMENTED();
+#endif  // SANITIZER_MAC
+  } else {
     int *infd = NULL;
     int *outfd = NULL;
     // The client program may close its stdin and/or stdout and/or stderr
@@ -234,7 +137,8 @@ class SymbolizerProcess : public ExternalSymbolizerInterface {
     CHECK(infd);
     CHECK(outfd);
 
-    int pid = fork();
+    // Real fork() may call user callbacks registered with pthread_atfork().
+    pid = internal_fork();
     if (pid == -1) {
       // Fork() failed.
       internal_close(infd[0]);
@@ -254,9 +158,11 @@ class SymbolizerProcess : public ExternalSymbolizerInterface {
       internal_close(outfd[1]);
       internal_close(infd[0]);
       internal_close(infd[1]);
-      for (int fd = getdtablesize(); fd > 2; fd--)
+      for (int fd = sysconf(_SC_OPEN_MAX); fd > 2; fd--)
         internal_close(fd);
-      ExecuteWithDefaultArgs(path_);
+      const char *argv[kArgVMax];
+      GetArgV(path_, argv);
+      execv(path_, const_cast<char **>(&argv[0]));
       internal__exit(1);
     }
 
@@ -265,89 +171,20 @@ class SymbolizerProcess : public ExternalSymbolizerInterface {
     internal_close(infd[1]);
     input_fd_ = infd[0];
     output_fd_ = outfd[1];
-
-    // Check that symbolizer subprocess started successfully.
-    int pid_status;
-    SleepForMillis(kSymbolizerStartupTimeMillis);
-    int exited_pid = waitpid(pid, &pid_status, WNOHANG);
-    if (exited_pid != 0) {
-      // Either waitpid failed, or child has already exited.
-      Report("WARNING: external symbolizer didn't start up correctly!\n");
-      return false;
-    }
-
-    return true;
   }
 
-  virtual bool RenderInputCommand(char *buffer, uptr max_length, bool is_data,
-                                  const char *module_name,
-                                  uptr module_offset) const {
-    UNIMPLEMENTED();
-  }
-
-  virtual bool ReachedEndOfOutput(const char *buffer, uptr length) const {
-    UNIMPLEMENTED();
-  }
-
-  virtual void ExecuteWithDefaultArgs(const char *path_to_binary) const {
-    UNIMPLEMENTED();
-  }
-
-  const char *path_;
-  int input_fd_;
-  int output_fd_;
-
-  static const uptr kBufferSize = 16 * 1024;
-  char buffer_[kBufferSize];
-
-  static const uptr kMaxTimesRestarted = 5;
-  static const int kSymbolizerStartupTimeMillis = 10;
-  uptr times_restarted_;
-  bool failed_to_start_;
-  bool reported_invalid_path_;
-};
-
-// For now we assume the following protocol:
-// For each request of the form
-//   <module_name> <module_offset>
-// passed to STDIN, external symbolizer prints to STDOUT response:
-//   <function_name>
-//   <file_name>:<line_number>:<column_number>
-//   <function_name>
-//   <file_name>:<line_number>:<column_number>
-//   ...
-//   <empty line>
-class LLVMSymbolizerProcess : public SymbolizerProcess {
- public:
-  explicit LLVMSymbolizerProcess(const char *path) : SymbolizerProcess(path) {}
-
- private:
-  bool RenderInputCommand(char *buffer, uptr max_length, bool is_data,
-                          const char *module_name, uptr module_offset) const {
-    internal_snprintf(buffer, max_length, "%s\"%s\" 0x%zx\n",
-                      is_data ? "DATA " : "", module_name, module_offset);
-    return true;
-  }
-
-  bool ReachedEndOfOutput(const char *buffer, uptr length) const {
-    // Empty line marks the end of llvm-symbolizer output.
-    return length >= 2 && buffer[length - 1] == '\n' &&
-           buffer[length - 2] == '\n';
+  // Check that symbolizer subprocess started successfully.
+  int pid_status;
+  SleepForMillis(kSymbolizerStartupTimeMillis);
+  int exited_pid = waitpid(pid, &pid_status, WNOHANG);
+  if (exited_pid != 0) {
+    // Either waitpid failed, or child has already exited.
+    Report("WARNING: external symbolizer didn't start up correctly!\n");
+    return false;
   }
 
-  void ExecuteWithDefaultArgs(const char *path_to_binary) const {
-#if defined(__x86_64__)
-    const char* const kSymbolizerArch = "--default-arch=x86_64";
-#elif defined(__i386__)
-    const char* const kSymbolizerArch = "--default-arch=i386";
-#elif defined(__powerpc64__)
-    const char* const kSymbolizerArch = "--default-arch=powerpc64";
-#else
-    const char* const kSymbolizerArch = "--default-arch=unknown";
-#endif
-    execl(path_to_binary, path_to_binary, kSymbolizerArch, (char *)0);
-  }
-};
+  return true;
+}
 
 class Addr2LineProcess : public SymbolizerProcess {
  public:
@@ -357,44 +194,76 @@ class Addr2LineProcess : public SymbolizerProcess {
   const char *module_name() const { return module_name_; }
 
  private:
-  bool RenderInputCommand(char *buffer, uptr max_length, bool is_data,
-                          const char *module_name, uptr module_offset) const {
-    if (is_data)
-      return false;
-    CHECK_EQ(0, internal_strcmp(module_name, module_name_));
-    internal_snprintf(buffer, max_length, "0x%zx\n", module_offset);
-    return true;
+  void GetArgV(const char *path_to_binary,
+               const char *(&argv)[kArgVMax]) const override {
+    int i = 0;
+    argv[i++] = path_to_binary;
+    argv[i++] = "-iCfe";
+    argv[i++] = module_name_;
+    argv[i++] = nullptr;
   }
 
-  bool ReachedEndOfOutput(const char *buffer, uptr length) const {
-    // Output should consist of two lines.
-    int num_lines = 0;
-    for (uptr i = 0; i < length; ++i) {
-      if (buffer[i] == '\n')
-        num_lines++;
-      if (num_lines >= 2)
-        return true;
-    }
-    return false;
-  }
+  bool ReachedEndOfOutput(const char *buffer, uptr length) const override;
 
-  void ExecuteWithDefaultArgs(const char *path_to_binary) const {
-    execl(path_to_binary, path_to_binary, "-Cfe", module_name_, (char *)0);
+  bool ReadFromSymbolizer(char *buffer, uptr max_length) override {
+    if (!SymbolizerProcess::ReadFromSymbolizer(buffer, max_length))
+      return false;
+    // We should cut out output_terminator_ at the end of given buffer,
+    // appended by addr2line to mark the end of its meaningful output.
+    // We cannot scan buffer from it's beginning, because it is legal for it
+    // to start with output_terminator_ in case given offset is invalid. So,
+    // scanning from second character.
+    char *garbage = internal_strstr(buffer + 1, output_terminator_);
+    // This should never be NULL since buffer must end up with
+    // output_terminator_.
+    CHECK(garbage);
+    // Trim the buffer.
+    garbage[0] = '\0';
+    return true;
   }
 
   const char *module_name_;  // Owned, leaked.
+  static const char output_terminator_[];
 };
 
-class Addr2LinePool : public ExternalSymbolizerInterface {
+const char Addr2LineProcess::output_terminator_[] = "??\n??:0\n";
+
+bool Addr2LineProcess::ReachedEndOfOutput(const char *buffer,
+                                          uptr length) const {
+  const size_t kTerminatorLen = sizeof(output_terminator_) - 1;
+  // Skip, if we read just kTerminatorLen bytes, because Addr2Line output
+  // should consist at least of two pairs of lines:
+  // 1. First one, corresponding to given offset to be symbolized
+  // (may be equal to output_terminator_, if offset is not valid).
+  // 2. Second one for output_terminator_, itself to mark the end of output.
+  if (length <= kTerminatorLen) return false;
+  // Addr2Line output should end up with output_terminator_.
+  return !internal_memcmp(buffer + length - kTerminatorLen,
+                          output_terminator_, kTerminatorLen);
+}
+
+class Addr2LinePool : public SymbolizerTool {
  public:
   explicit Addr2LinePool(const char *addr2line_path,
                          LowLevelAllocator *allocator)
       : addr2line_path_(addr2line_path), allocator_(allocator),
         addr2line_pool_(16) {}
 
-  char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {
-    if (is_data)
-      return 0;
+  bool SymbolizePC(uptr addr, SymbolizedStack *stack) override {
+    if (const char *buf =
+            SendCommand(stack->info.module, stack->info.module_offset)) {
+      ParseSymbolizePCOutput(buf, stack);
+      return true;
+    }
+    return false;
+  }
+
+  bool SymbolizeData(uptr addr, DataInfo *info) override {
+    return false;
+  }
+
+ private:
+  const char *SendCommand(const char *module_name, uptr module_offset) {
     Addr2LineProcess *addr2line = 0;
     for (uptr i = 0; i < addr2line_pool_.size(); ++i) {
       if (0 ==
@@ -408,13 +277,19 @@ class Addr2LinePool : public ExternalSymbolizerInterface {
           new(*allocator_) Addr2LineProcess(addr2line_path_, module_name);
       addr2line_pool_.push_back(addr2line);
     }
-    return addr2line->SendCommand(is_data, module_name, module_offset);
+    CHECK_EQ(0, internal_strcmp(module_name, addr2line->module_name()));
+    char buffer[kBufferSize];
+    internal_snprintf(buffer, kBufferSize, "0x%zx\n0x%zx\n",
+                      module_offset, dummy_address_);
+    return addr2line->SendCommand(buffer);
   }
 
- private:
+  static const uptr kBufferSize = 64;
   const char *addr2line_path_;
   LowLevelAllocator *allocator_;
   InternalMmapVector<Addr2LineProcess*> addr2line_pool_;
+  static const uptr dummy_address_ =
+      FIRST_32_SECOND_64(UINT32_MAX, UINT64_MAX);
 };
 
 #if SANITIZER_SUPPORTS_WEAK_HOOKS
@@ -432,10 +307,8 @@ int __sanitizer_symbolize_demangle(const char *Name, char *Buffer,
                                    int MaxLength);
 }  // extern "C"
 
-class InternalSymbolizer {
+class InternalSymbolizer : public SymbolizerTool {
  public:
-  typedef bool (*SanitizerSymbolizeFn)(const char*, u64, char*, int);
-
   static InternalSymbolizer *get(LowLevelAllocator *alloc) {
     if (__sanitizer_symbolize_code != 0 &&
         __sanitizer_symbolize_data != 0) {
@@ -444,20 +317,29 @@ class InternalSymbolizer {
     return 0;
   }
 
-  char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {
-    SanitizerSymbolizeFn symbolize_fn = is_data ? __sanitizer_symbolize_data
-                                                : __sanitizer_symbolize_code;
-    if (symbolize_fn(module_name, module_offset, buffer_, kBufferSize))
-      return buffer_;
-    return 0;
+  bool SymbolizePC(uptr addr, SymbolizedStack *stack) override {
+    bool result = __sanitizer_symbolize_code(
+        stack->info.module, stack->info.module_offset, buffer_, kBufferSize);
+    if (result) ParseSymbolizePCOutput(buffer_, stack);
+    return result;
   }
 
-  void Flush() {
+  bool SymbolizeData(uptr addr, DataInfo *info) override {
+    bool result = __sanitizer_symbolize_data(info->module, info->module_offset,
+                                             buffer_, kBufferSize);
+    if (result) {
+      ParseSymbolizeDataOutput(buffer_, info);
+      info->start += (addr - info->module_offset);  // Add the base address.
+    }
+    return result;
+  }
+
+  void Flush() override {
     if (__sanitizer_symbolize_flush)
       __sanitizer_symbolize_flush();
   }
 
-  const char *Demangle(const char *name) {
+  const char *Demangle(const char *name) override {
     if (__sanitizer_symbolize_demangle) {
       for (uptr res_length = 1024;
            res_length <= InternalSizeClassMap::kMaxSize;) {
@@ -484,272 +366,103 @@ class InternalSymbolizer {
 };
 #else  // SANITIZER_SUPPORTS_WEAK_HOOKS
 
-class InternalSymbolizer {
+class InternalSymbolizer : public SymbolizerTool {
  public:
   static InternalSymbolizer *get(LowLevelAllocator *alloc) { return 0; }
-  char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {
-    return 0;
-  }
-  void Flush() { }
-  const char *Demangle(const char *name) { return name; }
 };
 
 #endif  // SANITIZER_SUPPORTS_WEAK_HOOKS
 
-class POSIXSymbolizer : public Symbolizer {
- public:
-  POSIXSymbolizer(ExternalSymbolizerInterface *external_symbolizer,
-                  InternalSymbolizer *internal_symbolizer,
-                  LibbacktraceSymbolizer *libbacktrace_symbolizer)
-      : Symbolizer(),
-        external_symbolizer_(external_symbolizer),
-        internal_symbolizer_(internal_symbolizer),
-        libbacktrace_symbolizer_(libbacktrace_symbolizer) {}
-
-  uptr SymbolizePC(uptr addr, AddressInfo *frames, uptr max_frames) {
-    BlockingMutexLock l(&mu_);
-    if (max_frames == 0)
-      return 0;
-    const char *module_name;
-    uptr module_offset;
-    if (!FindModuleNameAndOffsetForAddress(addr, &module_name, &module_offset))
-      return 0;
-    // First, try to use libbacktrace symbolizer (if it's available).
-    if (libbacktrace_symbolizer_ != 0) {
-      mu_.CheckLocked();
-      uptr res = libbacktrace_symbolizer_->SymbolizeCode(
-          addr, frames, max_frames, module_name, module_offset);
-      if (res > 0)
-        return res;
-    }
-    const char *str = SendCommand(false, module_name, module_offset);
-    if (str == 0) {
-      // Symbolizer was not initialized or failed. Fill only data
-      // about module name and offset.
-      AddressInfo *info = &frames[0];
-      info->Clear();
-      info->FillAddressAndModuleInfo(addr, module_name, module_offset);
-      return 1;
-    }
-    uptr frame_id = 0;
-    for (frame_id = 0; frame_id < max_frames; frame_id++) {
-      AddressInfo *info = &frames[frame_id];
-      char *function_name = 0;
-      str = ExtractToken(str, "\n", &function_name);
-      CHECK(function_name);
-      if (function_name[0] == '\0') {
-        // There are no more frames.
-        break;
-      }
-      info->Clear();
-      info->FillAddressAndModuleInfo(addr, module_name, module_offset);
-      info->function = function_name;
-      // Parse <file>:<line>:<column> buffer.
-      char *file_line_info = 0;
-      str = ExtractToken(str, "\n", &file_line_info);
-      CHECK(file_line_info);
-      const char *line_info = ExtractToken(file_line_info, ":", &info->file);
-      line_info = ExtractInt(line_info, ":", &info->line);
-      line_info = ExtractInt(line_info, "", &info->column);
-      InternalFree(file_line_info);
-
-      // Functions and filenames can be "??", in which case we write 0
-      // to address info to mark that names are unknown.
-      if (0 == internal_strcmp(info->function, "??")) {
-        InternalFree(info->function);
-        info->function = 0;
-      }
-      if (0 == internal_strcmp(info->file, "??")) {
-        InternalFree(info->file);
-        info->file = 0;
-      }
-    }
-    if (frame_id == 0) {
-      // Make sure we return at least one frame.
-      AddressInfo *info = &frames[0];
-      info->Clear();
-      info->FillAddressAndModuleInfo(addr, module_name, module_offset);
-      frame_id = 1;
-    }
-    return frame_id;
-  }
-
-  bool SymbolizeData(uptr addr, DataInfo *info) {
-    BlockingMutexLock l(&mu_);
-    LoadedModule *module = FindModuleForAddress(addr);
-    if (module == 0)
-      return false;
-    const char *module_name = module->full_name();
-    uptr module_offset = addr - module->base_address();
-    internal_memset(info, 0, sizeof(*info));
-    info->address = addr;
-    info->module = internal_strdup(module_name);
-    info->module_offset = module_offset;
-    // First, try to use libbacktrace symbolizer (if it's available).
-    if (libbacktrace_symbolizer_ != 0) {
-      mu_.CheckLocked();
-      if (libbacktrace_symbolizer_->SymbolizeData(info))
-        return true;
-    }
-    const char *str = SendCommand(true, module_name, module_offset);
-    if (str == 0)
-      return true;
-    str = ExtractToken(str, "\n", &info->name);
-    str = ExtractUptr(str, " ", &info->start);
-    str = ExtractUptr(str, "\n", &info->size);
-    info->start += module->base_address();
-    return true;
-  }
-
-  bool GetModuleNameAndOffsetForPC(uptr pc, const char **module_name,
-                                   uptr *module_address) {
-    BlockingMutexLock l(&mu_);
-    return FindModuleNameAndOffsetForAddress(pc, module_name, module_address);
-  }
-
-  bool CanReturnFileLineInfo() {
-    return internal_symbolizer_ != 0 || external_symbolizer_ != 0 ||
-           libbacktrace_symbolizer_ != 0;
-  }
+const char *Symbolizer::PlatformDemangle(const char *name) {
+  return DemangleCXXABI(name);
+}
 
-  void Flush() {
-    BlockingMutexLock l(&mu_);
-    if (internal_symbolizer_ != 0) {
-      SymbolizerScope sym_scope(this);
-      internal_symbolizer_->Flush();
+void Symbolizer::PlatformPrepareForSandboxing() {}
+
+static SymbolizerTool *ChooseExternalSymbolizer(LowLevelAllocator *allocator) {
+  const char *path = common_flags()->external_symbolizer_path;
+  const char *binary_name = path ? StripModuleName(path) : "";
+  if (path && path[0] == '\0') {
+    VReport(2, "External symbolizer is explicitly disabled.\n");
+    return nullptr;
+  } else if (!internal_strcmp(binary_name, "llvm-symbolizer")) {
+    VReport(2, "Using llvm-symbolizer at user-specified path: %s\n", path);
+    return new(*allocator) LLVMSymbolizer(path, allocator);
+  } else if (!internal_strcmp(binary_name, "atos")) {
+#if SANITIZER_MAC
+    VReport(2, "Using atos at user-specified path: %s\n", path);
+    return new(*allocator) AtosSymbolizer(path, allocator);
+#else  // SANITIZER_MAC
+    Report("ERROR: Using `atos` is only supported on Darwin.\n");
+    Die();
+#endif  // SANITIZER_MAC
+  } else if (!internal_strcmp(binary_name, "addr2line")) {
+    VReport(2, "Using addr2line at user-specified path: %s\n", path);
+    return new(*allocator) Addr2LinePool(path, allocator);
+  } else if (path) {
+    Report("ERROR: External symbolizer path is set to '%s' which isn't "
+           "a known symbolizer. Please set the path to the llvm-symbolizer "
+           "binary or other known tool.\n", path);
+    Die();
+  }
+
+  // Otherwise symbolizer program is unknown, let's search $PATH
+  CHECK(path == nullptr);
+  if (const char *found_path = FindPathToBinary("llvm-symbolizer")) {
+    VReport(2, "Using llvm-symbolizer found at: %s\n", found_path);
+    return new(*allocator) LLVMSymbolizer(found_path, allocator);
+  }
+#if SANITIZER_MAC
+  if (const char *found_path = FindPathToBinary("atos")) {
+    VReport(2, "Using atos found at: %s\n", found_path);
+    return new(*allocator) AtosSymbolizer(found_path, allocator);
+  }
+#endif  // SANITIZER_MAC
+  if (common_flags()->allow_addr2line) {
+    if (const char *found_path = FindPathToBinary("addr2line")) {
+      VReport(2, "Using addr2line found at: %s\n", found_path);
+      return new(*allocator) Addr2LinePool(found_path, allocator);
     }
   }
+  return nullptr;
+}
 
-  const char *Demangle(const char *name) {
-    BlockingMutexLock l(&mu_);
-    // Run hooks even if we don't use internal symbolizer, as cxxabi
-    // demangle may call system functions.
-    SymbolizerScope sym_scope(this);
-    // Try to use libbacktrace demangler (if available).
-    if (libbacktrace_symbolizer_ != 0) {
-      if (const char *demangled = libbacktrace_symbolizer_->Demangle(name))
-        return demangled;
-    }
-    if (internal_symbolizer_ != 0)
-      return internal_symbolizer_->Demangle(name);
-    return DemangleCXXABI(name);
+static void ChooseSymbolizerTools(IntrusiveList<SymbolizerTool> *list,
+                                  LowLevelAllocator *allocator) {
+  if (!common_flags()->symbolize) {
+    VReport(2, "Symbolizer is disabled.\n");
+    return;
   }
-
-  void PrepareForSandboxing() {
-#if SANITIZER_LINUX && !SANITIZER_ANDROID
-    BlockingMutexLock l(&mu_);
-    // Cache /proc/self/exe on Linux.
-    CacheBinaryName();
-#endif
+  if (SymbolizerTool *tool = InternalSymbolizer::get(allocator)) {
+    VReport(2, "Using internal symbolizer.\n");
+    list->push_back(tool);
+    return;
   }
-
- private:
-  char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {
-    mu_.CheckLocked();
-    // First, try to use internal symbolizer.
-    if (internal_symbolizer_) {
-      SymbolizerScope sym_scope(this);
-      return internal_symbolizer_->SendCommand(is_data, module_name,
-                                               module_offset);
-    }
-    // Otherwise, fall back to external symbolizer.
-    if (external_symbolizer_) {
-      SymbolizerScope sym_scope(this);
-      return external_symbolizer_->SendCommand(is_data, module_name,
-                                               module_offset);
-    }
-    return 0;
+  if (SymbolizerTool *tool = LibbacktraceSymbolizer::get(allocator)) {
+    VReport(2, "Using libbacktrace symbolizer.\n");
+    list->push_back(tool);
+    return;
   }
 
-  LoadedModule *FindModuleForAddress(uptr address) {
-    mu_.CheckLocked();
-    bool modules_were_reloaded = false;
-    if (modules_ == 0 || !modules_fresh_) {
-      modules_ = (LoadedModule*)(symbolizer_allocator_.Allocate(
-          kMaxNumberOfModuleContexts * sizeof(LoadedModule)));
-      CHECK(modules_);
-      n_modules_ = GetListOfModules(modules_, kMaxNumberOfModuleContexts,
-                                    /* filter */ 0);
-      CHECK_GT(n_modules_, 0);
-      CHECK_LT(n_modules_, kMaxNumberOfModuleContexts);
-      modules_fresh_ = true;
-      modules_were_reloaded = true;
-    }
-    for (uptr i = 0; i < n_modules_; i++) {
-      if (modules_[i].containsAddress(address)) {
-        return &modules_[i];
-      }
-    }
-    // Reload the modules and look up again, if we haven't tried it yet.
-    if (!modules_were_reloaded) {
-      // FIXME: set modules_fresh_ from dlopen()/dlclose() interceptors.
-      // It's too aggressive to reload the list of modules each time we fail
-      // to find a module for a given address.
-      modules_fresh_ = false;
-      return FindModuleForAddress(address);
-    }
-    return 0;
-  }
-
-  bool FindModuleNameAndOffsetForAddress(uptr address, const char **module_name,
-                                         uptr *module_offset) {
-    mu_.CheckLocked();
-    LoadedModule *module = FindModuleForAddress(address);
-    if (module == 0)
-      return false;
-    *module_name = module->full_name();
-    *module_offset = address - module->base_address();
-    return true;
+  if (SymbolizerTool *tool = ChooseExternalSymbolizer(allocator)) {
+    list->push_back(tool);
   }
 
-  // 16K loaded modules should be enough for everyone.
-  static const uptr kMaxNumberOfModuleContexts = 1 << 14;
-  LoadedModule *modules_;  // Array of module descriptions is leaked.
-  uptr n_modules_;
-  // If stale, need to reload the modules before looking up addresses.
-  bool modules_fresh_;
-  BlockingMutex mu_;
-
-  ExternalSymbolizerInterface *external_symbolizer_;  // Leaked.
-  InternalSymbolizer *const internal_symbolizer_;     // Leaked.
-  LibbacktraceSymbolizer *libbacktrace_symbolizer_;   // Leaked.
-};
+#if SANITIZER_MAC
+  VReport(2, "Using dladdr symbolizer.\n");
+  list->push_back(new(*allocator) DlAddrSymbolizer());
+#endif  // SANITIZER_MAC
 
-Symbolizer *Symbolizer::PlatformInit(const char *path_to_external) {
-  if (!common_flags()->symbolize) {
-    return new(symbolizer_allocator_) POSIXSymbolizer(0, 0, 0);
-  }
-  InternalSymbolizer* internal_symbolizer =
-      InternalSymbolizer::get(&symbolizer_allocator_);
-  ExternalSymbolizerInterface *external_symbolizer = 0;
-  LibbacktraceSymbolizer *libbacktrace_symbolizer = 0;
-
-  if (!internal_symbolizer) {
-    libbacktrace_symbolizer =
-        LibbacktraceSymbolizer::get(&symbolizer_allocator_);
-    if (!libbacktrace_symbolizer) {
-      if (path_to_external && path_to_external[0] == '\0') {
-        // External symbolizer is explicitly disabled. Do nothing.
-      } else {
-        // Find path to llvm-symbolizer if it's not provided.
-        if (!path_to_external)
-          path_to_external = FindPathToBinary("llvm-symbolizer");
-        if (path_to_external) {
-          external_symbolizer = new(symbolizer_allocator_)
-              LLVMSymbolizerProcess(path_to_external);
-        } else if (common_flags()->allow_addr2line) {
-          // If llvm-symbolizer is not found, try to use addr2line.
-          if (const char *addr2line_path = FindPathToBinary("addr2line")) {
-            external_symbolizer = new(symbolizer_allocator_)
-                Addr2LinePool(addr2line_path, &symbolizer_allocator_);
-          }
-        }
-      }
-    }
+  if (list->size() == 0) {
+    Report("WARNING: no internal or external symbolizer found.\n");
   }
+}
 
-  return new(symbolizer_allocator_) POSIXSymbolizer(
-      external_symbolizer, internal_symbolizer, libbacktrace_symbolizer);
+Symbolizer *Symbolizer::PlatformInit() {
+  IntrusiveList<SymbolizerTool> list;
+  list.clear();
+  ChooseSymbolizerTools(&list, &symbolizer_allocator_);
+  return new(symbolizer_allocator_) Symbolizer(list);
 }
 
 }  // namespace __sanitizer