text: //===-- sanitizer_symbolizer_posix_libcdep.cc -----------------------------===//
  //
  //                     The LLVM Compiler Infrastructure
  //
  // This file is distributed under the University of Illinois Open Source
  // License. See LICENSE.TXT for details.
  //
  //===----------------------------------------------------------------------===//
  //
  // This file is shared between AddressSanitizer and ThreadSanitizer
  // run-time libraries.
  // POSIX-specific implementation of symbolizer parts.
  //===----------------------------------------------------------------------===//
  
  #include "sanitizer_platform.h"
  #if SANITIZER_POSIX
  #include "sanitizer_allocator_internal.h"
  #include "sanitizer_common.h"
  #include "sanitizer_flags.h"
  #include "sanitizer_internal_defs.h"
  #include "sanitizer_linux.h"
  #include "sanitizer_placement_new.h"
  #include "sanitizer_procmaps.h"
  #include "sanitizer_symbolizer.h"
  #include "sanitizer_symbolizer_libbacktrace.h"
  
  #include <errno.h>
  #include <stdlib.h>
  #include <sys/wait.h>
  #include <unistd.h>
  
  // 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.
  namespace __cxxabiv1 {
    extern "C" SANITIZER_WEAK_ATTRIBUTE
    char *__cxa_demangle(const char *mangled, char *buffer,
                                    size_t *length, int *status);
  }
  
  namespace __sanitizer {
  
  // Attempts to demangle the name via __cxa_demangle from __cxxabiv1.
  static 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
    // it does not allocate). For now, we just call it anyway, and we leak
    // the returned value.
    if (__cxxabiv1::__cxa_demangle)
      if (const char *demangled_name =
            __cxxabiv1::__cxa_demangle(name, 0, 0, 0))
        return demangled_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;
      }
      return true;
    }
  
    bool StartSymbolizerSubprocess() {
      if (!FileExists(path_)) {
        if (!reported_invalid_path_) {
          Report("WARNING: invalid path to external symbolizer!\n");
          reported_invalid_path_ = true;
        }
        return false;
      }
  
      int *infd = NULL;
      int *outfd = NULL;
      // The client program may close its stdin and/or stdout and/or stderr
      // thus allowing socketpair to reuse file descriptors 0, 1 or 2.
      // In this case the communication between the forked processes may be
      // broken if either the parent or the child tries to close or duplicate
      // these descriptors. The loop below produces two pairs of file
      // descriptors, each greater than 2 (stderr).
      int sock_pair[5][2];
      for (int i = 0; i < 5; i++) {
        if (pipe(sock_pair[i]) == -1) {
          for (int j = 0; j < i; j++) {
            internal_close(sock_pair[j][0]);
            internal_close(sock_pair[j][1]);
          }
          Report("WARNING: Can't create a socket pair to start "
                 "external symbolizer (errno: %d)\n", errno);
          return false;
        } else if (sock_pair[i][0] > 2 && sock_pair[i][1] > 2) {
          if (infd == NULL) {
            infd = sock_pair[i];
          } else {
            outfd = sock_pair[i];
            for (int j = 0; j < i; j++) {
              if (sock_pair[j] == infd) continue;
              internal_close(sock_pair[j][0]);
              internal_close(sock_pair[j][1]);
            }
            break;
          }
        }
      }
      CHECK(infd);
      CHECK(outfd);
  
      // Real fork() may call user callbacks registered with pthread_atfork().
      int pid = internal_fork();
      if (pid == -1) {
        // Fork() failed.
        internal_close(infd[0]);
        internal_close(infd[1]);
        internal_close(outfd[0]);
        internal_close(outfd[1]);
        Report("WARNING: failed to fork external symbolizer "
               " (errno: %d)\n", errno);
        return false;
      } else if (pid == 0) {
        // Child subprocess.
        internal_close(STDOUT_FILENO);
        internal_close(STDIN_FILENO);
        internal_dup2(outfd[0], STDIN_FILENO);
        internal_dup2(infd[1], STDOUT_FILENO);
        internal_close(outfd[0]);
        internal_close(outfd[1]);
        internal_close(infd[0]);
        internal_close(infd[1]);
        for (int fd = sysconf(_SC_OPEN_MAX); fd > 2; fd--)
          internal_close(fd);
        ExecuteWithDefaultArgs(path_);
        internal__exit(1);
      }
  
      // Continue execution in parent process.
      internal_close(outfd[0]);
      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';
    }
  
    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__) && defined(__BIG_ENDIAN__)
      const char* const kSymbolizerArch = "--default-arch=powerpc64";
  #elif defined(__powerpc64__) && defined(__LITTLE_ENDIAN__)
      const char* const kSymbolizerArch = "--default-arch=powerpc64le";
  #else
      const char* const kSymbolizerArch = "--default-arch=unknown";
  #endif
  
      const char *const inline_flag = common_flags()->symbolize_inline_frames
                                          ? "--inlining=true"
                                          : "--inlining=false";
      execl(path_to_binary, path_to_binary, inline_flag, kSymbolizerArch,
            (char *)0);
    }
  };
  
  class Addr2LineProcess : public SymbolizerProcess {
   public:
    Addr2LineProcess(const char *path, const char *module_name)
        : SymbolizerProcess(path), module_name_(internal_strdup(module_name)) {}
  
    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;
    }
  
    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;
    }
  
    void ExecuteWithDefaultArgs(const char *path_to_binary) const {
      execl(path_to_binary, path_to_binary, "-Cfe", module_name_, (char *)0);
    }
  
    const char *module_name_;  // Owned, leaked.
  };
  
  class Addr2LinePool : public ExternalSymbolizerInterface {
   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;
      Addr2LineProcess *addr2line = 0;
      for (uptr i = 0; i < addr2line_pool_.size(); ++i) {
        if (0 ==
            internal_strcmp(module_name, addr2line_pool_[i]->module_name())) {
          addr2line = addr2line_pool_[i];
          break;
        }
      }
      if (!addr2line) {
        addr2line =
            new(*allocator_) Addr2LineProcess(addr2line_path_, module_name);
        addr2line_pool_.push_back(addr2line);
      }
      return addr2line->SendCommand(is_data, module_name, module_offset);
    }
  
   private:
    const char *addr2line_path_;
    LowLevelAllocator *allocator_;
    InternalMmapVector<Addr2LineProcess*> addr2line_pool_;
  };
  
  #if SANITIZER_SUPPORTS_WEAK_HOOKS
  extern "C" {
  SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
  bool __sanitizer_symbolize_code(const char *ModuleName, u64 ModuleOffset,
                                  char *Buffer, int MaxLength);
  SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
  bool __sanitizer_symbolize_data(const char *ModuleName, u64 ModuleOffset,
                                  char *Buffer, int MaxLength);
  SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
  void __sanitizer_symbolize_flush();
  SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
  int __sanitizer_symbolize_demangle(const char *Name, char *Buffer,
                                     int MaxLength);
  }  // extern "C"
  
  class InternalSymbolizer {
   public:
    typedef bool (*SanitizerSymbolizeFn)(const char*, u64, char*, int);
  
    static InternalSymbolizer *get(LowLevelAllocator *alloc) {
      if (__sanitizer_symbolize_code != 0 &&
          __sanitizer_symbolize_data != 0) {
        return new(*alloc) 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;
    }
  
    void Flush() {
      if (__sanitizer_symbolize_flush)
        __sanitizer_symbolize_flush();
    }
  
    const char *Demangle(const char *name) {
      if (__sanitizer_symbolize_demangle) {
        for (uptr res_length = 1024;
             res_length <= InternalSizeClassMap::kMaxSize;) {
          char *res_buff = static_cast<char*>(InternalAlloc(res_length));
          uptr req_length =
              __sanitizer_symbolize_demangle(name, res_buff, res_length);
          if (req_length > res_length) {
            res_length = req_length + 1;
            InternalFree(res_buff);
            continue;
          }
          return res_buff;
        }
      }
      return name;
    }
  
   private:
    InternalSymbolizer() { }
  
    static const int kBufferSize = 16 * 1024;
    static const int kMaxDemangledNameSize = 1024;
    char buffer_[kBufferSize];
  };
  #else  // SANITIZER_SUPPORTS_WEAK_HOOKS
  
  class InternalSymbolizer {
   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) {}
  
    SymbolizedStack *SymbolizePC(uptr addr) override {
      BlockingMutexLock l(&mu_);
      const char *module_name;
      uptr module_offset;
      if (!FindModuleNameAndOffsetForAddress(addr, &module_name, &module_offset))
        return SymbolizedStack::New(addr);
      // First, try to use libbacktrace symbolizer (if it's available).
      if (libbacktrace_symbolizer_ != 0) {
        mu_.CheckLocked();
        if (SymbolizedStack *res = libbacktrace_symbolizer_->SymbolizeCode(
                addr, module_name, module_offset))
          return res;
      }
      // Always fill data about module name and offset.
      SymbolizedStack *res = SymbolizedStack::New(addr);
      res->info.FillAddressAndModuleInfo(addr, module_name, module_offset);
  
      const char *str = SendCommand(false, module_name, module_offset);
      if (str == 0) {
        // Symbolizer was not initialized or failed.
        return res;
      }
  
      bool top_frame = true;
      SymbolizedStack *last = res;
      while (true) {
        char *function_name = 0;
        str = ExtractToken(str, "\n", &function_name);
        CHECK(function_name);
        if (function_name[0] == '\0') {
          // There are no more frames.
          break;
        }
        SymbolizedStack *cur;
        if (top_frame) {
          cur = res;
          top_frame = false;
        } else {
          cur = SymbolizedStack::New(addr);
          cur->info.FillAddressAndModuleInfo(addr, module_name, module_offset);
          last->next = cur;
          last = cur;
        }
  
        AddressInfo *info = &cur->info;
        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;
        }
      }
      return res;
    }
  
    bool SymbolizeData(uptr addr, DataInfo *info) override {
      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();
      info->Clear();
      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) override {
      BlockingMutexLock l(&mu_);
      return FindModuleNameAndOffsetForAddress(pc, module_name, module_address);
    }
  
    bool CanReturnFileLineInfo() override {
      return internal_symbolizer_ != 0 || external_symbolizer_ != 0 ||
             libbacktrace_symbolizer_ != 0;
    }
  
    void Flush() override {
      BlockingMutexLock l(&mu_);
      if (internal_symbolizer_ != 0) {
        SymbolizerScope sym_scope(this);
        internal_symbolizer_->Flush();
      }
    }
  
    const char *Demangle(const char *name) override {
      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);
    }
  
    void PrepareForSandboxing() override {
  #if SANITIZER_LINUX && !SANITIZER_ANDROID
      BlockingMutexLock l(&mu_);
      // Cache /proc/self/exe on Linux.
      CacheBinaryName();
  #endif
    }
  
   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;
    }
  
    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;
    }
  
    // 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.
  };
  
  Symbolizer *Symbolizer::PlatformInit() {
    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) {
        const char *path_to_external = common_flags()->external_symbolizer_path;
        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_);
            }
          }
        }
      }
    }
  
    return new(symbolizer_allocator_) POSIXSymbolizer(
        external_symbolizer, internal_symbolizer, libbacktrace_symbolizer);
  }
  
  }  // namespace __sanitizer
  
  #endif  // SANITIZER_POSIX
  