Update aosp/master compiler-rt for rebase to r233350

Change-Id: I5f20256ce73ec7b5956f742b4062f850bf11b667
diff --git a/lib/sanitizer_common/CMakeLists.txt b/lib/sanitizer_common/CMakeLists.txt
index 6eb6ca8..5b32b6a 100644
--- a/lib/sanitizer_common/CMakeLists.txt
+++ b/lib/sanitizer_common/CMakeLists.txt
@@ -27,6 +27,7 @@
   sanitizer_suppressions.cc
   sanitizer_symbolizer.cc
   sanitizer_symbolizer_libbacktrace.cc
+  sanitizer_symbolizer_mac.cc
   sanitizer_symbolizer_win.cc
   sanitizer_tls_get_addr.cc
   sanitizer_thread_registry.cc
@@ -42,6 +43,7 @@
   sanitizer_stoptheworld_linux_libcdep.cc
   sanitizer_symbolizer_libcdep.cc
   sanitizer_symbolizer_posix_libcdep.cc
+  sanitizer_symbolizer_process_libcdep.cc
   sanitizer_unwind_posix_libcdep.cc)
 
 # Explicitly list all sanitizer_common headers. Not all of these are
@@ -91,7 +93,10 @@
   sanitizer_stoptheworld.h
   sanitizer_suppressions.h
   sanitizer_symbolizer.h
+  sanitizer_symbolizer_internal.h
   sanitizer_symbolizer_libbacktrace.h
+  sanitizer_symbolizer_mac.h
+  sanitizer_symbolizer_win.h
   sanitizer_syscall_generic.inc
   sanitizer_syscall_linux_x86_64.inc
   sanitizer_thread_registry.h)
@@ -137,12 +142,6 @@
       DEFS ${SANITIZER_COMMON_DEFINITIONS})
     list(APPEND SANITIZER_RUNTIME_LIBRARIES RTSanitizerCommon.${arch}
                                             RTSanitizerCommonLibc.${arch})
-    add_compiler_rt_runtime(clang_rt.san-${arch} ${arch} STATIC
-      SOURCES $<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
-              $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>
-      CFLAGS ${SANITIZER_CFLAGS}
-      DEFS ${SANITIZER_COMMON_DEFINITIONS})
-    add_dependencies(sanitizer_common clang_rt.san-${arch})
   endforeach()
 endif()
 
diff --git a/lib/sanitizer_common/sanitizer_common.cc b/lib/sanitizer_common/sanitizer_common.cc
index 4be3c7a..956fc99 100644
--- a/lib/sanitizer_common/sanitizer_common.cc
+++ b/lib/sanitizer_common/sanitizer_common.cc
@@ -16,6 +16,8 @@
 #include "sanitizer_flags.h"
 #include "sanitizer_libc.h"
 #include "sanitizer_placement_new.h"
+#include "sanitizer_stacktrace_printer.h"
+#include "sanitizer_symbolizer.h"
 
 namespace __sanitizer {
 
@@ -56,7 +58,7 @@
   }
 
   internal_snprintf(full_path, kMaxPathLength, "%s.%zu", path_prefix, pid);
-  uptr openrv = OpenFile(full_path, true);
+  uptr openrv = OpenFile(full_path, WrOnly);
   if (internal_iserror(openrv)) {
     const char *ErrorMsgPrefix = "ERROR: Can't open file: ";
     internal_write(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix));
@@ -142,7 +144,7 @@
   *buff_size = 0;
   // The files we usually open are not seekable, so try different buffer sizes.
   for (uptr size = kMinFileLen; size <= max_len; size *= 2) {
-    uptr openrv = OpenFile(file_name, /*write*/ false);
+    uptr openrv = OpenFile(file_name, RdOnly);
     if (internal_iserror(openrv, errno_p)) return 0;
     fd_t fd = openrv;
     UnmapOrDie(*buff, *buff_size);
@@ -206,12 +208,12 @@
                             const char *strip_path_prefix) {
   if (filepath == 0) return 0;
   if (strip_path_prefix == 0) return filepath;
-  const char *pos = internal_strstr(filepath, strip_path_prefix);
-  if (pos == 0) return filepath;
-  pos += internal_strlen(strip_path_prefix);
-  if (pos[0] == '.' && pos[1] == '/')
-    pos += 2;
-  return pos;
+  const char *res = filepath;
+  if (const char *pos = internal_strstr(filepath, strip_path_prefix))
+    res = pos + internal_strlen(strip_path_prefix);
+  if (res[0] == '.' && res[1] == '/')
+    res += 2;
+  return res;
 }
 
 const char *StripModuleName(const char *module) {
@@ -230,17 +232,16 @@
   __sanitizer_report_error_summary(buff.data());
 }
 
-void ReportErrorSummary(const char *error_type, const char *file,
-                        int line, const char *function) {
+#ifndef SANITIZER_GO
+void ReportErrorSummary(const char *error_type, const AddressInfo &info) {
   if (!common_flags()->print_summary)
     return;
   InternalScopedString buff(kMaxSummaryLength);
-  buff.append("%s %s:%d %s", error_type,
-              file ? StripPathPrefix(file, common_flags()->strip_path_prefix)
-                   : "??",
-              line, function ? function : "??");
+  buff.append("%s ", error_type);
+  RenderFrame(&buff, "%L %F", 0, info, common_flags()->strip_path_prefix);
   ReportErrorSummary(buff.data());
 }
+#endif
 
 LoadedModule::LoadedModule(const char *module_name, uptr base_address) {
   full_name_ = internal_strdup(module_name);
diff --git a/lib/sanitizer_common/sanitizer_common.h b/lib/sanitizer_common/sanitizer_common.h
index ff13ef1..0dfa815 100644
--- a/lib/sanitizer_common/sanitizer_common.h
+++ b/lib/sanitizer_common/sanitizer_common.h
@@ -25,6 +25,7 @@
 
 namespace __sanitizer {
 struct StackTrace;
+struct AddressInfo;
 
 // Constants.
 const uptr kWordSize = SANITIZER_WORDSIZE / 8;
@@ -186,7 +187,13 @@
 extern uptr stoptheworld_tracer_pid;
 extern uptr stoptheworld_tracer_ppid;
 
-uptr OpenFile(const char *filename, bool write);
+enum FileAccessMode {
+  RdOnly,
+  WrOnly,
+  RdWr
+};
+
+uptr OpenFile(const char *filename, FileAccessMode mode);
 // Opens the file 'file_name" and reads up to 'max_len' bytes.
 // The resulting buffer is mmaped and stored in '*buff'.
 // The size of the mmaped region is stored in '*buff_size',
@@ -215,6 +222,11 @@
 bool SetEnv(const char *name, const char *value);
 const char *GetPwd();
 char *FindPathToBinary(const char *name);
+bool IsPathSeparator(const char c);
+bool IsAbsolutePath(const char *path);
+
+// Returns the path to the main executable.
+uptr ReadBinaryName(/*out*/char *buf, uptr buf_len);
 u32 GetUid();
 void ReExec();
 bool StackSizeIsUnlimited();
@@ -288,9 +300,9 @@
 // and pass it to __sanitizer_report_error_summary.
 void ReportErrorSummary(const char *error_message);
 // Same as above, but construct error_message as:
-//   error_type file:line function
-void ReportErrorSummary(const char *error_type, const char *file,
-                        int line, const char *function);
+//   error_type file:line[:column][ function]
+void ReportErrorSummary(const char *error_type, const AddressInfo &info);
+// Same as above, but obtains AddressInfo by symbolizing top stack trace frame.
 void ReportErrorSummary(const char *error_type, StackTrace *trace);
 
 // Math
@@ -439,11 +451,15 @@
   const T *data() const {
     return data_;
   }
+  T *data() {
+    return data_;
+  }
   uptr capacity() const {
     return capacity_;
   }
 
   void clear() { size_ = 0; }
+  bool empty() const { return size() == 0; }
 
  private:
   void Resize(uptr new_capacity) {
@@ -532,6 +548,7 @@
 // executable or a shared object).
 class LoadedModule {
  public:
+  LoadedModule() : full_name_(nullptr), base_address_(0) {}
   LoadedModule(const char *module_name, uptr base_address);
   void clear();
   void addAddressRange(uptr beg, uptr end, bool executable);
@@ -607,6 +624,23 @@
 #endif
 }
 
+struct SignalContext {
+  void *context;
+  uptr addr;
+  uptr pc;
+  uptr sp;
+  uptr bp;
+
+  SignalContext(void *context, uptr addr, uptr pc, uptr sp, uptr bp) :
+      context(context), addr(addr), pc(pc), sp(sp), bp(bp) {
+  }
+
+  // Creates signal context in a platform-specific manner.
+  static SignalContext Create(void *siginfo, void *context);
+};
+
+void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp);
+
 }  // namespace __sanitizer
 
 inline void *operator new(__sanitizer::operator_new_size_type size,
diff --git a/lib/sanitizer_common/sanitizer_common_libcdep.cc b/lib/sanitizer_common/sanitizer_common_libcdep.cc
index 17ef689..9622e60 100644
--- a/lib/sanitizer_common/sanitizer_common_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_common_libcdep.cc
@@ -44,20 +44,16 @@
 void ReportErrorSummary(const char *error_type, StackTrace *stack) {
   if (!common_flags()->print_summary)
     return;
-#if !SANITIZER_GO
-  if (stack->size > 0 && Symbolizer::GetOrInit()->CanReturnFileLineInfo()) {
-    // Currently, we include the first stack frame into the report summary.
-    // Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc).
-    uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]);
-    SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc);
-    const AddressInfo &ai = frame->info;
-    ReportErrorSummary(error_type, ai.file, ai.line, ai.function);
-    frame->ClearAll();
+  if (stack->size == 0) {
+    ReportErrorSummary(error_type);
+    return;
   }
-#else
-  AddressInfo ai;
-  ReportErrorSummary(error_type, ai.file, ai.line, ai.function);
-#endif
+  // Currently, we include the first stack frame into the report summary.
+  // Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc).
+  uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]);
+  SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc);
+  ReportErrorSummary(error_type, frame->info);
+  frame->ClearAll();
 }
 
 static void (*SoftRssLimitExceededCallback)(bool exceeded);
diff --git a/lib/sanitizer_common/sanitizer_coverage_libcdep.cc b/lib/sanitizer_common/sanitizer_coverage_libcdep.cc
index 49887b1..94863c6 100644
--- a/lib/sanitizer_common/sanitizer_coverage_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_coverage_libcdep.cc
@@ -24,8 +24,12 @@
 //    and atomically set Guard to -Guard.
 //  - __sanitizer_cov_dump: dump the coverage data to disk.
 //  For every module of the current process that has coverage data
-//  this will create a file module_name.PID.sancov. The file format is simple:
-//  it's just a sorted sequence of 4-byte offsets in the module.
+//  this will create a file module_name.PID.sancov.
+//
+// The file format is simple: the first 8 bytes is the magic,
+// one of 0xC0BFFFFFFFFFFF64 and 0xC0BFFFFFFFFFFF32. The last byte of the
+// magic defines the size of the following offsets.
+// The rest of the data is the offsets in the module.
 //
 // Eventually, this coverage implementation should be obsoleted by a more
 // powerful general purpose Clang/LLVM coverage instrumentation.
@@ -43,6 +47,9 @@
 #include "sanitizer_symbolizer.h"
 #include "sanitizer_flags.h"
 
+static const u64 kMagic64 = 0xC0BFFFFFFFFFFF64ULL;
+static const u64 kMagic32 = 0xC0BFFFFFFFFFFF32ULL;
+
 static atomic_uint32_t dump_once_guard;  // Ensure that CovDump runs only once.
 
 static atomic_uintptr_t coverage_counter;
@@ -77,21 +84,32 @@
                  uptr cache_size);
   void DumpCallerCalleePairs();
   void DumpTrace();
+  void DumpAsBitSet();
+  void DumpCounters();
+  void DumpOffsets();
+  void DumpAll();
 
   ALWAYS_INLINE
   void TraceBasicBlock(s32 *id);
 
   void InitializeGuardArray(s32 *guards);
-  void InitializeGuards(s32 *guards, uptr n, const char *module_name);
+  void InitializeGuards(s32 *guards, uptr n, const char *module_name,
+                        uptr caller_pc);
+  void InitializeCounters(u8 *counters, uptr n);
   void ReinitializeGuards();
+  uptr GetNumberOf8bitCounters();
+  uptr Update8bitCounterBitsetAndClearCounters(u8 *bitset);
 
   uptr *data();
   uptr size();
 
  private:
+  void DirectOpen();
+  void UpdateModuleNameVec(uptr caller_pc, uptr range_beg, uptr range_end);
+
   // Maximal size pc array may ever grow.
   // We MmapNoReserve this space to ensure that the array is contiguous.
-  static const uptr kPcArrayMaxSize = FIRST_32_SECOND_64(1 << 24, 1 << 27);
+  static const uptr kPcArrayMaxSize = FIRST_32_SECOND_64(1 << 26, 1 << 27);
   // The amount file mapping for the pc array is grown by.
   static const uptr kPcArrayMmapSize = 64 * 1024;
 
@@ -110,8 +128,22 @@
   // Vector of coverage guard arrays, protected by mu.
   InternalMmapVectorNoCtor<s32*> guard_array_vec;
 
-  // Vector of module (compilation unit) names.
-  InternalMmapVectorNoCtor<const char*> comp_unit_name_vec;
+  struct NamedPcRange {
+    const char *copied_module_name;
+    uptr beg, end; // elements [beg,end) in pc_array.
+  };
+
+  // Vector of module and compilation unit pc ranges.
+  InternalMmapVectorNoCtor<NamedPcRange> comp_unit_name_vec;
+  InternalMmapVectorNoCtor<NamedPcRange> module_name_vec;
+
+  struct CounterAndSize {
+    u8 *counters;
+    uptr n;
+  };
+
+  InternalMmapVectorNoCtor<CounterAndSize> counters_vec;
+  uptr num_8bit_counters;
 
   // Caller-Callee (cc) array, size and current index.
   static const uptr kCcArrayMaxSize = FIRST_32_SECOND_64(1 << 18, 1 << 24);
@@ -133,8 +165,6 @@
   static const uptr kTrPcArrayMaxSize    = FIRST_32_SECOND_64(1 << 22, 1 << 27);
 
   StaticSpinMutex mu;
-
-  void DirectOpen();
 };
 
 static CoverageData coverage_data;
@@ -145,9 +175,9 @@
   InternalScopedString path(kMaxPathLength);
   internal_snprintf((char *)path.data(), path.size(), "%s/%zd.sancov.raw",
                     coverage_dir, internal_getpid());
-  pc_fd = OpenFile(path.data(), true);
+  pc_fd = OpenFile(path.data(), RdWr);
   if (internal_iserror(pc_fd)) {
-    Report(" Coverage: failed to open %s for writing\n", path.data());
+    Report(" Coverage: failed to open %s for reading/writing\n", path.data());
     Die();
   }
 
@@ -184,6 +214,8 @@
            GetMmapGranularity());
   tr_event_array_size = kTrEventArrayMaxSize;
   tr_event_pointer = tr_event_array;
+
+  num_8bit_counters = 0;
 }
 
 void CoverageData::InitializeGuardArray(s32 *guards) {
@@ -289,16 +321,70 @@
   atomic_store(&pc_array_size, size, memory_order_release);
 }
 
+void CoverageData::InitializeCounters(u8 *counters, uptr n) {
+  if (!counters) return;
+  CHECK_EQ(reinterpret_cast<uptr>(counters) % 16, 0);
+  n = RoundUpTo(n, 16); // The compiler must ensure that counters is 16-aligned.
+  SpinMutexLock l(&mu);
+  counters_vec.push_back({counters, n});
+  num_8bit_counters += n;
+}
+
+void CoverageData::UpdateModuleNameVec(uptr caller_pc, uptr range_beg,
+                                       uptr range_end) {
+  auto sym = Symbolizer::GetOrInit();
+  if (!sym)
+    return;
+  const char *module_name = sym->GetModuleNameForPc(caller_pc);
+  if (!module_name) return;
+  if (module_name_vec.empty() ||
+      internal_strcmp(module_name_vec.back().copied_module_name, module_name))
+    module_name_vec.push_back(
+        {internal_strdup(module_name), range_beg, range_end});
+  else
+    module_name_vec.back().end = range_end;
+}
+
 void CoverageData::InitializeGuards(s32 *guards, uptr n,
-                                    const char *module_name) {
+                                    const char *comp_unit_name,
+                                    uptr caller_pc) {
   // The array 'guards' has n+1 elements, we use the element zero
   // to store 'n'.
   CHECK_LT(n, 1 << 30);
   guards[0] = static_cast<s32>(n);
   InitializeGuardArray(guards);
   SpinMutexLock l(&mu);
-  comp_unit_name_vec.push_back(module_name);
+  uptr range_end = atomic_load(&pc_array_index, memory_order_relaxed);
+  uptr range_beg = range_end - n;
+  comp_unit_name_vec.push_back({comp_unit_name, range_beg, range_end});
   guard_array_vec.push_back(guards);
+  UpdateModuleNameVec(caller_pc, range_beg, range_end);
+}
+
+static const uptr kBundleCounterBits = 16;
+
+// When coverage_order_pcs==true and SANITIZER_WORDSIZE==64
+// we insert the global counter into the first 16 bits of the PC.
+uptr BundlePcAndCounter(uptr pc, uptr counter) {
+  if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs)
+    return pc;
+  static const uptr kMaxCounter = (1 << kBundleCounterBits) - 1;
+  if (counter > kMaxCounter)
+    counter = kMaxCounter;
+  CHECK_EQ(0, pc >> (SANITIZER_WORDSIZE - kBundleCounterBits));
+  return pc | (counter << (SANITIZER_WORDSIZE - kBundleCounterBits));
+}
+
+uptr UnbundlePc(uptr bundle) {
+  if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs)
+    return bundle;
+  return (bundle << kBundleCounterBits) >> kBundleCounterBits;
+}
+
+uptr UnbundleCounter(uptr bundle) {
+  if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs)
+    return 0;
+  return bundle >> (SANITIZER_WORDSIZE - kBundleCounterBits);
 }
 
 // If guard is negative, atomically set it to -guard and store the PC in
@@ -316,8 +402,8 @@
     return;  // May happen after fork when pc_array_index becomes 0.
   CHECK_LT(idx * sizeof(uptr),
            atomic_load(&pc_array_size, memory_order_acquire));
-  pc_array[idx] = pc;
-  atomic_fetch_add(&coverage_counter, 1, memory_order_relaxed);
+  uptr counter = atomic_fetch_add(&coverage_counter, 1, memory_order_relaxed);
+  pc_array[idx] = BundlePcAndCounter(pc, counter);
 }
 
 // Registers a pair caller=>callee.
@@ -354,6 +440,64 @@
   }
 }
 
+uptr CoverageData::GetNumberOf8bitCounters() {
+  return num_8bit_counters;
+}
+
+// Map every 8bit counter to a 8-bit bitset and clear the counter.
+uptr CoverageData::Update8bitCounterBitsetAndClearCounters(u8 *bitset) {
+  uptr num_new_bits = 0;
+  uptr cur = 0;
+  // For better speed we map 8 counters to 8 bytes of bitset at once.
+  static const uptr kBatchSize = 8;
+  CHECK_EQ(reinterpret_cast<uptr>(bitset) % kBatchSize, 0);
+  for (uptr i = 0, len = counters_vec.size(); i < len; i++) {
+    u8 *c = counters_vec[i].counters;
+    uptr n = counters_vec[i].n;
+    CHECK_EQ(n % 16, 0);
+    CHECK_EQ(cur % kBatchSize, 0);
+    CHECK_EQ(reinterpret_cast<uptr>(c) % kBatchSize, 0);
+    if (!bitset) {
+      internal_bzero_aligned16(c, n);
+      cur += n;
+      continue;
+    }
+    for (uptr j = 0; j < n; j += kBatchSize, cur += kBatchSize) {
+      CHECK_LT(cur, num_8bit_counters);
+      u64 *pc64 = reinterpret_cast<u64*>(c + j);
+      u64 *pb64 = reinterpret_cast<u64*>(bitset + cur);
+      u64 c64 = *pc64;
+      u64 old_bits_64 = *pb64;
+      u64 new_bits_64 = old_bits_64;
+      if (c64) {
+        *pc64 = 0;
+        for (uptr k = 0; k < kBatchSize; k++) {
+          u64 x = (c64 >> (8 * k)) & 0xff;
+          if (x) {
+            u64 bit = 0;
+            /**/ if (x >= 128) bit = 128;
+            else if (x >= 32) bit = 64;
+            else if (x >= 16) bit = 32;
+            else if (x >= 8) bit = 16;
+            else if (x >= 4) bit = 8;
+            else if (x >= 3) bit = 4;
+            else if (x >= 2) bit = 2;
+            else if (x >= 1) bit = 1;
+            u64 mask = bit << (8 * k);
+            if (!(new_bits_64 & mask)) {
+              num_new_bits++;
+              new_bits_64 |= mask;
+            }
+          }
+        }
+        *pb64 = new_bits_64;
+      }
+    }
+  }
+  CHECK_EQ(cur, num_8bit_counters);
+  return num_new_bits;
+}
+
 uptr *CoverageData::data() {
   return pc_array;
 }
@@ -413,23 +557,23 @@
 // If packed = true and name == 0: <pid>.<sancov>.<packed>.
 // If packed = true and name != 0: <name>.<sancov>.<packed> (name is
 // user-supplied).
-static int CovOpenFile(bool packed, const char *name,
-                       const char *extension = "sancov") {
-  InternalScopedString path(kMaxPathLength);
+static int CovOpenFile(InternalScopedString *path, bool packed,
+                       const char *name, const char *extension = "sancov") {
+  path->clear();
   if (!packed) {
     CHECK(name);
-    path.append("%s/%s.%zd.%s", coverage_dir, name, internal_getpid(),
+    path->append("%s/%s.%zd.%s", coverage_dir, name, internal_getpid(),
                 extension);
   } else {
     if (!name)
-      path.append("%s/%zd.%s.packed", coverage_dir, internal_getpid(),
+      path->append("%s/%zd.%s.packed", coverage_dir, internal_getpid(),
                   extension);
     else
-      path.append("%s/%s.%s.packed", coverage_dir, name, extension);
+      path->append("%s/%s.%s.packed", coverage_dir, name, extension);
   }
-  uptr fd = OpenFile(path.data(), true);
+  uptr fd = OpenFile(path->data(), WrOnly);
   if (internal_iserror(fd)) {
-    Report(" SanitizerCoverage: failed to open %s for writing\n", path.data());
+    Report(" SanitizerCoverage: failed to open %s for writing\n", path->data());
     return -1;
   }
   return fd;
@@ -446,24 +590,25 @@
   for (uptr i = 0, n = size(); i < n; i++) {
     const char *module_name = "<unknown>";
     uptr module_address = 0;
-    sym->GetModuleNameAndOffsetForPC(pc_array[i], &module_name,
+    sym->GetModuleNameAndOffsetForPC(UnbundlePc(pc_array[i]), &module_name,
                                      &module_address);
     out.append("%s 0x%zx\n", module_name, module_address);
   }
-  int fd = CovOpenFile(false, "trace-points");
+  InternalScopedString path(kMaxPathLength);
+  int fd = CovOpenFile(&path, false, "trace-points");
   if (fd < 0) return;
   internal_write(fd, out.data(), out.length());
   internal_close(fd);
 
-  fd = CovOpenFile(false, "trace-compunits");
+  fd = CovOpenFile(&path, false, "trace-compunits");
   if (fd < 0) return;
   out.clear();
   for (uptr i = 0; i < comp_unit_name_vec.size(); i++)
-    out.append("%s\n", comp_unit_name_vec[i]);
+    out.append("%s\n", comp_unit_name_vec[i].copied_module_name);
   internal_write(fd, out.data(), out.length());
   internal_close(fd);
 
-  fd = CovOpenFile(false, "trace-events");
+  fd = CovOpenFile(&path, false, "trace-events");
   if (fd < 0) return;
   uptr bytes_to_write = max_idx * sizeof(tr_event_array[0]);
   u8 *event_bytes = reinterpret_cast<u8*>(tr_event_array);
@@ -514,7 +659,8 @@
                  callee_module_address);
     }
   }
-  int fd = CovOpenFile(false, "caller-callee");
+  InternalScopedString path(kMaxPathLength);
+  int fd = CovOpenFile(&path, false, "caller-callee");
   if (fd < 0) return;
   internal_write(fd, out.data(), out.length());
   internal_close(fd);
@@ -534,86 +680,124 @@
   tr_event_pointer++;
 }
 
-static void CovDumpAsBitSet() {
-  if (!common_flags()->coverage_bitset) return;
-  if (!coverage_data.size()) return;
-  int fd = CovOpenFile(/* packed */false, "combined", "bitset-sancov");
-  if (fd < 0) return;
-  uptr n = coverage_data.size();
-  uptr n_set_bits = 0;
-  InternalScopedBuffer<char> out(n);
-  for (uptr i = 0; i < n; i++) {
-    uptr pc = coverage_data.data()[i];
-    out[i] = pc ? '1' : '0';
-    if (pc)
-      n_set_bits++;
+void CoverageData::DumpCounters() {
+  if (!common_flags()->coverage_counters) return;
+  uptr n = coverage_data.GetNumberOf8bitCounters();
+  if (!n) return;
+  InternalScopedBuffer<u8> bitset(n);
+  coverage_data.Update8bitCounterBitsetAndClearCounters(bitset.data());
+  InternalScopedString path(kMaxPathLength);
+
+  for (uptr m = 0; m < module_name_vec.size(); m++) {
+    auto r = module_name_vec[m];
+    CHECK(r.copied_module_name);
+    CHECK_LE(r.beg, r.end);
+    CHECK_LE(r.end, size());
+    const char *base_name = StripModuleName(r.copied_module_name);
+    int fd =
+        CovOpenFile(&path, /* packed */ false, base_name, "counters-sancov");
+    if (fd < 0) return;
+    internal_write(fd, bitset.data() + r.beg, r.end - r.beg);
+    internal_close(fd);
+    VReport(1, " CovDump: %zd counters written for '%s'\n", r.end - r.beg,
+            base_name);
   }
-  internal_write(fd, out.data(), n);
-  internal_close(fd);
-  VReport(1, " CovDump: bitset of %zd bits written, %zd bits are set\n", n,
-          n_set_bits);
 }
 
-// Dump the coverage on disk.
-static void CovDump() {
-  if (!coverage_enabled || common_flags()->coverage_direct) return;
-#if !SANITIZER_WINDOWS
-  if (atomic_fetch_add(&dump_once_guard, 1, memory_order_relaxed))
-    return;
-  CovDumpAsBitSet();
-  coverage_data.DumpTrace();
-  if (!common_flags()->coverage_pcs) return;
-  uptr size = coverage_data.size();
-  InternalMmapVector<u32> offsets(size);
-  uptr *vb = coverage_data.data();
-  uptr *ve = vb + size;
-  SortArray(vb, size);
-  MemoryMappingLayout proc_maps(/*cache_enabled*/true);
-  uptr mb, me, off, prot;
-  InternalScopedString module(kMaxPathLength);
+void CoverageData::DumpAsBitSet() {
+  if (!common_flags()->coverage_bitset) return;
+  if (!size()) return;
+  InternalScopedBuffer<char> out(size());
   InternalScopedString path(kMaxPathLength);
-  for (int i = 0;
-       proc_maps.Next(&mb, &me, &off, module.data(), module.size(), &prot);
-       i++) {
-    if ((prot & MemoryMappingLayout::kProtectionExecute) == 0)
-      continue;
-    while (vb < ve && *vb < mb) vb++;
-    if (vb >= ve) break;
-    if (*vb < me) {
-      offsets.clear();
-      const uptr *old_vb = vb;
-      CHECK_LE(off, *vb);
-      for (; vb < ve && *vb < me; vb++) {
-        uptr diff = *vb - (i ? mb : 0) + off;
-        CHECK_LE(diff, 0xffffffffU);
-        offsets.push_back(static_cast<u32>(diff));
+  for (uptr m = 0; m < module_name_vec.size(); m++) {
+    uptr n_set_bits = 0;
+    auto r = module_name_vec[m];
+    CHECK(r.copied_module_name);
+    CHECK_LE(r.beg, r.end);
+    CHECK_LE(r.end, size());
+    for (uptr i = r.beg; i < r.end; i++) {
+      uptr pc = UnbundlePc(pc_array[i]);
+      out[i] = pc ? '1' : '0';
+      if (pc)
+        n_set_bits++;
+    }
+    const char *base_name = StripModuleName(r.copied_module_name);
+    int fd = CovOpenFile(&path, /* packed */ false, base_name, "bitset-sancov");
+    if (fd < 0) return;
+    internal_write(fd, out.data() + r.beg, r.end - r.beg);
+    internal_close(fd);
+    VReport(1,
+            " CovDump: bitset of %zd bits written for '%s', %zd bits are set\n",
+            r.end - r.beg, base_name, n_set_bits);
+  }
+}
+
+void CoverageData::DumpOffsets() {
+  auto sym = Symbolizer::GetOrInit();
+  if (!common_flags()->coverage_pcs) return;
+  CHECK_NE(sym, nullptr);
+  InternalMmapVector<uptr> offsets(0);
+  InternalScopedString path(kMaxPathLength);
+  for (uptr m = 0; m < module_name_vec.size(); m++) {
+    offsets.clear();
+    uptr num_words_for_magic = SANITIZER_WORDSIZE == 64 ? 1 : 2;
+    for (uptr i = 0; i < num_words_for_magic; i++)
+      offsets.push_back(0);
+    auto r = module_name_vec[m];
+    CHECK(r.copied_module_name);
+    CHECK_LE(r.beg, r.end);
+    CHECK_LE(r.end, size());
+    for (uptr i = r.beg; i < r.end; i++) {
+      uptr pc = UnbundlePc(pc_array[i]);
+      uptr counter = UnbundleCounter(pc_array[i]);
+      if (!pc) continue; // Not visited.
+      const char *unused;
+      uptr offset = 0;
+      sym->GetModuleNameAndOffsetForPC(pc, &unused, &offset);
+      offsets.push_back(BundlePcAndCounter(offset, counter));
+    }
+
+    CHECK_GE(offsets.size(), num_words_for_magic);
+    SortArray(offsets.data(), offsets.size());
+    for (uptr i = 0; i < offsets.size(); i++)
+      offsets[i] = UnbundlePc(offsets[i]);
+
+    uptr num_offsets = offsets.size() - num_words_for_magic;
+    u64 *magic_p = reinterpret_cast<u64*>(offsets.data());
+    CHECK_EQ(*magic_p, 0ULL);
+    // FIXME: we may want to write 32-bit offsets even in 64-mode
+    // if all the offsets are small enough.
+    *magic_p = SANITIZER_WORDSIZE == 64 ? kMagic64 : kMagic32;
+
+    const char *module_name = StripModuleName(r.copied_module_name);
+    if (cov_sandboxed) {
+      if (cov_fd >= 0) {
+        CovWritePacked(internal_getpid(), module_name, offsets.data(),
+                       offsets.size() * sizeof(offsets[0]));
+        VReport(1, " CovDump: %zd PCs written to packed file\n", num_offsets);
       }
-      const char *module_name = StripModuleName(module.data());
-      if (cov_sandboxed) {
-        if (cov_fd >= 0) {
-          CovWritePacked(internal_getpid(), module_name, offsets.data(),
-                         offsets.size() * sizeof(u32));
-          VReport(1, " CovDump: %zd PCs written to packed file\n", vb - old_vb);
-        }
-      } else {
-        // One file per module per process.
-        path.clear();
-        path.append("%s/%s.%zd.sancov", coverage_dir, module_name,
-                    internal_getpid());
-        int fd = CovOpenFile(false /* packed */, module_name);
-        if (fd > 0) {
-          internal_write(fd, offsets.data(), offsets.size() * sizeof(u32));
-          internal_close(fd);
-          VReport(1, " CovDump: %s: %zd PCs written\n", path.data(),
-                  vb - old_vb);
-        }
-      }
+    } else {
+      // One file per module per process.
+      int fd = CovOpenFile(&path, false /* packed */, module_name);
+      if (fd < 0) continue;
+      internal_write(fd, offsets.data(), offsets.size() * sizeof(offsets[0]));
+      internal_close(fd);
+      VReport(1, " CovDump: %s: %zd PCs written\n", path.data(), num_offsets);
     }
   }
   if (cov_fd >= 0)
     internal_close(cov_fd);
-  coverage_data.DumpCallerCalleePairs();
-#endif  // !SANITIZER_WINDOWS
+}
+
+void CoverageData::DumpAll() {
+  if (!coverage_enabled || common_flags()->coverage_direct) return;
+  if (atomic_fetch_add(&dump_once_guard, 1, memory_order_relaxed))
+    return;
+  DumpAsBitSet();
+  DumpCounters();
+  DumpTrace();
+  DumpOffsets();
+  DumpCallerCalleePairs();
 }
 
 void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
@@ -623,15 +807,18 @@
   if (!cov_sandboxed) return;
   cov_fd = args->coverage_fd;
   cov_max_block_size = args->coverage_max_block_size;
-  if (cov_fd < 0)
+  if (cov_fd < 0) {
+    InternalScopedString path(kMaxPathLength);
     // Pre-open the file now. The sandbox won't allow us to do it later.
-    cov_fd = CovOpenFile(true /* packed */, 0);
+    cov_fd = CovOpenFile(&path, true /* packed */, 0);
+  }
 }
 
 int MaybeOpenCovFile(const char *name) {
   CHECK(name);
   if (!coverage_enabled) return -1;
-  return CovOpenFile(true /* packed */, name);
+  InternalScopedString path(kMaxPathLength);
+  return CovOpenFile(&path, true /* packed */, name);
 }
 
 void CovBeforeFork() {
@@ -674,7 +861,8 @@
 }
 SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_with_check(u32 *guard) {
   atomic_uint32_t *atomic_guard = reinterpret_cast<atomic_uint32_t*>(guard);
-  if (__sanitizer::atomic_load(atomic_guard, memory_order_relaxed))
+  if (static_cast<s32>(
+          __sanitizer::atomic_load(atomic_guard, memory_order_relaxed)) < 0)
     __sanitizer_cov(guard);
 }
 SANITIZER_INTERFACE_ATTRIBUTE void
@@ -687,10 +875,14 @@
   coverage_dir = common_flags()->coverage_dir;
   coverage_data.Init();
 }
-SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { CovDump(); }
+SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() {
+  coverage_data.DumpAll();
+}
 SANITIZER_INTERFACE_ATTRIBUTE void
-__sanitizer_cov_module_init(s32 *guards, uptr npcs, const char *module_name) {
-  coverage_data.InitializeGuards(guards, npcs, module_name);
+__sanitizer_cov_module_init(s32 *guards, uptr npcs, u8 *counters,
+                            const char *comp_unit_name) {
+  coverage_data.InitializeGuards(guards, npcs, comp_unit_name, GET_CALLER_PC());
+  coverage_data.InitializeCounters(counters, npcs);
   if (!common_flags()->coverage_direct) return;
   if (SANITIZER_ANDROID && coverage_enabled) {
     // dlopen/dlclose interceptors do not work on Android, so we rely on
@@ -728,4 +920,14 @@
   *data = coverage_data.data();
   return coverage_data.size();
 }
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_number_of_counters() {
+  return coverage_data.GetNumberOf8bitCounters();
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_update_counter_bitset_and_clear_counters(u8 *bitset) {
+  return coverage_data.Update8bitCounterBitsetAndClearCounters(bitset);
+}
 }  // extern "C"
diff --git a/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc b/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc
index 6b5e91f..d3bde4b 100644
--- a/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc
@@ -102,7 +102,7 @@
                                "%s/%zd.sancov.map.tmp", coverage_dir,
                                internal_getpid());
   CHECK_LE(res, tmp_path.size());
-  uptr map_fd = OpenFile(tmp_path.data(), true);
+  uptr map_fd = OpenFile(tmp_path.data(), WrOnly);
   if (internal_iserror(map_fd, &err)) {
     Report(" Coverage: failed to open %s for writing: %d\n", tmp_path.data(),
            err);
diff --git a/lib/sanitizer_common/sanitizer_flags.inc b/lib/sanitizer_common/sanitizer_flags.inc
index 58f7f37..884602f 100644
--- a/lib/sanitizer_common/sanitizer_flags.inc
+++ b/lib/sanitizer_common/sanitizer_flags.inc
@@ -111,13 +111,18 @@
     bool, coverage, false,
     "If set, coverage information will be dumped at program shutdown (if the "
     "coverage instrumentation was enabled at compile time).")
-// On by default, but works only if coverage == true.
 COMMON_FLAG(bool, coverage_pcs, true,
             "If set (and if 'coverage' is set too), the coverage information "
             "will be dumped as a set of PC offsets for every module.")
+COMMON_FLAG(bool, coverage_order_pcs, false,
+             "If true, the PCs will be dumped in the order they've"
+             " appeared during the execution.")
 COMMON_FLAG(bool, coverage_bitset, false,
             "If set (and if 'coverage' is set too), the coverage information "
             "will also be dumped as a bitset to a separate file.")
+COMMON_FLAG(bool, coverage_counters, false,
+            "If set (and if 'coverage' is set too), the bitmap that corresponds"
+            " to coverage counters will be dumped.")
 COMMON_FLAG(bool, coverage_direct, SANITIZER_ANDROID,
             "If set, coverage information will be dumped directly to a memory "
             "mapped file. This way data is not lost even if the process is "
diff --git a/lib/sanitizer_common/sanitizer_linux.cc b/lib/sanitizer_common/sanitizer_linux.cc
index 8029181..8d2ea48 100644
--- a/lib/sanitizer_common/sanitizer_linux.cc
+++ b/lib/sanitizer_common/sanitizer_linux.cc
@@ -57,6 +57,7 @@
 #include <sys/syscall.h>
 #include <sys/time.h>
 #include <sys/types.h>
+#include <ucontext.h>
 #include <unistd.h>
 
 #if SANITIZER_FREEBSD
@@ -147,11 +148,6 @@
 #endif
 }
 
-uptr OpenFile(const char *filename, bool write) {
-  return internal_open(filename,
-      write ? O_RDWR | O_CREAT /*| O_CLOEXEC*/ : O_RDONLY, 0660);
-}
-
 uptr internal_read(fd_t fd, void *buf, uptr count) {
   sptr res;
   HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(read), fd, (uptr)buf,
@@ -168,7 +164,8 @@
 
 uptr internal_ftruncate(fd_t fd, uptr size) {
   sptr res;
-  HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(ftruncate), fd, size));
+  HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(ftruncate), fd,
+               (OFF_T)size));
   return res;
 }
 
@@ -558,6 +555,7 @@
 }
 
 #if SANITIZER_LINUX
+#define SA_RESTORER 0x04000000
 // Doesn't set sa_restorer, use with caution (see below).
 int internal_sigaction_norestorer(int signum, const void *act, void *oldact) {
   __sanitizer_kernel_sigaction_t k_act, k_oldact;
@@ -570,7 +568,8 @@
     k_act.sigaction = u_act->sigaction;
     internal_memcpy(&k_act.sa_mask, &u_act->sa_mask,
                     sizeof(__sanitizer_kernel_sigset_t));
-    k_act.sa_flags = u_act->sa_flags;
+    // Without SA_RESTORER kernel ignores the calls (probably returns EINVAL).
+    k_act.sa_flags = u_act->sa_flags | SA_RESTORER;
     // FIXME: most often sa_restorer is unset, however the kernel requires it
     // to point to a valid signal restorer that calls the rt_sigreturn syscall.
     // If sa_restorer passed to the kernel is NULL, the program may crash upon
@@ -928,6 +927,78 @@
 void internal_join_thread(void *th) {}
 #endif
 
+void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
+#if defined(__arm__)
+  ucontext_t *ucontext = (ucontext_t*)context;
+  *pc = ucontext->uc_mcontext.arm_pc;
+  *bp = ucontext->uc_mcontext.arm_fp;
+  *sp = ucontext->uc_mcontext.arm_sp;
+#elif defined(__aarch64__)
+  ucontext_t *ucontext = (ucontext_t*)context;
+  *pc = ucontext->uc_mcontext.pc;
+  *bp = ucontext->uc_mcontext.regs[29];
+  *sp = ucontext->uc_mcontext.sp;
+#elif defined(__hppa__)
+  ucontext_t *ucontext = (ucontext_t*)context;
+  *pc = ucontext->uc_mcontext.sc_iaoq[0];
+  /* GCC uses %r3 whenever a frame pointer is needed.  */
+  *bp = ucontext->uc_mcontext.sc_gr[3];
+  *sp = ucontext->uc_mcontext.sc_gr[30];
+#elif defined(__x86_64__)
+# if SANITIZER_FREEBSD
+  ucontext_t *ucontext = (ucontext_t*)context;
+  *pc = ucontext->uc_mcontext.mc_rip;
+  *bp = ucontext->uc_mcontext.mc_rbp;
+  *sp = ucontext->uc_mcontext.mc_rsp;
+# else
+  ucontext_t *ucontext = (ucontext_t*)context;
+  *pc = ucontext->uc_mcontext.gregs[REG_RIP];
+  *bp = ucontext->uc_mcontext.gregs[REG_RBP];
+  *sp = ucontext->uc_mcontext.gregs[REG_RSP];
+# endif
+#elif defined(__i386__)
+# if SANITIZER_FREEBSD
+  ucontext_t *ucontext = (ucontext_t*)context;
+  *pc = ucontext->uc_mcontext.mc_eip;
+  *bp = ucontext->uc_mcontext.mc_ebp;
+  *sp = ucontext->uc_mcontext.mc_esp;
+# else
+  ucontext_t *ucontext = (ucontext_t*)context;
+  *pc = ucontext->uc_mcontext.gregs[REG_EIP];
+  *bp = ucontext->uc_mcontext.gregs[REG_EBP];
+  *sp = ucontext->uc_mcontext.gregs[REG_ESP];
+# endif
+#elif defined(__powerpc__) || defined(__powerpc64__)
+  ucontext_t *ucontext = (ucontext_t*)context;
+  *pc = ucontext->uc_mcontext.regs->nip;
+  *sp = ucontext->uc_mcontext.regs->gpr[PT_R1];
+  // The powerpc{,64}-linux ABIs do not specify r31 as the frame
+  // pointer, but GCC always uses r31 when we need a frame pointer.
+  *bp = ucontext->uc_mcontext.regs->gpr[PT_R31];
+#elif defined(__sparc__)
+  ucontext_t *ucontext = (ucontext_t*)context;
+  uptr *stk_ptr;
+# if defined (__arch64__)
+  *pc = ucontext->uc_mcontext.mc_gregs[MC_PC];
+  *sp = ucontext->uc_mcontext.mc_gregs[MC_O6];
+  stk_ptr = (uptr *) (*sp + 2047);
+  *bp = stk_ptr[15];
+# else
+  *pc = ucontext->uc_mcontext.gregs[REG_PC];
+  *sp = ucontext->uc_mcontext.gregs[REG_O6];
+  stk_ptr = (uptr *) *sp;
+  *bp = stk_ptr[15];
+# endif
+#elif defined(__mips__)
+  ucontext_t *ucontext = (ucontext_t*)context;
+  *pc = ucontext->uc_mcontext.gregs[31];
+  *bp = ucontext->uc_mcontext.gregs[30];
+  *sp = ucontext->uc_mcontext.gregs[29];
+#else
+# error "Unsupported arch"
+#endif
+}
+
 }  // namespace __sanitizer
 
 #endif  // SANITIZER_FREEBSD || SANITIZER_LINUX
diff --git a/lib/sanitizer_common/sanitizer_linux.h b/lib/sanitizer_common/sanitizer_linux.h
index b2e603d..2ce2025 100644
--- a/lib/sanitizer_common/sanitizer_linux.h
+++ b/lib/sanitizer_common/sanitizer_linux.h
@@ -80,8 +80,6 @@
 // information).
 bool LibraryNameIs(const char *full_name, const char *base_name);
 
-// Read the name of the current binary from /proc/self/exe.
-uptr ReadBinaryName(/*out*/char *buf, uptr buf_len);
 // Cache the value of /proc/self/exe.
 void CacheBinaryName();
 
diff --git a/lib/sanitizer_common/sanitizer_linux_libcdep.cc b/lib/sanitizer_common/sanitizer_linux_libcdep.cc
index c71b625..1c099d8 100644
--- a/lib/sanitizer_common/sanitizer_linux_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_linux_libcdep.cc
@@ -488,7 +488,7 @@
 uptr GetRSS() {
   if (!common_flags()->can_use_proc_maps_statm)
     return GetRSSFromGetrusage();
-  uptr fd = OpenFile("/proc/self/statm", false);
+  uptr fd = OpenFile("/proc/self/statm", RdOnly);
   if ((sptr)fd < 0)
     return GetRSSFromGetrusage();
   char buf[64];
diff --git a/lib/sanitizer_common/sanitizer_mac.cc b/lib/sanitizer_common/sanitizer_mac.cc
index 39a5c7e..91a5b7d 100644
--- a/lib/sanitizer_common/sanitizer_mac.cc
+++ b/lib/sanitizer_common/sanitizer_mac.cc
@@ -31,9 +31,11 @@
 
 #include <crt_externs.h>  // for _NSGetEnviron
 #include <fcntl.h>
+#include <mach-o/dyld.h>
 #include <pthread.h>
 #include <sched.h>
 #include <signal.h>
+#include <stdlib.h>
 #include <sys/mman.h>
 #include <sys/resource.h>
 #include <sys/stat.h>
@@ -69,11 +71,6 @@
   return open(filename, flags, mode);
 }
 
-uptr OpenFile(const char *filename, bool write) {
-  return internal_open(filename,
-      write ? O_WRONLY | O_CREAT : O_RDONLY, 0660);
-}
-
 uptr internal_read(fd_t fd, void *buf, uptr count) {
   return read(fd, buf, count);
 }
@@ -204,6 +201,21 @@
   return 0;
 }
 
+uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
+  CHECK_LE(kMaxPathLength, buf_len);
+
+  // On OS X the executable path is saved to the stack by dyld. Reading it
+  // from there is much faster than calling dladdr, especially for large
+  // binaries with symbols.
+  InternalScopedString exe_path(kMaxPathLength);
+  uint32_t size = exe_path.size();
+  if (_NSGetExecutablePath(exe_path.data(), &size) == 0 &&
+      realpath(exe_path.data(), buf) != 0) {
+    return internal_strlen(buf);
+  }
+  return 0;
+}
+
 void ReExec() {
   UNIMPLEMENTED();
 }
@@ -328,6 +340,19 @@
 void *internal_start_thread(void (*func)(void *arg), void *arg) { return 0; }
 void internal_join_thread(void *th) { }
 
+void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
+  ucontext_t *ucontext = (ucontext_t*)context;
+# if SANITIZER_WORDSIZE == 64
+  *pc = ucontext->uc_mcontext->__ss.__rip;
+  *bp = ucontext->uc_mcontext->__ss.__rbp;
+  *sp = ucontext->uc_mcontext->__ss.__rsp;
+# else
+  *pc = ucontext->uc_mcontext->__ss.__eip;
+  *bp = ucontext->uc_mcontext->__ss.__ebp;
+  *sp = ucontext->uc_mcontext->__ss.__esp;
+# endif  // SANITIZER_WORDSIZE
+}
+
 }  // namespace __sanitizer
 
 #endif  // SANITIZER_MAC
diff --git a/lib/sanitizer_common/sanitizer_posix.cc b/lib/sanitizer_common/sanitizer_posix.cc
index 5bc41c2..5e01212 100644
--- a/lib/sanitizer_common/sanitizer_posix.cc
+++ b/lib/sanitizer_common/sanitizer_posix.cc
@@ -20,6 +20,8 @@
 #include "sanitizer_procmaps.h"
 #include "sanitizer_stacktrace.h"
 
+#include <fcntl.h>
+#include <signal.h>
 #include <sys/mman.h>
 
 #if SANITIZER_LINUX
@@ -47,7 +49,7 @@
 #if SANITIZER_WORDSIZE == 32
 // Take care of unusable kernel area in top gigabyte.
 static uptr GetKernelAreaSize() {
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX && !SANITIZER_X32
   const uptr gbyte = 1UL << 30;
 
   // Firstly check if there are writable segments
@@ -79,7 +81,7 @@
   return gbyte;
 #else
   return 0;
-#endif  // SANITIZER_LINUX
+#endif  // SANITIZER_LINUX && !SANITIZER_X32
 }
 #endif  // SANITIZER_WORDSIZE == 32
 
@@ -203,8 +205,18 @@
                                MAP_NORESERVE, -1, 0);
 }
 
+uptr OpenFile(const char *filename, FileAccessMode mode) {
+  int flags;
+  switch (mode) {
+    case RdOnly: flags = O_RDONLY; break;
+    case WrOnly: flags = O_WRONLY | O_CREAT; break;
+    case RdWr: flags = O_RDWR | O_CREAT; break;
+  }
+  return internal_open(filename, flags, 0660);
+}
+
 void *MapFileToMemory(const char *file_name, uptr *buff_size) {
-  uptr openrv = OpenFile(file_name, false);
+  uptr openrv = OpenFile(file_name, RdOnly);
   CHECK(!internal_iserror(openrv));
   fd_t fd = openrv;
   uptr fsize = internal_filesize(fd);
@@ -219,9 +231,10 @@
   uptr flags = MAP_SHARED;
   if (addr) flags |= MAP_FIXED;
   uptr p = internal_mmap(addr, size, PROT_READ | PROT_WRITE, flags, fd, offset);
-  if (internal_iserror(p)) {
-    Printf("could not map writable file (%zd, %zu, %zu): %zd\n", fd, offset,
-           size, p);
+  int mmap_errno = 0;
+  if (internal_iserror(p, &mmap_errno)) {
+    Printf("could not map writable file (%zd, %zu, %zu): %zd, errno: %d\n",
+           fd, offset, size, p, mmap_errno);
     return 0;
   }
   return (void *)p;
@@ -293,6 +306,14 @@
   return 0;
 }
 
+bool IsPathSeparator(const char c) {
+  return c == '/';
+}
+
+bool IsAbsolutePath(const char *path) {
+  return path != nullptr && IsPathSeparator(path[0]);
+}
+
 void ReportFile::Write(const char *buffer, uptr length) {
   SpinMutexLock l(mu);
   static const char *kWriteError =
@@ -319,6 +340,13 @@
   return false;
 }
 
+SignalContext SignalContext::Create(void *siginfo, void *context) {
+  uptr addr = (uptr)((siginfo_t*)siginfo)->si_addr;
+  uptr pc, sp, bp;
+  GetPcSpBp(context, &pc, &sp, &bp);
+  return SignalContext(context, addr, pc, sp, bp);
+}
+
 }  // namespace __sanitizer
 
 #endif  // SANITIZER_POSIX
diff --git a/lib/sanitizer_common/sanitizer_stoptheworld.h b/lib/sanitizer_common/sanitizer_stoptheworld.h
index a326467..aa6f5d8 100644
--- a/lib/sanitizer_common/sanitizer_stoptheworld.h
+++ b/lib/sanitizer_common/sanitizer_stoptheworld.h
@@ -59,7 +59,8 @@
 
 // Suspend all threads in the current process and run the callback on the list
 // of suspended threads. This function will resume the threads before returning.
-// The callback should not call any libc functions.
+// The callback should not call any libc functions. The callback must not call
+// exit() nor _exit() and instead return to the caller.
 // This function should NOT be called from multiple threads simultaneously.
 void StopTheWorld(StopTheWorldCallback callback, void *argument);
 
diff --git a/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc b/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
index ad20e39..64e1c79 100644
--- a/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
@@ -19,6 +19,7 @@
 #include "sanitizer_stoptheworld.h"
 
 #include "sanitizer_platform_limits_posix.h"
+#include "sanitizer_atomic.h"
 
 #include <errno.h>
 #include <sched.h> // for CLONE_* definitions
@@ -70,11 +71,25 @@
 COMPILER_CHECK(sizeof(SuspendedThreadID) == sizeof(pid_t));
 
 namespace __sanitizer {
+
+// Structure for passing arguments into the tracer thread.
+struct TracerThreadArgument {
+  StopTheWorldCallback callback;
+  void *callback_argument;
+  // The tracer thread waits on this mutex while the parent finishes its
+  // preparations.
+  BlockingMutex mutex;
+  // Tracer thread signals its completion by setting done.
+  atomic_uintptr_t done;
+  uptr parent_pid;
+};
+
 // This class handles thread suspending/unsuspending in the tracer thread.
 class ThreadSuspender {
  public:
-  explicit ThreadSuspender(pid_t pid)
-    : pid_(pid) {
+  explicit ThreadSuspender(pid_t pid, TracerThreadArgument *arg)
+    : arg(arg)
+    , pid_(pid) {
       CHECK_GE(pid, 0);
     }
   bool SuspendAllThreads();
@@ -83,6 +98,7 @@
   SuspendedThreadsList &suspended_threads_list() {
     return suspended_threads_list_;
   }
+  TracerThreadArgument *arg;
  private:
   SuspendedThreadsList suspended_threads_list_;
   pid_t pid_;
@@ -103,7 +119,7 @@
     VReport(1, "Could not attach to thread %d (errno %d).\n", tid, pterrno);
     return false;
   } else {
-    VReport(1, "Attached to thread %d.\n", tid);
+    VReport(2, "Attached to thread %d.\n", tid);
     // The thread is not guaranteed to stop before ptrace returns, so we must
     // wait on it. Note: if the thread receives a signal concurrently,
     // we can get notification about the signal before notification about stop.
@@ -143,7 +159,7 @@
     int pterrno;
     if (!internal_iserror(internal_ptrace(PTRACE_DETACH, tid, NULL, NULL),
                           &pterrno)) {
-      VReport(1, "Detached from thread %d.\n", tid);
+      VReport(2, "Detached from thread %d.\n", tid);
     } else {
       // Either the thread is dead, or we are already detached.
       // The latter case is possible, for instance, if this function was called
@@ -188,25 +204,23 @@
 static const int kSyncSignals[] = { SIGABRT, SIGILL, SIGFPE, SIGSEGV, SIGBUS,
                                     SIGXCPU, SIGXFSZ };
 
-// Structure for passing arguments into the tracer thread.
-struct TracerThreadArgument {
-  StopTheWorldCallback callback;
-  void *callback_argument;
-  // The tracer thread waits on this mutex while the parent finishes its
-  // preparations.
-  BlockingMutex mutex;
-  uptr parent_pid;
-};
-
 static DieCallbackType old_die_callback;
 
 // Signal handler to wake up suspended threads when the tracer thread dies.
-static void TracerThreadSignalHandler(int signum, void *siginfo, void *) {
-  if (thread_suspender_instance != NULL) {
+static void TracerThreadSignalHandler(int signum, void *siginfo, void *uctx) {
+  SignalContext ctx = SignalContext::Create(siginfo, uctx);
+  VPrintf(1, "Tracer caught signal %d: addr=0x%zx pc=0x%zx sp=0x%zx\n",
+      signum, ctx.addr, ctx.pc, ctx.sp);
+  ThreadSuspender *inst = thread_suspender_instance;
+  if (inst != NULL) {
     if (signum == SIGABRT)
-      thread_suspender_instance->KillAllThreads();
+      inst->KillAllThreads();
     else
-      thread_suspender_instance->ResumeAllThreads();
+      inst->ResumeAllThreads();
+    SetDieCallback(old_die_callback);
+    old_die_callback = NULL;
+    thread_suspender_instance = NULL;
+    atomic_store(&inst->arg->done, 1, memory_order_relaxed);
   }
   internal__exit((signum == SIGABRT) ? 1 : 2);
 }
@@ -218,10 +232,15 @@
   // point. So we correctly handle calls to Die() from within the callback, but
   // not those that happen before or after the callback. Hopefully there aren't
   // a lot of opportunities for that to happen...
-  if (thread_suspender_instance)
-    thread_suspender_instance->KillAllThreads();
+  ThreadSuspender *inst = thread_suspender_instance;
+  if (inst != NULL && stoptheworld_tracer_pid == internal_getpid()) {
+    inst->KillAllThreads();
+    thread_suspender_instance = NULL;
+  }
   if (old_die_callback)
     old_die_callback();
+  SetDieCallback(old_die_callback);
+  old_die_callback = NULL;
 }
 
 // Size of alternative stack for signal handlers in the tracer thread.
@@ -244,7 +263,7 @@
   old_die_callback = GetDieCallback();
   SetDieCallback(TracerThreadDieCallback);
 
-  ThreadSuspender thread_suspender(internal_getppid());
+  ThreadSuspender thread_suspender(internal_getppid(), tracer_thread_argument);
   // Global pointer for the signal handler.
   thread_suspender_instance = &thread_suspender;
 
@@ -276,11 +295,9 @@
     thread_suspender.ResumeAllThreads();
     exit_code = 0;
   }
-  // Note, this is a bad race. If TracerThreadDieCallback is already started
-  // in another thread and observed that thread_suspender_instance != 0,
-  // it can call KillAllThreads on the destroyed variable.
   SetDieCallback(old_die_callback);
   thread_suspender_instance = NULL;
+  atomic_store(&tracer_thread_argument->done, 1, memory_order_relaxed);
   return exit_code;
 }
 
@@ -354,6 +371,7 @@
   tracer_thread_argument.callback = callback;
   tracer_thread_argument.callback_argument = argument;
   tracer_thread_argument.parent_pid = internal_getpid();
+  atomic_store(&tracer_thread_argument.done, 0, memory_order_relaxed);
   const uptr kTracerStackSize = 2 * 1024 * 1024;
   ScopedStackSpaceWithGuard tracer_stack(kTracerStackSize);
   // Block the execution of TracerThread until after we have set ptrace
@@ -402,14 +420,27 @@
 #endif
     // Allow the tracer thread to start.
     tracer_thread_argument.mutex.Unlock();
-    // Since errno is shared between this thread and the tracer thread, we
-    // must avoid using errno while the tracer thread is running.
-    // At this point, any signal will either be blocked or kill us, so waitpid
-    // should never return (and set errno) while the tracer thread is alive.
-    uptr waitpid_status = internal_waitpid(tracer_pid, NULL, __WALL);
-    if (internal_iserror(waitpid_status, &local_errno))
+    // NOTE: errno is shared between this thread and the tracer thread.
+    // internal_waitpid() may call syscall() which can access/spoil errno,
+    // so we can't call it now. Instead we for the tracer thread to finish using
+    // the spin loop below. Man page for sched_yield() says "In the Linux
+    // implementation, sched_yield() always succeeds", so let's hope it does not
+    // spoil errno. Note that this spin loop runs only for brief periods before
+    // the tracer thread has suspended us and when it starts unblocking threads.
+    while (atomic_load(&tracer_thread_argument.done, memory_order_relaxed) == 0)
+      sched_yield();
+    // Now the tracer thread is about to exit and does not touch errno,
+    // wait for it.
+    for (;;) {
+      uptr waitpid_status = internal_waitpid(tracer_pid, NULL, __WALL);
+      if (!internal_iserror(waitpid_status, &local_errno))
+        break;
+      if (local_errno == EINTR)
+        continue;
       VReport(1, "Waiting on the tracer thread failed (errno %d).\n",
               local_errno);
+      break;
+    }
   }
 }
 
diff --git a/lib/sanitizer_common/sanitizer_suppressions.cc b/lib/sanitizer_common/sanitizer_suppressions.cc
index 2b697e9..8009b4d 100644
--- a/lib/sanitizer_common/sanitizer_suppressions.cc
+++ b/lib/sanitizer_common/sanitizer_suppressions.cc
@@ -30,18 +30,50 @@
   internal_memset(has_suppression_type_, 0, suppression_types_num_);
 }
 
+static bool GetPathAssumingFileIsRelativeToExec(const char *file_path,
+                                                /*out*/char *new_file_path,
+                                                uptr new_file_path_size) {
+  InternalScopedString exec(kMaxPathLength);
+  if (ReadBinaryName(exec.data(), exec.size())) {
+    const char *file_name_pos = StripModuleName(exec.data());
+    uptr path_to_exec_len = file_name_pos - exec.data();
+    internal_strncat(new_file_path, exec.data(),
+                     Min(path_to_exec_len, new_file_path_size - 1));
+    internal_strncat(new_file_path, file_path,
+                     new_file_path_size - internal_strlen(new_file_path) - 1);
+    return true;
+  }
+  return false;
+}
+
 void SuppressionContext::ParseFromFile(const char *filename) {
   if (filename[0] == '\0')
     return;
+
+  // If we cannot find the file, check if its location is relative to
+  // the location of the executable.
+  InternalScopedString new_file_path(kMaxPathLength);
+  if (!FileExists(filename) && !IsAbsolutePath(filename) &&
+      GetPathAssumingFileIsRelativeToExec(filename, new_file_path.data(),
+                                          new_file_path.size())) {
+    filename = new_file_path.data();
+  }
+
+  // Read the file.
   char *file_contents;
   uptr buffer_size;
-  uptr contents_size = ReadFileToBuffer(filename, &file_contents, &buffer_size,
-                                        1 << 26 /* max_len */);
+  const uptr max_len = 1 << 26;
+  uptr contents_size =
+    ReadFileToBuffer(filename, &file_contents, &buffer_size, max_len);
+  VPrintf(1, "%s: reading suppressions file at %s\n",
+          SanitizerToolName, filename);
+
   if (contents_size == 0) {
     Printf("%s: failed to read suppressions file '%s'\n", SanitizerToolName,
            filename);
     Die();
   }
+
   Parse(file_contents);
 }
 
diff --git a/lib/sanitizer_common/sanitizer_symbolizer.cc b/lib/sanitizer_common/sanitizer_symbolizer.cc
index 135720e..df7661a 100644
--- a/lib/sanitizer_common/sanitizer_symbolizer.cc
+++ b/lib/sanitizer_common/sanitizer_symbolizer.cc
@@ -16,7 +16,7 @@
 #include "sanitizer_internal_defs.h"
 #include "sanitizer_libc.h"
 #include "sanitizer_placement_new.h"
-#include "sanitizer_symbolizer.h"
+#include "sanitizer_symbolizer_internal.h"
 
 namespace __sanitizer {
 
@@ -33,9 +33,7 @@
   function_offset = kUnknown;
 }
 
-void AddressInfo::FillAddressAndModuleInfo(uptr addr, const char *mod_name,
-                                           uptr mod_offset) {
-  address = addr;
+void AddressInfo::FillModuleInfo(const char *mod_name, uptr mod_offset) {
   module = internal_strdup(mod_name);
   module_offset = mod_offset;
 }
@@ -70,13 +68,6 @@
 StaticSpinMutex Symbolizer::init_mu_;
 LowLevelAllocator Symbolizer::symbolizer_allocator_;
 
-Symbolizer *Symbolizer::Disable() {
-  CHECK_EQ(0, symbolizer_);
-  // Initialize a dummy symbolizer.
-  symbolizer_ = new(symbolizer_allocator_) Symbolizer;
-  return symbolizer_;
-}
-
 void Symbolizer::AddHooks(Symbolizer::StartSymbolizationHook start_hook,
                           Symbolizer::EndSymbolizationHook end_hook) {
   CHECK(start_hook_ == 0 && end_hook_ == 0);
@@ -84,7 +75,8 @@
   end_hook_ = end_hook;
 }
 
-Symbolizer::Symbolizer() : start_hook_(0), end_hook_(0) {}
+Symbolizer::Symbolizer(IntrusiveList<SymbolizerTool> tools)
+    : tools_(tools), start_hook_(0), end_hook_(0) {}
 
 Symbolizer::SymbolizerScope::SymbolizerScope(const Symbolizer *sym)
     : sym_(sym) {
@@ -97,4 +89,76 @@
     sym_->end_hook_();
 }
 
+SymbolizedStack *Symbolizer::SymbolizePC(uptr addr) {
+  BlockingMutexLock l(&mu_);
+  const char *module_name;
+  uptr module_offset;
+  SymbolizedStack *res = SymbolizedStack::New(addr);
+  if (!PlatformFindModuleNameAndOffsetForAddress(addr, &module_name,
+                                                 &module_offset))
+    return res;
+  // Always fill data about module name and offset.
+  res->info.FillModuleInfo(module_name, module_offset);
+  for (auto iter = Iterator(&tools_); iter.hasNext();) {
+    auto *tool = iter.next();
+    SymbolizerScope sym_scope(this);
+    if (tool->SymbolizePC(addr, res)) {
+      return res;
+    }
+  }
+  return res;
+}
+
+bool Symbolizer::SymbolizeData(uptr addr, DataInfo *info) {
+  BlockingMutexLock l(&mu_);
+  const char *module_name;
+  uptr module_offset;
+  if (!PlatformFindModuleNameAndOffsetForAddress(addr, &module_name,
+                                                 &module_offset))
+    return false;
+  info->Clear();
+  info->module = internal_strdup(module_name);
+  info->module_offset = module_offset;
+  for (auto iter = Iterator(&tools_); iter.hasNext();) {
+    auto *tool = iter.next();
+    SymbolizerScope sym_scope(this);
+    if (tool->SymbolizeData(addr, info)) {
+      return true;
+    }
+  }
+  return true;
+}
+
+bool Symbolizer::GetModuleNameAndOffsetForPC(uptr pc, const char **module_name,
+                                 uptr *module_address) {
+  BlockingMutexLock l(&mu_);
+  return PlatformFindModuleNameAndOffsetForAddress(pc, module_name,
+                                                   module_address);
+}
+
+void Symbolizer::Flush() {
+  BlockingMutexLock l(&mu_);
+  for (auto iter = Iterator(&tools_); iter.hasNext();) {
+    auto *tool = iter.next();
+    SymbolizerScope sym_scope(this);
+    tool->Flush();
+  }
+}
+
+const char *Symbolizer::Demangle(const char *name) {
+  BlockingMutexLock l(&mu_);
+  for (auto iter = Iterator(&tools_); iter.hasNext();) {
+    auto *tool = iter.next();
+    SymbolizerScope sym_scope(this);
+    if (const char *demangled = tool->Demangle(name))
+      return demangled;
+  }
+  return PlatformDemangle(name);
+}
+
+void Symbolizer::PrepareForSandboxing() {
+  BlockingMutexLock l(&mu_);
+  PlatformPrepareForSandboxing();
+}
+
 }  // namespace __sanitizer
diff --git a/lib/sanitizer_common/sanitizer_symbolizer.h b/lib/sanitizer_common/sanitizer_symbolizer.h
index 3a80774..225da70 100644
--- a/lib/sanitizer_common/sanitizer_symbolizer.h
+++ b/lib/sanitizer_common/sanitizer_symbolizer.h
@@ -43,8 +43,7 @@
   AddressInfo();
   // Deletes all strings and resets all fields.
   void Clear();
-  void FillAddressAndModuleInfo(uptr addr, const char *mod_name,
-                                uptr mod_offset);
+  void FillModuleInfo(const char *mod_name, uptr mod_offset);
 };
 
 // Linked list of symbolized frames (each frame is described by AddressInfo).
@@ -74,6 +73,8 @@
   void Clear();
 };
 
+class SymbolizerTool;
+
 class Symbolizer {
  public:
   /// Initialize and return platform-specific implementation of symbolizer
@@ -81,26 +82,22 @@
   static Symbolizer *GetOrInit();
   // Returns a list of symbolized frames for a given address (containing
   // all inlined functions, if necessary).
-  virtual SymbolizedStack *SymbolizePC(uptr address) {
-    return SymbolizedStack::New(address);
-  }
-  virtual bool SymbolizeData(uptr address, DataInfo *info) {
-    return false;
-  }
-  virtual bool GetModuleNameAndOffsetForPC(uptr pc, const char **module_name,
-                                           uptr *module_address) {
-    return false;
-  }
-  virtual bool CanReturnFileLineInfo() {
-    return false;
+  SymbolizedStack *SymbolizePC(uptr address);
+  bool SymbolizeData(uptr address, DataInfo *info);
+  bool GetModuleNameAndOffsetForPC(uptr pc, const char **module_name,
+                                   uptr *module_address);
+  const char *GetModuleNameForPc(uptr pc) {
+    const char *module_name = 0;
+    uptr unused;
+    if (GetModuleNameAndOffsetForPC(pc, &module_name, &unused))
+      return module_name;
+    return nullptr;
   }
   // Release internal caches (if any).
-  virtual void Flush() {}
+  void Flush();
   // Attempts to demangle the provided C++ mangled name.
-  virtual const char *Demangle(const char *name) {
-    return name;
-  }
-  virtual void PrepareForSandboxing() {}
+  const char *Demangle(const char *name);
+  void PrepareForSandboxing();
 
   // Allow user to install hooks that would be called before/after Symbolizer
   // does the actual file/line info fetching. Specific sanitizers may need this
@@ -115,14 +112,28 @@
  private:
   /// Platform-specific function for creating a Symbolizer object.
   static Symbolizer *PlatformInit();
-  /// Initialize the symbolizer in a disabled state.  Not thread safe.
-  static Symbolizer *Disable();
+
+  virtual bool PlatformFindModuleNameAndOffsetForAddress(
+      uptr address, const char **module_name, uptr *module_offset) {
+    UNIMPLEMENTED();
+  }
+  // Platform-specific default demangler, must not return nullptr.
+  virtual const char *PlatformDemangle(const char *name) { UNIMPLEMENTED(); }
+  virtual void PlatformPrepareForSandboxing() { UNIMPLEMENTED(); }
 
   static Symbolizer *symbolizer_;
   static StaticSpinMutex init_mu_;
 
+  // Mutex locked from public methods of |Symbolizer|, so that the internals
+  // (including individual symbolizer tools and platform-specific methods) are
+  // always synchronized.
+  BlockingMutex mu_;
+
+  typedef IntrusiveList<SymbolizerTool>::Iterator Iterator;
+  IntrusiveList<SymbolizerTool> tools_;
+
  protected:
-  Symbolizer();
+  explicit Symbolizer(IntrusiveList<SymbolizerTool> tools);
 
   static LowLevelAllocator symbolizer_allocator_;
 
@@ -137,61 +148,6 @@
   };
 };
 
-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);
-  char *SendCommand(bool is_data, const char *module_name,
-                    uptr module_offset) override;
-
- private:
-  bool Restart();
-  char *SendCommandImpl(bool is_data, const char *module_name,
-                        uptr module_offset);
-  bool ReadFromSymbolizer(char *buffer, uptr max_length);
-  bool WriteToSymbolizer(const char *buffer, uptr length);
-  bool StartSymbolizerSubprocess();
-
-  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_;
-};
-
 }  // namespace __sanitizer
 
 #endif  // SANITIZER_SYMBOLIZER_H
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_internal.h b/lib/sanitizer_common/sanitizer_symbolizer_internal.h
new file mode 100644
index 0000000..66ae809
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_symbolizer_internal.h
@@ -0,0 +1,109 @@
+//===-- sanitizer_symbolizer_internal.h -------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Header for internal classes and functions to be used by implementations of
+// symbolizers.
+//
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_SYMBOLIZER_INTERNAL_H
+#define SANITIZER_SYMBOLIZER_INTERNAL_H
+
+#include "sanitizer_symbolizer.h"
+
+namespace __sanitizer {
+
+// Parsing helpers, 'str' is searched for delimiter(s) and a string or uptr
+// is extracted. When extracting a string, a newly allocated (using
+// InternalAlloc) and null-terminataed buffer is returned. They return a pointer
+// to the next characted after the found delimiter.
+const char *ExtractToken(const char *str, const char *delims, char **result);
+const char *ExtractInt(const char *str, const char *delims, int *result);
+const char *ExtractUptr(const char *str, const char *delims, uptr *result);
+const char *ExtractTokenUpToDelimiter(const char *str, const char *delimiter,
+                                      char **result);
+
+const char *DemangleCXXABI(const char *name);
+
+// SymbolizerTool is an interface that is implemented by individual "tools"
+// that can perform symbolication (external llvm-symbolizer, libbacktrace,
+// Windows DbgHelp symbolizer, etc.).
+class SymbolizerTool {
+ public:
+  // The main |Symbolizer| class implements a "fallback chain" of symbolizer
+  // tools. In a request to symbolize an address, if one tool returns false,
+  // the next tool in the chain will be tried.
+  SymbolizerTool *next;
+
+  SymbolizerTool() : next(nullptr) { }
+
+  // Can't declare pure virtual functions in sanitizer runtimes:
+  // __cxa_pure_virtual might be unavailable.
+
+  // The |stack| parameter is inout. It is pre-filled with the address,
+  // module base and module offset values and is to be used to construct
+  // other stack frames.
+  virtual bool SymbolizePC(uptr addr, SymbolizedStack *stack) {
+    UNIMPLEMENTED();
+  }
+
+  // The |info| parameter is inout. It is pre-filled with the module base
+  // and module offset values.
+  virtual bool SymbolizeData(uptr addr, DataInfo *info) {
+    UNIMPLEMENTED();
+  }
+
+  virtual void Flush() {}
+
+  // Return nullptr to fallback to the default platform-specific demangler.
+  virtual const char *Demangle(const char *name) {
+    return nullptr;
+  }
+};
+
+// 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:
+  explicit SymbolizerProcess(const char *path, bool use_forkpty = false);
+  const char *SendCommand(const char *command);
+
+ private:
+  bool Restart();
+  const char *SendCommandImpl(const char *command);
+  bool ReadFromSymbolizer(char *buffer, uptr max_length);
+  bool WriteToSymbolizer(const char *buffer, uptr length);
+  bool StartSymbolizerSubprocess();
+
+  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_;
+  bool use_forkpty_;
+};
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_SYMBOLIZER_INTERNAL_H
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc b/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc
index 9317a78..5735466 100644
--- a/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc
+++ b/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc
@@ -33,6 +33,8 @@
 
 namespace __sanitizer {
 
+static char *DemangleAlloc(const char *name, bool always_alloc);
+
 #if SANITIZER_LIBBACKTRACE
 
 namespace {
@@ -86,17 +88,20 @@
 struct SymbolizeCodeCallbackArg {
   SymbolizedStack *first;
   SymbolizedStack *last;
-  const char *module_name;
-  uptr module_offset;
+  uptr frames_symbolized;
 
-  void append(SymbolizedStack *f) {
-    if (last != nullptr) {
-      last->next = f;
-      last = f;
-    } else {
-      first = f;
-      last = f;
+  AddressInfo *get_new_frame(uintptr_t addr) {
+    CHECK(last);
+    if (frames_symbolized > 0) {
+      SymbolizedStack *cur = SymbolizedStack::New(addr);
+      AddressInfo *info = &cur->info;
+      info->FillModuleInfo(first->info.module, first->info.module_offset);
+      last->next = cur;
+      last = cur;
     }
+    CHECK_EQ(addr, first->info.address);
+    CHECK_EQ(addr, last->info.address);
+    return &last->info;
   }
 };
 
@@ -106,15 +111,12 @@
                                        const char *function) {
   SymbolizeCodeCallbackArg *cdata = (SymbolizeCodeCallbackArg *)vdata;
   if (function) {
-    SymbolizedStack *cur = SymbolizedStack::New(addr);
-    cdata->append(cur);
-    AddressInfo *info = &cur->info;
-    info->FillAddressAndModuleInfo(addr, cdata->module_name,
-                                   cdata->module_offset);
-    info->function = LibbacktraceSymbolizer::Demangle(function, true);
+    AddressInfo *info = cdata->get_new_frame(addr);
+    info->function = DemangleAlloc(function, /*always_alloc*/ true);
     if (filename)
       info->file = internal_strdup(filename);
     info->line = lineno;
+    cdata->frames_symbolized++;
   }
   return 0;
 }
@@ -123,12 +125,9 @@
                                   const char *symname, uintptr_t, uintptr_t) {
   SymbolizeCodeCallbackArg *cdata = (SymbolizeCodeCallbackArg *)vdata;
   if (symname) {
-    SymbolizedStack *cur = SymbolizedStack::New(addr);
-    cdata->append(cur);
-    AddressInfo *info = &cur->info;
-    info->FillAddressAndModuleInfo(addr, cdata->module_name,
-                                   cdata->module_offset);
-    info->function = LibbacktraceSymbolizer::Demangle(symname, true);
+    AddressInfo *info = cdata->get_new_frame(addr);
+    info->function = DemangleAlloc(symname, /*always_alloc*/ true);
+    cdata->frames_symbolized++;
   }
 }
 
@@ -136,7 +135,7 @@
                                   uintptr_t symval, uintptr_t symsize) {
   DataInfo *info = (DataInfo *)vdata;
   if (symname && symval) {
-    info->name = LibbacktraceSymbolizer::Demangle(symname, true);
+    info->name = DemangleAlloc(symname, /*always_alloc*/ true);
     info->start = symval;
     info->size = symsize;
   }
@@ -156,21 +155,18 @@
   return new(*alloc) LibbacktraceSymbolizer(state);
 }
 
-SymbolizedStack *LibbacktraceSymbolizer::SymbolizeCode(uptr addr,
-                                                       const char *module_name,
-                                                       uptr module_offset) {
+bool LibbacktraceSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
   SymbolizeCodeCallbackArg data;
-  data.first = nullptr;
-  data.last = nullptr;
-  data.module_name = module_name;
-  data.module_offset = module_offset;
+  data.first = stack;
+  data.last = stack;
+  data.frames_symbolized = 0;
   backtrace_pcinfo((backtrace_state *)state_, addr, SymbolizeCodePCInfoCallback,
                    ErrorCallback, &data);
-  if (data.first)
-    return data.first;
+  if (data.frames_symbolized > 0)
+    return true;
   backtrace_syminfo((backtrace_state *)state_, addr, SymbolizeCodeCallback,
                     ErrorCallback, &data);
-  return data.first;
+  return (data.frames_symbolized > 0);
 }
 
 bool LibbacktraceSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
@@ -185,11 +181,9 @@
   return 0;
 }
 
-SymbolizedStack *LibbacktraceSymbolizer::SymbolizeCode(uptr addr,
-                                                       const char *module_name,
-                                                       uptr module_offset) {
+bool LibbacktraceSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
   (void)state_;
-  return nullptr;
+  return false;
 }
 
 bool LibbacktraceSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
@@ -198,7 +192,7 @@
 
 #endif  // SANITIZER_LIBBACKTRACE
 
-char *LibbacktraceSymbolizer::Demangle(const char *name, bool always_alloc) {
+static char *DemangleAlloc(const char *name, bool always_alloc) {
 #if SANITIZER_LIBBACKTRACE && SANITIZER_CP_DEMANGLE
   if (char *demangled = CplusV3Demangle(name))
     return demangled;
@@ -208,4 +202,8 @@
   return 0;
 }
 
+const char *LibbacktraceSymbolizer::Demangle(const char *name) {
+  return DemangleAlloc(name, /*always_alloc*/ false);
+}
+
 }  // namespace __sanitizer
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h b/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h
index 1ff0050..00b465a 100644
--- a/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h
+++ b/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h
@@ -16,7 +16,7 @@
 
 #include "sanitizer_platform.h"
 #include "sanitizer_common.h"
-#include "sanitizer_symbolizer.h"
+#include "sanitizer_symbolizer_internal.h"
 
 #ifndef SANITIZER_LIBBACKTRACE
 # define SANITIZER_LIBBACKTRACE 0
@@ -28,17 +28,16 @@
 
 namespace __sanitizer {
 
-class LibbacktraceSymbolizer {
+class LibbacktraceSymbolizer : public SymbolizerTool {
  public:
   static LibbacktraceSymbolizer *get(LowLevelAllocator *alloc);
 
-  SymbolizedStack *SymbolizeCode(uptr addr, const char *module_name,
-                                 uptr module_offset);
+  bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
 
-  bool SymbolizeData(uptr addr, DataInfo *info);
+  bool SymbolizeData(uptr addr, DataInfo *info) override;
 
   // May return NULL if demangling failed.
-  static char *Demangle(const char *name, bool always_alloc = false);
+  const char *Demangle(const char *name) override;
 
  private:
   explicit LibbacktraceSymbolizer(void *state) : state_(state) {}
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc b/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc
index 63c9356..f1e2289 100644
--- a/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc
@@ -11,18 +11,62 @@
 // run-time libraries.
 //===----------------------------------------------------------------------===//
 
+#include "sanitizer_allocator_internal.h"
 #include "sanitizer_internal_defs.h"
-#include "sanitizer_symbolizer.h"
+#include "sanitizer_symbolizer_internal.h"
 
 namespace __sanitizer {
 
+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;
+}
+
+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;
+}
+
+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;
+}
+
+const char *ExtractTokenUpToDelimiter(const char *str, const char *delimiter,
+                                      char **result) {
+  const char *found_delimiter = internal_strstr(str, delimiter);
+  uptr prefix_len =
+      found_delimiter ? found_delimiter - str : internal_strlen(str);
+  *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 += internal_strlen(delimiter);
+  return prefix_end;
+}
+
 Symbolizer *Symbolizer::GetOrInit() {
   SpinMutexLock l(&init_mu_);
   if (symbolizer_)
     return symbolizer_;
-  if ((symbolizer_ = PlatformInit()))
-    return symbolizer_;
-  return Disable();
+  symbolizer_ = PlatformInit();
+  CHECK(symbolizer_);
+  return symbolizer_;
 }
 
 }  // namespace __sanitizer
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_mac.cc b/lib/sanitizer_common/sanitizer_symbolizer_mac.cc
new file mode 100644
index 0000000..c2397ef
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_symbolizer_mac.cc
@@ -0,0 +1,151 @@
+//===-- sanitizer_symbolizer_mac.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 various sanitizers' runtime libraries.
+//
+// Implementation of Mac-specific "atos" symbolizer.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_MAC
+
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_mac.h"
+#include "sanitizer_symbolizer_mac.h"
+
+namespace __sanitizer {
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <util.h>
+
+bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
+  Dl_info info;
+  int result = dladdr((const void *)addr, &info);
+  if (!result) return false;
+  const char *demangled = DemangleCXXABI(info.dli_sname);
+  stack->info.function = internal_strdup(demangled);
+  return true;
+}
+
+bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
+  return false;
+}
+
+class AtosSymbolizerProcess : public SymbolizerProcess {
+ public:
+  explicit AtosSymbolizerProcess(const char *path, pid_t parent_pid)
+      : SymbolizerProcess(path, /*use_forkpty*/ true),
+        parent_pid_(parent_pid) {}
+
+ private:
+  bool ReachedEndOfOutput(const char *buffer, uptr length) const override {
+    return (length >= 1 && buffer[length - 1] == '\n');
+  }
+
+  void ExecuteWithDefaultArgs(const char *path_to_binary) const override {
+    // The `atos` binary has some issues with DYLD_ROOT_PATH on i386.
+    unsetenv("DYLD_ROOT_PATH");
+
+    char pid_str[16];
+    internal_snprintf(pid_str, sizeof(pid_str), "%d", parent_pid_);
+    if (GetMacosVersion() == MACOS_VERSION_MAVERICKS) {
+      // On Mavericks atos prints a deprecation warning which we suppress by
+      // passing -d. The warning isn't present on other OSX versions, even the
+      // newer ones.
+      execl(path_to_binary, path_to_binary, "-p", pid_str, "-d", (char *)0);
+    } else {
+      execl(path_to_binary, path_to_binary, "-p", pid_str, (char *)0);
+    }
+  }
+
+  pid_t parent_pid_;
+};
+
+static const char *kAtosErrorMessages[] = {
+  "atos cannot examine process",
+  "unable to get permission to examine process",
+  "An admin user name and password is required",
+  "could not load inserted library",
+  "architecture mismatch between analysis process",
+};
+
+static bool IsAtosErrorMessage(const char *str) {
+  for (uptr i = 0; i < ARRAY_SIZE(kAtosErrorMessages); i++) {
+    if (internal_strstr(str, kAtosErrorMessages[i])) {
+      return true;
+    }
+  }
+  return false;
+}
+
+static bool ParseCommandOutput(const char *str, SymbolizedStack *res) {
+  // Trim ending newlines.
+  char *trim;
+  ExtractTokenUpToDelimiter(str, "\n", &trim);
+
+  // The line from `atos` is in one of these formats:
+  //   myfunction (in library.dylib) (sourcefile.c:17)
+  //   myfunction (in library.dylib) + 0x1fe
+  //   0xdeadbeef (in library.dylib) + 0x1fe
+  //   0xdeadbeef (in library.dylib)
+  //   0xdeadbeef
+
+  if (IsAtosErrorMessage(trim)) {
+    Report("atos returned an error: %s\n", trim);
+    InternalFree(trim);
+    return false;
+  }
+
+  const char *rest = trim;
+  char *function_name;
+  rest = ExtractTokenUpToDelimiter(rest, " (in ", &function_name);
+  if (internal_strncmp(function_name, "0x", 2) != 0)
+    res->info.function = function_name;
+  else
+    InternalFree(function_name);
+  rest = ExtractTokenUpToDelimiter(rest, ") ", &res->info.module);
+
+  if (rest[0] == '(') {
+    rest++;
+    rest = ExtractTokenUpToDelimiter(rest, ":", &res->info.file);
+    char *extracted_line_number;
+    rest = ExtractTokenUpToDelimiter(rest, ")", &extracted_line_number);
+    res->info.line = internal_atoll(extracted_line_number);
+    InternalFree(extracted_line_number);
+  }
+
+  InternalFree(trim);
+  return true;
+}
+
+AtosSymbolizer::AtosSymbolizer(const char *path, LowLevelAllocator *allocator)
+    : process_(new(*allocator) AtosSymbolizerProcess(path, getpid())) {}
+
+bool AtosSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
+  if (!process_) return false;
+  char command[32];
+  internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
+  const char *buf = process_->SendCommand(command);
+  if (!buf) return false;
+  if (!ParseCommandOutput(buf, stack)) {
+    process_ = nullptr;
+    return false;
+  }
+  return true;
+}
+
+bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { return false; }
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_MAC
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_mac.h b/lib/sanitizer_common/sanitizer_symbolizer_mac.h
new file mode 100644
index 0000000..068644d
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_symbolizer_mac.h
@@ -0,0 +1,48 @@
+//===-- sanitizer_symbolizer_mac.h ------------------------------*- C++ -*-===//
+//
+//                     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 various sanitizers' runtime libraries.
+//
+// Header for Mac-specific "atos" symbolizer.
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_SYMBOLIZER_MAC_H
+#define SANITIZER_SYMBOLIZER_MAC_H
+
+#include "sanitizer_platform.h"
+#if SANITIZER_MAC
+
+#include "sanitizer_symbolizer_internal.h"
+
+namespace __sanitizer {
+
+class DlAddrSymbolizer : public SymbolizerTool {
+ public:
+  bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
+  bool SymbolizeData(uptr addr, DataInfo *info) override;
+};
+
+class AtosSymbolizerProcess;
+
+class AtosSymbolizer : public SymbolizerTool {
+ public:
+  explicit AtosSymbolizer(const char *path, LowLevelAllocator *allocator);
+
+  bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
+  bool SymbolizeData(uptr addr, DataInfo *info) override;
+
+ private:
+  AtosSymbolizerProcess *process_;
+};
+
+} // namespace __sanitizer
+
+#endif  // SANITIZER_MAC
+
+#endif // SANITIZER_SYMBOLIZER_MAC_H
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc b/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc
index deb3429..189b0ab 100644
--- a/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc
@@ -21,12 +21,10 @@
 #include "sanitizer_linux.h"
 #include "sanitizer_placement_new.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>
 
 // C++ demangling function, as required by Itanium C++ ABI. This is weak,
@@ -41,7 +39,7 @@
 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,217 +53,6 @@
   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;
-}
-
-SymbolizerProcess::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 *SymbolizerProcess::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;
-}
-
-bool SymbolizerProcess::Restart() {
-  if (input_fd_ != kInvalidFd)
-    internal_close(input_fd_);
-  if (output_fd_ != kInvalidFd)
-    internal_close(output_fd_);
-  return StartSymbolizerSubprocess();
-}
-
-char *SymbolizerProcess::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 SymbolizerProcess::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 SymbolizerProcess::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 SymbolizerProcess::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;
-}
-
-
 // Parses one or more two-line strings in the following format:
 //   <function_name>
 //   <file_name>:<line_number>[:<column_number>]
@@ -288,8 +75,7 @@
       top_frame = false;
     } else {
       cur = SymbolizedStack::New(res->info.address);
-      cur->info.FillAddressAndModuleInfo(res->info.address, res->info.module,
-                                         res->info.module_offset);
+      cur->info.FillModuleInfo(res->info.module, res->info.module_offset);
       last->next = cur;
       last = cur;
     }
@@ -343,13 +129,6 @@
   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' &&
@@ -377,6 +156,44 @@
   }
 };
 
+class LLVMSymbolizer : public SymbolizerTool {
+ public:
+  explicit LLVMSymbolizer(const char *path, LowLevelAllocator *allocator)
+      : symbolizer_process_(new(*allocator) LLVMSymbolizerProcess(path)) {}
+
+  bool SymbolizePC(uptr addr, SymbolizedStack *stack) override {
+    if (const char *buf = SendCommand(/*is_data*/ false, stack->info.module,
+                                      stack->info.module_offset)) {
+      ParseSymbolizePCOutput(buf, stack);
+      return true;
+    }
+    return false;
+  }
+
+  bool SymbolizeData(uptr addr, DataInfo *info) override {
+    if (const char *buf =
+            SendCommand(/*is_data*/ true, info->module, info->module_offset)) {
+      ParseSymbolizeDataOutput(buf, info);
+      info->start += (addr - info->module_offset);  // Add the base address.
+      return true;
+    }
+    return false;
+  }
+
+ private:
+  const char *SendCommand(bool is_data, const char *module_name,
+                          uptr module_offset) {
+    CHECK(module_name);
+    internal_snprintf(buffer_, kBufferSize, "%s\"%s\" 0x%zx\n",
+                      is_data ? "DATA " : "", module_name, module_offset);
+    return symbolizer_process_->SendCommand(buffer_);
+  }
+
+  LLVMSymbolizerProcess *symbolizer_process_;
+  static const uptr kBufferSize = 16 * 1024;
+  char buffer_[kBufferSize];
+};
+
 class Addr2LineProcess : public SymbolizerProcess {
  public:
   Addr2LineProcess(const char *path, const char *module_name)
@@ -385,15 +202,6 @@
   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;
@@ -413,16 +221,28 @@
   const char *module_name_;  // Owned, leaked.
 };
 
-class Addr2LinePool : public ExternalSymbolizerInterface {
+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 ==
@@ -436,10 +256,13 @@
           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\n", module_offset);
+    return addr2line->SendCommand(buffer_);
   }
 
- private:
+  static const uptr kBufferSize = 32;
   const char *addr2line_path_;
   LowLevelAllocator *allocator_;
   InternalMmapVector<Addr2LineProcess*> addr2line_pool_;
@@ -460,10 +283,8 @@
                                    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) {
@@ -472,20 +293,29 @@
     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;) {
@@ -512,146 +342,35 @@
 };
 #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) {}
+  explicit POSIXSymbolizer(IntrusiveList<SymbolizerTool> tools)
+      : Symbolizer(tools), n_modules_(0), modules_fresh_(false) {}
 
-  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;
-    }
-
-    ParseSymbolizePCOutput(str, res);
-    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(addr, info))
-        return true;
-    }
-    const char *str = SendCommand(true, module_name, module_offset);
-    if (str == 0)
-      return true;
-    ParseSymbolizeDataOutput(str, info);
-    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);
+ private:
+  const char *PlatformDemangle(const char *name) override {
     return DemangleCXXABI(name);
   }
 
-  void PrepareForSandboxing() override {
+  void PlatformPrepareForSandboxing() 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_);
+    if (!modules_fresh_) {
+      for (uptr i = 0; i < n_modules_; i++)
+        modules_[i].clear();
       n_modules_ = GetListOfModules(modules_, kMaxNumberOfModuleContexts,
                                     /* filter */ 0);
       CHECK_GT(n_modules_, 0);
@@ -675,9 +394,9 @@
     return 0;
   }
 
-  bool FindModuleNameAndOffsetForAddress(uptr address, const char **module_name,
-                                         uptr *module_offset) {
-    mu_.CheckLocked();
+  bool PlatformFindModuleNameAndOffsetForAddress(uptr address,
+                                                 const char **module_name,
+                                                 uptr *module_offset) override {
     LoadedModule *module = FindModuleForAddress(address);
     if (module == 0)
       return false;
@@ -688,53 +407,94 @@
 
   // 16K loaded modules should be enough for everyone.
   static const uptr kMaxNumberOfModuleContexts = 1 << 14;
-  LoadedModule *modules_;  // Array of module descriptions is leaked.
+  LoadedModule modules_[kMaxNumberOfModuleContexts];
   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);
+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();
   }
-  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_);
-          }
-        }
-      }
+  // 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;
+}
 
-  return new(symbolizer_allocator_) POSIXSymbolizer(
-      external_symbolizer, internal_symbolizer, libbacktrace_symbolizer);
+static void ChooseSymbolizerTools(IntrusiveList<SymbolizerTool> *list,
+                                  LowLevelAllocator *allocator) {
+  if (!common_flags()->symbolize) {
+    VReport(2, "Symbolizer is disabled.\n");
+    return;
+  }
+  if (SymbolizerTool *tool = InternalSymbolizer::get(allocator)) {
+    VReport(2, "Using internal symbolizer.\n");
+    list->push_back(tool);
+    return;
+  }
+  if (SymbolizerTool *tool = LibbacktraceSymbolizer::get(allocator)) {
+    VReport(2, "Using libbacktrace symbolizer.\n");
+    list->push_back(tool);
+    return;
+  }
+
+  if (SymbolizerTool *tool = ChooseExternalSymbolizer(allocator)) {
+    list->push_back(tool);
+  } else {
+    VReport(2, "No internal or external symbolizer found.\n");
+  }
+
+#if SANITIZER_MAC
+  VReport(2, "Using dladdr symbolizer.\n");
+  list->push_back(new(*allocator) DlAddrSymbolizer());
+#endif  // SANITIZER_MAC
+}
+
+Symbolizer *Symbolizer::PlatformInit() {
+  IntrusiveList<SymbolizerTool> list;
+  list.clear();
+  ChooseSymbolizerTools(&list, &symbolizer_allocator_);
+  return new(symbolizer_allocator_) POSIXSymbolizer(list);
 }
 
 }  // namespace __sanitizer
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_process_libcdep.cc b/lib/sanitizer_common/sanitizer_symbolizer_process_libcdep.cc
new file mode 100644
index 0000000..98d16e0
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_symbolizer_process_libcdep.cc
@@ -0,0 +1,228 @@
+//===-- sanitizer_symbolizer_process_libcdep.cc ---------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementation of SymbolizerProcess used by external symbolizers.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_POSIX
+#include "sanitizer_symbolizer_internal.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
+
+namespace __sanitizer {
+
+SymbolizerProcess::SymbolizerProcess(const char *path, bool use_forkpty)
+    : path_(path),
+      input_fd_(kInvalidFd),
+      output_fd_(kInvalidFd),
+      times_restarted_(0),
+      failed_to_start_(false),
+      reported_invalid_path_(false),
+      use_forkpty_(use_forkpty) {
+  CHECK(path_);
+  CHECK_NE(path_[0], '\0');
+}
+
+const char *SymbolizerProcess::SendCommand(const char *command) {
+  for (; times_restarted_ < kMaxTimesRestarted; times_restarted_++) {
+    // Start or restart symbolizer if we failed to send command to it.
+    if (const char *res = SendCommandImpl(command))
+      return res;
+    Restart();
+  }
+  if (!failed_to_start_) {
+    Report("WARNING: Failed to use and restart external symbolizer!\n");
+    failed_to_start_ = true;
+  }
+  return 0;
+}
+
+bool SymbolizerProcess::Restart() {
+  if (input_fd_ != kInvalidFd)
+    internal_close(input_fd_);
+  if (output_fd_ != kInvalidFd)
+    internal_close(output_fd_);
+  return StartSymbolizerSubprocess();
+}
+
+const char *SymbolizerProcess::SendCommandImpl(const char *command) {
+  if (input_fd_ == kInvalidFd || output_fd_ == kInvalidFd)
+      return 0;
+  if (!WriteToSymbolizer(command, internal_strlen(command)))
+      return 0;
+  if (!ReadFromSymbolizer(buffer_, kBufferSize))
+      return 0;
+  return buffer_;
+}
+
+bool SymbolizerProcess::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 SymbolizerProcess::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 SymbolizerProcess::StartSymbolizerSubprocess() {
+  if (!FileExists(path_)) {
+    if (!reported_invalid_path_) {
+      Report("WARNING: invalid path to external symbolizer!\n");
+      reported_invalid_path_ = true;
+    }
+    return false;
+  }
+
+  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.
+      ExecuteWithDefaultArgs(path_);
+      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
+    // 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().
+    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;
+}
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_POSIX
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_win.cc b/lib/sanitizer_common/sanitizer_symbolizer_win.cc
index ed96a3a..67ed4b3 100644
--- a/lib/sanitizer_common/sanitizer_symbolizer_win.cc
+++ b/lib/sanitizer_common/sanitizer_symbolizer_win.cc
@@ -18,139 +18,153 @@
 #include <dbghelp.h>
 #pragma comment(lib, "dbghelp.lib")
 
-#include "sanitizer_symbolizer.h"
+#include "sanitizer_symbolizer_win.h"
+#include "sanitizer_symbolizer_internal.h"
 
 namespace __sanitizer {
 
-class WinSymbolizer : public Symbolizer {
- public:
-  WinSymbolizer() : initialized_(false) {}
+namespace {
 
-  SymbolizedStack *SymbolizePC(uptr addr) override {
-    SymbolizedStack *frame = SymbolizedStack::New(addr);
+bool is_dbghelp_initialized = false;
 
-    BlockingMutexLock l(&dbghelp_mu_);
-    InitializeIfNeeded();
+bool TrySymInitialize() {
+  SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES);
+  return SymInitialize(GetCurrentProcess(), 0, TRUE);
+  // FIXME: We don't call SymCleanup() on exit yet - should we?
+}
 
-    // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
-    char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)];
-    PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
-    symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
-    symbol->MaxNameLen = MAX_SYM_NAME;
-    DWORD64 offset = 0;
-    BOOL got_objname = SymFromAddr(GetCurrentProcess(),
-                                   (DWORD64)addr, &offset, symbol);
-    if (!got_objname)
-      return frame;
-
-    DWORD unused;
-    IMAGEHLP_LINE64 line_info;
-    line_info.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
-    BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(), (DWORD64)addr,
-                                             &unused, &line_info);
-    frame->info.function = internal_strdup(symbol->Name);
-    frame->info.function_offset = (uptr)offset;
-    if (got_fileline) {
-      frame->info.file = internal_strdup(line_info.FileName);
-      frame->info.line = line_info.LineNumber;
-    }
-
-    IMAGEHLP_MODULE64 mod_info;
-    internal_memset(&mod_info, 0, sizeof(mod_info));
-    mod_info.SizeOfStruct = sizeof(mod_info);
-    if (SymGetModuleInfo64(GetCurrentProcess(), addr, &mod_info))
-      frame->info.FillAddressAndModuleInfo(addr, mod_info.ImageName,
-                                           addr - (uptr)mod_info.BaseOfImage);
-    return frame;
+// Initializes DbgHelp library, if it's not yet initialized. Calls to this
+// function should be synchronized with respect to other calls to DbgHelp API
+// (e.g. from WinSymbolizerTool).
+void InitializeDbgHelpIfNeeded() {
+  if (is_dbghelp_initialized)
+    return;
+  if (!TrySymInitialize()) {
+    // OK, maybe the client app has called SymInitialize already.
+    // That's a bit unfortunate for us as all the DbgHelp functions are
+    // single-threaded and we can't coordinate with the app.
+    // FIXME: Can we stop the other threads at this point?
+    // Anyways, we have to reconfigure stuff to make sure that SymInitialize
+    // has all the appropriate options set.
+    // Cross our fingers and reinitialize DbgHelp.
+    Report("*** WARNING: Failed to initialize DbgHelp!              ***\n");
+    Report("*** Most likely this means that the app is already      ***\n");
+    Report("*** using DbgHelp, possibly with incompatible flags.    ***\n");
+    Report("*** Due to technical reasons, symbolization might crash ***\n");
+    Report("*** or produce wrong results.                           ***\n");
+    SymCleanup(GetCurrentProcess());
+    TrySymInitialize();
   }
+  is_dbghelp_initialized = true;
 
-  bool CanReturnFileLineInfo() override {
+  // When an executable is run from a location different from the one where it
+  // was originally built, we may not see the nearby PDB files.
+  // To work around this, let's append the directory of the main module
+  // to the symbol search path.  All the failures below are not fatal.
+  const size_t kSymPathSize = 2048;
+  static wchar_t path_buffer[kSymPathSize + 1 + MAX_PATH];
+  if (!SymGetSearchPathW(GetCurrentProcess(), path_buffer, kSymPathSize)) {
+    Report("*** WARNING: Failed to SymGetSearchPathW ***\n");
+    return;
+  }
+  size_t sz = wcslen(path_buffer);
+  if (sz) {
+    CHECK_EQ(0, wcscat_s(path_buffer, L";"));
+    sz++;
+  }
+  DWORD res = GetModuleFileNameW(NULL, path_buffer + sz, MAX_PATH);
+  if (res == 0 || res == MAX_PATH) {
+    Report("*** WARNING: Failed to getting the EXE directory ***\n");
+    return;
+  }
+  // Write the zero character in place of the last backslash to get the
+  // directory of the main module at the end of path_buffer.
+  wchar_t *last_bslash = wcsrchr(path_buffer + sz, L'\\');
+  CHECK_NE(last_bslash, 0);
+  *last_bslash = L'\0';
+  if (!SymSetSearchPathW(GetCurrentProcess(), path_buffer)) {
+    Report("*** WARNING: Failed to SymSetSearchPathW\n");
+    return;
+  }
+}
+
+}  // namespace
+
+bool WinSymbolizerTool::SymbolizePC(uptr addr, SymbolizedStack *frame) {
+  InitializeDbgHelpIfNeeded();
+
+  // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
+  char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)];
+  PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
+  symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
+  symbol->MaxNameLen = MAX_SYM_NAME;
+  DWORD64 offset = 0;
+  BOOL got_objname = SymFromAddr(GetCurrentProcess(),
+                                 (DWORD64)addr, &offset, symbol);
+  if (!got_objname)
+    return false;
+
+  DWORD unused;
+  IMAGEHLP_LINE64 line_info;
+  line_info.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+  BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(), (DWORD64)addr,
+                                           &unused, &line_info);
+  frame->info.function = internal_strdup(symbol->Name);
+  frame->info.function_offset = (uptr)offset;
+  if (got_fileline) {
+    frame->info.file = internal_strdup(line_info.FileName);
+    frame->info.line = line_info.LineNumber;
+  }
+  return true;
+}
+
+const char *WinSymbolizerTool::Demangle(const char *name) {
+  CHECK(is_dbghelp_initialized);
+  static char demangle_buffer[1000];
+  if (name[0] == '\01' &&
+      UnDecorateSymbolName(name + 1, demangle_buffer, sizeof(demangle_buffer),
+                           UNDNAME_NAME_ONLY))
+    return demangle_buffer;
+  else
+    return name;
+}
+
+bool FindModuleNameAndOffsetForAddress(uptr addr, const char **module_name,
+                                       uptr *module_offset) {
+  InitializeDbgHelpIfNeeded();
+
+  IMAGEHLP_MODULE64 mod_info;
+  internal_memset(&mod_info, 0, sizeof(mod_info));
+  mod_info.SizeOfStruct = sizeof(mod_info);
+  if (SymGetModuleInfo64(GetCurrentProcess(), addr, &mod_info)) {
+    *module_name = mod_info.ImageName;
+    *module_offset = addr - (uptr)mod_info.BaseOfImage;
     return true;
   }
+  return false;
+}
 
-  const char *Demangle(const char *name) override {
-    CHECK(initialized_);
-    static char demangle_buffer[1000];
-    if (name[0] == '\01' &&
-        UnDecorateSymbolName(name + 1, demangle_buffer, sizeof(demangle_buffer),
-                             UNDNAME_NAME_ONLY))
-      return demangle_buffer;
-    else
-      return name;
-  }
-
-  // FIXME: Implement GetModuleNameAndOffsetForPC().
+// TODO(kuba.brecka): To be merged with POSIXSymbolizer.
+class WinSymbolizer : public Symbolizer {
+ public:
+  explicit WinSymbolizer(IntrusiveList<SymbolizerTool> tools)
+      : Symbolizer(tools) {}
 
  private:
-  void InitializeIfNeeded() {
-    if (initialized_)
-      return;
-    if (!TrySymInitialize()) {
-      // OK, maybe the client app has called SymInitialize already.
-      // That's a bit unfortunate for us as all the DbgHelp functions are
-      // single-threaded and we can't coordinate with the app.
-      // FIXME: Can we stop the other threads at this point?
-      // Anyways, we have to reconfigure stuff to make sure that SymInitialize
-      // has all the appropriate options set.
-      // Cross our fingers and reinitialize DbgHelp.
-      Report("*** WARNING: Failed to initialize DbgHelp!              ***\n");
-      Report("*** Most likely this means that the app is already      ***\n");
-      Report("*** using DbgHelp, possibly with incompatible flags.    ***\n");
-      Report("*** Due to technical reasons, symbolization might crash ***\n");
-      Report("*** or produce wrong results.                           ***\n");
-      SymCleanup(GetCurrentProcess());
-      TrySymInitialize();
-    }
-    initialized_ = true;
-
-    // When an executable is run from a location different from the one where it
-    // was originally built, we may not see the nearby PDB files.
-    // To work around this, let's append the directory of the main module
-    // to the symbol search path.  All the failures below are not fatal.
-    const size_t kSymPathSize = 2048;
-    static wchar_t path_buffer[kSymPathSize + 1 + MAX_PATH];
-    if (!SymGetSearchPathW(GetCurrentProcess(), path_buffer, kSymPathSize)) {
-      Report("*** WARNING: Failed to SymGetSearchPathW ***\n");
-      return;
-    }
-    size_t sz = wcslen(path_buffer);
-    if (sz) {
-      CHECK_EQ(0, wcscat_s(path_buffer, L";"));
-      sz++;
-    }
-    DWORD res = GetModuleFileNameW(NULL, path_buffer + sz, MAX_PATH);
-    if (res == 0 || res == MAX_PATH) {
-      Report("*** WARNING: Failed to getting the EXE directory ***\n");
-      return;
-    }
-    // Write the zero character in place of the last backslash to get the
-    // directory of the main module at the end of path_buffer.
-    wchar_t *last_bslash = wcsrchr(path_buffer + sz, L'\\');
-    CHECK_NE(last_bslash, 0);
-    *last_bslash = L'\0';
-    if (!SymSetSearchPathW(GetCurrentProcess(), path_buffer)) {
-      Report("*** WARNING: Failed to SymSetSearchPathW\n");
-      return;
-    }
+  bool PlatformFindModuleNameAndOffsetForAddress(
+      uptr addr, const char **module_name, uptr *module_offset) override {
+    return ::FindModuleNameAndOffsetForAddress(addr, module_name,
+                                               module_offset);
   }
-
-  bool TrySymInitialize() {
-    SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES);
-    return SymInitialize(GetCurrentProcess(), 0, TRUE);
-    // FIXME: We don't call SymCleanup() on exit yet - should we?
-  }
-
-  // All DbgHelp functions are single threaded, so we should use a mutex to
-  // serialize accesses.
-  BlockingMutex dbghelp_mu_;
-  bool initialized_;
+  const char *PlatformDemangle(const char *name) override { return name; }
+  void PlatformPrepareForSandboxing() override { }
 };
 
 Symbolizer *Symbolizer::PlatformInit() {
-  static bool called_once = false;
-  CHECK(!called_once && "Shouldn't create more than one symbolizer");
-  called_once = true;
-  return new(symbolizer_allocator_) WinSymbolizer();
+  IntrusiveList<SymbolizerTool> list;
+  list.clear();
+  list.push_back(new(symbolizer_allocator_) WinSymbolizerTool());
+  return new(symbolizer_allocator_) WinSymbolizer(list);
 }
 
 }  // namespace __sanitizer
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_win.h b/lib/sanitizer_common/sanitizer_symbolizer_win.h
new file mode 100644
index 0000000..72ac5e5
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_symbolizer_win.h
@@ -0,0 +1,31 @@
+//===-- sanitizer_symbolizer_win.h ------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Header file for the Windows symbolizer tool.
+//
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_SYMBOLIZER_WIN_H
+#define SANITIZER_SYMBOLIZER_WIN_H
+
+#include "sanitizer_symbolizer_internal.h"
+
+namespace __sanitizer {
+
+class WinSymbolizerTool : public SymbolizerTool {
+ public:
+  bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
+  bool SymbolizeData(uptr addr, DataInfo *info) override {
+    return false;
+  }
+  const char *Demangle(const char *name) override;
+};
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_SYMBOLIZER_WIN_H
diff --git a/lib/sanitizer_common/sanitizer_win.cc b/lib/sanitizer_common/sanitizer_win.cc
index 335ceca..3ec9084 100644
--- a/lib/sanitizer_common/sanitizer_win.cc
+++ b/lib/sanitizer_common/sanitizer_win.cc
@@ -313,6 +313,19 @@
   return 0;
 }
 
+uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
+  // Nothing here for now.
+  return 0;
+}
+
+bool IsPathSeparator(const char c) {
+  return c == '\\' || c == '/';
+}
+
+bool IsAbsolutePath(const char *path) {
+  UNIMPLEMENTED();
+}
+
 void SleepForSeconds(int seconds) {
   Sleep(seconds * 1000);
 }
@@ -368,7 +381,7 @@
   UNIMPLEMENTED();
 }
 
-uptr OpenFile(const char *filename, bool write) {
+uptr OpenFile(const char *filename, FileAccessMode mode) {
   UNIMPLEMENTED();
 }
 
@@ -618,6 +631,23 @@
   return true;
 }
 
+SignalContext SignalContext::Create(void *siginfo, void *context) {
+  EXCEPTION_RECORD *exception_record = (EXCEPTION_RECORD*)siginfo;
+  CONTEXT *context_record = (CONTEXT*)context;
+
+  uptr pc = (uptr)exception_record->ExceptionAddress;
+#ifdef _WIN64
+  uptr bp = (uptr)context_record->Rbp;
+  uptr sp = (uptr)context_record->Rsp;
+#else
+  uptr bp = (uptr)context_record->Ebp;
+  uptr sp = (uptr)context_record->Esp;
+#endif
+  uptr access_addr = exception_record->ExceptionInformation[1];
+
+  return SignalContext(context, access_addr, pc, sp, bp);
+}
+
 }  // namespace __sanitizer
 
 #endif  // _WIN32
diff --git a/lib/sanitizer_common/scripts/check_lint.sh b/lib/sanitizer_common/scripts/check_lint.sh
index 7ed05d7..9108a81 100755
--- a/lib/sanitizer_common/scripts/check_lint.sh
+++ b/lib/sanitizer_common/scripts/check_lint.sh
@@ -17,7 +17,6 @@
 
 # Filters
 # TODO: remove some of these filters
-LLVM_LINT_FILTER=-,+whitespace
 COMMON_LINT_FILTER=-build/include,-build/header_guard,-legal/copyright,-whitespace/comments,-readability/casting,\
 -build/namespaces
 ASAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/int
@@ -60,9 +59,6 @@
   ${LITLINT} "$@" 2>>$ERROR_LOG
 }
 
-run_lint ${LLVM_LINT_FILTER} --filter=${LLVM_LINT_FILTER} \
-  lib/Transforms/Instrumentation/*Sanitizer.cpp &
-
 if [ "${COMPILER_RT}" = "" ]; then
   COMPILER_RT=projects/compiler-rt
 fi
diff --git a/lib/sanitizer_common/scripts/sancov.py b/lib/sanitizer_common/scripts/sancov.py
index 566116e..53180d0 100755
--- a/lib/sanitizer_common/scripts/sancov.py
+++ b/lib/sanitizer_common/scripts/sancov.py
@@ -9,35 +9,77 @@
 import bisect
 import os.path
 
-prog_name = "";
+prog_name = ""
 
 def Usage():
   print >> sys.stderr, "Usage: \n" + \
-      " " + prog_name + " merge file1 [file2 ...]  > output\n" \
-      " " + prog_name + " print file1 [file2 ...]\n" \
-      " " + prog_name + " unpack file1 [file2 ...]\n" \
-      " " + prog_name + " rawunpack file1 [file2 ...]\n"
+      " " + prog_name + " [32|64] merge file1 [file2 ...]  > output\n" \
+      " " + prog_name + " [32|64] print file1 [file2 ...]\n" \
+      " " + prog_name + " [32|64] unpack file1 [file2 ...]\n" \
+      " " + prog_name + " [32|64] rawunpack file1 [file2 ...]\n"
   exit(1)
 
+def CheckBits(bits):
+  if bits != 32 and bits != 64:
+    raise Exception("Wrong bitness: %d" % bits)
+
+def TypeCodeForBits(bits):
+  CheckBits(bits)
+  return 'L' if bits == 64 else 'I'
+
+kMagic32SecondHalf = 0xFFFFFF32;
+kMagic64SecondHalf = 0xFFFFFF64;
+kMagicFirstHalf    = 0xC0BFFFFF;
+
+def MagicForBits(bits):
+  CheckBits(bits)
+  if sys.byteorder == 'little':
+    return [kMagic64SecondHalf if bits == 64 else kMagic32SecondHalf, kMagicFirstHalf]
+  else:
+    return [kMagicFirstHalf, kMagic64SecondHalf if bits == 64 else kMagic32SecondHalf]
+
+def ReadMagicAndReturnBitness(f, path):
+  magic_bytes = f.read(8)
+  magic_words = struct.unpack('II', magic_bytes);
+  bits = 0
+  idx = 1 if sys.byteorder == 'little' else 0
+  if magic_words[idx] == kMagicFirstHalf:
+    if magic_words[1-idx] == kMagic64SecondHalf:
+      bits = 64
+    elif magic_words[1-idx] == kMagic32SecondHalf:
+      bits = 32
+  if bits == 0:
+    raise Exception('Bad magic word in %s' % path)
+  return bits
+
 def ReadOneFile(path):
   with open(path, mode="rb") as f:
     f.seek(0, 2)
     size = f.tell()
     f.seek(0, 0)
-    s = set(array.array('I', f.read(size)))
-  print >>sys.stderr, "%s: read %d PCs from %s" % (prog_name, size / 4, path)
+    if size < 8:
+      raise Exception('File %s is short (< 8 bytes)' % path)
+    bits = ReadMagicAndReturnBitness(f, path)
+    size -= 8
+    s = array.array(TypeCodeForBits(bits), f.read(size))
+  print >>sys.stderr, "%s: read %d %d-bit PCs from %s" % (prog_name, size * 8 / bits, bits, path)
   return s
 
 def Merge(files):
   s = set()
   for f in files:
-    s = s.union(ReadOneFile(f))
+    s = s.union(set(ReadOneFile(f)))
   print >> sys.stderr, "%s: %d files merged; %d PCs total" % \
     (prog_name, len(files), len(s))
   return sorted(s)
 
 def PrintFiles(files):
-  s = Merge(files)
+  if len(files) > 1:
+    s = Merge(files)
+  else:  # If there is just on file, print the PCs in order.
+    s = ReadOneFile(files[0])
+    print >> sys.stderr, "%s: 1 file merged; %d PCs total" % \
+      (prog_name, len(s))
   for i in s:
     print "0x%x" % i
 
@@ -45,7 +87,11 @@
   if sys.stdout.isatty():
     Usage()
   s = Merge(files)
-  a = array.array('I', s)
+  bits = 32
+  if max(s) > 0xFFFFFFFF:
+    bits = 64
+  array.array('I', MagicForBits(bits)).tofile(sys.stdout)
+  a = array.array(TypeCodeForBits(bits), s)
   a.tofile(sys.stdout)
 
 
@@ -82,6 +128,8 @@
   with open(map_path, mode="rt") as f_map:
     print >> sys.stderr, "%s: reading map %s" % (prog_name, map_path)
     bits = int(f_map.readline())
+    if bits != 32 and bits != 64:
+      raise Exception('Wrong bits size in the map')
     for line in f_map:
       parts = line.rstrip().split()
       mem_map.append((int(parts[0], 16),
@@ -97,11 +145,7 @@
     f.seek(0, 2)
     size = f.tell()
     f.seek(0, 0)
-    if bits == 64:
-      typecode = 'L'
-    else:
-      typecode = 'I'
-    pcs = array.array(typecode, f.read(size))
+    pcs = array.array(TypeCodeForBits(bits), f.read(size))
     mem_map_pcs = [[] for i in range(0, len(mem_map))]
 
     for pc in pcs:
@@ -119,9 +163,10 @@
       assert path.endswith('.sancov.raw')
       dst_path = module_path + '.' + os.path.basename(path)[:-4]
       print >> sys.stderr, "%s: writing %d PCs to %s" % (prog_name, len(pc_list), dst_path)
-      arr = array.array('I')
+      arr = array.array(TypeCodeForBits(bits))
       arr.fromlist(sorted(pc_list))
       with open(dst_path, 'ab') as f2:
+        array.array('I', MagicForBits(bits)).tofile(f2)
         arr.tofile(f2)
 
 def RawUnpack(files):
@@ -135,6 +180,7 @@
   prog_name = sys.argv[0]
   if len(sys.argv) <= 2:
     Usage();
+
   if sys.argv[1] == "print":
     PrintFiles(sys.argv[2:])
   elif sys.argv[1] == "merge":
diff --git a/lib/sanitizer_common/tests/CMakeLists.txt b/lib/sanitizer_common/tests/CMakeLists.txt
index 75008db..b062c5a 100644
--- a/lib/sanitizer_common/tests/CMakeLists.txt
+++ b/lib/sanitizer_common/tests/CMakeLists.txt
@@ -28,6 +28,7 @@
   sanitizer_stacktrace_test.cc
   sanitizer_stoptheworld_test.cc
   sanitizer_suppressions_test.cc
+  sanitizer_symbolizer_test.cc
   sanitizer_test_main.cc
   sanitizer_thread_registry_test.cc)
 
diff --git a/lib/sanitizer_common/tests/sanitizer_libc_test.cc b/lib/sanitizer_common/tests/sanitizer_libc_test.cc
index 8712d2c..279f6fe 100644
--- a/lib/sanitizer_common/tests/sanitizer_libc_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_libc_test.cc
@@ -78,14 +78,14 @@
 
   char tmpfile[128];
   temp_file_name(tmpfile, sizeof(tmpfile), "sanitizer_common.fileops.tmp.");
-  uptr openrv = OpenFile(tmpfile, true);
+  uptr openrv = OpenFile(tmpfile, WrOnly);
   EXPECT_FALSE(internal_iserror(openrv));
   fd_t fd = openrv;
   EXPECT_EQ(len1, internal_write(fd, str1, len1));
   EXPECT_EQ(len2, internal_write(fd, str2, len2));
   internal_close(fd);
 
-  openrv = OpenFile(tmpfile, false);
+  openrv = OpenFile(tmpfile, RdOnly);
   EXPECT_FALSE(internal_iserror(openrv));
   fd = openrv;
   uptr fsize = internal_filesize(fd);
@@ -134,7 +134,7 @@
   char tmpfile[128];
   temp_file_name(tmpfile, sizeof(tmpfile),
                  "sanitizer_common.internalmmapwithoffset.tmp.");
-  uptr res = OpenFile(tmpfile, true);
+  uptr res = OpenFile(tmpfile, RdWr);
   ASSERT_FALSE(internal_iserror(res));
   fd_t fd = res;
 
diff --git a/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc b/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc
index ac820c2..a8bd726 100644
--- a/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc
@@ -30,11 +30,11 @@
   }
 
   void *mapping;
-  uptr *fake_stack;
+  uhwptr *fake_stack;
   const uptr fake_stack_size = 10;
-  uptr start_pc;
-  uptr fake_top;
-  uptr fake_bottom;
+  uhwptr start_pc;
+  uhwptr fake_top;
+  uhwptr fake_bottom;
   BufferedStackTrace trace;
 };
 
@@ -48,7 +48,7 @@
   Mprotect((uptr)mapping, ps);
 
   // Unwinder may peek 1 word down from the starting FP.
-  fake_stack = (uptr *)((uptr)mapping + ps + sizeof(uptr));
+  fake_stack = (uhwptr *)((uptr)mapping + ps + sizeof(uhwptr));
 
   // Fill an array of pointers with fake fp+retaddr pairs.  Frame pointers have
   // even indices.
@@ -57,12 +57,12 @@
     fake_stack[i+1] = PC(i + 1); // retaddr
   }
   // Mark the last fp point back up to terminate the stack trace.
-  fake_stack[RoundDownTo(fake_stack_size - 1, 2)] = (uptr)&fake_stack[0];
+  fake_stack[RoundDownTo(fake_stack_size - 1, 2)] = (uhwptr)&fake_stack[0];
 
   // Top is two slots past the end because FastUnwindStack subtracts two.
-  fake_top = (uptr)&fake_stack[fake_stack_size + 2];
+  fake_top = (uhwptr)&fake_stack[fake_stack_size + 2];
   // Bottom is one slot before the start because FastUnwindStack uses >.
-  fake_bottom = (uptr)mapping;
+  fake_bottom = (uhwptr)mapping;
   start_pc = PC(0);
 }
 
@@ -85,7 +85,7 @@
 // From: http://code.google.com/p/address-sanitizer/issues/detail?id=162
 TEST_F(FastUnwindTest, FramePointerLoop) {
   // Make one fp point to itself.
-  fake_stack[4] = (uptr)&fake_stack[4];
+  fake_stack[4] = (uhwptr)&fake_stack[4];
   if (!TryFastUnwind(kStackTraceMax))
     return;
   // Should get all on-stack retaddrs up to the 4th slot and start_pc.
@@ -114,7 +114,7 @@
     return;
   EXPECT_EQ(1U, trace.size);
   EXPECT_EQ(start_pc, trace.trace[0]);
-  EXPECT_EQ((uptr)&fake_stack[0], trace.top_frame_bp);
+  EXPECT_EQ((uhwptr)&fake_stack[0], trace.top_frame_bp);
 }
 
 TEST_F(FastUnwindTest, ZeroFramesStackTrace) {
@@ -127,7 +127,7 @@
 TEST_F(FastUnwindTest, FPBelowPrevFP) {
   // The next FP points to unreadable memory inside the stack limits, but below
   // current FP.
-  fake_stack[0] = (uptr)&fake_stack[-50];
+  fake_stack[0] = (uhwptr)&fake_stack[-50];
   fake_stack[1] = PC(1);
   if (!TryFastUnwind(3))
     return;
diff --git a/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc b/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc
index b6786ba..802af39 100644
--- a/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc
@@ -189,6 +189,16 @@
   pthread_mutex_destroy(&advanced_incrementer_thread_exit_mutex);
 }
 
+static void SegvCallback(const SuspendedThreadsList &suspended_threads_list,
+                         void *argument) {
+  *(volatile int*)0x1234 = 0;
+}
+
+TEST(StopTheWorld, SegvInCallback) {
+  // Test that tracer thread catches SIGSEGV.
+  StopTheWorld(&SegvCallback, NULL);
+}
+
 }  // namespace __sanitizer
 
 #endif  // SANITIZER_LINUX && defined(__x86_64__)
diff --git a/lib/sanitizer_common/tests/sanitizer_symbolizer_test.cc b/lib/sanitizer_common/tests/sanitizer_symbolizer_test.cc
new file mode 100644
index 0000000..429ac59
--- /dev/null
+++ b/lib/sanitizer_common/tests/sanitizer_symbolizer_test.cc
@@ -0,0 +1,58 @@
+//===-- sanitizer_symbolizer_test.cc --------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Tests for sanitizer_symbolizer.h and sanitizer_symbolizer_internal.h
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_allocator_internal.h"
+#include "sanitizer_common/sanitizer_symbolizer_internal.h"
+#include "gtest/gtest.h"
+
+namespace __sanitizer {
+
+TEST(Symbolizer, ExtractToken) {
+  char *token;
+  const char *rest;
+
+  rest = ExtractToken("a;b;c", ";", &token);
+  EXPECT_STREQ("a", token);
+  EXPECT_STREQ("b;c", rest);
+  InternalFree(token);
+
+  rest = ExtractToken("aaa-bbb.ccc", ";.-*", &token);
+  EXPECT_STREQ("aaa", token);
+  EXPECT_STREQ("bbb.ccc", rest);
+  InternalFree(token);
+}
+
+TEST(Symbolizer, ExtractInt) {
+  int token;
+  const char *rest = ExtractInt("123,456;789", ";,", &token);
+  EXPECT_EQ(123, token);
+  EXPECT_STREQ("456;789", rest);
+}
+
+TEST(Symbolizer, ExtractUptr) {
+  uptr token;
+  const char *rest = ExtractUptr("123,456;789", ";,", &token);
+  EXPECT_EQ(123U, token);
+  EXPECT_STREQ("456;789", rest);
+}
+
+TEST(Symbolizer, ExtractTokenUpToDelimiter) {
+  char *token;
+  const char *rest =
+      ExtractTokenUpToDelimiter("aaa-+-bbb-+-ccc", "-+-", &token);
+  EXPECT_STREQ("aaa", token);
+  EXPECT_STREQ("bbb-+-ccc", rest);
+  InternalFree(token);
+}
+
+}  // namespace __sanitizer