Merge "Move libc_logging.cpp over to CachedProperty."
diff --git a/libc/Android.bp b/libc/Android.bp
index 8c6f596..9773542 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -129,6 +129,7 @@
     defaults: ["libc_defaults"],
     srcs: [
         "tzcode/**/*.c",
+        "tzcode/bionic.cpp",
         "upstream-openbsd/lib/libc/time/wcsftime.c", // tzcode doesn't include wcsftime, so we use the OpenBSD one.
     ],
 
@@ -1849,6 +1850,7 @@
 cc_defaults {
     name: "crt_defaults",
     defaults: ["linux_bionic_supported"],
+    vendor_available: true,
 
     no_default_compiler_flags: true,
 
@@ -1878,6 +1880,7 @@
 cc_defaults {
     name: "crt_so_defaults",
 
+    vendor_available: true,
     arch: {
         mips: {
             cflags: ["-fPIC"],
@@ -2177,13 +2180,70 @@
 }
 
 ndk_library {
-    name: "libc.ndk",
+    name: "libc",
     symbol_file: "libc.map.txt",
     first_version: "9",
 }
 
+llndk_library {
+    name: "libc",
+    symbol_file: "libc.map.txt",
+    export_headers_as_system: true,
+    export_preprocessed_headers: ["include"],
+    arch: {
+        arm: {
+            export_include_dirs: [
+                "arch-arm/include",
+                "kernel/uapi",
+                "kernel/uapi/asm-arm",
+                "kernel/android/uapi",
+            ],
+        },
+        arm64: {
+            export_include_dirs: [
+                "arch-arm64/include",
+                "kernel/uapi",
+                "kernel/uapi/asm-arm64",
+                "kernel/android/uapi",
+            ],
+        },
+        mips: {
+            export_include_dirs: [
+                "arch-mips/include",
+                "kernel/uapi",
+                "kernel/uapi/asm-mips",
+                "kernel/android/uapi",
+            ],
+        },
+        mips64: {
+            export_include_dirs: [
+                "arch-mips64/include",
+                "kernel/uapi",
+                "kernel/uapi/asm-mips",
+                "kernel/android/uapi",
+            ],
+        },
+        x86: {
+            export_include_dirs: [
+                "arch-x86/include",
+                "kernel/uapi",
+                "kernel/uapi/asm-x86",
+                "kernel/android/uapi",
+            ],
+        },
+        x86_64: {
+            export_include_dirs: [
+                "arch-x86_64/include",
+                "kernel/uapi",
+                "kernel/uapi/asm-x86",
+                "kernel/android/uapi",
+            ],
+        },
+    },
+}
+
 ndk_library {
-    name: "libstdc++.ndk",
+    name: "libstdc++",
     symbol_file: "libstdc++.map.txt",
     first_version: "9",
 }
diff --git a/libc/bionic/system_properties.cpp b/libc/bionic/system_properties.cpp
index a4faf85..2a42390 100644
--- a/libc/bionic/system_properties.cpp
+++ b/libc/bionic/system_properties.cpp
@@ -54,6 +54,7 @@
 #include <sys/_system_properties.h>
 #include <sys/system_properties.h>
 
+#include "private/ErrnoRestorer.h"
 #include "private/bionic_futex.h"
 #include "private/bionic_lock.h"
 #include "private/bionic_macros.h"
@@ -1096,6 +1097,9 @@
 }
 
 int __system_properties_init() {
+  // This is called from __libc_init_common, and should leave errno at 0 (http://b/37248982).
+  ErrnoRestorer errno_restorer;
+
   if (initialized) {
     list_foreach(contexts, [](context_node* l) { l->reset_access(); });
     return 0;
diff --git a/libc/malloc_debug/BacktraceData.cpp b/libc/malloc_debug/BacktraceData.cpp
index 3d46bf0..752d507 100644
--- a/libc/malloc_debug/BacktraceData.cpp
+++ b/libc/malloc_debug/BacktraceData.cpp
@@ -51,26 +51,26 @@
 
 BacktraceData::BacktraceData(DebugData* debug_data, const Config& config, size_t* offset)
     : OptionData(debug_data) {
-  size_t hdr_len = sizeof(BacktraceHeader) + sizeof(uintptr_t) * config.backtrace_frames;
+  size_t hdr_len = sizeof(BacktraceHeader) + sizeof(uintptr_t) * config.backtrace_frames();
   alloc_offset_ = *offset;
   *offset += BIONIC_ALIGN(hdr_len, MINIMUM_ALIGNMENT_BYTES);
 }
 
 bool BacktraceData::Initialize(const Config& config) {
-  enabled_ = config.backtrace_enabled;
-  if (config.backtrace_enable_on_signal) {
+  enabled_ = config.backtrace_enabled();
+  if (config.backtrace_enable_on_signal()) {
     struct sigaction enable_act;
     memset(&enable_act, 0, sizeof(enable_act));
 
     enable_act.sa_sigaction = EnableToggle;
     enable_act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
     sigemptyset(&enable_act.sa_mask);
-    if (sigaction(config.backtrace_signal, &enable_act, nullptr) != 0) {
+    if (sigaction(config.backtrace_signal(), &enable_act, nullptr) != 0) {
       error_log("Unable to set up backtrace signal enable function: %s", strerror(errno));
       return false;
     }
     info_log("%s: Run: 'kill -%d %d' to enable backtracing.", getprogname(),
-             config.backtrace_signal, getpid());
+             config.backtrace_signal(), getpid());
   }
   return true;
 }
diff --git a/libc/malloc_debug/BacktraceData.h b/libc/malloc_debug/BacktraceData.h
index dbc3989..6dee505 100644
--- a/libc/malloc_debug/BacktraceData.h
+++ b/libc/malloc_debug/BacktraceData.h
@@ -36,7 +36,7 @@
 #include "OptionData.h"
 
 // Forward declarations.
-struct Config;
+class Config;
 
 class BacktraceData : public OptionData {
  public:
diff --git a/libc/malloc_debug/Config.cpp b/libc/malloc_debug/Config.cpp
index cb75bd6..df453a9 100644
--- a/libc/malloc_debug/Config.cpp
+++ b/libc/malloc_debug/Config.cpp
@@ -26,6 +26,7 @@
  * SUCH DAMAGE.
  */
 
+#include <assert.h>
 #include <ctype.h>
 #include <errno.h>
 #include <limits.h>
@@ -66,174 +67,227 @@
 static constexpr size_t MAX_RECORD_ALLOCS = 50000000;
 static constexpr const char DEFAULT_RECORD_ALLOCS_FILE[] = "/data/local/tmp/record_allocs.txt";
 
-struct Option {
-  Option(std::string name, uint64_t option, bool combo_option = false, bool* config = nullptr)
-      : name(name), option(option), combo_option(combo_option), config(config) {}
-  virtual ~Option() = default;
+const std::unordered_map<std::string, Config::OptionInfo> Config::kOptions = {
+    {"guard",
+      {FRONT_GUARD | REAR_GUARD, &Config::SetGuard},
+    },
+    {"front_guard",
+      {FRONT_GUARD, &Config::SetFrontGuard},
+    },
+    {"rear_guard",
+      {REAR_GUARD, &Config::SetRearGuard},
+    },
 
-  std::string name;
+    {"backtrace",
+      {BACKTRACE | TRACK_ALLOCS, &Config::SetBacktrace},
+    },
+    {"backtrace_enable_on_signal",
+      {BACKTRACE | TRACK_ALLOCS, &Config::SetBacktraceEnableOnSignal},
+    },
 
-  uint64_t option;
-  // If set to true, then all of the options following are set on until
-  // the combo_option value is set to false.
-  bool combo_option = false;
-  bool* config;
+    {"fill",
+      {FILL_ON_ALLOC | FILL_ON_FREE, &Config::SetFill},
+    },
+    {"fill_on_alloc",
+      {FILL_ON_ALLOC, &Config::SetFillOnAlloc},
+    },
+    {"fill_on_free",
+      {FILL_ON_FREE, &Config::SetFillOnFree},
+    },
 
-  virtual bool ParseValue(const std::string& option_name, const std::string& value) const;
+    {"expand_alloc",
+      {EXPAND_ALLOC, &Config::SetExpandAlloc},
+    },
 
-  virtual void SetDefault() const { }
+    {"free_track",
+      {FREE_TRACK | FILL_ON_FREE, &Config::SetFreeTrack},
+    },
+    {"free_track_backtrace_num_frames",
+      {0, &Config::SetFreeTrackBacktraceNumFrames},
+    },
+
+    {"leak_track",
+      {LEAK_TRACK | TRACK_ALLOCS, &Config::VerifyValueEmpty},
+    },
+
+    {"record_allocs",
+      {RECORD_ALLOCS, &Config::SetRecordAllocs},
+    },
+    {"record_allocs_file",
+      {0, &Config::SetRecordAllocsFile},
+    },
 };
 
-bool Option::ParseValue(const std::string& option_name, const std::string& raw_value) const {
-  if (!raw_value.empty()) {
-    error_log("%s: value set for option '%s' which does not take a value",
-              getprogname(), option_name.c_str());
-    return false;
-  }
-  return true;
-}
-
-struct OptionString : public Option {
-  OptionString(std::string name, uint64_t option, std::string default_value,
-                std::string* value, bool combo_option = false,
-                bool* config = nullptr)
-      : Option(name, option, combo_option, config), default_value(default_value), value(value) {}
-  virtual ~OptionString() = default;
-
-  std::string default_value;
-  std::string* value;
-
-  bool ParseValue(const std::string& option_name, const std::string& value) const override;
-
-  void SetDefault() const override { if (value) *value = default_value; }
-};
-
-bool OptionString::ParseValue(const std::string&, const std::string& raw_value) const {
-  if (!raw_value.empty()) {
-    *value = raw_value;
-  }
-  return true;
-}
-
-struct OptionSizeT : public Option {
-  OptionSizeT(std::string name, size_t default_value, size_t min_value, size_t max_value,
-          uint64_t option, size_t* value, bool combo_option = false, bool* config = nullptr)
-      : Option(name, option, combo_option, config), default_value(default_value),
-        min_value(min_value), max_value(max_value), value(value) {}
-  virtual ~OptionSizeT() = default;
-
-  size_t default_value;
-  size_t min_value;
-  size_t max_value;
-
-  size_t* value;
-
-  bool ParseValue(const std::string& option_name, const std::string& value) const override;
-
-  void SetDefault() const override { if (value) *value = default_value; }
-};
-
-bool OptionSizeT::ParseValue(const std::string& option_name, const std::string& raw_value) const {
-  if (raw_value.empty()) {
-    // Value should have been set by the SetDefault() pass.
-    return true;
-  }
+bool Config::ParseValue(const std::string& option, const std::string& value,
+                        size_t min_value, size_t max_value, size_t* parsed_value) const {
+  assert(!value.empty());
 
   // Parse the value into a size_t value.
   errno = 0;
   char* end;
-  long parsed_value = strtol(raw_value.c_str(), &end, 10);
+  long long_value = strtol(value.c_str(), &end, 10);
   if (errno != 0) {
-    error_log("%s: bad value for option '%s': %s", getprogname(), option_name.c_str(),
+    error_log("%s: bad value for option '%s': %s", getprogname(), option.c_str(),
               strerror(errno));
     return false;
   }
-  if (end == raw_value.c_str()) {
-    error_log("%s: bad value for option '%s'", getprogname(), option_name.c_str());
+  if (end == value.c_str()) {
+    error_log("%s: bad value for option '%s'", getprogname(), option.c_str());
     return false;
   }
-  if (static_cast<size_t>(end - raw_value.c_str()) != raw_value.size()) {
+  if (static_cast<size_t>(end - value.c_str()) != value.size()) {
     error_log("%s: bad value for option '%s', non space found after option: %s",
-              getprogname(), option_name.c_str(), end);
+              getprogname(), option.c_str(), end);
     return false;
   }
-  if (parsed_value < 0) {
+  if (long_value < 0) {
     error_log("%s: bad value for option '%s', value cannot be negative: %ld",
-              getprogname(), option_name.c_str(), parsed_value);
+              getprogname(), option.c_str(), long_value);
     return false;
   }
 
-  if (static_cast<size_t>(parsed_value) < min_value) {
+  if (static_cast<size_t>(long_value) < min_value) {
     error_log("%s: bad value for option '%s', value must be >= %zu: %ld",
-              getprogname(), option_name.c_str(), min_value, parsed_value);
+              getprogname(), option.c_str(), min_value, long_value);
     return false;
   }
-  if (static_cast<size_t>(parsed_value) > max_value) {
+  if (static_cast<size_t>(long_value) > max_value) {
     error_log("%s: bad value for option '%s', value must be <= %zu: %ld",
-              getprogname(), option_name.c_str(), max_value, parsed_value);
+              getprogname(), option.c_str(), max_value, long_value);
     return false;
   }
-  *value = static_cast<size_t>(parsed_value);
+  *parsed_value = static_cast<size_t>(long_value);
   return true;
 }
 
-class PropertyParser {
- public:
-  explicit PropertyParser(const char* property) : cur_(property) {}
+bool Config::ParseValue(const std::string& option, const std::string& value, size_t default_value,
+                        size_t min_value, size_t max_value, size_t* new_value) const {
+  if (value.empty()) {
+    *new_value = default_value;
+    return true;
+  }
+  return ParseValue(option, value, min_value, max_value, new_value);
+}
 
-  bool Get(std::string* property, std::string* value);
+bool Config::SetGuard(const std::string& option, const std::string& value) {
+  if (value.empty()) {
+    // Set the defaults.
+    front_guard_bytes_ = DEFAULT_GUARD_BYTES;
+    rear_guard_bytes_ = DEFAULT_GUARD_BYTES;
+    return true;
+  }
 
-  bool Done() { return done_; }
-
-  void LogUsage();
-
- private:
-  const char* cur_ = nullptr;
-
-  bool done_ = false;
-
-  DISALLOW_COPY_AND_ASSIGN(PropertyParser);
-};
-
-bool PropertyParser::Get(std::string* property, std::string* value) {
-  // Process each property name we can find.
-  while (isspace(*cur_))
-    ++cur_;
-
-  if (*cur_ == '\0') {
-    done_ = true;
+  if (!ParseValue(option, value, 1, MAX_GUARD_BYTES, &rear_guard_bytes_)) {
     return false;
   }
 
-  const char* start = cur_;
-  while (!isspace(*cur_) && *cur_ != '=' && *cur_ != '\0')
-    ++cur_;
+  // It's necessary to align the front guard to MINIMUM_ALIGNMENT_BYTES to
+  // make sure that the header is aligned properly.
+  front_guard_bytes_ = BIONIC_ALIGN(rear_guard_bytes_, MINIMUM_ALIGNMENT_BYTES);
+  return true;
+}
 
-  *property = std::string(start, cur_ - start);
+bool Config::SetFrontGuard(const std::string& option, const std::string& value) {
+  if (!ParseValue(option, value, DEFAULT_GUARD_BYTES, 1, MAX_GUARD_BYTES, &front_guard_bytes_)) {
+    return false;
+  }
+  // It's necessary to align the front guard to MINIMUM_ALIGNMENT_BYTES to
+  // make sure that the header is aligned properly.
+  front_guard_bytes_ = BIONIC_ALIGN(front_guard_bytes_, MINIMUM_ALIGNMENT_BYTES);
+  return true;
+}
 
-  // Skip any spaces after the name.
-  while (isspace(*cur_))
-    ++cur_;
+bool Config::SetRearGuard(const std::string& option, const std::string& value) {
+  return ParseValue(option, value, DEFAULT_GUARD_BYTES, 1, MAX_GUARD_BYTES, &rear_guard_bytes_);
+}
 
-  value->clear();
-  if (*cur_ == '=') {
-    ++cur_;
-    // Skip the space after the equal.
-    while (isspace(*cur_))
-      ++cur_;
+bool Config::SetFill(const std::string& option, const std::string& value) {
+  if (value.empty()) {
+    // Set the defaults.
+    fill_on_alloc_bytes_ = SIZE_MAX;
+    fill_on_free_bytes_ = SIZE_MAX;
+    return true;
+  }
 
-    start = cur_;
-    while (!isspace(*cur_) && *cur_ != '\0')
-      ++cur_;
+  if (!ParseValue(option, value, 1, SIZE_MAX, &fill_on_alloc_bytes_)) {
+    return false;
+  }
+  fill_on_free_bytes_ = fill_on_alloc_bytes_;
+  return true;
+}
 
-    if (cur_ != start) {
-      *value = std::string(start, cur_ - start);
-    }
+bool Config::SetFillOnAlloc(const std::string& option, const std::string& value) {
+  return ParseValue(option, value, SIZE_MAX, 1, SIZE_MAX, &fill_on_alloc_bytes_);
+}
+
+bool Config::SetFillOnFree(const std::string& option, const std::string& value) {
+  return ParseValue(option, value, SIZE_MAX, 1, SIZE_MAX, &fill_on_free_bytes_);
+}
+
+bool Config::SetBacktrace(const std::string& option, const std::string& value) {
+  backtrace_enabled_ = true;
+  return ParseValue(option, value, DEFAULT_BACKTRACE_FRAMES, 1, MAX_BACKTRACE_FRAMES,
+                    &backtrace_frames_);
+}
+
+bool Config::SetBacktraceEnableOnSignal(const std::string& option, const std::string& value) {
+  backtrace_enable_on_signal_ = true;
+  return ParseValue(option, value, DEFAULT_BACKTRACE_FRAMES, 1, MAX_BACKTRACE_FRAMES,
+                    &backtrace_frames_);
+}
+
+bool Config::SetExpandAlloc(const std::string& option, const std::string& value) {
+  return ParseValue(option, value, DEFAULT_EXPAND_BYTES, 1, MAX_EXPAND_BYTES, &expand_alloc_bytes_);
+}
+
+bool Config::SetFreeTrack(const std::string& option, const std::string& value) {
+  // This option enables fill on free, so set the bytes to the default value.
+  if (fill_on_free_bytes_ == 0) {
+    fill_on_free_bytes_ = SIZE_MAX;
+  }
+  if (free_track_backtrace_num_frames_ == 0) {
+    free_track_backtrace_num_frames_ = DEFAULT_BACKTRACE_FRAMES;
+  }
+
+  return ParseValue(option, value, DEFAULT_FREE_TRACK_ALLOCATIONS, 1, MAX_FREE_TRACK_ALLOCATIONS,
+                    &free_track_allocations_);
+}
+
+bool Config::SetFreeTrackBacktraceNumFrames(const std::string& option, const std::string& value) {
+  return ParseValue(option, value, DEFAULT_BACKTRACE_FRAMES, 0, MAX_BACKTRACE_FRAMES,
+                    &free_track_backtrace_num_frames_);
+}
+
+bool Config::SetRecordAllocs(const std::string& option, const std::string& value) {
+  if (record_allocs_file_.empty()) {
+    record_allocs_file_ = DEFAULT_RECORD_ALLOCS_FILE;
+  }
+  return ParseValue(option, value, DEFAULT_RECORD_ALLOCS, 1, MAX_RECORD_ALLOCS,
+                    &record_allocs_num_entries_);
+}
+
+bool Config::SetRecordAllocsFile(const std::string&, const std::string& value) {
+  if (value.empty()) {
+    // Set the default.
+    record_allocs_file_ = DEFAULT_RECORD_ALLOCS_FILE;
+    return true;
+  }
+  record_allocs_file_ = value;
+  return true;
+}
+
+bool Config::VerifyValueEmpty(const std::string& option, const std::string& value) {
+  if (!value.empty()) {
+    // This is not valid.
+    error_log("%s: value set for option '%s' which does not take a value",
+              getprogname(), option.c_str());
+    return false;
   }
   return true;
 }
 
-void PropertyParser::LogUsage() {
+
+void Config::LogUsage() const {
   error_log("malloc debug options usage:");
   error_log("");
   error_log("  front_guard[=XX]");
@@ -325,155 +379,85 @@
   error_log("    The default is %s.", DEFAULT_RECORD_ALLOCS_FILE);
 }
 
-// This function is designed to be called once. A second call will not
-// reset all variables.
-bool Config::Set(const char* options_str) {
-  // Initialize a few default values.
-  fill_alloc_value = DEFAULT_FILL_ALLOC_VALUE;
-  fill_free_value = DEFAULT_FILL_FREE_VALUE;
-  front_guard_value = DEFAULT_FRONT_GUARD_VALUE;
-  rear_guard_value = DEFAULT_REAR_GUARD_VALUE;
-  backtrace_signal = SIGRTMAX - 19;
-  record_allocs_signal = SIGRTMAX - 18;
-  free_track_backtrace_num_frames = 0;
-  record_allocs_file.clear();
+bool Config::GetOption(const char** options_str, std::string* option, std::string* value) {
+  const char* cur = *options_str;
+  // Process each property name we can find.
+  while (isspace(*cur))
+    ++cur;
 
-  // Parse the options are of the format:
-  //   option_name or option_name=XX
-
-  // Supported options:
-  const OptionSizeT option_guard(
-      "guard", DEFAULT_GUARD_BYTES, 1, MAX_GUARD_BYTES, 0, nullptr, true);
-  // Enable front guard. Value is the size of the guard.
-  const OptionSizeT option_front_guard(
-      "front_guard", DEFAULT_GUARD_BYTES, 1, MAX_GUARD_BYTES, FRONT_GUARD,
-      &this->front_guard_bytes, true);
-  // Enable end guard. Value is the size of the guard.
-  const OptionSizeT option_rear_guard(
-      "rear_guard", DEFAULT_GUARD_BYTES, 1, MAX_GUARD_BYTES, REAR_GUARD, &this->rear_guard_bytes,
-      true);
-
-  // Enable logging the backtrace on allocation. Value is the total
-  // number of frames to log.
-  const OptionSizeT option_backtrace(
-      "backtrace", DEFAULT_BACKTRACE_FRAMES, 1, MAX_BACKTRACE_FRAMES, BACKTRACE | TRACK_ALLOCS,
-      &this->backtrace_frames, false, &this->backtrace_enabled);
-  // Enable gathering backtrace values on a signal.
-  const OptionSizeT option_backtrace_enable_on_signal(
-      "backtrace_enable_on_signal", DEFAULT_BACKTRACE_FRAMES, 1, MAX_BACKTRACE_FRAMES,
-      BACKTRACE | TRACK_ALLOCS, &this->backtrace_frames, false, &this->backtrace_enable_on_signal);
-
-  const OptionSizeT option_fill("fill", SIZE_MAX, 1, SIZE_MAX, 0, nullptr, true);
-  // Fill the allocation with an arbitrary pattern on allocation.
-  // Value is the number of bytes of the allocation to fill
-  // (default entire allocation).
-  const OptionSizeT option_fill_on_alloc(
-      "fill_on_alloc", SIZE_MAX, 1, SIZE_MAX, FILL_ON_ALLOC, &this->fill_on_alloc_bytes, true);
-  // Fill the allocation with an arbitrary pattern on free.
-  // Value is the number of bytes of the allocation to fill
-  // (default entire allocation).
-  const OptionSizeT option_fill_on_free(
-      "fill_on_free", SIZE_MAX, 1, SIZE_MAX, FILL_ON_FREE, &this->fill_on_free_bytes, true);
-
-  // Expand the size of every alloc by this number bytes. Value is
-  // the total number of bytes to expand every allocation by.
-  const OptionSizeT option_expand_alloc(
-      "expand_alloc", DEFAULT_EXPAND_BYTES, 1, MAX_EXPAND_BYTES, EXPAND_ALLOC,
-      &this->expand_alloc_bytes);
-
-  // Keep track of the freed allocations and verify at a later date
-  // that they have not been used. Turning this on, also turns on
-  // fill on free.
-  const OptionSizeT option_free_track(
-      "free_track", DEFAULT_FREE_TRACK_ALLOCATIONS, 1, MAX_FREE_TRACK_ALLOCATIONS,
-      FREE_TRACK | FILL_ON_FREE, &this->free_track_allocations);
-  // Number of backtrace frames to keep when free_track is enabled. If this
-  // value is set to zero, no backtrace will be kept.
-  const OptionSizeT option_free_track_backtrace_num_frames(
-      "free_track_backtrace_num_frames", DEFAULT_BACKTRACE_FRAMES, 0, MAX_BACKTRACE_FRAMES, 0,
-      &this->free_track_backtrace_num_frames);
-
-  // Enable printing leaked allocations.
-  const Option option_leak_track("leak_track", LEAK_TRACK | TRACK_ALLOCS);
-
-  const OptionSizeT option_record_allocs(
-      "record_allocs", DEFAULT_RECORD_ALLOCS, 1, MAX_RECORD_ALLOCS, RECORD_ALLOCS,
-      &this->record_allocs_num_entries);
-  const OptionString option_record_allocs_file(
-      "record_allocs_file", 0, DEFAULT_RECORD_ALLOCS_FILE, &this->record_allocs_file);
-
-  const Option* option_list[] = {
-    &option_guard, &option_front_guard, &option_rear_guard,
-    &option_backtrace, &option_backtrace_enable_on_signal,
-    &option_fill, &option_fill_on_alloc, &option_fill_on_free,
-    &option_expand_alloc,
-    &option_free_track, &option_free_track_backtrace_num_frames,
-    &option_leak_track,
-    &option_record_allocs, &option_record_allocs_file,
-  };
-
-  // Set defaults for all of the options.
-  for (size_t i = 0; i < sizeof(option_list)/sizeof(Option*); i++) {
-    option_list[i]->SetDefault();
+  if (*cur == '\0') {
+    *options_str = cur;
+    return false;
   }
 
-  // Process each property name we can find.
-  PropertyParser parser(options_str);
-  bool valid = true;
-  std::string property;
-  std::string value;
-  while (valid && parser.Get(&property, &value)) {
-    bool found = false;
-    for (size_t i = 0; i < sizeof(option_list)/sizeof(Option*); i++) {
-      if (property == option_list[i]->name) {
-        if (option_list[i]->option == 0 && option_list[i]->combo_option) {
-          const std::string* option_name = &option_list[i]->name;
-          i++;
-          for (; i < sizeof(option_list)/sizeof(Option*) && option_list[i]->combo_option; i++) {
-            if (!option_list[i]->ParseValue(*option_name, value)) {
-              valid = false;
-              break;
-            }
-            if (option_list[i]->config) {
-              *option_list[i]->config = true;
-            }
-            options |= option_list[i]->option;
-          }
-          if (!valid) {
-            break;
-          }
-        } else {
-          if (!option_list[i]->ParseValue(option_list[i]->name, value)) {
-            valid = false;
-            break;
-          }
-          if (option_list[i]->config) {
-            *option_list[i]->config = true;
-          }
-          options |= option_list[i]->option;
-        }
-        found = true;
-        break;
-      }
+  const char* start = cur;
+  while (!isspace(*cur) && *cur != '=' && *cur != '\0')
+    ++cur;
+
+  *option = std::string(start, cur - start);
+
+  // Skip any spaces after the name.
+  while (isspace(*cur))
+    ++cur;
+
+  value->clear();
+  if (*cur == '=') {
+    ++cur;
+    // Skip the space after the equal.
+    while (isspace(*cur))
+      ++cur;
+
+    start = cur;
+    while (!isspace(*cur) && *cur != '\0')
+      ++cur;
+
+    if (cur != start) {
+      *value = std::string(start, cur - start);
     }
-    if (valid && !found) {
-      error_log("%s: unknown option %s", getprogname(), property.c_str());
+  }
+  *options_str = cur;
+  return true;
+}
+
+bool Config::Init(const char* options_str) {
+  // Initialize a few default values.
+  fill_alloc_value_ = DEFAULT_FILL_ALLOC_VALUE;
+  fill_free_value_ = DEFAULT_FILL_FREE_VALUE;
+  front_guard_value_ = DEFAULT_FRONT_GUARD_VALUE;
+  rear_guard_value_ = DEFAULT_REAR_GUARD_VALUE;
+  backtrace_signal_ = SIGRTMAX - 19;
+  record_allocs_signal_ = SIGRTMAX - 18;
+  free_track_backtrace_num_frames_ = 0;
+  record_allocs_file_.clear();
+  fill_on_free_bytes_ = 0;
+  backtrace_enable_on_signal_ = false;
+  backtrace_enabled_ = false;
+
+  // Process each option name we can find.
+  std::string option;
+  std::string value;
+  bool valid = true;
+  while (GetOption(&options_str, &option, &value)) {
+    auto entry = kOptions.find(option);
+    if (entry == kOptions.end()) {
+      error_log("%s: unknown option %s", getprogname(), option.c_str());
       valid = false;
       break;
     }
-  }
 
-  valid = valid && parser.Done();
-
-  if (valid) {
-    // It's necessary to align the front guard to MINIMUM_ALIGNMENT_BYTES to
-    // make sure that the header is aligned properly.
-    if (options & FRONT_GUARD) {
-      front_guard_bytes = BIONIC_ALIGN(front_guard_bytes, MINIMUM_ALIGNMENT_BYTES);
+    const OptionInfo* info = &entry->second;
+    auto process_func = info->process_func;
+    if (process_func != nullptr && !(this->*process_func)(option, value)) {
+      valid = false;
+      break;
     }
-  } else {
-    parser.LogUsage();
+    options_ |= info->option;
   }
 
-  return valid;
+  if (!valid || *options_str != '\0') {
+    LogUsage();
+    return false;
+  }
+
+  return true;
 }
diff --git a/libc/malloc_debug/Config.h b/libc/malloc_debug/Config.h
index ca56dc8..d8a7069 100644
--- a/libc/malloc_debug/Config.h
+++ b/libc/malloc_debug/Config.h
@@ -32,6 +32,7 @@
 #include <stdint.h>
 
 #include <string>
+#include <unordered_map>
 
 constexpr uint64_t FRONT_GUARD = 0x1;
 constexpr uint64_t REAR_GUARD = 0x2;
@@ -55,34 +56,100 @@
 // If one or more of these options is set, then a special header is needed.
 constexpr uint64_t HEADER_OPTIONS = FRONT_GUARD | REAR_GUARD | BACKTRACE | FREE_TRACK | LEAK_TRACK;
 
-struct Config {
-  bool Set(const char* str);
+class Config {
+ public:
+  bool Init(const char* options_str);
 
-  size_t front_guard_bytes = 0;
-  size_t rear_guard_bytes = 0;
+  void LogUsage() const;
 
-  bool backtrace_enable_on_signal = false;
-  int backtrace_signal = 0;
-  bool backtrace_enabled = false;
-  size_t backtrace_frames = 0;
+  uint64_t options() const { return options_; }
 
-  size_t fill_on_alloc_bytes = 0;
-  size_t fill_on_free_bytes = 0;
+  int backtrace_signal() const { return backtrace_signal_; }
+  size_t backtrace_frames() const { return backtrace_frames_; }
+  size_t backtrace_enabled() const { return backtrace_enabled_; }
+  size_t backtrace_enable_on_signal() const { return backtrace_enable_on_signal_; }
 
-  size_t expand_alloc_bytes = 0;
+  size_t front_guard_bytes() const { return front_guard_bytes_; }
+  size_t rear_guard_bytes() const { return rear_guard_bytes_; }
+  uint8_t front_guard_value() const { return front_guard_value_; }
+  uint8_t rear_guard_value() const { return rear_guard_value_; }
 
-  size_t free_track_allocations = 0;
-  size_t free_track_backtrace_num_frames = 0;
+  size_t expand_alloc_bytes() const { return expand_alloc_bytes_; }
 
-  int record_allocs_signal = 0;
-  size_t record_allocs_num_entries = 0;
-  std::string record_allocs_file;
+  size_t free_track_allocations() const { return free_track_allocations_; }
+  size_t free_track_backtrace_num_frames() const { return free_track_backtrace_num_frames_; }
 
-  uint64_t options = 0;
-  uint8_t fill_alloc_value;
-  uint8_t fill_free_value;
-  uint8_t front_guard_value;
-  uint8_t rear_guard_value;
+  size_t fill_on_alloc_bytes() const { return fill_on_alloc_bytes_; }
+  size_t fill_on_free_bytes() const { return fill_on_free_bytes_; }
+  uint8_t fill_alloc_value() const { return fill_alloc_value_; }
+  uint8_t fill_free_value() const { return fill_free_value_; }
+
+  int record_allocs_signal() const { return record_allocs_signal_; }
+  size_t record_allocs_num_entries() const { return record_allocs_num_entries_; }
+  const std::string& record_allocs_file() const { return record_allocs_file_; }
+
+ private:
+  struct OptionInfo {
+    uint64_t option;
+    bool (Config::*process_func)(const std::string&, const std::string&);
+  };
+
+  bool ParseValue(const std::string& option, const std::string& value, size_t default_value,
+                  size_t min_value, size_t max_value, size_t* new_value) const;
+
+  bool ParseValue(const std::string& option, const std::string& value, size_t min_value,
+                  size_t max_value, size_t* parsed_value) const;
+
+  bool SetGuard(const std::string& option, const std::string& value);
+  bool SetFrontGuard(const std::string& option, const std::string& value);
+  bool SetRearGuard(const std::string& option, const std::string& value);
+
+  bool SetFill(const std::string& option, const std::string& value);
+  bool SetFillOnAlloc(const std::string& option, const std::string& value);
+  bool SetFillOnFree(const std::string& option, const std::string& value);
+
+  bool SetBacktrace(const std::string& option, const std::string& value);
+  bool SetBacktraceEnableOnSignal(const std::string& option, const std::string& value);
+
+  bool SetExpandAlloc(const std::string& option, const std::string& value);
+
+  bool SetFreeTrack(const std::string& option, const std::string& value);
+  bool SetFreeTrackBacktraceNumFrames(const std::string& option, const std::string& value);
+
+  bool SetRecordAllocs(const std::string& option, const std::string& value);
+  bool SetRecordAllocsFile(const std::string& option, const std::string& value);
+
+  bool VerifyValueEmpty(const std::string& option, const std::string& value);
+
+  static bool GetOption(const char** option_str, std::string* option, std::string* value);
+
+  const static std::unordered_map<std::string, OptionInfo> kOptions;
+
+  size_t front_guard_bytes_ = 0;
+  size_t rear_guard_bytes_ = 0;
+
+  bool backtrace_enable_on_signal_ = false;
+  int backtrace_signal_ = 0;
+  bool backtrace_enabled_ = false;
+  size_t backtrace_frames_ = 0;
+
+  size_t fill_on_alloc_bytes_ = 0;
+  size_t fill_on_free_bytes_ = 0;
+
+  size_t expand_alloc_bytes_ = 0;
+
+  size_t free_track_allocations_ = 0;
+  size_t free_track_backtrace_num_frames_ = 0;
+
+  int record_allocs_signal_ = 0;
+  size_t record_allocs_num_entries_ = 0;
+  std::string record_allocs_file_;
+
+  uint64_t options_ = 0;
+  uint8_t fill_alloc_value_;
+  uint8_t fill_free_value_;
+  uint8_t front_guard_value_;
+  uint8_t rear_guard_value_;
 };
 
 #endif  // MALLOC_DEBUG_CONFIG_H
diff --git a/libc/malloc_debug/DebugData.cpp b/libc/malloc_debug/DebugData.cpp
index 339efdf..e9974d7 100644
--- a/libc/malloc_debug/DebugData.cpp
+++ b/libc/malloc_debug/DebugData.cpp
@@ -38,54 +38,54 @@
 #include "TrackData.h"
 
 bool DebugData::Initialize(const char* options) {
-  if (!config_.Set(options)) {
+  if (!config_.Init(options)) {
     return false;
   }
 
   // Check to see if the options that require a header are enabled.
-  if (config_.options & HEADER_OPTIONS) {
+  if (config_.options() & HEADER_OPTIONS) {
     need_header_ = true;
 
     // Initialize all of the static header offsets.
     pointer_offset_ = BIONIC_ALIGN(sizeof(Header), MINIMUM_ALIGNMENT_BYTES);
 
-    if (config_.options & BACKTRACE) {
+    if (config_.options() & BACKTRACE) {
       backtrace.reset(new BacktraceData(this, config_, &pointer_offset_));
       if (!backtrace->Initialize(config_)) {
         return false;
       }
     }
 
-    if (config_.options & FRONT_GUARD) {
+    if (config_.options() & FRONT_GUARD) {
       front_guard.reset(new FrontGuardData(this, config_, &pointer_offset_));
     }
 
     extra_bytes_ = pointer_offset_;
 
     // Initialize all of the non-header data.
-    if (config_.options & REAR_GUARD) {
+    if (config_.options() & REAR_GUARD) {
       rear_guard.reset(new RearGuardData(this, config_));
-      extra_bytes_ += config_.rear_guard_bytes;
+      extra_bytes_ += config_.rear_guard_bytes();
     }
 
-    if (config_.options & FREE_TRACK) {
+    if (config_.options() & FREE_TRACK) {
       free_track.reset(new FreeTrackData(this, config_));
     }
 
-    if (config_.options & TRACK_ALLOCS) {
+    if (config_.options() & TRACK_ALLOCS) {
       track.reset(new TrackData(this));
     }
   }
 
-  if (config_.options & RECORD_ALLOCS) {
+  if (config_.options() & RECORD_ALLOCS) {
     record.reset(new RecordData());
     if (!record->Initialize(config_)) {
       return false;
     }
   }
 
-  if (config_.options & EXPAND_ALLOC) {
-    extra_bytes_ += config_.expand_alloc_bytes;
+  if (config_.options() & EXPAND_ALLOC) {
+    extra_bytes_ += config_.expand_alloc_bytes();
   }
   return true;
 }
diff --git a/libc/malloc_debug/FreeTrackData.cpp b/libc/malloc_debug/FreeTrackData.cpp
index 8e6502e..e8e7a67 100644
--- a/libc/malloc_debug/FreeTrackData.cpp
+++ b/libc/malloc_debug/FreeTrackData.cpp
@@ -37,15 +37,15 @@
 #include "malloc_debug.h"
 
 FreeTrackData::FreeTrackData(DebugData* debug, const Config& config)
-    : OptionData(debug), backtrace_num_frames_(config.free_track_backtrace_num_frames) {
+    : OptionData(debug), backtrace_num_frames_(config.free_track_backtrace_num_frames()) {
   cmp_mem_.resize(4096);
-  memset(cmp_mem_.data(), config.fill_free_value, cmp_mem_.size());
+  memset(cmp_mem_.data(), config.fill_free_value(), cmp_mem_.size());
 }
 
 void FreeTrackData::LogFreeError(const Header* header, const uint8_t* pointer) {
   error_log(LOG_DIVIDER);
   error_log("+++ ALLOCATION %p USED AFTER FREE", pointer);
-  uint8_t fill_free_value = debug_->config().fill_free_value;
+  uint8_t fill_free_value = debug_->config().fill_free_value();
   for (size_t i = 0; i < header->usable_size; i++) {
     if (pointer[i] != fill_free_value) {
       error_log("  allocation[%zu] = 0x%02x (expected 0x%02x)", i, pointer[i], fill_free_value);
@@ -69,8 +69,8 @@
   } else {
     const uint8_t* memory = reinterpret_cast<const uint8_t*>(pointer);
     size_t bytes = header->usable_size;
-    bytes = (bytes < debug_->config().fill_on_free_bytes) ? bytes
-        : debug_->config().fill_on_free_bytes;
+    bytes = (bytes < debug_->config().fill_on_free_bytes()) ? bytes
+        : debug_->config().fill_on_free_bytes();
     while (bytes > 0) {
       size_t bytes_to_cmp = (bytes < cmp_mem_.size()) ? bytes : cmp_mem_.size();
       if (memcmp(memory, cmp_mem_.data(), bytes_to_cmp) != 0) {
@@ -92,7 +92,7 @@
 
 void FreeTrackData::Add(const Header* header) {
   pthread_mutex_lock(&mutex_);
-  if (list_.size() == debug_->config().free_track_allocations) {
+  if (list_.size() == debug_->config().free_track_allocations()) {
     const Header* old_header = list_.back();
     VerifyAndFree(old_header);
     list_.pop_back();
diff --git a/libc/malloc_debug/FreeTrackData.h b/libc/malloc_debug/FreeTrackData.h
index 21f845f..1758ef5 100644
--- a/libc/malloc_debug/FreeTrackData.h
+++ b/libc/malloc_debug/FreeTrackData.h
@@ -42,7 +42,7 @@
 
 // Forward declarations.
 struct BacktraceHeader;
-struct Config;
+class Config;
 class DebugData;
 struct Header;
 
diff --git a/libc/malloc_debug/GuardData.cpp b/libc/malloc_debug/GuardData.cpp
index e207b86..d6cef85 100644
--- a/libc/malloc_debug/GuardData.cpp
+++ b/libc/malloc_debug/GuardData.cpp
@@ -70,13 +70,13 @@
 }
 
 FrontGuardData::FrontGuardData(DebugData* debug_data, const Config& config, size_t* offset)
-   : GuardData(debug_data, config.front_guard_value, config.front_guard_bytes) {
+   : GuardData(debug_data, config.front_guard_value(), config.front_guard_bytes()) {
   // Create a buffer for fast comparisons of the front guard.
-  cmp_mem_.resize(config.front_guard_bytes);
-  memset(cmp_mem_.data(), config.front_guard_value, cmp_mem_.size());
+  cmp_mem_.resize(config.front_guard_bytes());
+  memset(cmp_mem_.data(), config.front_guard_value(), cmp_mem_.size());
   // Assumes that front_bytes is a multiple of MINIMUM_ALIGNMENT_BYTES.
   offset_ = *offset;
-  *offset += config.front_guard_bytes;
+  *offset += config.front_guard_bytes();
 }
 
 bool FrontGuardData::Valid(const Header* header) {
@@ -88,7 +88,7 @@
 }
 
 RearGuardData::RearGuardData(DebugData* debug_data, const Config& config)
-    : GuardData(debug_data, config.rear_guard_value, config.rear_guard_bytes) {
+    : GuardData(debug_data, config.rear_guard_value(), config.rear_guard_bytes()) {
 }
 
 bool RearGuardData::Valid(const Header* header) {
diff --git a/libc/malloc_debug/GuardData.h b/libc/malloc_debug/GuardData.h
index bfb3949..ddf6ce1 100644
--- a/libc/malloc_debug/GuardData.h
+++ b/libc/malloc_debug/GuardData.h
@@ -41,7 +41,7 @@
 // Forward declarations.
 class DebugData;
 struct Header;
-struct Config;
+class Config;
 
 class GuardData : public OptionData {
  public:
diff --git a/libc/malloc_debug/RecordData.cpp b/libc/malloc_debug/RecordData.cpp
index c0f3486..5a68deb 100644
--- a/libc/malloc_debug/RecordData.cpp
+++ b/libc/malloc_debug/RecordData.cpp
@@ -185,20 +185,20 @@
   dump_act.sa_sigaction = RecordDump;
   dump_act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
   sigemptyset(&dump_act.sa_mask);
-  if (sigaction(config.record_allocs_signal, &dump_act, nullptr) != 0) {
+  if (sigaction(config.record_allocs_signal(), &dump_act, nullptr) != 0) {
     error_log("Unable to set up record dump signal function: %s", strerror(errno));
     return false;
   }
   pthread_setspecific(key_, nullptr);
 
   info_log("%s: Run: 'kill -%d %d' to dump the allocation records.", getprogname(),
-           config.record_allocs_signal, getpid());
+           config.record_allocs_signal(), getpid());
 
-  num_entries_ = config.record_allocs_num_entries;
+  num_entries_ = config.record_allocs_num_entries();
   entries_ = new const RecordEntry*[num_entries_];
   cur_index_ = 0;
   dump_ = false;
-  dump_file_ = config.record_allocs_file;
+  dump_file_ = config.record_allocs_file();
 
   return true;
 }
diff --git a/libc/malloc_debug/RecordData.h b/libc/malloc_debug/RecordData.h
index 741afd5..6e19923 100644
--- a/libc/malloc_debug/RecordData.h
+++ b/libc/malloc_debug/RecordData.h
@@ -144,7 +144,7 @@
   DISALLOW_COPY_AND_ASSIGN(MemalignEntry);
 };
 
-struct Config;
+class Config;
 
 class RecordData {
  public:
diff --git a/libc/malloc_debug/TrackData.cpp b/libc/malloc_debug/TrackData.cpp
index d4064f8..7ce477c 100644
--- a/libc/malloc_debug/TrackData.cpp
+++ b/libc/malloc_debug/TrackData.cpp
@@ -92,7 +92,7 @@
   for (const auto& header : list) {
     error_log("+++ %s leaked block of size %zu at %p (leak %zu of %zu)", getprogname(),
               header->real_size(), debug_->GetPointer(header), ++track_count, list.size());
-    if (debug_->config().options & BACKTRACE) {
+    if (debug_->config().options() & BACKTRACE) {
       BacktraceHeader* back_header = debug_->GetAllocBacktrace(header);
       if (back_header->num_frames > 0) {
         error_log("Backtrace at time of allocation:");
@@ -111,7 +111,7 @@
     return;
   }
 
-  *backtrace_size = debug_->config().backtrace_frames;
+  *backtrace_size = debug_->config().backtrace_frames();
   *info_size = sizeof(size_t) * 2 + sizeof(uintptr_t) * *backtrace_size;
   *info = reinterpret_cast<uint8_t*>(g_dispatch->calloc(*info_size, total_backtrace_allocs_));
   if (*info == nullptr) {
diff --git a/libc/malloc_debug/TrackData.h b/libc/malloc_debug/TrackData.h
index fcd8f2a..e4c8951 100644
--- a/libc/malloc_debug/TrackData.h
+++ b/libc/malloc_debug/TrackData.h
@@ -41,7 +41,7 @@
 
 // Forward declarations.
 struct Header;
-struct Config;
+class Config;
 class DebugData;
 
 class TrackData : public OptionData {
diff --git a/libc/malloc_debug/malloc_debug.cpp b/libc/malloc_debug/malloc_debug.cpp
index bb16faa..addb5d4 100644
--- a/libc/malloc_debug/malloc_debug.cpp
+++ b/libc/malloc_debug/malloc_debug.cpp
@@ -118,7 +118,7 @@
   error_log(LOG_DIVIDER);
   if (header->tag == DEBUG_FREE_TAG) {
     error_log("+++ ALLOCATION %p USED AFTER FREE (%s)", pointer, name);
-    if (g_debug->config().options & FREE_TRACK) {
+    if (g_debug->config().options() & FREE_TRACK) {
       g_debug->free_track->LogBacktrace(header);
     }
   } else {
@@ -147,32 +147,32 @@
   header->usable_size -= g_debug->pointer_offset() +
       reinterpret_cast<uintptr_t>(header) - reinterpret_cast<uintptr_t>(orig_pointer);
 
-  if (g_debug->config().options & FRONT_GUARD) {
+  if (g_debug->config().options() & FRONT_GUARD) {
     uint8_t* guard = g_debug->GetFrontGuard(header);
-    memset(guard, g_debug->config().front_guard_value, g_debug->config().front_guard_bytes);
+    memset(guard, g_debug->config().front_guard_value(), g_debug->config().front_guard_bytes());
   }
 
-  if (g_debug->config().options & REAR_GUARD) {
+  if (g_debug->config().options() & REAR_GUARD) {
     uint8_t* guard = g_debug->GetRearGuard(header);
-    memset(guard, g_debug->config().rear_guard_value, g_debug->config().rear_guard_bytes);
+    memset(guard, g_debug->config().rear_guard_value(), g_debug->config().rear_guard_bytes());
     // If the rear guard is enabled, set the usable size to the exact size
     // of the allocation.
     header->usable_size = header->real_size();
   }
 
   bool backtrace_found = false;
-  if (g_debug->config().options & BACKTRACE) {
+  if (g_debug->config().options() & BACKTRACE) {
     BacktraceHeader* back_header = g_debug->GetAllocBacktrace(header);
     if (g_debug->backtrace->enabled()) {
       back_header->num_frames = backtrace_get(
-          &back_header->frames[0], g_debug->config().backtrace_frames);
+          &back_header->frames[0], g_debug->config().backtrace_frames());
       backtrace_found = back_header->num_frames > 0;
     } else {
       back_header->num_frames = 0;
     }
   }
 
-  if (g_debug->config().options & TRACK_ALLOCS) {
+  if (g_debug->config().options() & TRACK_ALLOCS) {
     g_debug->track->Add(header, backtrace_found);
   }
 
@@ -215,11 +215,11 @@
     return;
   }
 
-  if (g_debug->config().options & FREE_TRACK) {
+  if (g_debug->config().options() & FREE_TRACK) {
     g_debug->free_track->VerifyAll();
   }
 
-  if (g_debug->config().options & LEAK_TRACK) {
+  if (g_debug->config().options() & LEAK_TRACK) {
     g_debug->track->DisplayLeaks();
   }
 
@@ -250,7 +250,7 @@
   *total_memory = 0;
   *backtrace_size = 0;
 
-  if (!(g_debug->config().options & BACKTRACE)) {
+  if (!(g_debug->config().options() & BACKTRACE)) {
     error_log("get_malloc_leak_info: Allocations not being tracked, to enable "
               "set the option 'backtrace'.");
     return;
@@ -315,11 +315,11 @@
     pointer = g_dispatch->malloc(real_size);
   }
 
-  if (pointer != nullptr && g_debug->config().options & FILL_ON_ALLOC) {
+  if (pointer != nullptr && g_debug->config().options() & FILL_ON_ALLOC) {
     size_t bytes = internal_malloc_usable_size(pointer);
-    size_t fill_bytes = g_debug->config().fill_on_alloc_bytes;
+    size_t fill_bytes = g_debug->config().fill_on_alloc_bytes();
     bytes = (bytes < fill_bytes) ? bytes : fill_bytes;
-    memset(pointer, g_debug->config().fill_alloc_value, bytes);
+    memset(pointer, g_debug->config().fill_alloc_value(), bytes);
   }
   return pointer;
 }
@@ -332,7 +332,7 @@
 
   void* pointer = internal_malloc(size);
 
-  if (g_debug->config().options & RECORD_ALLOCS) {
+  if (g_debug->config().options() & RECORD_ALLOCS) {
     g_debug->record->AddEntry(new MallocEntry(pointer, size));
   }
 
@@ -351,20 +351,20 @@
     }
     free_pointer = header->orig_pointer;
 
-    if (g_debug->config().options & FRONT_GUARD) {
+    if (g_debug->config().options() & FRONT_GUARD) {
       if (!g_debug->front_guard->Valid(header)) {
         g_debug->front_guard->LogFailure(header);
       }
     }
-    if (g_debug->config().options & REAR_GUARD) {
+    if (g_debug->config().options() & REAR_GUARD) {
       if (!g_debug->rear_guard->Valid(header)) {
         g_debug->rear_guard->LogFailure(header);
       }
     }
 
-    if (g_debug->config().options & TRACK_ALLOCS) {
+    if (g_debug->config().options() & TRACK_ALLOCS) {
       bool backtrace_found = false;
-      if (g_debug->config().options & BACKTRACE) {
+      if (g_debug->config().options() & BACKTRACE) {
         BacktraceHeader* back_header = g_debug->GetAllocBacktrace(header);
         backtrace_found = back_header->num_frames > 0;
       }
@@ -377,13 +377,13 @@
     bytes = g_dispatch->malloc_usable_size(pointer);
   }
 
-  if (g_debug->config().options & FILL_ON_FREE) {
-    size_t fill_bytes = g_debug->config().fill_on_free_bytes;
+  if (g_debug->config().options() & FILL_ON_FREE) {
+    size_t fill_bytes = g_debug->config().fill_on_free_bytes();
     bytes = (bytes < fill_bytes) ? bytes : fill_bytes;
-    memset(pointer, g_debug->config().fill_free_value, bytes);
+    memset(pointer, g_debug->config().fill_free_value(), bytes);
   }
 
-  if (g_debug->config().options & FREE_TRACK) {
+  if (g_debug->config().options() & FREE_TRACK) {
     // Do not add the allocation until we are done modifying the pointer
     // itself. This avoids a race if a lot of threads are all doing
     // frees at the same time and we wind up trying to really free this
@@ -401,7 +401,7 @@
   }
   ScopedDisableDebugCalls disable;
 
-  if (g_debug->config().options & RECORD_ALLOCS) {
+  if (g_debug->config().options() & RECORD_ALLOCS) {
     g_debug->record->AddEntry(new FreeEntry(pointer));
   }
 
@@ -466,14 +466,14 @@
     pointer = g_dispatch->memalign(alignment, real_size);
   }
 
-  if (pointer != nullptr && g_debug->config().options & FILL_ON_ALLOC) {
+  if (pointer != nullptr && g_debug->config().options() & FILL_ON_ALLOC) {
     size_t bytes = internal_malloc_usable_size(pointer);
-    size_t fill_bytes = g_debug->config().fill_on_alloc_bytes;
+    size_t fill_bytes = g_debug->config().fill_on_alloc_bytes();
     bytes = (bytes < fill_bytes) ? bytes : fill_bytes;
-    memset(pointer, g_debug->config().fill_alloc_value, bytes);
+    memset(pointer, g_debug->config().fill_alloc_value(), bytes);
   }
 
-  if (g_debug->config().options & RECORD_ALLOCS) {
+  if (g_debug->config().options() & RECORD_ALLOCS) {
     g_debug->record->AddEntry(new MemalignEntry(pointer, bytes, alignment));
   }
 
@@ -488,14 +488,14 @@
 
   if (pointer == nullptr) {
     pointer = internal_malloc(bytes);
-    if (g_debug->config().options & RECORD_ALLOCS) {
+    if (g_debug->config().options() & RECORD_ALLOCS) {
       g_debug->record->AddEntry(new ReallocEntry(pointer, bytes, nullptr));
     }
     return pointer;
   }
 
   if (bytes == 0) {
-    if (g_debug->config().options & RECORD_ALLOCS) {
+    if (g_debug->config().options() & RECORD_ALLOCS) {
       g_debug->record->AddEntry(new ReallocEntry(nullptr, bytes, pointer));
     }
 
@@ -504,8 +504,8 @@
   }
 
   size_t real_size = bytes;
-  if (g_debug->config().options & EXPAND_ALLOC) {
-    real_size += g_debug->config().expand_alloc_bytes;
+  if (g_debug->config().options() & EXPAND_ALLOC) {
+    real_size += g_debug->config().expand_alloc_bytes();
     if (real_size < bytes) {
       // Overflow.
       errno = ENOMEM;
@@ -539,12 +539,12 @@
       if (*g_malloc_zygote_child) {
         header->set_zygote();
       }
-      if (g_debug->config().options & REAR_GUARD) {
+      if (g_debug->config().options() & REAR_GUARD) {
         // Don't bother allocating a smaller pointer in this case, simply
         // change the header usable_size and reset the rear guard.
         header->usable_size = header->real_size();
-        memset(g_debug->GetRearGuard(header), g_debug->config().rear_guard_value,
-               g_debug->config().rear_guard_bytes);
+        memset(g_debug->GetRearGuard(header), g_debug->config().rear_guard_value(),
+               g_debug->config().rear_guard_bytes());
       }
       // Do not bother recording, this is essentially a nop.
       return pointer;
@@ -568,18 +568,18 @@
     }
   }
 
-  if (g_debug->config().options & FILL_ON_ALLOC) {
+  if (g_debug->config().options() & FILL_ON_ALLOC) {
     size_t bytes = internal_malloc_usable_size(new_pointer);
-    if (bytes > g_debug->config().fill_on_alloc_bytes) {
-      bytes = g_debug->config().fill_on_alloc_bytes;
+    if (bytes > g_debug->config().fill_on_alloc_bytes()) {
+      bytes = g_debug->config().fill_on_alloc_bytes();
     }
     if (bytes > prev_size) {
       memset(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(new_pointer) + prev_size),
-             g_debug->config().fill_alloc_value, bytes - prev_size);
+             g_debug->config().fill_alloc_value(), bytes - prev_size);
     }
   }
 
-  if (g_debug->config().options & RECORD_ALLOCS) {
+  if (g_debug->config().options() & RECORD_ALLOCS) {
     g_debug->record->AddEntry(new ReallocEntry(new_pointer, bytes, pointer));
   }
 
@@ -629,7 +629,7 @@
   } else {
     pointer = g_dispatch->calloc(1, real_size);
   }
-  if (g_debug->config().options & RECORD_ALLOCS) {
+  if (g_debug->config().options() & RECORD_ALLOCS) {
     g_debug->record->AddEntry(new CallocEntry(pointer, bytes, nmemb));
   }
   return pointer;
@@ -668,7 +668,7 @@
         const void* pointer = reinterpret_cast<void*>(base);
         if (g_debug->need_header()) {
           const Header* header = reinterpret_cast<const Header*>(pointer);
-          if (g_debug->config().options & TRACK_ALLOCS) {
+          if (g_debug->config().options() & TRACK_ALLOCS) {
             if (g_debug->track->Contains(header)) {
               // Return just the body of the allocation if we're sure the header exists
               ctx->callback(reinterpret_cast<uintptr_t>(g_debug->GetPointer(header)),
@@ -704,7 +704,7 @@
 
   if (g_debug->need_header()) {
     Header* header;
-    if (g_debug->config().options & TRACK_ALLOCS) {
+    if (g_debug->config().options() & TRACK_ALLOCS) {
       header = g_debug->GetHeader(pointer);
       if (!g_debug->track->Contains(header)) {
         return 0;
@@ -715,7 +715,7 @@
     if (header->tag != DEBUG_TAG) {
       return 0;
     }
-    if (g_debug->config().options & BACKTRACE) {
+    if (g_debug->config().options() & BACKTRACE) {
       BacktraceHeader* back_header = g_debug->GetAllocBacktrace(header);
       if (back_header->num_frames > 0) {
         if (frame_count > back_header->num_frames) {
diff --git a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
index f988124..77dc848 100644
--- a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
@@ -31,14 +31,11 @@
     resetLogs();
   }
 
-  void TearDown() override {
-  }
-
   std::unique_ptr<Config> config;
 
   bool InitConfig(const char* options) {
     config.reset(new Config);
-    return config->Set(options);
+    return config->Init(options);
   }
 };
 
@@ -201,8 +198,8 @@
 
 TEST_F(MallocDebugConfigTest, space_before_equal) {
   ASSERT_TRUE(InitConfig("backtrace  =10")) << getFakeLogPrint();
-  ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options);
-  ASSERT_EQ(10U, config->backtrace_frames);
+  ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options());
+  ASSERT_EQ(10U, config->backtrace_frames());
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -210,8 +207,8 @@
 
 TEST_F(MallocDebugConfigTest, space_after_equal) {
   ASSERT_TRUE(InitConfig("backtrace=  10")) << getFakeLogPrint();
-  ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options);
-  ASSERT_EQ(10U, config->backtrace_frames);
+  ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options());
+  ASSERT_EQ(10U, config->backtrace_frames());
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -219,8 +216,8 @@
 
 TEST_F(MallocDebugConfigTest, extra_space) {
   ASSERT_TRUE(InitConfig("   backtrace=64   ")) << getFakeLogPrint();
-  ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options);
-  ASSERT_EQ(64U, config->backtrace_frames);
+  ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options());
+  ASSERT_EQ(64U, config->backtrace_frames());
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -228,9 +225,9 @@
 
 TEST_F(MallocDebugConfigTest, multiple_options) {
   ASSERT_TRUE(InitConfig("  backtrace=64   front_guard=48")) << getFakeLogPrint();
-  ASSERT_EQ(BACKTRACE | TRACK_ALLOCS | FRONT_GUARD, config->options);
-  ASSERT_EQ(64U, config->backtrace_frames);
-  ASSERT_EQ(48U, config->front_guard_bytes);
+  ASSERT_EQ(BACKTRACE | TRACK_ALLOCS | FRONT_GUARD, config->options());
+  ASSERT_EQ(64U, config->backtrace_frames());
+  ASSERT_EQ(48U, config->front_guard_bytes());
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -238,24 +235,24 @@
 
 TEST_F(MallocDebugConfigTest, front_guard) {
   ASSERT_TRUE(InitConfig("front_guard=48")) << getFakeLogPrint();
-  ASSERT_EQ(FRONT_GUARD, config->options);
-  ASSERT_EQ(48U, config->front_guard_bytes);
+  ASSERT_EQ(FRONT_GUARD, config->options());
+  ASSERT_EQ(48U, config->front_guard_bytes());
 
   ASSERT_TRUE(InitConfig("front_guard")) << getFakeLogPrint();
-  ASSERT_EQ(FRONT_GUARD, config->options);
-  ASSERT_EQ(32U, config->front_guard_bytes);
+  ASSERT_EQ(FRONT_GUARD, config->options());
+  ASSERT_EQ(32U, config->front_guard_bytes());
 
   ASSERT_TRUE(InitConfig("front_guard=39")) << getFakeLogPrint();
-  ASSERT_EQ(FRONT_GUARD, config->options);
+  ASSERT_EQ(FRONT_GUARD, config->options());
 #if defined(__LP64__)
-  ASSERT_EQ(48U, config->front_guard_bytes);
+  ASSERT_EQ(48U, config->front_guard_bytes());
 #else
-  ASSERT_EQ(40U, config->front_guard_bytes);
+  ASSERT_EQ(40U, config->front_guard_bytes());
 #endif
 
   ASSERT_TRUE(InitConfig("front_guard=41")) << getFakeLogPrint();
-  ASSERT_EQ(FRONT_GUARD, config->options);
-  ASSERT_EQ(48U, config->front_guard_bytes);
+  ASSERT_EQ(FRONT_GUARD, config->options());
+  ASSERT_EQ(48U, config->front_guard_bytes());
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -263,12 +260,12 @@
 
 TEST_F(MallocDebugConfigTest, rear_guard) {
   ASSERT_TRUE(InitConfig("rear_guard=50")) << getFakeLogPrint();
-  ASSERT_EQ(REAR_GUARD, config->options);
-  ASSERT_EQ(50U, config->rear_guard_bytes);
+  ASSERT_EQ(REAR_GUARD, config->options());
+  ASSERT_EQ(50U, config->rear_guard_bytes());
 
   ASSERT_TRUE(InitConfig("rear_guard")) << getFakeLogPrint();
-  ASSERT_EQ(REAR_GUARD, config->options);
-  ASSERT_EQ(32U, config->rear_guard_bytes);
+  ASSERT_EQ(REAR_GUARD, config->options());
+  ASSERT_EQ(32U, config->rear_guard_bytes());
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -276,14 +273,14 @@
 
 TEST_F(MallocDebugConfigTest, guard) {
   ASSERT_TRUE(InitConfig("guard=32")) << getFakeLogPrint();
-  ASSERT_EQ(FRONT_GUARD | REAR_GUARD, config->options);
-  ASSERT_EQ(32U, config->front_guard_bytes);
-  ASSERT_EQ(32U, config->rear_guard_bytes);
+  ASSERT_EQ(FRONT_GUARD | REAR_GUARD, config->options());
+  ASSERT_EQ(32U, config->front_guard_bytes());
+  ASSERT_EQ(32U, config->rear_guard_bytes());
 
   ASSERT_TRUE(InitConfig("guard")) << getFakeLogPrint();
-  ASSERT_EQ(FRONT_GUARD | REAR_GUARD, config->options);
-  ASSERT_EQ(32U, config->front_guard_bytes);
-  ASSERT_EQ(32U, config->rear_guard_bytes);
+  ASSERT_EQ(FRONT_GUARD | REAR_GUARD, config->options());
+  ASSERT_EQ(32U, config->front_guard_bytes());
+  ASSERT_EQ(32U, config->rear_guard_bytes());
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -291,12 +288,16 @@
 
 TEST_F(MallocDebugConfigTest, backtrace) {
   ASSERT_TRUE(InitConfig("backtrace=64")) << getFakeLogPrint();
-  ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options);
-  ASSERT_EQ(64U, config->backtrace_frames);
+  ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options());
+  ASSERT_EQ(64U, config->backtrace_frames());
+  ASSERT_TRUE(config->backtrace_enabled());
+  ASSERT_FALSE(config->backtrace_enable_on_signal());
 
   ASSERT_TRUE(InitConfig("backtrace")) << getFakeLogPrint();
-  ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options);
-  ASSERT_EQ(16U, config->backtrace_frames);
+  ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options());
+  ASSERT_EQ(16U, config->backtrace_frames());
+  ASSERT_TRUE(config->backtrace_enabled());
+  ASSERT_FALSE(config->backtrace_enable_on_signal());
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -304,12 +305,50 @@
 
 TEST_F(MallocDebugConfigTest, backtrace_enable_on_signal) {
   ASSERT_TRUE(InitConfig("backtrace_enable_on_signal=64")) << getFakeLogPrint();
-  ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options);
-  ASSERT_EQ(64U, config->backtrace_frames);
+  ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options());
+  ASSERT_EQ(64U, config->backtrace_frames());
+  ASSERT_FALSE(config->backtrace_enabled());
+  ASSERT_TRUE(config->backtrace_enable_on_signal());
 
   ASSERT_TRUE(InitConfig("backtrace_enable_on_signal")) << getFakeLogPrint();
-  ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options);
-  ASSERT_EQ(16U, config->backtrace_frames);
+  ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options());
+  ASSERT_EQ(16U, config->backtrace_frames());
+  ASSERT_FALSE(config->backtrace_enabled());
+  ASSERT_TRUE(config->backtrace_enable_on_signal());
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, backtrace_enable_on_signal_init) {
+  ASSERT_TRUE(InitConfig("backtrace_enable_on_signal=64")) << getFakeLogPrint();
+  ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options());
+  ASSERT_EQ(64U, config->backtrace_frames());
+  ASSERT_FALSE(config->backtrace_enabled());
+  ASSERT_TRUE(config->backtrace_enable_on_signal());
+
+  ASSERT_TRUE(InitConfig("backtrace")) << getFakeLogPrint();
+  ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options());
+  ASSERT_EQ(16U, config->backtrace_frames());
+  ASSERT_TRUE(config->backtrace_enabled());
+  ASSERT_FALSE(config->backtrace_enable_on_signal());
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, backtrace_enable_and_backtrace) {
+  ASSERT_TRUE(InitConfig("backtrace_enable_on_signal backtrace")) << getFakeLogPrint();
+  ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options());
+  ASSERT_EQ(16U, config->backtrace_frames());
+  ASSERT_TRUE(config->backtrace_enabled());
+  ASSERT_TRUE(config->backtrace_enable_on_signal());
+
+  ASSERT_TRUE(InitConfig("backtrace backtrace_enable_on_signal")) << getFakeLogPrint();
+  ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options());
+  ASSERT_EQ(16U, config->backtrace_frames());
+  ASSERT_TRUE(config->backtrace_enabled());
+  ASSERT_TRUE(config->backtrace_enable_on_signal());
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -317,12 +356,12 @@
 
 TEST_F(MallocDebugConfigTest, fill_on_alloc) {
   ASSERT_TRUE(InitConfig("fill_on_alloc=64")) << getFakeLogPrint();
-  ASSERT_EQ(FILL_ON_ALLOC, config->options);
-  ASSERT_EQ(64U, config->fill_on_alloc_bytes);
+  ASSERT_EQ(FILL_ON_ALLOC, config->options());
+  ASSERT_EQ(64U, config->fill_on_alloc_bytes());
 
   ASSERT_TRUE(InitConfig("fill_on_alloc")) << getFakeLogPrint();
-  ASSERT_EQ(FILL_ON_ALLOC, config->options);
-  ASSERT_EQ(SIZE_MAX, config->fill_on_alloc_bytes);
+  ASSERT_EQ(FILL_ON_ALLOC, config->options());
+  ASSERT_EQ(SIZE_MAX, config->fill_on_alloc_bytes());
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -330,12 +369,12 @@
 
 TEST_F(MallocDebugConfigTest, fill_on_free) {
   ASSERT_TRUE(InitConfig("fill_on_free=64")) << getFakeLogPrint();
-  ASSERT_EQ(FILL_ON_FREE, config->options);
-  ASSERT_EQ(64U, config->fill_on_free_bytes);
+  ASSERT_EQ(FILL_ON_FREE, config->options());
+  ASSERT_EQ(64U, config->fill_on_free_bytes());
 
   ASSERT_TRUE(InitConfig("fill_on_free")) << getFakeLogPrint();
-  ASSERT_EQ(FILL_ON_FREE, config->options);
-  ASSERT_EQ(SIZE_MAX, config->fill_on_free_bytes);
+  ASSERT_EQ(FILL_ON_FREE, config->options());
+  ASSERT_EQ(SIZE_MAX, config->fill_on_free_bytes());
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -343,14 +382,14 @@
 
 TEST_F(MallocDebugConfigTest, fill) {
   ASSERT_TRUE(InitConfig("fill=64")) << getFakeLogPrint();
-  ASSERT_EQ(FILL_ON_ALLOC | FILL_ON_FREE, config->options);
-  ASSERT_EQ(64U, config->fill_on_alloc_bytes);
-  ASSERT_EQ(64U, config->fill_on_free_bytes);
+  ASSERT_EQ(FILL_ON_ALLOC | FILL_ON_FREE, config->options());
+  ASSERT_EQ(64U, config->fill_on_alloc_bytes());
+  ASSERT_EQ(64U, config->fill_on_free_bytes());
 
   ASSERT_TRUE(InitConfig("fill")) << getFakeLogPrint();
-  ASSERT_EQ(FILL_ON_ALLOC | FILL_ON_FREE, config->options);
-  ASSERT_EQ(SIZE_MAX, config->fill_on_alloc_bytes);
-  ASSERT_EQ(SIZE_MAX, config->fill_on_free_bytes);
+  ASSERT_EQ(FILL_ON_ALLOC | FILL_ON_FREE, config->options());
+  ASSERT_EQ(SIZE_MAX, config->fill_on_alloc_bytes());
+  ASSERT_EQ(SIZE_MAX, config->fill_on_free_bytes());
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -358,12 +397,12 @@
 
 TEST_F(MallocDebugConfigTest, expand_alloc) {
   ASSERT_TRUE(InitConfig("expand_alloc=1234")) << getFakeLogPrint();
-  ASSERT_EQ(EXPAND_ALLOC, config->options);
-  ASSERT_EQ(1234U, config->expand_alloc_bytes);
+  ASSERT_EQ(EXPAND_ALLOC, config->options());
+  ASSERT_EQ(1234U, config->expand_alloc_bytes());
 
   ASSERT_TRUE(InitConfig("expand_alloc")) << getFakeLogPrint();
-  ASSERT_EQ(EXPAND_ALLOC, config->options);
-  ASSERT_EQ(16U, config->expand_alloc_bytes);
+  ASSERT_EQ(EXPAND_ALLOC, config->options());
+  ASSERT_EQ(16U, config->expand_alloc_bytes());
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -371,16 +410,16 @@
 
 TEST_F(MallocDebugConfigTest, free_track) {
   ASSERT_TRUE(InitConfig("free_track=1234")) << getFakeLogPrint();
-  ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options);
-  ASSERT_EQ(1234U, config->free_track_allocations);
-  ASSERT_EQ(SIZE_MAX, config->fill_on_free_bytes);
-  ASSERT_EQ(16U, config->free_track_backtrace_num_frames);
+  ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options());
+  ASSERT_EQ(1234U, config->free_track_allocations());
+  ASSERT_EQ(SIZE_MAX, config->fill_on_free_bytes());
+  ASSERT_EQ(16U, config->free_track_backtrace_num_frames());
 
   ASSERT_TRUE(InitConfig("free_track")) << getFakeLogPrint();
-  ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options);
-  ASSERT_EQ(100U, config->free_track_allocations);
-  ASSERT_EQ(SIZE_MAX, config->fill_on_free_bytes);
-  ASSERT_EQ(16U, config->free_track_backtrace_num_frames);
+  ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options());
+  ASSERT_EQ(100U, config->free_track_allocations());
+  ASSERT_EQ(SIZE_MAX, config->fill_on_free_bytes());
+  ASSERT_EQ(16U, config->free_track_backtrace_num_frames());
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -388,16 +427,23 @@
 
 TEST_F(MallocDebugConfigTest, free_track_and_fill_on_free) {
   ASSERT_TRUE(InitConfig("free_track=1234 fill_on_free=32")) << getFakeLogPrint();
-  ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options);
-  ASSERT_EQ(1234U, config->free_track_allocations);
-  ASSERT_EQ(32U, config->fill_on_free_bytes);
-  ASSERT_EQ(16U, config->free_track_backtrace_num_frames);
+  ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options());
+  ASSERT_EQ(1234U, config->free_track_allocations());
+  ASSERT_EQ(32U, config->fill_on_free_bytes());
+  ASSERT_EQ(16U, config->free_track_backtrace_num_frames());
 
   ASSERT_TRUE(InitConfig("free_track fill_on_free=60")) << getFakeLogPrint();
-  ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options);
-  ASSERT_EQ(100U, config->free_track_allocations);
-  ASSERT_EQ(60U, config->fill_on_free_bytes);
-  ASSERT_EQ(16U, config->free_track_backtrace_num_frames);
+  ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options());
+  ASSERT_EQ(100U, config->free_track_allocations());
+  ASSERT_EQ(60U, config->fill_on_free_bytes());
+  ASSERT_EQ(16U, config->free_track_backtrace_num_frames());
+
+  // Now reverse the arguments.
+  ASSERT_TRUE(InitConfig("fill_on_free=32 free_track=1234")) << getFakeLogPrint();
+  ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options());
+  ASSERT_EQ(1234U, config->free_track_allocations());
+  ASSERT_EQ(32U, config->fill_on_free_bytes());
+  ASSERT_EQ(16U, config->free_track_backtrace_num_frames());
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -406,12 +452,12 @@
 TEST_F(MallocDebugConfigTest, free_track_backtrace_num_frames) {
   ASSERT_TRUE(InitConfig("free_track_backtrace_num_frames=123")) << getFakeLogPrint();
 
-  ASSERT_EQ(0U, config->options);
-  ASSERT_EQ(123U, config->free_track_backtrace_num_frames);
+  ASSERT_EQ(0U, config->options());
+  ASSERT_EQ(123U, config->free_track_backtrace_num_frames());
 
   ASSERT_TRUE(InitConfig("free_track_backtrace_num_frames")) << getFakeLogPrint();
-  ASSERT_EQ(0U, config->options);
-  ASSERT_EQ(16U, config->free_track_backtrace_num_frames);
+  ASSERT_EQ(0U, config->options());
+  ASSERT_EQ(16U, config->free_track_backtrace_num_frames());
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -420,8 +466,8 @@
 TEST_F(MallocDebugConfigTest, free_track_backtrace_num_frames_zero) {
   ASSERT_TRUE(InitConfig("free_track_backtrace_num_frames=0")) << getFakeLogPrint();
 
-  ASSERT_EQ(0U, config->options);
-  ASSERT_EQ(0U, config->free_track_backtrace_num_frames);
+  ASSERT_EQ(0U, config->options());
+  ASSERT_EQ(0U, config->free_track_backtrace_num_frames());
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -429,12 +475,12 @@
 
 TEST_F(MallocDebugConfigTest, free_track_backtrace_num_frames_and_free_track) {
   ASSERT_TRUE(InitConfig("free_track free_track_backtrace_num_frames=123")) << getFakeLogPrint();
-  ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options);
-  ASSERT_EQ(123U, config->free_track_backtrace_num_frames);
+  ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options());
+  ASSERT_EQ(123U, config->free_track_backtrace_num_frames());
 
   ASSERT_TRUE(InitConfig("free_track free_track_backtrace_num_frames")) << getFakeLogPrint();
-  ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options);
-  ASSERT_EQ(16U, config->free_track_backtrace_num_frames);
+  ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options());
+  ASSERT_EQ(16U, config->free_track_backtrace_num_frames());
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -442,7 +488,7 @@
 
 TEST_F(MallocDebugConfigTest, leak_track) {
   ASSERT_TRUE(InitConfig("leak_track")) << getFakeLogPrint();
-  ASSERT_EQ(LEAK_TRACK | TRACK_ALLOCS, config->options);
+  ASSERT_EQ(LEAK_TRACK | TRACK_ALLOCS, config->options());
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -460,14 +506,14 @@
 
 TEST_F(MallocDebugConfigTest, record_allocs) {
   ASSERT_TRUE(InitConfig("record_allocs=1234")) << getFakeLogPrint();
-  ASSERT_EQ(RECORD_ALLOCS, config->options);
-  ASSERT_EQ(1234U, config->record_allocs_num_entries);
-  ASSERT_STREQ("/data/local/tmp/record_allocs.txt", config->record_allocs_file.c_str());
+  ASSERT_EQ(RECORD_ALLOCS, config->options());
+  ASSERT_EQ(1234U, config->record_allocs_num_entries());
+  ASSERT_STREQ("/data/local/tmp/record_allocs.txt", config->record_allocs_file().c_str());
 
   ASSERT_TRUE(InitConfig("record_allocs")) << getFakeLogPrint();
-  ASSERT_EQ(RECORD_ALLOCS, config->options);
-  ASSERT_EQ(8000000U, config->record_allocs_num_entries);
-  ASSERT_STREQ("/data/local/tmp/record_allocs.txt", config->record_allocs_file.c_str());
+  ASSERT_EQ(RECORD_ALLOCS, config->options());
+  ASSERT_EQ(8000000U, config->record_allocs_num_entries());
+  ASSERT_STREQ("/data/local/tmp/record_allocs.txt", config->record_allocs_file().c_str());
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -475,10 +521,10 @@
 
 TEST_F(MallocDebugConfigTest, record_allocs_file) {
   ASSERT_TRUE(InitConfig("record_allocs=1234 record_allocs_file=/fake/file")) << getFakeLogPrint();
-  ASSERT_STREQ("/fake/file", config->record_allocs_file.c_str());
+  ASSERT_STREQ("/fake/file", config->record_allocs_file().c_str());
 
   ASSERT_TRUE(InitConfig("record_allocs_file")) << getFakeLogPrint();
-  ASSERT_STREQ("/data/local/tmp/record_allocs.txt", config->record_allocs_file.c_str());
+  ASSERT_STREQ("/data/local/tmp/record_allocs.txt", config->record_allocs_file().c_str());
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
diff --git a/libc/private/CachedProperty.h b/libc/private/CachedProperty.h
index 0a41abf..f0c81c9 100644
--- a/libc/private/CachedProperty.h
+++ b/libc/private/CachedProperty.h
@@ -33,10 +33,11 @@
 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
 #include <sys/_system_properties.h>
 
-#include "private/bionic_lock.h"
-
+// Cached system property lookup. For code that needs to read the same property multiple times,
+// this class helps optimize those lookups.
 class CachedProperty {
  public:
+  // The lifetime of `property_name` must be greater than that of this CachedProperty.
   CachedProperty(const char* property_name)
     : property_name_(property_name),
       prop_info_(nullptr),
@@ -45,9 +46,10 @@
     cached_value_[0] = '\0';
   }
 
+  // Returns the current value of the underlying system property as cheaply as possible.
+  // The returned pointer is valid until the next call to Get. It is the caller's responsibility
+  // to provide a lock for thread-safety.
   const char* Get() {
-    lock_.lock();
-
     // Do we have a `struct prop_info` yet?
     if (prop_info_ == nullptr) {
       // `__system_property_find` is expensive, so only retry if a property
@@ -67,12 +69,10 @@
       }
     }
 
-    lock_.unlock();
     return cached_value_;
   }
 
  private:
-  Lock lock_;
   const char* property_name_;
   const prop_info* prop_info_;
   uint32_t cached_area_serial_;
diff --git a/libc/private/ScopeGuard.h b/libc/private/ScopeGuard.h
deleted file mode 100644
index d5a9235..0000000
--- a/libc/private/ScopeGuard.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _SCOPE_GUARD_H
-#define _SCOPE_GUARD_H
-
-#include "private/bionic_macros.h"
-
-// TODO: include explicit std::move when it becomes available
-template<typename F>
-class ScopeGuard {
- public:
-  ScopeGuard(F f) : f_(f), active_(true) {}
-
-  ScopeGuard(ScopeGuard&& that) : f_(that.f_), active_(that.active_) {
-    that.active_ = false;
-  }
-
-  ~ScopeGuard() {
-    if (active_) {
-      f_();
-    }
-  }
-
-  void disable() {
-    active_ = false;
-  }
- private:
-  F f_;
-  bool active_;
-
-  DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeGuard);
-};
-
-template<typename T>
-ScopeGuard<T> make_scope_guard(T f) {
-  return ScopeGuard<T>(f);
-}
-
-#endif  // _SCOPE_GUARD_H
diff --git a/libc/tools/gensyscalls.py b/libc/tools/gensyscalls.py
index fa35984..ab7c247 100755
--- a/libc/tools/gensyscalls.py
+++ b/libc/tools/gensyscalls.py
@@ -511,7 +511,7 @@
     def parse_file(self, file_path):
         logging.debug("parse_file: %s" % file_path)
         with open(file_path) as fp:
-            parse_open_file(fp)
+            self.parse_open_file(fp)
 
 
 class State:
diff --git a/libc/tzcode/bionic.cpp b/libc/tzcode/bionic.cpp
new file mode 100644
index 0000000..5ae3fab
--- /dev/null
+++ b/libc/tzcode/bionic.cpp
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <arpa/inet.h> // For ntohl(3).
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "private/CachedProperty.h"
+
+extern "C" void tzset_unlocked(void);
+extern "C" int __bionic_open_tzdata(const char*, int32_t*);
+
+extern "C" void tzsetlcl(char const*);
+
+void tzset_unlocked() {
+  // The TZ environment variable is meant to override the system-wide setting.
+  const char* name = getenv("TZ");
+  char buf[PROP_VALUE_MAX];
+
+  // If that's not set, look at the "persist.sys.timezone" system property.
+  if (name == nullptr) {
+    static CachedProperty persist_sys_timezone("persist.sys.timezone");
+
+    if ((name = persist_sys_timezone.Get()) != nullptr && strlen(name) > 3) {
+      // POSIX and Java disagree about the sign in a timezone string. For POSIX, "GMT+3" means
+      // "3 hours west/behind", but for Java it means "3 hours east/ahead". Since (a) Java is
+      // the one that matches human expectations and (b) this system property is used directly
+      // by Java, we flip the sign here to translate from Java to POSIX. http://b/25463955.
+      char sign = name[3];
+      if (sign == '-' || sign == '+') {
+        strlcpy(buf, name, sizeof(buf));
+        buf[3] = (sign == '-') ? '+' : '-';
+        name = buf;
+      }
+    }
+  }
+
+  // If the system property is also not available (because you're running AOSP on a WiFi-only
+  // device, say), fall back to GMT.
+  if (name == nullptr) name = "GMT";
+
+  tzsetlcl(name);
+}
+
+#if !defined(__ANDROID__)
+static char* make_path(const char* path_prefix_variable,
+                       const char* path_suffix) {
+  const char* path_prefix = getenv(path_prefix_variable);
+  if (path_prefix == nullptr) {
+    fprintf(stderr, "%s: %s not set!\n", __FUNCTION__, path_prefix_variable);
+    return -1;
+  }
+  char* path;
+  if (asprintf(&path, "%s/%s", path_prefix, path_suffix) == -1) {
+    fprintf(stderr, "%s: couldn't allocate \"%s/%s\"\n", __FUNCTION__, path_prefix, path_suffix);
+    return -1;
+  }
+  return path;
+}
+#endif
+
+static int __bionic_open_tzdata_path(const char* path,
+                                     const char* olson_id,
+                                     int32_t* entry_length) {
+  int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC));
+  if (fd == -1) {
+    return -2; // Distinguish failure to find any data from failure to find a specific id.
+  }
+
+  // byte[12] tzdata_version  -- "tzdata2012f\0"
+  // int index_offset
+  // int data_offset
+  // int zonetab_offset
+  struct bionic_tzdata_header {
+    char tzdata_version[12];
+    int32_t index_offset;
+    int32_t data_offset;
+    int32_t zonetab_offset;
+  } header;
+  memset(&header, 0, sizeof(header));
+  ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, &header, sizeof(header)));
+  if (bytes_read != sizeof(header)) {
+    fprintf(stderr, "%s: could not read header of \"%s\": %s\n",
+            __FUNCTION__, path, (bytes_read == -1) ? strerror(errno) : "short read");
+    close(fd);
+    return -1;
+  }
+
+  if (strncmp(header.tzdata_version, "tzdata", 6) != 0 || header.tzdata_version[11] != 0) {
+    fprintf(stderr, "%s: bad magic in \"%s\": \"%.6s\"\n",
+            __FUNCTION__, path, header.tzdata_version);
+    close(fd);
+    return -1;
+  }
+
+#if 0
+  fprintf(stderr, "version: %s\n", header.tzdata_version);
+  fprintf(stderr, "index_offset = %d\n", ntohl(header.index_offset));
+  fprintf(stderr, "data_offset = %d\n", ntohl(header.data_offset));
+  fprintf(stderr, "zonetab_offset = %d\n", ntohl(header.zonetab_offset));
+#endif
+
+  if (TEMP_FAILURE_RETRY(lseek(fd, ntohl(header.index_offset), SEEK_SET)) == -1) {
+    fprintf(stderr, "%s: couldn't seek to index in \"%s\": %s\n",
+            __FUNCTION__, path, strerror(errno));
+    close(fd);
+    return -1;
+  }
+
+  off_t specific_zone_offset = -1;
+  ssize_t index_size = ntohl(header.data_offset) - ntohl(header.index_offset);
+  char* index = new char[index_size];
+  if (index == nullptr) {
+    fprintf(stderr, "%s: couldn't allocate %zd-byte index for \"%s\"\n",
+            __FUNCTION__, index_size, path);
+    close(fd);
+    return -1;
+  }
+  if (TEMP_FAILURE_RETRY(read(fd, index, index_size)) != index_size) {
+    fprintf(stderr, "%s: could not read index of \"%s\": %s\n",
+            __FUNCTION__, path, (bytes_read == -1) ? strerror(errno) : "short read");
+    delete[] index;
+    close(fd);
+    return -1;
+  }
+
+  static const size_t NAME_LENGTH = 40;
+  struct index_entry_t {
+    char buf[NAME_LENGTH];
+    int32_t start;
+    int32_t length;
+    int32_t unused; // Was raw GMT offset; always 0 since tzdata2014f (L).
+  };
+
+  size_t id_count = (ntohl(header.data_offset) - ntohl(header.index_offset)) / sizeof(struct index_entry_t);
+  struct index_entry_t* entry = (struct index_entry_t*) index;
+  for (size_t i = 0; i < id_count; ++i) {
+    char this_id[NAME_LENGTH + 1];
+    memcpy(this_id, entry->buf, NAME_LENGTH);
+    this_id[NAME_LENGTH] = '\0';
+
+    if (strcmp(this_id, olson_id) == 0) {
+      specific_zone_offset = ntohl(entry->start) + ntohl(header.data_offset);
+      *entry_length = ntohl(entry->length);
+      break;
+    }
+
+    ++entry;
+  }
+  delete[] index;
+
+  if (specific_zone_offset == -1) {
+    close(fd);
+    return -1;
+  }
+
+  if (TEMP_FAILURE_RETRY(lseek(fd, specific_zone_offset, SEEK_SET)) == -1) {
+    fprintf(stderr, "%s: could not seek to %ld in \"%s\": %s\n",
+            __FUNCTION__, specific_zone_offset, path, strerror(errno));
+    close(fd);
+    return -1;
+  }
+
+  // TODO: check that there's TZ_MAGIC at this offset, so we can fall back to the other file if not.
+
+  return fd;
+}
+
+int __bionic_open_tzdata(const char* olson_id, int32_t* entry_length) {
+  int fd;
+
+#if defined(__ANDROID__)
+  // On Android, try the two hard-coded locations.
+  fd = __bionic_open_tzdata_path("/data/misc/zoneinfo/current/tzdata",
+                                 olson_id, entry_length);
+  if (fd >= 0) return fd;
+
+  fd = __bionic_open_tzdata_path("/system/usr/share/zoneinfo/tzdata",
+                                 olson_id, entry_length);
+  if (fd >= 0) return fd;
+#else
+  // On the host, we don't expect those locations to exist, and we're not
+  // worried about security so we trust $ANDROID_DATA and $ANDROID_ROOT to
+  // point us in the right direction.
+  char* path = make_path("ANDROID_DATA", "/misc/zoneinfo/current/tzdata");
+  fd = __bionic_open_tzdata_path(path, olson_id, entry_length);
+  free(path);
+  if (fd >= 0) return fd;
+
+  path = make_path("ANDROID_ROOT", "/usr/share/zoneinfo/tzdata");
+  fd = __bionic_open_tzdata_path(path, olson_id, entry_length);
+  free(path);
+  if (fd >= 0) return fd;
+#endif
+
+  // Not finding any tzdata is more serious that not finding a specific zone,
+  // and worth logging.
+  if (fd == -2) {
+    // The first thing that 'recovery' does is try to format the current time. It doesn't have
+    // any tzdata available, so we must not abort here --- doing so breaks the recovery image!
+    fprintf(stderr, "%s: couldn't find any tzdata when looking for %s!\n", __FUNCTION__, olson_id);
+  }
+
+  return fd;
+}
diff --git a/libc/tzcode/localtime.c b/libc/tzcode/localtime.c
index 8d5cd07..091bab0 100644
--- a/libc/tzcode/localtime.c
+++ b/libc/tzcode/localtime.c
@@ -369,8 +369,6 @@
   } u;
 };
 
-static int __bionic_open_tzdata(const char*, int32_t*);
-
 /* Load tz data from the file named NAME into *SP.  Read extended
    format if DOEXTEND.  Use *LSP for temporary storage.  Return 0 on
    success, an errno value on failure.  */
@@ -398,6 +396,7 @@
 	}
 
 #if defined(__BIONIC__)
+	extern int __bionic_open_tzdata(const char*, int32_t*);
 	int32_t entry_length;
 	fid = __bionic_open_tzdata(name, &entry_length);
 #else
@@ -1279,7 +1278,7 @@
   }
 }
 
-static void
+void
 tzsetlcl(char const *name)
 {
   struct state *sp = lclptr;
@@ -1314,58 +1313,14 @@
 #endif
 
 #if defined(__BIONIC__)
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-#include <sys/_system_properties.h> // For __system_property_serial.
-#endif
-
+extern void tzset_unlocked(void);
+#else
 static void
 tzset_unlocked(void)
 {
-#if defined(__BIONIC__)
-  // The TZ environment variable is meant to override the system-wide setting.
-  const char* name = getenv("TZ");
-
-  // If that's not set, look at the "persist.sys.timezone" system property.
-  if (name == NULL) {
-    // The lookup is the most expensive part by several orders of magnitude, so we cache it.
-    // We check for null more than once because the system property may not have been set
-    // yet, so our first lookup may fail.
-    static const prop_info* pi;
-    if (pi == NULL) pi = __system_property_find("persist.sys.timezone");
-
-    if (pi) {
-      // If the property hasn't changed since the last time we read it, there's nothing else to do.
-      static uint32_t last_serial = -1;
-      uint32_t serial = __system_property_serial(pi);
-      if (serial == last_serial) return;
-
-      // Otherwise read the new value...
-      last_serial = serial;
-      char buf[PROP_VALUE_MAX];
-      if (__system_property_read(pi, NULL, buf) > 0) {
-        // POSIX and Java disagree about the sign in a timezone string. For POSIX, "GMT+3" means
-        // "3 hours west/behind", but for Java it means "3 hours east/ahead". Since (a) Java is
-        // the one that matches human expectations and (b) this system property is used directly
-        // by Java, we flip the sign here to translate from Java to POSIX. http://b/25463955.
-        if (buf[3] == '-') {
-          buf[3] = '+';
-        } else if (buf[3] == '+') {
-          buf[3] = '-';
-        }
-        name = buf;
-      }
-    }
-  }
-
-  // If the system property is also not available (because you're running AOSP on a WiFi-only
-  // device, say), fall back to GMT.
-  if (name == NULL) name = gmt;
-
-  tzsetlcl(name);
-#else
   tzsetlcl(getenv("TZ"));
-#endif
 }
+#endif
 
 void
 tzset(void)
@@ -2346,175 +2301,3 @@
 }
 
 #endif
-
-// BEGIN android-added
-
-#include <assert.h>
-#include <stdint.h>
-#include <arpa/inet.h> // For ntohl(3).
-
-#if !defined(__ANDROID__)
-static char* make_path(const char* path_prefix_variable,
-                       const char* path_suffix) {
-  const char* path_prefix = getenv(path_prefix_variable);
-  if (path_prefix == NULL) {
-    fprintf(stderr, "%s: %s not set!\n", __FUNCTION__, path_prefix_variable);
-    return -1;
-  }
-  size_t path_length = strlen(path_prefix) + 1 + strlen(path_suffix) + 1;
-  char* path = malloc(path_length);
-  if (path == NULL) {
-    fprintf(stderr, "%s: couldn't allocate %zu-byte path\n", __FUNCTION__, path_length);
-    return -1;
-  }
-  snprintf(path, path_length, "%s/%s", path_prefix, path_suffix);
-  return path;
-}
-#endif
-
-static int __bionic_open_tzdata_path(const char* path,
-                                     const char* olson_id,
-                                     int32_t* entry_length) {
-  int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC));
-  if (fd == -1) {
-    return -2; // Distinguish failure to find any data from failure to find a specific id.
-  }
-
-  // byte[12] tzdata_version  -- "tzdata2012f\0"
-  // int index_offset
-  // int data_offset
-  // int zonetab_offset
-  struct bionic_tzdata_header {
-    char tzdata_version[12];
-    int32_t index_offset;
-    int32_t data_offset;
-    int32_t zonetab_offset;
-  } header;
-  memset(&header, 0, sizeof(header));
-  ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, &header, sizeof(header)));
-  if (bytes_read != sizeof(header)) {
-    fprintf(stderr, "%s: could not read header of \"%s\": %s\n",
-            __FUNCTION__, path, (bytes_read == -1) ? strerror(errno) : "short read");
-    close(fd);
-    return -1;
-  }
-
-  if (strncmp(header.tzdata_version, "tzdata", 6) != 0 || header.tzdata_version[11] != 0) {
-    fprintf(stderr, "%s: bad magic in \"%s\": \"%.6s\"\n",
-            __FUNCTION__, path, header.tzdata_version);
-    close(fd);
-    return -1;
-  }
-
-#if 0
-  fprintf(stderr, "version: %s\n", header.tzdata_version);
-  fprintf(stderr, "index_offset = %d\n", ntohl(header.index_offset));
-  fprintf(stderr, "data_offset = %d\n", ntohl(header.data_offset));
-  fprintf(stderr, "zonetab_offset = %d\n", ntohl(header.zonetab_offset));
-#endif
-
-  if (TEMP_FAILURE_RETRY(lseek(fd, ntohl(header.index_offset), SEEK_SET)) == -1) {
-    fprintf(stderr, "%s: couldn't seek to index in \"%s\": %s\n",
-            __FUNCTION__, path, strerror(errno));
-    close(fd);
-    return -1;
-  }
-
-  off_t specific_zone_offset = -1;
-  ssize_t index_size = ntohl(header.data_offset) - ntohl(header.index_offset);
-  char* index = malloc(index_size);
-  if (index == NULL) {
-    fprintf(stderr, "%s: couldn't allocate %zd-byte index for \"%s\"\n",
-            __FUNCTION__, index_size, path);
-    close(fd);
-    return -1;
-  }
-  if (TEMP_FAILURE_RETRY(read(fd, index, index_size)) != index_size) {
-    fprintf(stderr, "%s: could not read index of \"%s\": %s\n",
-            __FUNCTION__, path, (bytes_read == -1) ? strerror(errno) : "short read");
-    free(index);
-    close(fd);
-    return -1;
-  }
-
-  static const size_t NAME_LENGTH = 40;
-  struct index_entry_t {
-    char buf[NAME_LENGTH];
-    int32_t start;
-    int32_t length;
-    int32_t unused; // Was raw GMT offset; always 0 since tzdata2014f (L).
-  };
-
-  size_t id_count = (ntohl(header.data_offset) - ntohl(header.index_offset)) / sizeof(struct index_entry_t);
-  struct index_entry_t* entry = (struct index_entry_t*) index;
-  for (size_t i = 0; i < id_count; ++i) {
-    char this_id[NAME_LENGTH + 1];
-    memcpy(this_id, entry->buf, NAME_LENGTH);
-    this_id[NAME_LENGTH] = '\0';
-
-    if (strcmp(this_id, olson_id) == 0) {
-      specific_zone_offset = ntohl(entry->start) + ntohl(header.data_offset);
-      *entry_length = ntohl(entry->length);
-      break;
-    }
-
-    ++entry;
-  }
-  free(index);
-
-  if (specific_zone_offset == -1) {
-    close(fd);
-    return -1;
-  }
-
-  if (TEMP_FAILURE_RETRY(lseek(fd, specific_zone_offset, SEEK_SET)) == -1) {
-    fprintf(stderr, "%s: could not seek to %ld in \"%s\": %s\n",
-            __FUNCTION__, specific_zone_offset, path, strerror(errno));
-    close(fd);
-    return -1;
-  }
-
-  // TODO: check that there's TZ_MAGIC at this offset, so we can fall back to the other file if not.
-
-  return fd;
-}
-
-static int __bionic_open_tzdata(const char* olson_id, int32_t* entry_length) {
-  int fd;
-
-#if defined(__ANDROID__)
-  // On Android, try the two hard-coded locations.
-  fd = __bionic_open_tzdata_path("/data/misc/zoneinfo/current/tzdata",
-                                 olson_id, entry_length);
-  if (fd >= 0) return fd;
-
-  fd = __bionic_open_tzdata_path("/system/usr/share/zoneinfo/tzdata",
-                                 olson_id, entry_length);
-  if (fd >= 0) return fd;
-#else
-  // On the host, we don't expect those locations to exist, and we're not
-  // worried about security so we trust $ANDROID_DATA and $ANDROID_ROOT to
-  // point us in the right direction.
-  char* path = make_path("ANDROID_DATA", "/misc/zoneinfo/current/tzdata");
-  fd = __bionic_open_tzdata_path(path, olson_id, entry_length);
-  free(path);
-  if (fd >= 0) return fd;
-
-  path = make_path("ANDROID_ROOT", "/usr/share/zoneinfo/tzdata");
-  fd = __bionic_open_tzdata_path(path, olson_id, entry_length);
-  free(path);
-  if (fd >= 0) return fd;
-#endif
-
-  // Not finding any tzdata is more serious that not finding a specific zone,
-  // and worth logging.
-  if (fd == -2) {
-    // The first thing that 'recovery' does is try to format the current time. It doesn't have
-    // any tzdata available, so we must not abort here --- doing so breaks the recovery image!
-    fprintf(stderr, "%s: couldn't find any tzdata when looking for %s!\n", __FUNCTION__, olson_id);
-  }
-
-  return fd;
-}
-
-// END android-added
diff --git a/libdl/Android.bp b/libdl/Android.bp
index 5b67e38..ffdf7f7 100644
--- a/libdl/Android.bp
+++ b/libdl/Android.bp
@@ -120,7 +120,12 @@
 }
 
 ndk_library {
-    name: "libdl.ndk",
+    name: "libdl",
     symbol_file: "libdl.map.txt",
     first_version: "9",
 }
+
+llndk_library {
+    name: "libdl",
+    symbol_file: "libdl.map.txt",
+}
diff --git a/libm/Android.bp b/libm/Android.bp
index 7489bfe..07d4261 100644
--- a/libm/Android.bp
+++ b/libm/Android.bp
@@ -534,7 +534,12 @@
 }
 
 ndk_library {
-    name: "libm.ndk",
+    name: "libm",
     symbol_file: "libm.map.txt",
     first_version: "9",
 }
+
+llndk_library {
+    name: "libm",
+    symbol_file: "libm.map.txt",
+}
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 2777d73..f43d6fc 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -43,8 +43,9 @@
 #include <unordered_map>
 #include <vector>
 
+#include <android-base/scopeguard.h>
+
 // Private C library headers.
-#include "private/ScopeGuard.h"
 
 #include "linker.h"
 #include "linker_block_allocator.h"
@@ -1536,13 +1537,13 @@
   // list of libraries to link - see step 2.
   size_t soinfos_count = 0;
 
-  auto scope_guard = make_scope_guard([&]() {
+  auto scope_guard = android::base::make_scope_guard([&]() {
     for (LoadTask* t : load_tasks) {
       LoadTask::deleter(t);
     }
   });
 
-  auto failure_guard = make_scope_guard([&]() {
+  auto failure_guard = android::base::make_scope_guard([&]() {
     // Housekeeping
     soinfo_unload(soinfos, soinfos_count);
   });
@@ -1661,7 +1662,7 @@
       }
     });
 
-    failure_guard.disable();
+    failure_guard.Disable();
   }
 
   return linked;
@@ -1904,9 +1905,8 @@
          ns == nullptr ? "(null)" : ns->get_name(),
          ns);
 
-  auto failure_guard = make_scope_guard([&]() {
-    LD_LOG(kLogDlopen, "... dlopen failed: %s", linker_get_error_buffer());
-  });
+  auto failure_guard = android::base::make_scope_guard(
+      [&]() { LD_LOG(kLogDlopen, "... dlopen failed: %s", linker_get_error_buffer()); });
 
   if ((flags & ~(RTLD_NOW|RTLD_LAZY|RTLD_LOCAL|RTLD_GLOBAL|RTLD_NODELETE|RTLD_NOLOAD)) != 0) {
     DL_ERR("invalid flags to dlopen: %x", flags);
@@ -1966,7 +1966,7 @@
            "... dlopen calling constructors: realpath=\"%s\", soname=\"%s\", handle=%p",
            si->get_realpath(), si->get_soname(), handle);
     si->call_constructors();
-    failure_guard.disable();
+    failure_guard.Disable();
     LD_LOG(kLogDlopen,
            "... dlopen successful: realpath=\"%s\", soname=\"%s\", handle=%p",
            si->get_realpath(), si->get_soname(), handle);
@@ -2044,9 +2044,8 @@
          ns == nullptr ? "(null)" : ns->get_name(),
          ns);
 
-  auto failure_guard = make_scope_guard([&]() {
-    LD_LOG(kLogDlsym, "... dlsym failed: %s", linker_get_error_buffer());
-  });
+  auto failure_guard = android::base::make_scope_guard(
+      [&]() { LD_LOG(kLogDlsym, "... dlsym failed: %s", linker_get_error_buffer()); });
 
   if (sym_name == nullptr) {
     DL_ERR("dlsym failed: symbol name is null");
@@ -2077,7 +2076,7 @@
 
     if ((bind == STB_GLOBAL || bind == STB_WEAK) && sym->st_shndx != 0) {
       *symbol = reinterpret_cast<void*>(found->resolve_symbol_address(sym));
-      failure_guard.disable();
+      failure_guard.Disable();
       LD_LOG(kLogDlsym,
              "... dlsym successful: sym_name=\"%s\", sym_ver=\"%s\", found in=\"%s\", address=%p",
              sym_name, sym_ver, found->get_soname(), *symbol);
@@ -3251,13 +3250,16 @@
   if (has_text_relocations) {
     // Fail if app is targeting M or above.
     if (get_application_target_sdk_version() >= __ANDROID_API_M__) {
-      DL_ERR_AND_LOG("\"%s\" has text relocations", get_realpath());
+      DL_ERR_AND_LOG("\"%s\" has text relocations (https://android.googlesource.com/platform/"
+                     "bionic/+/master/android-changes-for-ndk-developers.md#Text-Relocations-"
+                     "Enforced-for-API-level-23)", get_realpath());
       return false;
     }
     // Make segments writable to allow text relocations to work properly. We will later call
     // phdr_table_protect_segments() after all of them are applied.
-    DL_WARN("\"%s\" has text relocations. This is wasting memory and prevents "
-            "security hardening. Please fix.", get_realpath());
+    DL_WARN("\"%s\" has text relocations (https://android.googlesource.com/platform/"
+            "bionic/+/master/android-changes-for-ndk-developers.md#Text-Relocations-Enforced-"
+            "for-API-level-23)", get_realpath());
     add_dlwarning(get_realpath(), "text relocations");
     if (phdr_table_unprotect_segments(phdr, phnum, load_bias) < 0) {
       DL_ERR("can't unprotect loadable segments for \"%s\": %s",
diff --git a/linker/linker_config.cpp b/linker/linker_config.cpp
index 33616f7..a12cfbe 100644
--- a/linker/linker_config.cpp
+++ b/linker/linker_config.cpp
@@ -33,10 +33,9 @@
 #include "linker_utils.h"
 
 #include <android-base/file.h>
+#include <android-base/scopeguard.h>
 #include <android-base/strings.h>
 
-#include <private/ScopeGuard.h>
-
 #include <stdlib.h>
 
 #include <string>
@@ -379,9 +378,7 @@
 
   Properties properties(std::move(property_map));
 
-  auto failure_guard = make_scope_guard([] {
-    g_config.clear();
-  });
+  auto failure_guard = android::base::make_scope_guard([] { g_config.clear(); });
 
   std::unordered_map<std::string, NamespaceConfig*> namespace_configs;
 
@@ -469,7 +466,7 @@
     ns_config->set_permitted_paths(properties.get_paths(property_name_prefix + ".permitted.paths"));
   }
 
-  failure_guard.disable();
+  failure_guard.Disable();
   *config = &g_config;
   return true;
 }
diff --git a/linker/linker_globals.h b/linker/linker_globals.h
index e4e3d97..1ed479c 100644
--- a/linker/linker_globals.h
+++ b/linker/linker_globals.h
@@ -34,6 +34,8 @@
 
 #include <unordered_map>
 
+#include "private/libc_logging.h"
+
 #define DL_ERR(fmt, x...) \
     do { \
       __libc_format_buffer(linker_get_error_buffer(), linker_get_error_buffer_size(), fmt, ##x); \
diff --git a/linker/linker_namespaces.cpp b/linker/linker_namespaces.cpp
index 273ff9b..3c86f99 100644
--- a/linker/linker_namespaces.cpp
+++ b/linker/linker_namespaces.cpp
@@ -27,6 +27,7 @@
  */
 
 #include "linker_namespaces.h"
+#include "linker_globals.h"
 #include "linker_soinfo.h"
 #include "linker_utils.h"
 
@@ -58,6 +59,13 @@
 
 bool android_namespace_t::is_accessible(soinfo* s) {
   auto is_accessible_ftor = [this] (soinfo* si) {
+    // This is workaround for apps hacking into soinfo list.
+    // and inserting their own entries into it. (http://b/37191433)
+    if (!si->has_min_version(3)) {
+      DL_WARN("invalid soinfo version for \"%s\"", si->get_soname());
+      return false;
+    }
+
     if (si->get_primary_namespace() == this) {
       return true;
     }
diff --git a/linker/tests/linker_config_test.cpp b/linker/tests/linker_config_test.cpp
index 64ab00f..418cbda 100644
--- a/linker/tests/linker_config_test.cpp
+++ b/linker/tests/linker_config_test.cpp
@@ -36,12 +36,11 @@
 
 #include <unistd.h>
 
+#include <android-base/scopeguard.h>
 #include <android-base/stringprintf.h>
 #include <android-base/file.h>
 #include <android-base/test_utils.h>
 
-#include "private/ScopeGuard.h"
-
 
 static const char* config_str =
   "# comment \n"
@@ -116,9 +115,8 @@
   std::string executable_path = std::string(tmp_dir.path) + "/some-binary";
   std::string version_file = std::string(tmp_dir.path) + "/.version";
 
-  auto file_guard = make_scope_guard([&version_file] {
-    unlink(version_file.c_str());
-  });
+  auto file_guard =
+      android::base::make_scope_guard([&version_file] { unlink(version_file.c_str()); });
 
   ASSERT_TRUE(write_version(version_file, 113U)) << strerror(errno);
 
diff --git a/tests/Android.bp b/tests/Android.bp
index 2bdbbbc..519c28c 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -384,6 +384,110 @@
             shared_libs: ["libicuuc"],
         },
     },
+
+    required: [
+        "cfi_test_helper",
+        "cfi_test_helper2",
+        "libtest_dt_runpath_a",
+        "libtest_dt_runpath_b",
+        "libtest_dt_runpath_c",
+        "libtest_dt_runpath_x",
+        "libatest_simple_zip",
+        "libcfi-test",
+        "libcfi-test-bad",
+        "libdlext_test_different_soname",
+        "libdlext_test_fd",
+        "libdlext_test_norelro",
+        "libdlext_test_runpath_zip_zipaligned",
+        "libdlext_test",
+        "libdlext_test_zip",
+        "libdlext_test_zip_zipaligned",
+        "libdl_preempt_test_1",
+        "libdl_preempt_test_2",
+        "libdl_test_df_1_global",
+        "libsysv-hash-table-library",
+        "libtest_atexit",
+        "libtest_check_order_dlsym_1_left",
+        "libtest_check_order_dlsym_2_right",
+        "libtest_check_order_dlsym_3_c",
+        "libtest_check_order_dlsym_a",
+        "libtest_check_order_dlsym_b",
+        "libtest_check_order_dlsym_d",
+        "libtest_check_order_dlsym",
+        "libtest_check_order_reloc_root_1",
+        "libtest_check_order_reloc_root_2",
+        "libtest_check_order_reloc_root",
+        "libtest_check_order_reloc_siblings_1",
+        "libtest_check_order_reloc_siblings_2",
+        "libtest_check_order_reloc_siblings_3",
+        "libtest_check_order_reloc_siblings_a",
+        "libtest_check_order_reloc_siblings_b",
+        "libtest_check_order_reloc_siblings_c_1",
+        "libtest_check_order_reloc_siblings_c_2",
+        "libtest_check_order_reloc_siblings_c",
+        "libtest_check_order_reloc_siblings_d",
+        "libtest_check_order_reloc_siblings_e",
+        "libtest_check_order_reloc_siblings_f",
+        "libtest_check_order_reloc_siblings",
+        "libtest_check_rtld_next_from_library",
+        "libtest_dlopen_from_ctor_main",
+        "libtest_dlopen_from_ctor",
+        "libtest_dlopen_weak_undefined_func",
+        "libtest_dlsym_df_1_global",
+        "libtest_dlsym_from_this_child",
+        "libtest_dlsym_from_this_grandchild",
+        "libtest_dlsym_from_this",
+        "libtest_dlsym_weak_func",
+        "libtest_dt_runpath_d",
+        "libtest_empty",
+        "libtest_init_fini_order_child",
+        "libtest_init_fini_order_grand_child",
+        "libtest_init_fini_order_root2",
+        "libtest_init_fini_order_root",
+        "libtest_nodelete_1",
+        "libtest_nodelete_2",
+        "libtest_nodelete_dt_flags_1",
+        "libtest_pthread_atfork",
+        "libtest_relo_check_dt_needed_order_1",
+        "libtest_relo_check_dt_needed_order_2",
+        "libtest_relo_check_dt_needed_order",
+        "libtest_simple",
+        "libtest_two_parents_child",
+        "libtest_two_parents_parent1",
+        "libtest_two_parents_parent2",
+        "libtest_versioned_lib",
+        "libtest_versioned_libv1",
+        "libtest_versioned_libv2",
+        "libtest_versioned_otherlib_empty",
+        "libtest_versioned_otherlib",
+        "libtest_versioned_uselibv1",
+        "libtest_versioned_uselibv2_other",
+        "libtest_versioned_uselibv2",
+        "libtest_versioned_uselibv3_other",
+        "libtest_with_dependency_loop_a",
+        "libtest_with_dependency_loop_b",
+        "libtest_with_dependency_loop_c",
+        "libtest_with_dependency_loop",
+        "libtest_with_dependency",
+        "libtest_invalid-empty_shdr_table.so",
+        "libtest_invalid-rw_load_segment.so",
+        "libtest_invalid-unaligned_shdr_offset.so",
+        "libtest_invalid-zero_shdr_table_content.so",
+        "libtest_invalid-zero_shdr_table_offset.so",
+        "libtest_invalid-zero_shentsize.so",
+        "libtest_invalid-zero_shstrndx.so",
+        "libtest_invalid-textrels.so",
+        "libtest_invalid-textrels2.so",
+        "preinit_getauxval_test_helper",
+        "preinit_syscall_test_helper",
+        "libnstest_private_external",
+        "libnstest_dlopened",
+        "libnstest_private",
+        "libnstest_root_not_isolated",
+        "libnstest_root",
+        "libnstest_public",
+        "libnstest_public_internal",
+    ],
 }
 
 // -----------------------------------------------------------------------------
diff --git a/tests/bug_26110743_test.cpp b/tests/bug_26110743_test.cpp
index c49a9dc..ef474a0 100644
--- a/tests/bug_26110743_test.cpp
+++ b/tests/bug_26110743_test.cpp
@@ -22,7 +22,7 @@
 #include <sys/stat.h>
 #include <sys/prctl.h>
 
-#include "private/ScopeGuard.h"
+#include <android-base/scopeguard.h>
 
 extern "C" pid_t gettid();
 
@@ -56,7 +56,7 @@
 TEST(bug_26110743, ProcSelfReadlink_NotDumpable) {
   int dumpable = prctl(PR_GET_DUMPABLE, 0, 0, 0, 0);
   prctl(PR_SET_DUMPABLE, 0, 0, 0, 0);
-  auto guard = make_scope_guard([&]() {
+  auto guard = android::base::make_scope_guard([&]() {
     // restore dumpable
     prctl(PR_SET_DUMPABLE, dumpable, 0, 0, 0);
   });
@@ -100,7 +100,7 @@
 TEST(bug_26110743, ProcTaskFdReadlink_NotDumpable) {
   int dumpable = prctl(PR_GET_DUMPABLE, 0, 0, 0, 0);
   prctl(PR_SET_DUMPABLE, 0, 0, 0, 0);
-  auto guard = make_scope_guard([&]() {
+  auto guard = android::base::make_scope_guard([&]() {
     // restore dumpable
     prctl(PR_SET_DUMPABLE, dumpable, 0, 0, 0);
   });
diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp
index ad8444e..4ff324e 100644
--- a/tests/dlfcn_test.cpp
+++ b/tests/dlfcn_test.cpp
@@ -22,11 +22,11 @@
 #include <stdint.h>
 #include <string.h>
 
-#include "private/ScopeGuard.h"
-
 #include <string>
 #include <thread>
 
+#include <android-base/scopeguard.h>
+
 #include "gtest_globals.h"
 #include "dlfcn_symlink_support.h"
 #include "utils.h"
@@ -330,9 +330,7 @@
   // in both dt_needed libraries, the correct relocation should
   // use the function defined in libtest_relo_check_dt_needed_order_1.so
   void* handle = nullptr;
-  auto guard = make_scope_guard([&]() {
-    dlclose(handle);
-  });
+  auto guard = android::base::make_scope_guard([&]() { dlclose(handle); });
 
   handle = dlopen("libtest_relo_check_dt_needed_order.so", RTLD_NOW);
   ASSERT_TRUE(handle != nullptr) << dlerror();
@@ -986,9 +984,7 @@
   dlerror(); // Clear any pending errors.
   void* handle = dlopen("libgnu-hash-table-library.so", RTLD_NOW);
   ASSERT_TRUE(handle != nullptr) << dlerror();
-  auto guard = make_scope_guard([&]() {
-    dlclose(handle);
-  });
+  auto guard = android::base::make_scope_guard([&]() { dlclose(handle); });
   void* sym = dlsym(handle, "getRandomNumber");
   ASSERT_TRUE(sym != nullptr) << dlerror();
   int (*fn)(void);
@@ -1009,9 +1005,7 @@
 TEST(dlfcn, dlopen_library_with_only_sysv_hash) {
   void* handle = dlopen("libsysv-hash-table-library.so", RTLD_NOW);
   ASSERT_TRUE(handle != nullptr) << dlerror();
-  auto guard = make_scope_guard([&]() {
-    dlclose(handle);
-  });
+  auto guard = android::base::make_scope_guard([&]() { dlclose(handle); });
   void* sym = dlsym(handle, "getRandomNumber");
   ASSERT_TRUE(sym != nullptr) << dlerror();
   int (*fn)(void);
diff --git a/tests/fortify_test.cpp b/tests/fortify_test.cpp
index c21c9da..67103e1 100644
--- a/tests/fortify_test.cpp
+++ b/tests/fortify_test.cpp
@@ -58,71 +58,44 @@
   char b[10];
 };
 
-#ifndef __clang__
-// This test is disabled in clang because clang doesn't properly detect
-// this buffer overflow. TODO: Fix clang.
 TEST_F(DEATHTEST, stpncpy_fortified2) {
   foo myfoo;
   int copy_amt = atoi("11");
   ASSERT_FORTIFY(stpncpy(myfoo.a, "01234567890", copy_amt));
 }
-#endif
 
-#ifndef __clang__
-// This test is disabled in clang because clang doesn't properly detect
-// this buffer overflow. TODO: Fix clang.
 TEST_F(DEATHTEST, stpncpy2_fortified2) {
   foo myfoo;
   memset(&myfoo, 0, sizeof(myfoo));
   myfoo.one[0] = 'A'; // not null terminated string
   ASSERT_FORTIFY(stpncpy(myfoo.b, myfoo.one, sizeof(myfoo.b)));
 }
-#endif
 
-#ifndef __clang__
-// This test is disabled in clang because clang doesn't properly detect
-// this buffer overflow. TODO: Fix clang.
 TEST_F(DEATHTEST, strncpy_fortified2) {
   foo myfoo;
   int copy_amt = atoi("11");
   ASSERT_FORTIFY(strncpy(myfoo.a, "01234567890", copy_amt));
 }
-#endif
 
-#ifndef __clang__
-// This test is disabled in clang because clang doesn't properly detect
-// this buffer overflow. TODO: Fix clang.
 TEST_F(DEATHTEST, strncpy2_fortified2) {
   foo myfoo;
   memset(&myfoo, 0, sizeof(myfoo));
   myfoo.one[0] = 'A'; // not null terminated string
   ASSERT_FORTIFY(strncpy(myfoo.b, myfoo.one, sizeof(myfoo.b)));
 }
-#endif
 
-#ifndef __clang__
-// This test is disabled in clang because clang doesn't properly detect
-// this buffer overflow. TODO: Fix clang.
 TEST_F(DEATHTEST, sprintf_fortified2) {
   foo myfoo;
   char source_buf[15];
   memcpy(source_buf, "12345678901234", 15);
   ASSERT_FORTIFY(sprintf(myfoo.a, "%s", source_buf));
 }
-#endif
 
-#ifndef __clang__
-// This test is disabled in clang because clang doesn't properly detect
-// this buffer overflow. TODO: Fix clang.
 TEST_F(DEATHTEST, sprintf2_fortified2) {
   foo myfoo;
   ASSERT_FORTIFY(sprintf(myfoo.a, "0123456789"));
 }
-#endif
 
-#ifndef __clang__
-// These tests are disabled in clang because clang doesn't properly detect
-// this buffer overflow. TODO: Fix clang.
 static int vsprintf_helper2(const char *fmt, ...) {
   foo myfoo;
   va_list va;
@@ -141,11 +114,7 @@
 TEST_F(DEATHTEST, vsprintf2_fortified2) {
   ASSERT_FORTIFY(vsprintf_helper2("0123456789"));
 }
-#endif
 
-#ifndef __clang__
-// These tests are disabled in clang because clang doesn't properly detect
-// this buffer overflow. TODO: Fix clang.
 static int vsnprintf_helper2(const char *fmt, ...) {
   foo myfoo;
   va_list va;
@@ -165,12 +134,8 @@
 TEST_F(DEATHTEST, vsnprintf2_fortified2) {
   ASSERT_FORTIFY(vsnprintf_helper2("0123456789"));
 }
-#endif
 
-#ifndef __clang__
 // zero sized target with "\0" source (should fail)
-// This test is disabled in clang because clang doesn't properly detect
-// this buffer overflow. TODO: Fix clang.
 TEST_F(DEATHTEST, stpcpy_fortified2) {
 #if defined(__BIONIC__)
   foo myfoo;
@@ -181,12 +146,8 @@
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif // __BIONIC__
 }
-#endif
 
-#ifndef __clang__
 // zero sized target with "\0" source (should fail)
-// This test is disabled in clang because clang doesn't properly detect
-// this buffer overflow. TODO: Fix clang.
 TEST_F(DEATHTEST, strcpy_fortified2) {
 #if defined(__BIONIC__)
   foo myfoo;
@@ -197,12 +158,8 @@
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif // __BIONIC__
 }
-#endif
 
-#ifndef __clang__
 // zero sized target with longer source (should fail)
-// This test is disabled in clang because clang doesn't properly detect
-// this buffer overflow. TODO: Fix clang.
 TEST_F(DEATHTEST, strcpy2_fortified2) {
 #if defined(__BIONIC__)
   foo myfoo;
@@ -213,12 +170,8 @@
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif // __BIONIC__
 }
-#endif
 
-#ifndef __clang__
 // one byte target with longer source (should fail)
-// This test is disabled in clang because clang doesn't properly detect
-// this buffer overflow. TODO: Fix clang.
 TEST_F(DEATHTEST, strcpy3_fortified2) {
 #if defined(__BIONIC__)
   foo myfoo;
@@ -229,7 +182,6 @@
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif // __BIONIC__
 }
-#endif
 
 TEST_F(DEATHTEST, strchr_fortified2) {
 #if defined(__BIONIC__)
@@ -267,8 +219,6 @@
 #endif // __BIONIC__
 }
 
-#ifndef __clang__
-// This test is disabled in clang because clang doesn't properly detect
 // this buffer overflow. TODO: Fix clang.
 TEST_F(DEATHTEST, strlcpy_fortified2) {
 #if defined(__BIONIC__)
@@ -280,10 +230,7 @@
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif // __BIONIC__
 }
-#endif
 
-#ifndef __clang__
-// This test is disabled in clang because clang doesn't properly detect
 // this buffer overflow. TODO: Fix clang.
 TEST_F(DEATHTEST, strlcat_fortified2) {
 #if defined(__BIONIC__)
@@ -296,29 +243,20 @@
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif // __BIONIC__
 }
-#endif
 
-#ifndef __clang__
-// This test is disabled in clang because clang doesn't properly detect
-// this buffer overflow. TODO: Fix clang.
 TEST_F(DEATHTEST, strncat_fortified2) {
   foo myfoo;
   size_t n = atoi("10"); // avoid compiler optimizations
   strncpy(myfoo.a, "012345678", n);
   ASSERT_FORTIFY(strncat(myfoo.a, "9", n));
 }
-#endif
 
-#ifndef __clang__
-// This test is disabled in clang because clang doesn't properly detect
-// this buffer overflow. TODO: Fix clang.
 TEST_F(DEATHTEST, strncat2_fortified2) {
   foo myfoo;
   myfoo.a[0] = '\0';
   size_t n = atoi("10"); // avoid compiler optimizations
   ASSERT_FORTIFY(strncat(myfoo.a, "0123456789", n));
 }
-#endif
 
 TEST_F(DEATHTEST, strncat3_fortified2) {
   foo myfoo;
@@ -328,9 +266,6 @@
   ASSERT_FORTIFY(strncat(myfoo.b, myfoo.a, n));
 }
 
-#ifndef __clang__
-// This test is disabled in clang because clang doesn't properly detect
-// this buffer overflow. TODO: Fix clang.
 TEST_F(DEATHTEST, strcat_fortified2) {
   char src[11];
   strcpy(src, "0123456789");
@@ -338,7 +273,6 @@
   myfoo.a[0] = '\0';
   ASSERT_FORTIFY(strcat(myfoo.a, src));
 }
-#endif
 
 TEST_F(DEATHTEST, strcat2_fortified2) {
   foo myfoo;
@@ -473,7 +407,12 @@
   ASSERT_FORTIFY(sprintf(buf, "%s", source_buf));
 }
 
-#ifndef __clang__
+#ifdef __clang__
+// Exists upstream, but hasn't been pulled in yet.
+#if __has_attribute(alloc_size)
+#error "Reenable this test"
+#endif
+#else
 // This test is disabled in clang because clang doesn't properly detect
 // this buffer overflow. TODO: Fix clang.
 TEST_F(DEATHTEST, sprintf_malloc_fortified) {
diff --git a/tests/math_test.cpp b/tests/math_test.cpp
index 6c7dffb..b960944 100644
--- a/tests/math_test.cpp
+++ b/tests/math_test.cpp
@@ -57,7 +57,7 @@
 #include <limits.h>
 #include <stdint.h>
 
-#include <private/ScopeGuard.h>
+#include <android-base/scopeguard.h>
 
 float float_subnormal() {
   union {
@@ -775,9 +775,7 @@
 }
 
 TEST(math, lrint) {
-  auto guard = make_scope_guard([]() {
-    fesetenv(FE_DFL_ENV);
-  });
+  auto guard = android::base::make_scope_guard([]() { fesetenv(FE_DFL_ENV); });
 
   fesetround(FE_UPWARD); // lrint/lrintf/lrintl obey the rounding mode.
   ASSERT_EQ(1235, lrint(1234.01));
@@ -799,9 +797,7 @@
 }
 
 TEST(math, rint) {
-  auto guard = make_scope_guard([]() {
-    fesetenv(FE_DFL_ENV);
-  });
+  auto guard = android::base::make_scope_guard([]() { fesetenv(FE_DFL_ENV); });
 
   fesetround(FE_UPWARD); // rint/rintf/rintl obey the rounding mode.
   feclearexcept(FE_ALL_EXCEPT); // rint/rintf/rintl do set the FE_INEXACT flag.
@@ -829,9 +825,7 @@
 }
 
 TEST(math, nearbyint) {
-  auto guard = make_scope_guard([]() {
-    fesetenv(FE_DFL_ENV);
-  });
+  auto guard = android::base::make_scope_guard([]() { fesetenv(FE_DFL_ENV); });
   fesetround(FE_UPWARD); // nearbyint/nearbyintf/nearbyintl obey the rounding mode.
   feclearexcept(FE_ALL_EXCEPT); // nearbyint/nearbyintf/nearbyintl don't set the FE_INEXACT flag.
   ASSERT_EQ(1234.0, nearbyint(1234.0));
@@ -858,9 +852,7 @@
 }
 
 TEST(math, lround) {
-  auto guard = make_scope_guard([]() {
-    fesetenv(FE_DFL_ENV);
-  });
+  auto guard = android::base::make_scope_guard([]() { fesetenv(FE_DFL_ENV); });
   fesetround(FE_UPWARD); // lround ignores the rounding mode.
   ASSERT_EQ(1234, lround(1234.01));
   ASSERT_EQ(1234, lroundf(1234.01f));
@@ -868,9 +860,7 @@
 }
 
 TEST(math, llround) {
-  auto guard = make_scope_guard([]() {
-    fesetenv(FE_DFL_ENV);
-  });
+  auto guard = android::base::make_scope_guard([]() { fesetenv(FE_DFL_ENV); });
   fesetround(FE_UPWARD); // llround ignores the rounding mode.
   ASSERT_EQ(1234L, llround(1234.01));
   ASSERT_EQ(1234L, llroundf(1234.01f));
@@ -965,9 +955,7 @@
 }
 
 TEST(math, round) {
-  auto guard = make_scope_guard([]() {
-    fesetenv(FE_DFL_ENV);
-  });
+  auto guard = android::base::make_scope_guard([]() { fesetenv(FE_DFL_ENV); });
   fesetround(FE_TOWARDZERO); // round ignores the rounding mode and always rounds away from zero.
   ASSERT_DOUBLE_EQ(1.0, round(0.5));
   ASSERT_DOUBLE_EQ(-1.0, round(-0.5));
@@ -978,9 +966,7 @@
 }
 
 TEST(math, roundf) {
-  auto guard = make_scope_guard([]() {
-    fesetenv(FE_DFL_ENV);
-  });
+  auto guard = android::base::make_scope_guard([]() { fesetenv(FE_DFL_ENV); });
   fesetround(FE_TOWARDZERO); // roundf ignores the rounding mode and always rounds away from zero.
   ASSERT_FLOAT_EQ(1.0f, roundf(0.5f));
   ASSERT_FLOAT_EQ(-1.0f, roundf(-0.5f));
@@ -991,9 +977,7 @@
 }
 
 TEST(math, roundl) {
-  auto guard = make_scope_guard([]() {
-    fesetenv(FE_DFL_ENV);
-  });
+  auto guard = android::base::make_scope_guard([]() { fesetenv(FE_DFL_ENV); });
   fesetround(FE_TOWARDZERO); // roundl ignores the rounding mode and always rounds away from zero.
   ASSERT_DOUBLE_EQ(1.0L, roundl(0.5L));
   ASSERT_DOUBLE_EQ(-1.0L, roundl(-0.5L));
@@ -1004,9 +988,7 @@
 }
 
 TEST(math, trunc) {
-  auto guard = make_scope_guard([]() {
-    fesetenv(FE_DFL_ENV);
-  });
+  auto guard = android::base::make_scope_guard([]() { fesetenv(FE_DFL_ENV); });
   fesetround(FE_UPWARD); // trunc ignores the rounding mode and always rounds toward zero.
   ASSERT_DOUBLE_EQ(1.0, trunc(1.5));
   ASSERT_DOUBLE_EQ(-1.0, trunc(-1.5));
@@ -1017,9 +999,7 @@
 }
 
 TEST(math, truncf) {
-  auto guard = make_scope_guard([]() {
-    fesetenv(FE_DFL_ENV);
-  });
+  auto guard = android::base::make_scope_guard([]() { fesetenv(FE_DFL_ENV); });
   fesetround(FE_UPWARD); // truncf ignores the rounding mode and always rounds toward zero.
   ASSERT_FLOAT_EQ(1.0f, truncf(1.5f));
   ASSERT_FLOAT_EQ(-1.0f, truncf(-1.5f));
@@ -1030,9 +1010,7 @@
 }
 
 TEST(math, truncl) {
-  auto guard = make_scope_guard([]() {
-    fesetenv(FE_DFL_ENV);
-  });
+  auto guard = android::base::make_scope_guard([]() { fesetenv(FE_DFL_ENV); });
   fesetround(FE_UPWARD); // truncl ignores the rounding mode and always rounds toward zero.
   ASSERT_DOUBLE_EQ(1.0L, truncl(1.5L));
   ASSERT_DOUBLE_EQ(-1.0L, truncl(-1.5L));
diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp
index 60fe294..87b4c81 100755
--- a/tests/pthread_test.cpp
+++ b/tests/pthread_test.cpp
@@ -33,9 +33,10 @@
 #include <atomic>
 #include <vector>
 
+#include <android-base/scopeguard.h>
+
 #include "private/bionic_constants.h"
 #include "private/bionic_macros.h"
-#include "private/ScopeGuard.h"
 #include "BionicDeathTest.h"
 #include "ScopedSignalHandler.h"
 #include "utils.h"
@@ -64,7 +65,7 @@
   int nkeys = PTHREAD_KEYS_MAX / 2;
   std::vector<pthread_key_t> keys;
 
-  auto scope_guard = make_scope_guard([&keys]{
+  auto scope_guard = android::base::make_scope_guard([&keys] {
     for (const auto& key : keys) {
       EXPECT_EQ(0, pthread_key_delete(key));
     }
@@ -1362,7 +1363,7 @@
   }
   EXPECT_EQ(rl.rlim_cur, stack_size);
 
-  auto guard = make_scope_guard([&rl, original_rlim_cur]() {
+  auto guard = android::base::make_scope_guard([&rl, original_rlim_cur]() {
     rl.rlim_cur = original_rlim_cur;
     ASSERT_EQ(0, setrlimit(RLIMIT_STACK, &rl));
   });
diff --git a/tests/unistd_test.cpp b/tests/unistd_test.cpp
index d90b01e..9eae06e 100644
--- a/tests/unistd_test.cpp
+++ b/tests/unistd_test.cpp
@@ -996,7 +996,7 @@
   if (rl.rlim_cur == RLIM_INFINITY) {
     rl.rlim_cur = 8 * 1024 * 1024; // Bionic reports unlimited stacks as 8MiB.
   }
-  auto guard = make_scope_guard([&rl, original_rlim_cur]() {
+  auto guard = android::base::make_scope_guard([&rl, original_rlim_cur]() {
     rl.rlim_cur = original_rlim_cur;
     ASSERT_EQ(0, setrlimit(RLIMIT_STACK, &rl));
   });
diff --git a/tests/utils.h b/tests/utils.h
index fa85545..2e9b994 100644
--- a/tests/utils.h
+++ b/tests/utils.h
@@ -28,10 +28,9 @@
 #include <regex>
 
 #include <android-base/file.h>
+#include <android-base/scopeguard.h>
 #include <android-base/stringprintf.h>
 
-#include "private/ScopeGuard.h"
-
 #if defined(__LP64__)
 #define PATH_TO_SYSTEM_LIB "/system/lib64/"
 #else
@@ -68,9 +67,7 @@
       return false;
     }
 
-    auto fp_guard = make_scope_guard([&]() {
-      fclose(fp);
-    });
+    auto fp_guard = android::base::make_scope_guard([&]() { fclose(fp); });
 
     char line[BUFSIZ];
     while (fgets(line, sizeof(line), fp) != nullptr) {
diff --git a/tools/versioner/src/VFS.cpp b/tools/versioner/src/VFS.cpp
index cdf232f..8f9de88 100644
--- a/tools/versioner/src/VFS.cpp
+++ b/tools/versioner/src/VFS.cpp
@@ -54,7 +54,7 @@
       err(1, "failed to open header '%s'", file_path);
     }
 
-    auto buffer_opt = llvm::MemoryBuffer::getOpenFileSlice(fd, file_path, -1, 0);
+    auto buffer_opt = llvm::MemoryBuffer::getOpenFile(fd, file_path, -1, false, false);
     if (!buffer_opt) {
       errx(1, "failed to map header '%s'", file_path);
     }