Import Cobalt 20.master.0.217501
diff --git a/src/LICENSE b/src/LICENSE
index 3d0f7d3..a32e00c 100644
--- a/src/LICENSE
+++ b/src/LICENSE
@@ -1,4 +1,4 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors. All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions are
diff --git a/src/cobalt/CHANGELOG.md b/src/cobalt/CHANGELOG.md
index 6f368aa..45f312d 100644
--- a/src/cobalt/CHANGELOG.md
+++ b/src/cobalt/CHANGELOG.md
@@ -3,11 +3,22 @@
 This document records all notable changes made to Cobalt since the last release.
 
 ## Version 20
- - **Improvements and Bug Fixes**
-  - Fix bug where Cobalt would not refresh the layout when the textContent
-    property of a DOM TextNode is modified.
 
- - **Add support for decoding JPEG images as multi-plane YUV**
+ - **Support for QUIC and SPDY is now enabled.**
+
+   QUIC and SPDY networking protocol support is added and these are enabled by
+   default (if the server supports the protocol).  This reduces roundtrip
+   overhead and will improve networking overhead performance, especially on
+   lower quality connections.
+
+ - **BoringSSL replaces OpenSSL enabling architecture-specific optimizations.**
+
+   The update to BoringSSL brings with it the introduction of assembly-optimized
+   architecture-specific optimizations that can bring a 5x speed up to
+   cryptographic functions.  This is especially useful when decrypting TLS for
+   high-bandwidth video.
+
+ - **Added support for decoding JPEG images as multi-plane YUV.**
 
    JPEG images are decoded into RGBA in previous versions of Cobalt.  The native
    format of most JPEG images is YV12, which takes only 3/8 of memory compare to
@@ -19,6 +30,108 @@
    parameter "allow_image_decoding_to_multi_plane" to Cobalt with value "true"
    or "false".
 
+ - **Improved image cache purge strategy.**
+
+   Cobalt's image cache is now more aware of which images are likely to be
+   reused (e.g. the ones referenced by HTMLImageElements, even if they are not
+   currently displayed), and will now prioritize purging completely unreferenced
+   images before weakly referenced images.
+
+ - **Added support for encoded image caching.**
+
+   Cobalt now has support for caching the encoded image data fetched from the
+   network.  This enables Cobalt to keep the cached encoded image data resident
+   so that when the user resumes, Cobalt does not need to do a network fetch
+   for images again.  The size of this cache can be adjusted via the gyp
+   option `encoded_image_cache_size_in_bytes`, or the command line switch
+   `--encoded_image_cache_size_in_bytes`.
+
+ - **Added support for Device Authentication URL signing.**
+
+   Cobalt will now add URL parameters signed with the device's secret key and
+   certification scope to the initial URL.
+
+ - **Updated Chromium net and base libraries from m25 to m70.**
+
+   Cobalt now has rebased its m25 net and base libraries to Chromium's m70
+   version of those libraries, bringing with it more functionality, better
+   performance, and fewer bugs.
+
+ - **Media Codec Support.**
+
+   Cobalt now supports the AV1 codec, the HEVC (H.265) codec, Dolby Digital
+   (AC-3) and Dolby Digital Plus (Enhanced AC-3, or EAC-3).
+
+ - **Flexbox support added.**
+
+   Cobalt now supports the Flexible Box Module, enabling web applications to
+   take advantage of the more expressive layout model.
+
+ - **Support added for Chromium DevTools with V8.**
+
+   Cobalt now supports the Chromium DevTools to help debug web applications.
+   You can access it on non-gold builds by first starting Cobalt, and then using
+   a browser to navigate to `http://YOUR_DEVICE_IP:9222`.  The Elements,
+   Sources, Console and Performance panels are supported.
+
+ - **Support added for Intersection Observer Web API.**
+
+   Cobalt now supports the Intersection Observer Web API to enable more
+   performant checking of whether HTML elements are visible or not.  Note that
+   Cobalt's implementation currently does not support triggering visibility
+   events that occur during CSS animation/transition playback.
+
+ - **Support for custom interface HTMLVideoElement.setMaxVideoCapabilities()**
+
+   This allows the web application to express a guarantee to the platform's
+   video player that a video will never exceed a maximum quality in a particular
+   property.  You could use this for example to indicate that a video will never
+   adapt past a maximum resolution.
+
+ - **Platform Services API added**
+
+   This API enables web applications to communicate directly to
+   platform-specific services and systems, assuming the platform explicitly
+   enables the given service.
+
+ - **EGL/GLES-based reference implementation of the Blitter API now available.**
+
+   The Blitter API is now much easier to test locally on desktop computers or
+   any device that already supports EGL/GLES.  The new platform configuration
+   is named `linux-x64x11-blittergles`.
+
+ - **Add support for AbortController to the  Fetch API.**
+
+   The Fetch API is now updated to support the `AbortController` feature.
+
+ - **Support added for HTMLAudioElement HTML tag.**
+
+   This can be used to play audio-only media.
+
+ - **Add support for CSS3 Media Queries “dpi” value.**
+
+   This can be used by the web application to adjust its layout depending
+   on the physical size of the display device, if known.
+
+ - **Add support for scrollWidth, scrollHeight, scrollLeft, and scrollTop.**
+
+   The web application can now query and set scroll properties of containers.
+
+ - **Initial support for Cobalt Evergreen automatic software updater added.**
+
+   Cobalt is transitioning towards a runtime-linkable environment in order to
+   support automatic software updates.  Changes have been made around the
+   Starboard interface in anticipation of this.  Most notably, the EGL/GLES
+   interface is no longer assumed to be available but rather Cobalt will now
+   query the Starboard implementation for a structure of function pointers that
+   implement the EGL/GLES APIs.
+
+   Part of this process involves moving options that were formerly build-time
+   options to be instead run-time options.  This will primarily be enabled
+   by the new Starboard extensions framework.  An example of an platform
+   specific option added in this way can be found in
+   `cobalt/extension/graphis.h`.
+
  - **Cobalt code assumes that no errors are generated for unused parameters**
 
    There now exists Cobalt code where input parameters may be unused, and it
@@ -45,6 +158,19 @@
   that may be copied and queried on any thread to get a coherent view of
   attributes set by the the web app on the `MediaSession`.
 
+ - **Improvements and Bug Fixes**
+
+   - Fix bug where Cobalt would not refresh the layout when the textContent
+     property of a DOM TextNode is modified.
+   - Media codecs can now be disabled with the “--disable_media_codecs” command
+     line option to help with debugging.
+   - Enable “--proxy” command line flag in gold builds of Cobalt.
+   - Add `GetMaximumFrameIntervalInMilliseconds()` platform Cobalt configuration
+     setting (in `cobalt/extension/graphics.h`) to allow a platform to indicate a
+     minimum framerate causing Cobalt to rerender the display even if nothing has
+     changed after the specified interval.
+
+
 ## Version 19
  - **Add support for V8 JavaScript Engine**
 
diff --git a/src/cobalt/audio/audio_device.cc b/src/cobalt/audio/audio_device.cc
index 73f91bc..11ed4d2 100644
--- a/src/cobalt/audio/audio_device.cc
+++ b/src/cobalt/audio/audio_device.cc
@@ -30,17 +30,13 @@
 
 namespace {
 const int kRenderBufferSizeFrames = 1024;
+// AudioDevice will keep writing silence frames at the end of playback until the
+// buffer is full to ensure the audible frames being played on platforms with
+// strict underflow control. A large |kFramesPerChannel| will increase the
+// silence between two sounds. So it shouldn't be set to a very large number.
 const int kFramesPerChannel = kRenderBufferSizeFrames * 8;
 }  // namespace
 
-namespace {
-// Write |kTailSizeInFrames| frames of silence at the end of playback to ensure
-// the audible frames being played on platforms with strict underflow control.
-// Increasing this value will also increase the silence between two sounds.  So
-// it shouldn't be set to a very too large.
-const int kTailSizeInFrames = kRenderBufferSizeFrames * 8;
-}  // namespace
-
 class AudioDevice::Impl {
  public:
   Impl(int number_of_channels, RenderCallback* callback);
@@ -162,6 +158,9 @@
                                            bool* is_playing,
                                            bool* is_eos_reached) {
   TRACE_EVENT0("cobalt::audio", "AudioDevice::Impl::UpdateSourceStatus()");
+  // AudioDevice may be reused after stopped but before destroyed. Keep writing
+  // silence before destroyed to let |audio_sink_| keep working without
+  // underflow. It will cause latency between two sounds.
   *is_playing = true;
   *is_eos_reached = false;
 
@@ -182,28 +181,23 @@
 
     render_callback_->FillAudioBus(all_consumed, &input_audio_bus_, &silence);
 
-    bool fill_output = true;
     if (silence) {
-      fill_output = kTailSizeInFrames > silence_written_;
       silence_written_ += kRenderBufferSizeFrames;
     } else {
       // Reset |silence_written_| if a new sound is played after some silence
       // frames were injected.
       silence_written_ = 0;
     }
-    if (fill_output) {
-      FillOutputAudioBus();
 
-      frames_rendered_ += kRenderBufferSizeFrames;
-      *frames_in_buffer += kRenderBufferSizeFrames;
-    }
+    FillOutputAudioBus();
+
+    frames_rendered_ += kRenderBufferSizeFrames;
+    *frames_in_buffer += kRenderBufferSizeFrames;
 
     was_silence_last_update_ = silence;
   }
 
   *offset_in_frames = frames_consumed_ % kFramesPerChannel;
-  *is_playing =
-      !(silence_written_ != 0 && *frames_in_buffer <= silence_written_);
 }
 
 void AudioDevice::Impl::ConsumeFrames(int frames_consumed) {
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index fad5331..1708028 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -465,7 +465,11 @@
 #endif
 }
 
-void BrowserModule::Navigate(const GURL& url) {
+void BrowserModule::Navigate(const GURL& url_reference) {
+  // The argument is sometimes |pending_navigate_url_|, and Navigate can modify
+  // |pending_navigate_url_|, so we want to keep a copy of the argument to
+  // preserve its original value.
+  GURL url = url_reference;
   DLOG(INFO) << "In BrowserModule::Navigate " << url;
   TRACE_EVENT1("cobalt::browser", "BrowserModule::Navigate()", "url",
                url.spec());
diff --git a/src/cobalt/browser/browser_module.h b/src/cobalt/browser/browser_module.h
index 74e0fc3..0bc7d1a 100644
--- a/src/cobalt/browser/browser_module.h
+++ b/src/cobalt/browser/browser_module.h
@@ -125,7 +125,7 @@
   // currently suspended, this defers the navigation and instead sets
   // |pending_navigate_url_| to the specified url, which will trigger a
   // navigation when Cobalt resumes.
-  void Navigate(const GURL& url);
+  void Navigate(const GURL& url_reference);
   // Reloads web module.
   void Reload();
 
diff --git a/src/cobalt/build/all.gyp b/src/cobalt/build/all.gyp
index 8909d7a..a53c52f 100644
--- a/src/cobalt/build/all.gyp
+++ b/src/cobalt/build/all.gyp
@@ -86,6 +86,7 @@
         '<(DEPTH)/cobalt/websocket/websocket.gyp:*',
         '<(DEPTH)/cobalt/xhr/xhr.gyp:*',
         '<(DEPTH)/crypto/crypto.gyp:crypto_unittests',
+        '<(DEPTH)/third_party/boringssl/boringssl_tool.gyp:*',
         '<(DEPTH)/net/net.gyp:net_unittests',
         '<(DEPTH)/sql/sql.gyp:sql_unittests',
       ],
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index 04c18cb..80c1e89 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-215766
\ No newline at end of file
+217501
\ No newline at end of file
diff --git a/src/cobalt/cssom/cssom.gyp b/src/cobalt/cssom/cssom.gyp
index bab7890..3fdf9f1 100644
--- a/src/cobalt/cssom/cssom.gyp
+++ b/src/cobalt/cssom/cssom.gyp
@@ -233,7 +233,6 @@
         'unicode_range_value.h',
         'universal_selector.cc',
         'universal_selector.h',
-        'used_style.h',
         'user_agent_style_sheet.cc',
         'user_agent_style_sheet.h',
         'url_src_value.cc',
diff --git a/src/cobalt/cssom/used_style.h b/src/cobalt/cssom/used_style.h
deleted file mode 100644
index 7aa05c7..0000000
--- a/src/cobalt/cssom/used_style.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2019 The Cobalt Authors. All Rights Reserved.
-//
-// 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 COBALT_CSSOM_USED_STYLE_H_
-#define COBALT_CSSOM_USED_STYLE_H_
-
-#include "base/optional.h"
-#include "cobalt/cssom/calc_value.h"
-#include "cobalt/cssom/length_value.h"
-#include "cobalt/cssom/percentage_value.h"
-#include "cobalt/cssom/property_value_visitor.h"
-#include "cobalt/layout/layout_unit.h"
-
-namespace cobalt {
-namespace cssom {
-
-template <typename LengthType>
-class UsedLengthValueProvider : public NotReachedPropertyValueVisitor {
- public:
-  explicit UsedLengthValueProvider(LengthType percentage_base,
-                                   bool calc_permitted = false)
-      : percentage_base_(percentage_base), calc_permitted_(calc_permitted) {}
-
-  void VisitLength(LengthValue* length) {
-    depends_on_containing_block_ = false;
-
-    DCHECK_EQ(kPixelsUnit, length->unit());
-    used_length_ = LengthType(length->value());
-  }
-
-  void VisitPercentage(PercentageValue* percentage) {
-    depends_on_containing_block_ = true;
-    used_length_ = percentage->value() * percentage_base_;
-  }
-
-  void VisitCalc(CalcValue* calc) {
-    if (!calc_permitted_) {
-      NOTREACHED();
-    }
-    depends_on_containing_block_ = true;
-    used_length_ = LengthType(calc->length_value()->value()) +
-                   calc->percentage_value()->value() * percentage_base_;
-  }
-
-  bool depends_on_containing_block() const {
-    return depends_on_containing_block_;
-  }
-
-  const base::Optional<LengthType>& used_length() const { return used_length_; }
-
- protected:
-  bool depends_on_containing_block_;
-
- private:
-  const LengthType percentage_base_;
-  const bool calc_permitted_;
-
-  base::Optional<LengthType> used_length_;
-
-  DISALLOW_COPY_AND_ASSIGN(UsedLengthValueProvider);
-};
-
-}  // namespace cssom
-}  // namespace cobalt
-
-#endif  // COBALT_CSSOM_USED_STYLE_H_
diff --git a/src/cobalt/demos/content/media-element-demo/multi-video-demo.js b/src/cobalt/demos/content/media-element-demo/multi-video-demo.js
index 6df7c71..cab4d58 100644
--- a/src/cobalt/demos/content/media-element-demo/multi-video-demo.js
+++ b/src/cobalt/demos/content/media-element-demo/multi-video-demo.js
@@ -37,7 +37,7 @@
   }
 
   onsourceopen() {
-    if (this.video_url.base::EndsWith('.mp4')) {
+    if (this.video_url.endsWith('.mp4')) {
         this.video_source_buffer = this.mediasource.addSourceBuffer(
                                        'video/mp4; codecs="avc1.640028"');
       } else {
diff --git a/src/cobalt/dom/dom.gyp b/src/cobalt/dom/dom.gyp
index 7d1278a..f20646d 100644
--- a/src/cobalt/dom/dom.gyp
+++ b/src/cobalt/dom/dom.gyp
@@ -99,6 +99,8 @@
         'dom_token_list.h',
         'element.cc',
         'element.h',
+        'element_intersection_observer_module.cc',
+        'element_intersection_observer_module.h',
         'eme/media_encrypted_event.cc',
         'eme/media_encrypted_event.h',
         'eme/media_key_message_event.cc',
@@ -193,9 +195,6 @@
         'intersection_observer_entry.h',
         'intersection_observer_entry_init.h',
         'intersection_observer_init.h',
-        'intersection_observer_registration.h',
-        'intersection_observer_target.h',
-        'intersection_observer_target.cc',
         'intersection_observer_task_manager.cc',
         'intersection_observer_task_manager.h',
         'keyboard_event.cc',
@@ -264,8 +263,6 @@
         'progress_event.h',
         'pseudo_element.cc',
         'pseudo_element.h',
-        'intersection_observer_registration_list.cc',
-        'intersection_observer_registration_list.h',
         'registered_observer.h',
         'registered_observer_list.cc',
         'registered_observer_list.h',
diff --git a/src/cobalt/dom/element.cc b/src/cobalt/dom/element.cc
index dd4638f..04aff96 100644
--- a/src/cobalt/dom/element.cc
+++ b/src/cobalt/dom/element.cc
@@ -15,7 +15,6 @@
 #include "cobalt/dom/element.h"
 
 #include <algorithm>
-#include <ctime>
 
 #include "base/lazy_instance.h"
 #include "base/strings/string_util.h"
@@ -240,7 +239,7 @@
   if (document && GetRootNode() == document) {
     document->OnDOMMutation();
   }
-  OnSetAttribute(name, value);
+  OnSetAttribute(attr_name, value);
 }
 
 // Algorithm for RemoveAttribute:
@@ -302,7 +301,7 @@
   if (document && GetRootNode() == document) {
     document->OnDOMMutation();
   }
-  OnRemoveAttribute(name);
+  OnRemoveAttribute(attr_name);
 }
 
 // Algorithm for tag_name:
@@ -625,37 +624,71 @@
   }
 }
 
-void Element::RegisterIntersectionObserverTarget(
-    const scoped_refptr<IntersectionObserver>& observer) {
-  if (!intersection_observer_target_) {
-    intersection_observer_target_ = std::unique_ptr<IntersectionObserverTarget>(
-        new IntersectionObserverTarget(this));
+void Element::RegisterIntersectionObserverRoot(IntersectionObserver* observer) {
+  EnsureIntersectionObserverModuleInitialized();
+  element_intersection_observer_module_->RegisterIntersectionObserverForRoot(
+      observer);
+}
+
+void Element::UnregisterIntersectionObserverRoot(
+    IntersectionObserver* observer) {
+  if (element_intersection_observer_module_) {
+    element_intersection_observer_module_
+        ->UnregisterIntersectionObserverForRoot(observer);
   }
-  intersection_observer_target_->RegisterIntersectionObserver(observer);
+}
+
+void Element::RegisterIntersectionObserverTarget(
+    IntersectionObserver* observer) {
+  EnsureIntersectionObserverModuleInitialized();
+  element_intersection_observer_module_->RegisterIntersectionObserverForTarget(
+      observer);
 }
 
 void Element::UnregisterIntersectionObserverTarget(
-    const scoped_refptr<IntersectionObserver>& observer) {
-  intersection_observer_target_->UnregisterIntersectionObserver(observer);
+    IntersectionObserver* observer) {
+  element_intersection_observer_module_
+      ->UnregisterIntersectionObserverForTarget(observer);
 }
 
-void Element::UpdateIntersectionObservationsForTarget(
-    const scoped_refptr<IntersectionObserver>& observer) {
-  intersection_observer_target_->UpdateIntersectionObservationsForTarget(
-      observer);
+ElementIntersectionObserverModule::LayoutIntersectionObserverRootVector
+Element::GetLayoutIntersectionObserverRoots() {
+  ElementIntersectionObserverModule::LayoutIntersectionObserverRootVector
+      layout_roots;
+  if (element_intersection_observer_module_) {
+    layout_roots = element_intersection_observer_module_
+                       ->GetLayoutIntersectionObserverRootsForElement();
+  }
+  return layout_roots;
+}
+
+ElementIntersectionObserverModule::LayoutIntersectionObserverTargetVector
+Element::GetLayoutIntersectionObserverTargets() {
+  ElementIntersectionObserverModule::LayoutIntersectionObserverTargetVector
+      layout_targets;
+  if (element_intersection_observer_module_) {
+    layout_targets = element_intersection_observer_module_
+                         ->GetLayoutIntersectionObserverTargetsForElement();
+  }
+  return layout_targets;
 }
 
 scoped_refptr<HTMLElement> Element::AsHTMLElement() { return NULL; }
 
 // Explicitly defined because DOMTokenList is forward declared and held by
 // scoped_refptr in Element's header.
-Element::~Element() {}
+Element::~Element() {
+  // Reset the ElementIntersectionObserverModule so that functions such as
+  // UnregisterIntersectionRoot/Target will not be called on deleted objects.
+  element_intersection_observer_module_.reset();
+}
 
 void Element::TraceMembers(script::Tracer* tracer) {
   Node::TraceMembers(tracer);
 
   tracer->Trace(named_node_map_);
   tracer->Trace(class_list_);
+  tracer->Trace(element_intersection_observer_module_);
 }
 
 bool Element::GetBooleanAttribute(const std::string& name) const {
@@ -695,5 +728,13 @@
   LOG(WARNING) << "Error when parsing inner HTML or outer HTML: " << error;
 }
 
+void Element::EnsureIntersectionObserverModuleInitialized() {
+  if (!element_intersection_observer_module_) {
+    element_intersection_observer_module_ =
+        std::unique_ptr<ElementIntersectionObserverModule>(
+            new ElementIntersectionObserverModule(this));
+  }
+}
+
 }  // namespace dom
 }  // namespace cobalt
diff --git a/src/cobalt/dom/element.h b/src/cobalt/dom/element.h
index 22b1bfd..d37f9bc 100644
--- a/src/cobalt/dom/element.h
+++ b/src/cobalt/dom/element.h
@@ -25,8 +25,8 @@
 #include "cobalt/base/token.h"
 #include "cobalt/cssom/style_sheet_list.h"
 #include "cobalt/dom/dom_exception.h"
+#include "cobalt/dom/element_intersection_observer_module.h"
 #include "cobalt/dom/intersection_observer.h"
-#include "cobalt/dom/intersection_observer_target.h"
 #include "cobalt/dom/node.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/web_animations/animation_set.h"
@@ -199,17 +199,14 @@
     return animations_;
   }
 
-  void RegisterIntersectionObserverTarget(
-      const scoped_refptr<IntersectionObserver>& observer);
-
-  void UnregisterIntersectionObserverTarget(
-      const scoped_refptr<IntersectionObserver>& observer);
-
-  // Queues an IntersectionObserverEntry if the thresholdIndex or isIntersecting
-  // properties have changed for the IntersectionObserverRegistration record
-  // corresponding to the given observer and this element (the target).
-  void UpdateIntersectionObservationsForTarget(
-      const scoped_refptr<IntersectionObserver>& observer);
+  void RegisterIntersectionObserverRoot(IntersectionObserver* observer);
+  void UnregisterIntersectionObserverRoot(IntersectionObserver* observer);
+  void RegisterIntersectionObserverTarget(IntersectionObserver* observer);
+  void UnregisterIntersectionObserverTarget(IntersectionObserver* observer);
+  ElementIntersectionObserverModule::LayoutIntersectionObserverRootVector
+  GetLayoutIntersectionObserverRoots();
+  ElementIntersectionObserverModule::LayoutIntersectionObserverTargetVector
+  GetLayoutIntersectionObserverTargets();
 
   DEFINE_WRAPPABLE_TYPE(Element);
   void TraceMembers(script::Tracer* tracer) override;
@@ -243,6 +240,8 @@
   // Callback for error when parsing inner / outer HTML.
   void HTMLParseError(const std::string& error);
 
+  void EnsureIntersectionObserverModuleInitialized();
+
   // Local name of the element.
   base::Token local_name_;
   // A map that holds the actual element attributes.
@@ -261,7 +260,8 @@
   // A set of all animations currently applied to this element.
   scoped_refptr<web_animations::AnimationSet> animations_;
 
-  std::unique_ptr<IntersectionObserverTarget> intersection_observer_target_;
+  std::unique_ptr<ElementIntersectionObserverModule>
+      element_intersection_observer_module_;
 };
 
 }  // namespace dom
diff --git a/src/cobalt/dom/element_intersection_observer_module.cc b/src/cobalt/dom/element_intersection_observer_module.cc
new file mode 100644
index 0000000..01fb8a3
--- /dev/null
+++ b/src/cobalt/dom/element_intersection_observer_module.cc
@@ -0,0 +1,183 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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.
+
+#include "cobalt/dom/element_intersection_observer_module.h"
+
+#include "cobalt/dom/document.h"
+#include "cobalt/dom/dom_rect_read_only.h"
+#include "cobalt/dom/element.h"
+#include "cobalt/dom/intersection_observer_entry_init.h"
+#include "cobalt/dom/performance.h"
+
+namespace cobalt {
+namespace dom {
+
+ElementIntersectionObserverModule::ElementIntersectionObserverModule(
+    Element* element)
+    : element_(element) {}
+
+void ElementIntersectionObserverModule::RegisterIntersectionObserverForRoot(
+    IntersectionObserver* observer) {
+  for (auto it = root_registered_intersection_observers_.begin();
+       it != root_registered_intersection_observers_.end(); ++it) {
+    if (*it == observer) {
+      return;
+    }
+  }
+  root_registered_intersection_observers_.push_back(
+      base::WrapRefCounted(observer));
+
+  // Also create the observer's layout root at this time.
+  scoped_refptr<layout::IntersectionObserverRoot> new_root =
+      new layout::IntersectionObserverRoot(
+          observer->root_margin_property_value(),
+          observer->thresholds_vector());
+  observer->set_layout_root(new_root);
+
+  InvalidateLayoutBoxesForElement();
+}
+
+void ElementIntersectionObserverModule::UnregisterIntersectionObserverForRoot(
+    IntersectionObserver* observer) {
+  for (auto it = root_registered_intersection_observers_.begin();
+       it != root_registered_intersection_observers_.end(); ++it) {
+    if (*it == observer) {
+      root_registered_intersection_observers_.erase(it);
+      InvalidateLayoutBoxesForElement();
+      return;
+    }
+  }
+  NOTREACHED()
+      << "Did not find an intersection observer to unregister for the root.";
+}
+
+void ElementIntersectionObserverModule::RegisterIntersectionObserverForTarget(
+    IntersectionObserver* observer) {
+  for (auto it = target_registered_intersection_observers_.begin();
+       it != target_registered_intersection_observers_.end(); ++it) {
+    if (*it == observer) {
+      return;
+    }
+  }
+  target_registered_intersection_observers_.push_back(
+      base::WrapRefCounted(observer));
+  AddLayoutTargetForObserver(observer);
+
+  InvalidateLayoutBoxesForElement();
+}
+
+void ElementIntersectionObserverModule::UnregisterIntersectionObserverForTarget(
+    IntersectionObserver* observer) {
+  for (auto it = target_registered_intersection_observers_.begin();
+       it != target_registered_intersection_observers_.end(); ++it) {
+    if (*it == observer) {
+      target_registered_intersection_observers_.erase(it);
+      RemoveLayoutTargetForObserver(observer);
+      InvalidateLayoutBoxesForElement();
+      return;
+    }
+  }
+  NOTREACHED()
+      << "Did not find an intersection observer to unregister for the target.";
+}
+
+ElementIntersectionObserverModule::LayoutIntersectionObserverRootVector
+ElementIntersectionObserverModule::
+    GetLayoutIntersectionObserverRootsForElement() {
+  ElementIntersectionObserverModule::LayoutIntersectionObserverRootVector
+      layout_roots;
+  for (const auto& intersection_observer :
+       root_registered_intersection_observers_) {
+    layout_roots.push_back(intersection_observer->layout_root());
+  }
+  return layout_roots;
+}
+
+ElementIntersectionObserverModule::LayoutIntersectionObserverTargetVector
+ElementIntersectionObserverModule::
+    GetLayoutIntersectionObserverTargetsForElement() {
+  return layout_targets_;
+}
+
+void ElementIntersectionObserverModule::
+    CreateIntersectionObserverEntryForObserver(
+        const base::WeakPtr<IntersectionObserver>& observer,
+        math::RectF root_bounds, math::RectF target_rect,
+        math::RectF intersection_rect, bool is_intersecting,
+        float intersection_ratio) {
+  if (!observer) {
+    return;
+  }
+
+  IntersectionObserverEntryInit init_dict;
+  init_dict.set_time(element_->owner_document()
+                         ->window()
+                         ->performance()
+                         ->timing()
+                         ->GetNavigationStartClock()
+                         ->Now()
+                         .InMillisecondsF());
+  init_dict.set_root_bounds(
+      base::WrapRefCounted(new DOMRectReadOnly(root_bounds)));
+  init_dict.set_bounding_client_rect(
+      base::WrapRefCounted(new DOMRectReadOnly(target_rect)));
+  init_dict.set_intersection_rect(
+      base::WrapRefCounted(new DOMRectReadOnly(intersection_rect)));
+  init_dict.set_is_intersecting(is_intersecting);
+  init_dict.set_intersection_ratio(intersection_ratio);
+  init_dict.set_target(base::WrapRefCounted(element_));
+  observer->QueueIntersectionObserverEntry(
+      base::WrapRefCounted(new IntersectionObserverEntry(init_dict)));
+}
+
+void ElementIntersectionObserverModule::TraceMembers(script::Tracer* tracer) {
+  tracer->TraceItems(root_registered_intersection_observers_);
+  tracer->TraceItems(target_registered_intersection_observers_);
+}
+
+void ElementIntersectionObserverModule::AddLayoutTargetForObserver(
+    IntersectionObserver* observer) {
+  layout::IntersectionObserverTarget::OnIntersectionCallback
+      on_intersection_callback =
+          base::Bind(&ElementIntersectionObserverModule::
+                         CreateIntersectionObserverEntryForObserver,
+                     base::Unretained(this), base::AsWeakPtr(observer));
+  scoped_refptr<layout::IntersectionObserverTarget> layout_target =
+      new layout::IntersectionObserverTarget(on_intersection_callback,
+                                             observer->layout_root());
+  layout_targets_.push_back(layout_target);
+}
+
+void ElementIntersectionObserverModule::RemoveLayoutTargetForObserver(
+    IntersectionObserver* observer) {
+  for (auto it = layout_targets_.begin(); it != layout_targets_.end(); ++it) {
+    if ((*it)->intersection_observer_root() == observer->layout_root()) {
+      layout_targets_.erase(it);
+      return;
+    }
+  }
+  NOTREACHED() << "Did not find a layout target to remove";
+}
+
+void ElementIntersectionObserverModule::InvalidateLayoutBoxesForElement() {
+  HTMLElement* html_element = element_->AsHTMLElement();
+  if (!html_element) {
+    NOTREACHED();
+    return;
+  }
+  html_element->InvalidateLayoutBoxesOfNodeAndDescendants();
+}
+
+}  // namespace dom
+}  // namespace cobalt
diff --git a/src/cobalt/dom/element_intersection_observer_module.h b/src/cobalt/dom/element_intersection_observer_module.h
new file mode 100644
index 0000000..348dddc
--- /dev/null
+++ b/src/cobalt/dom/element_intersection_observer_module.h
@@ -0,0 +1,76 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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 COBALT_DOM_ELEMENT_INTERSECTION_OBSERVER_MODULE_H_
+#define COBALT_DOM_ELEMENT_INTERSECTION_OBSERVER_MODULE_H_
+
+#include <vector>
+
+#include "cobalt/dom/intersection_observer.h"
+#include "cobalt/math/rect_f.h"
+#include "cobalt/script/tracer.h"
+
+namespace cobalt {
+namespace dom {
+
+class Element;
+
+// This helper class groups the methods and  data related to the root and target
+// elements outlined in the intersection observer spec.
+// https://www.w3.org/TR/intersection-observer
+class ElementIntersectionObserverModule : public script::Traceable {
+ public:
+  typedef std::vector<scoped_refptr<IntersectionObserver>>
+      IntersectionObserverVector;
+  typedef std::vector<scoped_refptr<layout::IntersectionObserverRoot>>
+      LayoutIntersectionObserverRootVector;
+  typedef std::vector<scoped_refptr<layout::IntersectionObserverTarget>>
+      LayoutIntersectionObserverTargetVector;
+
+  explicit ElementIntersectionObserverModule(Element* element);
+  ~ElementIntersectionObserverModule() {}
+
+  void RegisterIntersectionObserverForRoot(IntersectionObserver* observer);
+  void UnregisterIntersectionObserverForRoot(IntersectionObserver* observer);
+  void RegisterIntersectionObserverForTarget(IntersectionObserver* observer);
+  void UnregisterIntersectionObserverForTarget(IntersectionObserver* observer);
+
+  LayoutIntersectionObserverRootVector
+  GetLayoutIntersectionObserverRootsForElement();
+  LayoutIntersectionObserverTargetVector
+  GetLayoutIntersectionObserverTargetsForElement();
+
+  void CreateIntersectionObserverEntryForObserver(
+      const base::WeakPtr<IntersectionObserver>& observer,
+      math::RectF root_bounds, math::RectF target_rect,
+      math::RectF intersection_rect, bool is_intersecting,
+      float intersection_ratio);
+
+  void TraceMembers(script::Tracer* tracer) override;
+
+ private:
+  void InvalidateLayoutBoxesForElement();
+  void AddLayoutTargetForObserver(IntersectionObserver* observer);
+  void RemoveLayoutTargetForObserver(IntersectionObserver* observer);
+
+  Element* element_;
+  IntersectionObserverVector root_registered_intersection_observers_;
+  IntersectionObserverVector target_registered_intersection_observers_;
+  LayoutIntersectionObserverTargetVector layout_targets_;
+};
+
+}  // namespace dom
+}  // namespace cobalt
+
+#endif  // COBALT_DOM_ELEMENT_INTERSECTION_OBSERVER_MODULE_H_
diff --git a/src/cobalt/dom/html_anchor_element.cc b/src/cobalt/dom/html_anchor_element.cc
index 74c96aa..c262595 100644
--- a/src/cobalt/dom/html_anchor_element.cc
+++ b/src/cobalt/dom/html_anchor_element.cc
@@ -32,12 +32,16 @@
     if (!ResolveAndSetURL(value)) {
       url_utils_.set_url(GURL(value));
     }
+  } else {
+    HTMLElement::OnSetAttribute(name, value);
   }
 }
 
 void HTMLAnchorElement::OnRemoveAttribute(const std::string& name) {
   if (name == "href") {
     url_utils_.set_url(GURL());
+  } else {
+    HTMLElement::OnRemoveAttribute(name);
   }
 }
 
diff --git a/src/cobalt/dom/html_element.cc b/src/cobalt/dom/html_element.cc
index 4cb3861..636ef72 100644
--- a/src/cobalt/dom/html_element.cc
+++ b/src/cobalt/dom/html_element.cc
@@ -1015,7 +1015,7 @@
   descendant_computed_styles_valid_ = true;
 }
 
-void HTMLElement::MarkDisplayNoneOnNodeAndDescendants() {
+void HTMLElement::MarkNotDisplayedOnNodeAndDescendants() {
   // While we do want to clear the animations immediately, we also want to
   // ensure that they are also reset starting with the next computed style
   // update.  This ensures that for example a transition will not be triggered
@@ -1039,7 +1039,7 @@
     }
   }
 
-  MarkDisplayNoneOnDescendants();
+  MarkNotDisplayedOnDescendants();
 }
 
 void HTMLElement::PurgeCachedBackgroundImagesOfNodeAndDescendants() {
@@ -1727,7 +1727,7 @@
   }
 
   if (invalidation_flags.mark_descendants_as_display_none) {
-    MarkDisplayNoneOnDescendants();
+    MarkNotDisplayedOnDescendants();
   }
   if (invalidation_flags.invalidate_computed_styles_of_descendants) {
     InvalidateComputedStylesOfDescendants();
diff --git a/src/cobalt/dom/html_element.h b/src/cobalt/dom/html_element.h
index de9d187..abb2f2e 100644
--- a/src/cobalt/dom/html_element.h
+++ b/src/cobalt/dom/html_element.h
@@ -278,7 +278,7 @@
       const base::TimeDelta& style_change_event_time,
       AncestorsAreDisplayed ancestor_is_displayed);
 
-  void MarkDisplayNoneOnNodeAndDescendants() override;
+  void MarkNotDisplayedOnNodeAndDescendants() override;
   void PurgeCachedBackgroundImagesOfNodeAndDescendants() override;
   void InvalidateComputedStylesOfNodeAndDescendants() override;
   void InvalidateLayoutBoxesOfNodeAndAncestors() override;
@@ -346,6 +346,11 @@
   void OnInsertedIntoDocument() override;
   void OnRemovedFromDocument() override;
 
+  // From Element.
+  void OnSetAttribute(const std::string& name,
+                      const std::string& value) override;
+  void OnRemoveAttribute(const std::string& name) override;
+
   // HTMLElement keeps a pointer to the dom stat tracker to ensure that it can
   // make stat updates even after its weak pointer to its document has been
   // deleted. This is protected because some derived classes need access to it.
@@ -355,11 +360,6 @@
   // From Node.
   void OnMutation() override;
 
-  // From Element.
-  void OnSetAttribute(const std::string& name,
-                      const std::string& value) override;
-  void OnRemoveAttribute(const std::string& name) override;
-
   bool IsFocusable();
   bool HasTabindexFocusFlag() const;
   bool IsBeingRendered();
diff --git a/src/cobalt/dom/html_element_test.cc b/src/cobalt/dom/html_element_test.cc
index df6f1be..8fa740f 100644
--- a/src/cobalt/dom/html_element_test.cc
+++ b/src/cobalt/dom/html_element_test.cc
@@ -68,6 +68,11 @@
 
 const char kFooBarDeclarationString[] = "foo: bar;";
 const char kDisplayInlineDeclarationString[] = "display: inline;";
+const char* kHtmlElementTagNames[] = {
+  // "audio", "script", and "video" are excluded since they need more setup.
+  "a", "body", "br", "div", "head", "h1", "html", "img", "link",
+  "meta", "p", "span", "style", "title"
+};
 
 class MockLayoutBoxes : public LayoutBoxes {
  public:
@@ -204,31 +209,48 @@
 }
 
 TEST_F(HTMLElementTest, Dir) {
-  scoped_refptr<HTMLElement> html_element =
-      document_->CreateElement("div")->AsHTMLElement();
-  EXPECT_EQ("", html_element->dir());
+  for (size_t i = 0; i < arraysize(kHtmlElementTagNames); ++i) {
+    scoped_refptr<HTMLElement> html_element =
+        document_->CreateElement(kHtmlElementTagNames[i])->AsHTMLElement();
+    EXPECT_EQ("", html_element->dir());
 
-  html_element->set_dir("invalid");
-  EXPECT_EQ("", html_element->dir());
+    html_element->set_dir("invalid");
+    EXPECT_EQ("", html_element->dir());
 
-  html_element->set_dir("ltr");
-  EXPECT_EQ("ltr", html_element->dir());
+    html_element->set_dir("ltr");
+    EXPECT_EQ("ltr", html_element->dir());
 
-  html_element->set_dir("rtl");
-  EXPECT_EQ("rtl", html_element->dir());
+    html_element->set_dir("rtl");
+    EXPECT_EQ("rtl", html_element->dir());
 
-  // Value "auto" is not supported.
-  html_element->set_dir("auto");
-  EXPECT_EQ("", html_element->dir());
+    // Value "auto" is not supported.
+    html_element->set_dir("auto");
+    EXPECT_EQ("", html_element->dir());
+
+    html_element->SetAttribute("Dir", "rtl");
+    EXPECT_EQ("rtl", html_element->dir());
+
+    html_element->RemoveAttribute("diR");
+    EXPECT_EQ("", html_element->dir());
+  }
 }
 
 TEST_F(HTMLElementTest, TabIndex) {
-  scoped_refptr<HTMLElement> html_element =
-      document_->CreateElement("div")->AsHTMLElement();
-  EXPECT_EQ(0, html_element->tab_index());
+  for (size_t i = 0; i < arraysize(kHtmlElementTagNames); ++i) {
+    scoped_refptr<HTMLElement> html_element =
+        document_->CreateElement(kHtmlElementTagNames[i])->AsHTMLElement();
 
-  html_element->set_tab_index(-1);
-  EXPECT_EQ(-1, html_element->tab_index());
+    EXPECT_EQ(0, html_element->tab_index());
+
+    html_element->set_tab_index(-1);
+    EXPECT_EQ(-1, html_element->tab_index());
+
+    html_element->SetAttribute("tabIndex", "-2");
+    EXPECT_EQ(-2, html_element->tab_index());
+
+    html_element->RemoveAttribute("Tabindex");
+    EXPECT_EQ(0, html_element->tab_index());
+  }
 }
 
 TEST_F(HTMLElementTest, Focus) {
diff --git a/src/cobalt/dom/html_image_element.cc b/src/cobalt/dom/html_image_element.cc
index b98a5f9..511d83c 100644
--- a/src/cobalt/dom/html_image_element.cc
+++ b/src/cobalt/dom/html_image_element.cc
@@ -51,7 +51,7 @@
 }
 
 void HTMLImageElement::OnSetAttribute(const std::string& name,
-                                      const std::string& /* value */) {
+                                      const std::string& value) {
   // A user agent that obtains images immediately must synchronously update the
   // image data of an img element whenever that element is created with a src
   // attribute. A user agent that obtains images immediately must also
@@ -59,6 +59,8 @@
   // has its src or crossorigin attribute set, changed, or removed.
   if (name == "src") {
     UpdateImageData();
+  } else {
+    HTMLElement::OnSetAttribute(name, value);
   }
 }
 
@@ -70,6 +72,8 @@
   // has its src or crossorigin attribute set, changed, or removed.
   if (name == "src") {
     UpdateImageData();
+  } else {
+    HTMLElement::OnRemoveAttribute(name);
   }
 }
 
diff --git a/src/cobalt/dom/intersection_observer.cc b/src/cobalt/dom/intersection_observer.cc
index 7af988a..6fce9cd 100644
--- a/src/cobalt/dom/intersection_observer.cc
+++ b/src/cobalt/dom/intersection_observer.cc
@@ -81,13 +81,23 @@
 }
 
 IntersectionObserver::~IntersectionObserver() {
-  task_manager()->OnIntersectionObserverDestroyed(this);
+  if (root_) {
+    root_->UnregisterIntersectionObserverRoot(this);
+  }
+  if (intersection_observer_task_manager_) {
+    intersection_observer_task_manager_->OnIntersectionObserverDestroyed(this);
+  }
 }
 
-script::Sequence<double> const IntersectionObserver::thresholds() {
+void IntersectionObserver::set_layout_root(
+    const scoped_refptr<layout::IntersectionObserverRoot>& layout_root) {
+  layout_root_ = layout_root;
+}
+
+script::Sequence<double> IntersectionObserver::thresholds() const {
   script::Sequence<double> result;
 
-  for (std::vector<double>::iterator it = thresholds_.begin();
+  for (std::vector<double>::const_iterator it = thresholds_.begin();
        it != thresholds_.end(); ++it) {
     result.push_back(*it);
   }
@@ -101,7 +111,7 @@
     NOTREACHED();
     return;
   }
-  target->RegisterIntersectionObserverTarget(base::WrapRefCounted(this));
+  target->RegisterIntersectionObserverTarget(this);
   TrackObservationTarget(target);
 }
 
@@ -112,7 +122,7 @@
     NOTREACHED();
     return;
   }
-  target->UnregisterIntersectionObserverTarget(base::WrapRefCounted(this));
+  target->UnregisterIntersectionObserverTarget(this);
   UntrackObservationTarget(target);
 }
 
@@ -125,7 +135,7 @@
        it != observation_targets_.end(); ++it) {
     Element* target = it->get();
     if (target != NULL) {
-      target->UnregisterIntersectionObserverTarget(base::WrapRefCounted(this));
+      target->UnregisterIntersectionObserverTarget(this);
     }
   }
   observation_targets_.clear();
@@ -146,21 +156,8 @@
   TRACE_EVENT0("cobalt::dom",
                "IntersectionObserver::QueueIntersectionObserverEntry()");
   queued_entries_.push_back(entry);
-  task_manager()->QueueIntersectionObserverTask();
-}
-
-void IntersectionObserver::UpdateObservationTargets() {
-  TRACE_EVENT0("cobalt::dom",
-               "IntersectionObserver::UpdateObservationTargets()");
-  // https://www.w3.org/TR/intersection-observer/#notify-intersection-observers-algo
-  // Step 2 of "run the update intersection observations steps" algorithm:
-  //     1. Let rootBounds be observer's root intersection rectangle.
-  //     2. For each target in observer's internal [[ObservationTargets]] slot,
-  //        processed in the same order that observe() was called on each
-  //        target, run a set of subtasks (subtasks are implemented in
-  //        IntersectionObserverRegistration::Update):
-  for (auto target : observation_targets_) {
-    target->UpdateIntersectionObservationsForTarget(this);
+  if (intersection_observer_task_manager_) {
+    intersection_observer_task_manager_->QueueIntersectionObserverTask();
   }
 }
 
@@ -184,35 +181,18 @@
 }
 
 void IntersectionObserver::TraceMembers(script::Tracer* tracer) {
+  tracer->Trace(root_);
   tracer->TraceItems(observation_targets_);
   tracer->TraceItems(queued_entries_);
 }
 
-IntersectionObserverTaskManager* IntersectionObserver::task_manager() {
-  return root_->owner_document()->intersection_observer_task_manager();
-}
-
 void IntersectionObserver::InitIntersectionObserverInternal(
     script::EnvironmentSettings* settings,
     const IntersectionObserverCallbackArg& callback,
     const IntersectionObserverInit& options,
     script::ExceptionState* exception_state) {
   // https://www.w3.org/TR/intersection-observer/#intersection-observer-interface
-  // 1. Let this be a new IntersectionObserver object
-  // 2. Set this's internal [[callback]] slot to callback.
-  callback_.reset(new ScriptCallback(callback, this));
-
-  // 3. Set this.root to options.root.
-  if (options.root()) {
-    root_ = options.root();
-  } else {
-    root_ = base::polymorphic_downcast<dom::DOMSettings*>(settings)
-                ->window()
-                ->document()
-                ->document_element();
-  }
-
-  // 4. Attempt to parse a root margin from options.rootMargin. If a list is
+  // Attempt to parse a root margin from options.rootMargin. If a list is
   // returned, set this's internal [[rootMargin]] slot to that. Otherwise, throw
   // a SyntaxError exception.
   // https://www.w3.org/TR/intersection-observer/#parse-a-root-margin
@@ -237,12 +217,9 @@
         "Not able to parse IntersectionObserver root margin.");
   }
 
-  // 5. Let thresholds be a list equal to options.threshold.
-  // 6. If any value in thresholds is less than 0.0 or greater than 1.0, throw a
+  // Let thresholds be a list equal to options.threshold.
+  // If any value in thresholds is less than 0.0 or greater than 1.0, throw a
   // RangeError exception.
-  // 7. Sort thresholds in ascending order.
-  // 8. If thresholds is empty, append 0 to thresholds.
-  // 9. Set this.thresholds to thresholds.
   ThresholdType threshold = options.threshold();
   if (threshold.IsType<double>()) {
     if (threshold.AsType<double>() < 0 || threshold.AsType<double>() > 1) {
@@ -266,13 +243,33 @@
       }
       thresholds_.push_back(*it);
     }
+    // Sort thresholds in ascending order.
     sort(thresholds_.begin(), thresholds_.end());
   }
+  // If thresholds is empty, append 0 to thresholds.
+
   if (thresholds_.empty()) {
     thresholds_.push_back(0);
   }
 
-  task_manager()->OnIntersectionObserverCreated(this);
+  // Set this's internal [[callback]] slot to callback.
+  callback_.reset(new ScriptCallback(callback, this));
+
+  // Set this.root to options.root.
+  if (options.root()) {
+    root_ = base::AsWeakPtr(options.root().get());
+  } else {
+    root_ =
+        base::AsWeakPtr(base::polymorphic_downcast<dom::DOMSettings*>(settings)
+                            ->window()
+                            ->document()
+                            ->document_element()
+                            .get());
+  }
+  root_->RegisterIntersectionObserverRoot(this);
+  intersection_observer_task_manager_ =
+      root_->owner_document()->intersection_observer_task_manager();
+  intersection_observer_task_manager_->OnIntersectionObserverCreated(this);
 }
 
 void IntersectionObserver::TrackObservationTarget(
@@ -288,7 +285,7 @@
     }
     ++it;
   }
-  observation_targets_.push_back(base::WrapRefCounted(target.get()));
+  observation_targets_.push_back(base::AsWeakPtr(target.get()));
 }
 
 void IntersectionObserver::UntrackObservationTarget(
diff --git a/src/cobalt/dom/intersection_observer.h b/src/cobalt/dom/intersection_observer.h
index 10b6bab..c1b6ba6 100644
--- a/src/cobalt/dom/intersection_observer.h
+++ b/src/cobalt/dom/intersection_observer.h
@@ -20,8 +20,11 @@
 
 #include "base/callback.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
 #include "cobalt/cssom/property_list_value.h"
 #include "cobalt/dom/intersection_observer_entry.h"
+#include "cobalt/layout/intersection_observer_root.h"
+#include "cobalt/layout/intersection_observer_target.h"
 #include "cobalt/script/callback_function.h"
 #include "cobalt/script/environment_settings.h"
 #include "cobalt/script/exception_state.h"
@@ -40,7 +43,8 @@
 // The IntersectionObserver can be used to observe changes in the intersection
 // of an intersection root and one or more target Elements.
 // https://www.w3.org/TR/intersection-observer/#intersection-observer-interfaces
-class IntersectionObserver : public script::Wrappable {
+class IntersectionObserver : public base::SupportsWeakPtr<IntersectionObserver>,
+                             public script::Wrappable {
  public:
   typedef script::Sequence<scoped_refptr<IntersectionObserverEntry>>
       IntersectionObserverEntrySequence;
@@ -64,17 +68,24 @@
                        script::ExceptionState* exception_state);
   ~IntersectionObserver();
 
-  const scoped_refptr<Element>& root() { return root_; }
+  const base::WeakPtr<Element>& root() const { return root_; }
+  void set_layout_root(
+      const scoped_refptr<layout::IntersectionObserverRoot>& layout_root);
+  const scoped_refptr<layout::IntersectionObserverRoot>& layout_root() const {
+    return layout_root_;
+  }
   // Part of the IntersectionObserver interface, but differs from the web spec
   // in that it returns the exact string passed into |options|, regardless of
   // whether it is formatted as four space-separated pixels lengths/percentages.
   std::string root_margin() const { return root_margin_; }
   // Not part of the IntersectionObserver interface. Returns the root margin
   // parsed as a PropertyListValue.
-  const scoped_refptr<cssom::PropertyListValue>& root_margin_property_value() {
+  const scoped_refptr<cssom::PropertyListValue>& root_margin_property_value()
+      const {
     return root_margin_property_value_;
   }
-  script::Sequence<double> const thresholds();
+  script::Sequence<double> thresholds() const;
+  std::vector<double> thresholds_vector() const { return thresholds_; }
   void Observe(const scoped_refptr<Element>& target);
   void Unobserve(const scoped_refptr<Element>& target);
   void Disconnect();
@@ -86,11 +97,6 @@
   void QueueIntersectionObserverEntry(
       const scoped_refptr<IntersectionObserverEntry>& entry);
 
-  // Not part of the IntersectionObserver interface. Implements step (2) of the
-  // "run the update intersection observations steps" algorithm.
-  // https://www.w3.org/TR/intersection-observer/#notify-intersection-observers-algo
-  void UpdateObservationTargets();
-
   // Not part of the the IntersectionObserver interface. Implements step (3) of
   // the "notify intersection observers" algorithm.
   // https://www.w3.org/TR/intersection-observer/#notify-intersection-observers-algo
@@ -115,13 +121,16 @@
   void UntrackObservationTarget(const scoped_refptr<Element>& target);
 
   std::unique_ptr<CallbackInternal> callback_;
-  scoped_refptr<Element> root_;
+  base::WeakPtr<Element> root_;
+  scoped_refptr<layout::IntersectionObserverRoot> layout_root_;
   std::string root_margin_;
   scoped_refptr<cssom::PropertyListValue> root_margin_property_value_;
   std::vector<double> thresholds_;
-  typedef std::vector<scoped_refptr<Element>> ElementVector;
+  typedef std::vector<base::WeakPtr<Element>> ElementVector;
   ElementVector observation_targets_;
   IntersectionObserverEntrySequence queued_entries_;
+  scoped_refptr<IntersectionObserverTaskManager>
+      intersection_observer_task_manager_;
 };
 
 }  // namespace dom
diff --git a/src/cobalt/dom/intersection_observer_registration.h b/src/cobalt/dom/intersection_observer_registration.h
deleted file mode 100644
index 4979227..0000000
--- a/src/cobalt/dom/intersection_observer_registration.h
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2019 The Cobalt Authors. All Rights Reserved.
-//
-// 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 COBALT_DOM_INTERSECTION_OBSERVER_REGISTRATION_H_
-#define COBALT_DOM_INTERSECTION_OBSERVER_REGISTRATION_H_
-
-#include "base/memory/ref_counted.h"
-#include "cobalt/dom/intersection_observer.h"
-#include "cobalt/script/tracer.h"
-
-namespace cobalt {
-namespace dom {
-
-class Element;
-
-// Represents the concept of an "intersection observer registration" as
-// described in the Intersection Observer spec.
-// https://www.w3.org/TR/intersection-observer/#intersectionobserverregistration
-// This class is expected to be used internally in the Element class as a part
-// of intersection reporting.
-class IntersectionObserverRegistration : public script::Traceable {
- public:
-  // An IntersectionObserverRegistration must not outlive the |target| element
-  // that is being observed.
-  IntersectionObserverRegistration(
-      const scoped_refptr<IntersectionObserver>& observer)
-      : observer_(observer),
-        previous_threshold_index_(-1),
-        previous_is_intersecting_(false) {}
-
-  const IntersectionObserver* observer() const { return observer_; }
-  int32 previous_threshold_index() const { return previous_threshold_index_; }
-  void set_previous_threshold_index(int32 previous_threshold_index) {
-    previous_threshold_index_ = previous_threshold_index;
-  }
-  bool previous_is_intersecting() const { return previous_is_intersecting_; }
-  void set_previous_is_intersecting(bool previous_is_intersecting) {
-    previous_is_intersecting_ = previous_is_intersecting;
-  }
-
-  void TraceMembers(script::Tracer* tracer) override {
-    tracer->Trace(observer_);
-  }
-
- private:
-  IntersectionObserver* observer_;
-  int32 previous_threshold_index_;
-  bool previous_is_intersecting_;
-};
-
-}  // namespace dom
-}  // namespace cobalt
-
-#endif  // COBALT_DOM_INTERSECTION_OBSERVER_REGISTRATION_H_
diff --git a/src/cobalt/dom/intersection_observer_registration_list.cc b/src/cobalt/dom/intersection_observer_registration_list.cc
deleted file mode 100644
index 14b0541..0000000
--- a/src/cobalt/dom/intersection_observer_registration_list.cc
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2019 The Cobalt Authors. All Rights Reserved.
-//
-// 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.
-
-#include "cobalt/dom/intersection_observer_registration_list.h"
-
-namespace cobalt {
-namespace dom {
-
-void IntersectionObserverRegistrationList::AddIntersectionObserver(
-    const scoped_refptr<IntersectionObserver>& observer) {
-  // https://www.w3.org/TR/intersection-observer/#ref-for-dom-intersectionobserver-observe
-  // 1. If target is in this's internal [[ObservationTargets]] slot, return.
-  for (auto it = registered_intersection_observers_.begin();
-       it != registered_intersection_observers_.end(); ++it) {
-    if (it->observer() == observer) {
-      registered_intersection_observers_.erase(it);
-      return;
-    }
-  }
-  // 2. Let intersectionObserverRegistration be an
-  //    IntersectionObserverRegistration record with an observer property set to
-  //    this, a previousThresholdIndex property set to -1, and a
-  //    previousIsIntersecting property set to false.
-  // 3. Append intersectionObserverRegistration to target's internal
-  //    [[RegisteredIntersectionObservers]] slot.
-  registered_intersection_observers_.push_back(
-      IntersectionObserverRegistration(observer));
-  return;
-}
-
-void IntersectionObserverRegistrationList::RemoveIntersectionObserver(
-    const scoped_refptr<IntersectionObserver>& observer) {
-  for (auto it = registered_intersection_observers_.begin();
-       it != registered_intersection_observers_.end(); ++it) {
-    if (it->observer() == observer) {
-      registered_intersection_observers_.erase(it);
-      return;
-    }
-  }
-  NOTREACHED() << "Did not find an intersection observer to unregister.";
-}
-
-IntersectionObserverRegistration*
-IntersectionObserverRegistrationList::FindRegistrationForObserver(
-    const scoped_refptr<IntersectionObserver>& observer) {
-  for (auto& record : registered_intersection_observers_) {
-    if (record.observer() == observer) {
-      return &record;
-    }
-  }
-  return NULL;
-}
-
-void IntersectionObserverRegistrationList::TraceMembers(
-    script::Tracer* tracer) {
-  tracer->TraceItems(registered_intersection_observers_);
-}
-
-}  // namespace dom
-}  // namespace cobalt
diff --git a/src/cobalt/dom/intersection_observer_registration_list.h b/src/cobalt/dom/intersection_observer_registration_list.h
deleted file mode 100644
index c3f5b07..0000000
--- a/src/cobalt/dom/intersection_observer_registration_list.h
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2019 The Cobalt Authors. All Rights Reserved.
-//
-// 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 COBALT_DOM_INTERSECTION_OBSERVER_REGISTRATION_LIST_H_
-#define COBALT_DOM_INTERSECTION_OBSERVER_REGISTRATION_LIST_H_
-
-#include <vector>
-
-#include "base/memory/ref_counted.h"
-#include "cobalt/dom/intersection_observer.h"
-#include "cobalt/dom/intersection_observer_registration.h"
-
-namespace cobalt {
-namespace dom {
-
-// A list of "intersection observer registrations" as described in the
-// Intersection Observer spec.
-// https://www.w3.org/TR/intersection-observer/#intersectionobserverregistration
-//
-// Implements the functionality described in the IntersectionObserver.observe
-// method:
-// https://www.w3.org/TR/intersection-observer/#ref-for-dom-intersectionobserver-observe
-class IntersectionObserverRegistrationList : public script::Traceable {
- public:
-  typedef std::vector<IntersectionObserverRegistration>
-      IntersectionObserverRegistrationVector;
-
-  IntersectionObserverRegistrationList() {}
-
-  // Implement the IntersectionObserver.observe method
-  // https://www.w3.org/TR/intersection-observer/#ref-for-dom-intersectionobserver-observe
-  void AddIntersectionObserver(
-      const scoped_refptr<IntersectionObserver>& observer);
-  // Implement the IntersectionObserver.unobserve method
-  // https://www.w3.org/TR/intersection-observer/#dom-intersectionobserver-unobserve
-  void RemoveIntersectionObserver(
-      const scoped_refptr<IntersectionObserver>& observer);
-
-  IntersectionObserverRegistration* FindRegistrationForObserver(
-      const scoped_refptr<IntersectionObserver>& observer);
-
-  const IntersectionObserverRegistrationVector&
-  registered_intersection_observers() {
-    return registered_intersection_observers_;
-  }
-
-  void TraceMembers(script::Tracer* tracer) override;
-
- private:
-  IntersectionObserverRegistrationVector registered_intersection_observers_;
-};
-
-}  // namespace dom
-}  // namespace cobalt
-
-#endif  // COBALT_DOM_INTERSECTION_OBSERVER_REGISTRATION_LIST_H_
diff --git a/src/cobalt/dom/intersection_observer_target.cc b/src/cobalt/dom/intersection_observer_target.cc
deleted file mode 100644
index 36c944c..0000000
--- a/src/cobalt/dom/intersection_observer_target.cc
+++ /dev/null
@@ -1,440 +0,0 @@
-// Copyright 2019 The Cobalt Authors. All Rights Reserved.
-//
-// 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.
-
-#include "cobalt/dom/intersection_observer_target.h"
-
-#include <algorithm>
-
-#include "cobalt/cssom/computed_style_utils.h"
-#include "cobalt/cssom/css_computed_style_data.h"
-#include "cobalt/cssom/used_style.h"
-#include "cobalt/dom/document.h"
-#include "cobalt/dom/element.h"
-#include "cobalt/dom/html_element.h"
-#include "cobalt/dom/intersection_observer_entry_init.h"
-#include "cobalt/dom/performance.h"
-#include "cobalt/script/sequence.h"
-
-namespace cobalt {
-namespace dom {
-
-namespace {
-
-HTMLElement* GetContainingBlockOfHTMLElement(HTMLElement* html_element) {
-  // Establish the containing block, as described in
-  // http://www.w3.org/TR/CSS2/visudet.html#containing-block-details
-  DCHECK(html_element->node_document());
-  html_element->node_document()->DoSynchronousLayout();
-
-  scoped_refptr<cssom::PropertyValue> position =
-      html_element->computed_style()->position();
-
-  // The containing block in which the root element lives is a rectangle called
-  // the initial containing block. For continuous media, it has the dimensions
-  // of the viewport and is anchored at the canvas origin; it is the page area
-  // for paged media.
-  if (html_element->IsRootElement()) {
-    return html_element->owner_document()->document_element()->AsHTMLElement();
-  }
-
-  for (Node* ancestor_node = html_element->parent_node(); ancestor_node;
-       ancestor_node = ancestor_node->parent_node()) {
-    Element* ancestor_element = ancestor_node->AsElement();
-    if (!ancestor_element) {
-      continue;
-    }
-    HTMLElement* ancestor_html_element = ancestor_element->AsHTMLElement();
-    if (!ancestor_html_element) {
-      continue;
-    }
-
-    // If the element has 'position: absolute', the containing block is
-    // established by the nearest ancestor with a 'position' of 'absolute',
-    // 'relative' or 'fixed'.
-    // Transformed elements also act as a containing block for all descendants.
-    // https://www.w3.org/TR/css-transforms-1/#transform-rendering.
-    if (position == cssom::KeywordValue::GetAbsolute() &&
-        ancestor_html_element->computed_style()->position() ==
-            cssom::KeywordValue::GetStatic() &&
-        ancestor_html_element->computed_style()->transform() ==
-            cssom::KeywordValue::GetNone()) {
-      continue;
-    }
-
-    // If the element has 'position: fixed', the containing block is established
-    // by the viewport in the case of continuous media or the page area in the
-    // case of paged media.
-    // Transformed elements also act as a containing block for all descendants.
-    // https://www.w3.org/TR/css-transforms-1/#transform-rendering.
-    if (position == cssom::KeywordValue::GetFixed() &&
-        ancestor_html_element->computed_style()->transform() ==
-            cssom::KeywordValue::GetNone()) {
-      continue;
-    }
-
-    // For other elements, if the element's position is 'relative' or 'static',
-    // the containing block is formed by the content edge of the nearest block
-    // container ancestor box.
-    return ancestor_html_element;
-  }
-
-  // If there is no such ancestor, the containing block is the initial
-  // containing block.
-  return html_element->owner_document()->document_element()->AsHTMLElement();
-}
-
-bool IsInContainingBlockChain(const HTMLElement* potential_containing_block,
-                              HTMLElement* html_element) {
-  // Walk up the containing block chain, as described in
-  // http://www.w3.org/TR/CSS2/visudet.html#containing-block-details
-  HTMLElement* containing_block_element =
-      GetContainingBlockOfHTMLElement(html_element);
-  while (containing_block_element != potential_containing_block) {
-    if (!containing_block_element->parent_element()) {
-      return false;
-    }
-    containing_block_element =
-        GetContainingBlockOfHTMLElement(containing_block_element);
-  }
-  return true;
-}
-
-}  // namespace
-
-IntersectionObserverTarget::IntersectionObserverTarget(Element* target_element)
-    : target_element_(target_element) {}
-
-void IntersectionObserverTarget::RegisterIntersectionObserver(
-    const scoped_refptr<IntersectionObserver>& observer) {
-  intersection_observer_registration_list_.AddIntersectionObserver(observer);
-}
-
-void IntersectionObserverTarget::UnregisterIntersectionObserver(
-    const scoped_refptr<IntersectionObserver>& observer) {
-  intersection_observer_registration_list_.RemoveIntersectionObserver(observer);
-}
-
-void IntersectionObserverTarget::UpdateIntersectionObservationsForTarget(
-    const scoped_refptr<IntersectionObserver>& observer) {
-  // https://www.w3.org/TR/intersection-observer/#update-intersection-observations-algo
-  // Subtasks for step 2 of the "run the update intersection observations steps"
-  // algorithm:
-  //     1. If the intersection root is not the implicit root and target is
-  //        not a descendant of the intersection root in the containing block
-  //        chain, skip further processing for target.
-  HTMLElement* html_target = target_element_->AsHTMLElement();
-  HTMLElement* html_intersection_root = observer->root()->AsHTMLElement();
-  if (!html_target || !html_intersection_root) {
-    NOTREACHED();
-    return;
-  }
-
-  if (html_intersection_root !=
-          target_element_->owner_document()->document_element() &&
-      !IsInContainingBlockChain(html_intersection_root, html_target)) {
-    return;
-  }
-
-  //     2. If the intersection root is not the implicit root, and target is
-  //        not in the same Document as the intersection root, skip further
-  //        processing for target.
-  if (html_intersection_root !=
-          target_element_->owner_document()->document_element() &&
-      html_intersection_root->owner_document() !=
-          target_element_->owner_document()) {
-    return;
-  }
-
-  //     3. Let targetRect be a DOMRectReadOnly obtained by running the
-  //        getBoundingClientRect() algorithm on target.
-  scoped_refptr<DOMRectReadOnly> target_rect =
-      target_element_->GetBoundingClientRect();
-
-  //     4. Let intersectionRect be the result of running the compute the
-  //        intersection algorithm on target.
-  scoped_refptr<DOMRectReadOnly> root_bounds = GetRootBounds(
-      html_intersection_root, observer->root_margin_property_value());
-  scoped_refptr<DOMRectReadOnly> intersection_rect =
-      ComputeIntersectionBetweenTargetAndRoot(
-          html_intersection_root, root_bounds, target_rect,
-          base::WrapRefCounted(html_target));
-
-  //     5. Let targetArea be targetRect's area.
-  float target_area = target_rect->rect().size().GetArea();
-
-  //     6. Let intersectionArea be intersectionRect's area.
-  float intersection_area = intersection_rect->rect().size().GetArea();
-
-  //     7. Let isIntersecting be true if targetRect and rootBounds intersect or
-  //        are edge-adjacent, even if the intersection has zero area (because
-  //        rootBounds or targetRect have zero area); otherwise, let
-  //        isIntersecting be false.
-  bool is_intersecting =
-      intersection_rect->width() != 0 || intersection_rect->height() != 0;
-
-  //     8. If targetArea is non-zero, let intersectionRatio be intersectionArea
-  //        divided by targetArea. Otherwise, let intersectionRatio be 1 if
-  //        isIntersecting is true, or 0 if isIntersecting is false.
-  float intersection_ratio = is_intersecting ? 1.0f : 0.0f;
-  if (target_area != 0) {
-    intersection_ratio = intersection_area / target_area;
-  }
-
-  //     9. Let thresholdIndex be the index of the first entry in
-  //        observer.thresholds whose value is greater than intersectionRatio,
-  //        or the length of observer.thresholds if intersectionRatio is greater
-  //        than or equal to the last entry in observer.thresholds.
-  const script::Sequence<double>& thresholds = observer->thresholds();
-  size_t threshold_index;
-  for (threshold_index = 0; threshold_index < thresholds.size();
-       ++threshold_index) {
-    if (thresholds.at(threshold_index) > intersection_ratio) {
-      break;
-    }
-  }
-
-  //     10. Let intersectionObserverRegistration be the
-  //         IntersectionObserverRegistration record in target's internal
-  //         [[RegisteredIntersectionObservers]] slot whose observer property is
-  //         equal to observer.
-  IntersectionObserverRegistration* intersection_observer_registration =
-      intersection_observer_registration_list_.FindRegistrationForObserver(
-          observer);
-
-  if (!intersection_observer_registration) {
-    NOTREACHED();
-    return;
-  }
-
-  //     11. Let previousThresholdIndex be the
-  //         intersectionObserverRegistration's previousThresholdIndex property.
-  int32 previous_threshold_index =
-      intersection_observer_registration->previous_threshold_index();
-
-  //     12. Let previousIsIntersecting be the
-  //         intersectionObserverRegistration's previousIsIntersecting property.
-  bool previous_is_intersecting =
-      intersection_observer_registration->previous_is_intersecting();
-
-  //     13. If thresholdIndex does not equal previousThresholdIndex or if
-  //         isIntersecting does not equal previousIsIntersecting, queue an
-  //         IntersectionObserverEntry, passing in observer, time, rootBounds,
-  //         boundingClientRect, intersectionRect, isIntersecting, and target.
-  if (static_cast<int32>(threshold_index) != previous_threshold_index ||
-      is_intersecting != previous_is_intersecting) {
-    IntersectionObserverEntryInit init_dict;
-    init_dict.set_time(target_element_->owner_document()
-                           ->window()
-                           ->performance()
-                           ->timing()
-                           ->GetNavigationStartClock()
-                           ->Now()
-                           .InMillisecondsF());
-    init_dict.set_root_bounds(root_bounds);
-    init_dict.set_bounding_client_rect(target_rect);
-    init_dict.set_intersection_rect(intersection_rect);
-    init_dict.set_is_intersecting(is_intersecting);
-    init_dict.set_intersection_ratio(intersection_ratio);
-    init_dict.set_target(base::WrapRefCounted(target_element_));
-    observer->QueueIntersectionObserverEntry(
-        base::WrapRefCounted(new IntersectionObserverEntry(init_dict)));
-  }
-
-  //     14. Assign threshold to intersectionObserverRegistration's
-  //         previousThresholdIndex property.
-  intersection_observer_registration->set_previous_threshold_index(
-      static_cast<int32>(threshold_index));
-
-  //     15. Assign isIntersecting to intersectionObserverRegistration's
-  //         previousIsIntersecting property.
-  intersection_observer_registration->set_previous_is_intersecting(
-      is_intersecting);
-}
-
-scoped_refptr<DOMRectReadOnly> IntersectionObserverTarget::GetRootBounds(
-    const scoped_refptr<HTMLElement>& html_intersection_root,
-    scoped_refptr<cssom::PropertyListValue> root_margin_property_value) {
-  // https://www.w3.org/TR/intersection-observer/#intersectionobserver-root-intersection-rectangle
-  // Rules for determining the root intersection rectangle bounds.
-  LayoutBoxes* intersection_root_layout_boxes =
-      html_intersection_root->layout_boxes();
-  DCHECK(intersection_root_layout_boxes);
-
-  math::RectF root_bounds_without_margins;
-  // If the intersection root is the implicit root, it's the viewport's size.
-  if (html_intersection_root ==
-      html_intersection_root->owner_document()->document_element()) {
-    root_bounds_without_margins = math::RectF(
-        0.0, 0.0,
-        html_intersection_root->owner_document()->viewport_size().width(),
-        html_intersection_root->owner_document()->viewport_size().height());
-  } else if (IsOverflowCropped(html_intersection_root->computed_style())) {
-    // If the intersection root has an overflow clip, it's the element's content
-    // area.
-    math::Vector2dF content_edge_offset =
-        intersection_root_layout_boxes->GetContentEdgeOffset();
-    root_bounds_without_margins =
-        math::RectF(content_edge_offset.x(), content_edge_offset.y(),
-                    intersection_root_layout_boxes->GetContentEdgeWidth(),
-                    intersection_root_layout_boxes->GetContentEdgeHeight());
-  } else {
-    // Otherwise, it's the result of running the getBoundingClientRect()
-    // algorithm on the intersection root.
-    root_bounds_without_margins =
-        math::RectF(html_intersection_root->GetBoundingClientRect()->rect());
-  }
-  int32 top_margin = GetUsedLengthOfRootMarginPropertyValue(
-      root_margin_property_value->value()[0],
-      root_bounds_without_margins.height());
-  int32 right_margin = GetUsedLengthOfRootMarginPropertyValue(
-      root_margin_property_value->value()[1],
-      root_bounds_without_margins.width());
-  int32 bottom_margin = GetUsedLengthOfRootMarginPropertyValue(
-      root_margin_property_value->value()[2],
-      root_bounds_without_margins.height());
-  int32 left_margin = GetUsedLengthOfRootMarginPropertyValue(
-      root_margin_property_value->value()[3],
-      root_bounds_without_margins.width());
-
-  // Remember to grow or shrink the root intersection rectangle bounds based
-  // on the root margin property.
-  scoped_refptr<DOMRectReadOnly> root_bounds = new DOMRectReadOnly(
-      root_bounds_without_margins.x() - left_margin,
-      root_bounds_without_margins.y() - top_margin,
-      root_bounds_without_margins.width() + left_margin + right_margin,
-      root_bounds_without_margins.height() + top_margin + bottom_margin);
-  return root_bounds;
-}
-
-int32 IntersectionObserverTarget::GetUsedLengthOfRootMarginPropertyValue(
-    const scoped_refptr<cssom::PropertyValue>& length_property_value,
-    float percentage_base) {
-  cssom::UsedLengthValueProvider<float> used_length_provider(percentage_base);
-  length_property_value->Accept(&used_length_provider);
-  // Not explicitly stated in web spec, but has been observed that Chrome
-  // truncates root margin decimal values
-  return static_cast<int32>(used_length_provider.used_length().value_or(0.0f));
-}
-
-scoped_refptr<DOMRectReadOnly>
-IntersectionObserverTarget::ComputeIntersectionBetweenTargetAndRoot(
-    const scoped_refptr<HTMLElement>& html_intersection_root,
-    const scoped_refptr<DOMRectReadOnly>& root_bounds,
-    const scoped_refptr<DOMRectReadOnly>& target_rect,
-    const scoped_refptr<HTMLElement>& html_target) {
-  // https://www.w3.org/TR/intersection-observer/#calculate-intersection-rect-algo
-  // To compute the intersection between a target and the observer's
-  // intersection root, run these steps:
-  // 1. Let intersectionRect be the result of running the
-  // getBoundingClientRect() algorithm on the target.
-  math::RectF intersection_rect = target_rect->rect();
-
-  // 2. Let container be the containing block of the target.
-  LayoutBoxes* target_layout_boxes = html_target->layout_boxes();
-  DCHECK(target_layout_boxes);
-  math::Vector2dF total_offset_from_containing_block =
-      target_layout_boxes->GetBorderEdgeOffsetFromContainingBlock();
-
-  HTMLElement* prev_container = html_target;
-  HTMLElement* container = GetContainingBlockOfHTMLElement(prev_container);
-
-  // 3. While container is not the intersection root:
-  while (container != html_intersection_root) {
-    //     1. Map intersectionRect to the coordinate space of container.
-    intersection_rect.set_x(total_offset_from_containing_block.x());
-    intersection_rect.set_y(total_offset_from_containing_block.y());
-
-    //     2. If container has overflow clipping or a css clip-path property,
-    //     update intersectionRect by applying container's clip.
-    //     (Note: The containing block of an element with 'position: absolute'
-    //     is formed by the padding edge of the ancestor.
-    //     https://www.w3.org/TR/CSS2/visudet.html)
-    LayoutBoxes* container_layout_boxes = container->layout_boxes();
-    DCHECK(container_layout_boxes);
-    if (IsOverflowCropped(container->computed_style())) {
-      math::Vector2dF container_clip_dimensions =
-          prev_container->computed_style()->position() ==
-                  cssom::KeywordValue::GetAbsolute()
-              ? math::Vector2dF(container_layout_boxes->GetPaddingEdgeWidth(),
-                                container_layout_boxes->GetPaddingEdgeHeight())
-              : math::Vector2dF(container_layout_boxes->GetContentEdgeWidth(),
-                                container_layout_boxes->GetContentEdgeHeight());
-      math::RectF container_clip(0, 0, container_clip_dimensions.x(),
-                                 container_clip_dimensions.y());
-      intersection_rect =
-          IntersectIntersectionObserverRects(intersection_rect, container_clip);
-    }
-
-    //     3. If container is the root element of a nested browsing context,
-    //     update container to be the browsing context container of container,
-    //     and update intersectionRect by clipping to the viewport of the nested
-    //     browsing context. Otherwise, update container to be the containing
-    //     block of container.
-    //     (Note: The containing block of an element with 'position: absolute'
-    //     is formed by the padding edge of the ancestor.
-    //     https://www.w3.org/TR/CSS2/visudet.html)
-    math::Vector2dF next_offset_from_containing_block =
-        prev_container->computed_style()->position() ==
-                cssom::KeywordValue::GetAbsolute()
-            ? container_layout_boxes->GetPaddingEdgeOffsetFromContainingBlock()
-            : container_layout_boxes->GetContentEdgeOffsetFromContainingBlock();
-    total_offset_from_containing_block += next_offset_from_containing_block;
-
-    prev_container = container;
-    container = GetContainingBlockOfHTMLElement(prev_container);
-  }
-
-  // Modification of steps 4-6:
-  // Map intersectionRect to the coordinate space of the viewport of the
-  // Document containing the target.
-  // (Note: The containing block of an element with 'position: absolute'
-  // is formed by the padding edge of the ancestor.
-  // https://www.w3.org/TR/CSS2/visudet.html)
-  LayoutBoxes* container_layout_boxes = container->layout_boxes();
-  DCHECK(container_layout_boxes);
-  math::Vector2dF containing_block_offset_from_origin =
-      prev_container->computed_style()->position() ==
-              cssom::KeywordValue::GetAbsolute()
-          ? container_layout_boxes->GetPaddingEdgeOffset()
-          : container_layout_boxes->GetContentEdgeOffset();
-
-  intersection_rect.set_x(total_offset_from_containing_block.x() +
-                          containing_block_offset_from_origin.x());
-  intersection_rect.set_y(total_offset_from_containing_block.y() +
-                          containing_block_offset_from_origin.y());
-
-  // Update intersectionRect by intersecting it with the root intersection
-  // rectangle, which is already in this coordinate space.
-  intersection_rect = IntersectIntersectionObserverRects(intersection_rect,
-                                                         root_bounds->rect());
-
-  return base::WrapRefCounted(new DOMRectReadOnly(intersection_rect));
-}
-
-math::RectF IntersectionObserverTarget::IntersectIntersectionObserverRects(
-    const math::RectF& a, const math::RectF& b) {
-  float rx = std::max(a.x(), b.x());
-  float ry = std::max(a.y(), b.y());
-  float rr = std::min(a.right(), b.right());
-  float rb = std::min(a.bottom(), b.bottom());
-
-  if (rx > rr || ry > rb) {
-    return math::RectF(0.0f, 0.0f, 0.0f, 0.0f);
-  }
-
-  return math::RectF(rx, ry, rr - rx, rb - ry);
-}
-
-}  // namespace dom
-}  // namespace cobalt
diff --git a/src/cobalt/dom/intersection_observer_target.h b/src/cobalt/dom/intersection_observer_target.h
deleted file mode 100644
index 9ac0b4e..0000000
--- a/src/cobalt/dom/intersection_observer_target.h
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2019 The Cobalt Authors. All Rights Reserved.
-//
-// 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 COBALT_DOM_INTERSECTION_OBSERVER_TARGET_H_
-#define COBALT_DOM_INTERSECTION_OBSERVER_TARGET_H_
-
-#include "cobalt/dom/intersection_observer_registration_list.h"
-#include "cobalt/math/rect_f.h"
-
-namespace cobalt {
-namespace dom {
-
-class Element;
-class HTMLElement;
-
-// The Intersection Observer spec describes a sequence of steps to update the
-// the target elements of an observer in step 2.2 of the "Run the Update
-// Intersection Observations Steps" algorithm. This helper class represents
-// one of those target elements.
-// https://www.w3.org/TR/intersection-observer/#update-intersection-observations-algo
-class IntersectionObserverTarget {
- public:
-  explicit IntersectionObserverTarget(Element* target_element);
-
-  void RegisterIntersectionObserver(
-      const scoped_refptr<IntersectionObserver>& observer);
-
-  void UnregisterIntersectionObserver(
-      const scoped_refptr<IntersectionObserver>& observer);
-
-  void UpdateIntersectionObservationsForTarget(
-      const scoped_refptr<IntersectionObserver>& observer);
-
- private:
-  scoped_refptr<DOMRectReadOnly> GetRootBounds(
-      const scoped_refptr<HTMLElement>& html_intersection_root,
-      scoped_refptr<cssom::PropertyListValue> root_margin_property_value);
-
-  int32 GetUsedLengthOfRootMarginPropertyValue(
-      const scoped_refptr<cssom::PropertyValue>& length_property_value,
-      float percentage_base);
-
-  scoped_refptr<DOMRectReadOnly> ComputeIntersectionBetweenTargetAndRoot(
-      const scoped_refptr<HTMLElement>& intersection_root,
-      const scoped_refptr<DOMRectReadOnly>& root_bounds,
-      const scoped_refptr<DOMRectReadOnly>& target_rect,
-      const scoped_refptr<HTMLElement>& html_target);
-
-  // Similar to the IntersectRects function in math::RectF, but handles edge
-  // adjacent intersections as valid intersections (instead of returning a
-  // rectangle with zero dimensions)
-  math::RectF IntersectIntersectionObserverRects(const math::RectF& a,
-                                                 const math::RectF& b);
-
-  Element* target_element_;
-
-  IntersectionObserverRegistrationList intersection_observer_registration_list_;
-};
-
-}  // namespace dom
-}  // namespace cobalt
-
-#endif  // COBALT_DOM_INTERSECTION_OBSERVER_TARGET_H_
diff --git a/src/cobalt/dom/intersection_observer_task_manager.cc b/src/cobalt/dom/intersection_observer_task_manager.cc
index 4ce591b..c46c09d 100644
--- a/src/cobalt/dom/intersection_observer_task_manager.cc
+++ b/src/cobalt/dom/intersection_observer_task_manager.cc
@@ -71,23 +71,6 @@
                  base::Unretained(this)));
 }
 
-void IntersectionObserverTaskManager::UpdateIntersectionObservations() {
-  TRACE_EVENT0(
-      "cobalt::dom",
-      "IntersectionObserverTaskManager::UpdateIntersectionObservations()");
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-  // https://www.w3.org/TR/intersection-observer/#update-intersection-observations-algo
-  // To run the update intersection observations steps for a Document document
-  // given a timestamp time, run these steps: 1. Let observer list be a list of
-  // all IntersectionObservers whose root is in the DOM tree of document.
-  // 2. For each observer in observer list, run a set of subtasks (subtasks are
-  //    implemented in IntersectionObserver::UpdateObservationTargets):
-  for (auto observer : observer_list_) {
-    observer->UpdateObservationTargets();
-  }
-}
-
 void IntersectionObserverTaskManager::TraceMembers(script::Tracer* tracer) {
   tracer->TraceItems(observer_list_);
 }
diff --git a/src/cobalt/dom/intersection_observer_task_manager.h b/src/cobalt/dom/intersection_observer_task_manager.h
index 21b960f..5d78e9b 100644
--- a/src/cobalt/dom/intersection_observer_task_manager.h
+++ b/src/cobalt/dom/intersection_observer_task_manager.h
@@ -48,10 +48,6 @@
   // posted.
   void QueueIntersectionObserverTask();
 
-  // Update intersection observer registrations for intersection observers whose
-  // root is in the DOM tree of document
-  void UpdateIntersectionObservations();
-
   void TraceMembers(script::Tracer* tracer) override;
 
  private:
diff --git a/src/cobalt/dom/layout_boxes.h b/src/cobalt/dom/layout_boxes.h
index 79fc8c3..69c160e 100644
--- a/src/cobalt/dom/layout_boxes.h
+++ b/src/cobalt/dom/layout_boxes.h
@@ -59,7 +59,6 @@
   virtual float GetBorderEdgeTop() const = 0;
   virtual float GetBorderEdgeWidth() const = 0;
   virtual float GetBorderEdgeHeight() const = 0;
-  virtual math::Vector2dF GetBorderEdgeOffsetFromContainingBlock() const = 0;
 
   // Returns the border width (thickness) values.
   virtual float GetBorderLeftWidth() const = 0;
@@ -75,14 +74,6 @@
   virtual math::Vector2dF GetPaddingEdgeOffset() const = 0;
   virtual float GetPaddingEdgeWidth() const = 0;
   virtual float GetPaddingEdgeHeight() const = 0;
-  virtual math::Vector2dF GetPaddingEdgeOffsetFromContainingBlock() const = 0;
-
-  // Returns content edge values.
-  // See https://www.w3.org/TR/CSS21/box.html#box-dimensions
-  virtual math::Vector2dF GetContentEdgeOffset() const = 0;
-  virtual float GetContentEdgeWidth() const = 0;
-  virtual float GetContentEdgeHeight() const = 0;
-  virtual math::Vector2dF GetContentEdgeOffsetFromContainingBlock() const = 0;
 
   // Return scrolling area.
   // See https://www.w3.org/TR/cssom-view-1/#scrolling-area
diff --git a/src/cobalt/dom/node.cc b/src/cobalt/dom/node.cc
index 8e6ed9d..c7f01b7 100644
--- a/src/cobalt/dom/node.cc
+++ b/src/cobalt/dom/node.cc
@@ -497,8 +497,8 @@
   }
 }
 
-void Node::MarkDisplayNoneOnNodeAndDescendants() {
-  MarkDisplayNoneOnDescendants();
+void Node::MarkNotDisplayedOnNodeAndDescendants() {
+  MarkNotDisplayedOnDescendants();
 }
 
 void Node::PurgeCachedBackgroundImagesOfNodeAndDescendants() {
@@ -517,10 +517,10 @@
   InvalidateLayoutBoxesOfDescendants();
 }
 
-void Node::MarkDisplayNoneOnDescendants() {
+void Node::MarkNotDisplayedOnDescendants() {
   Node* child = first_child_.get();
   while (child) {
-    child->MarkDisplayNoneOnNodeAndDescendants();
+    child->MarkNotDisplayedOnNodeAndDescendants();
     child = child->next_sibling_.get();
   }
 }
@@ -772,6 +772,7 @@
   bool was_inserted_to_document = node->inserted_into_document_;
   if (was_inserted_to_document) {
     node->OnRemovedFromDocument();
+    node->MarkNotDisplayedOnNodeAndDescendants();
   }
 
   // 1. 5. Not needed by Cobalt.
diff --git a/src/cobalt/dom/node.h b/src/cobalt/dom/node.h
index 3653d7c..3f6cd23 100644
--- a/src/cobalt/dom/node.h
+++ b/src/cobalt/dom/node.h
@@ -250,7 +250,7 @@
 
   virtual bool IsInDocument() const { return inserted_into_document_; }
 
-  virtual void MarkDisplayNoneOnNodeAndDescendants();
+  virtual void MarkNotDisplayedOnNodeAndDescendants();
   virtual void PurgeCachedBackgroundImagesOfNodeAndDescendants();
   virtual void InvalidateComputedStylesOfNodeAndDescendants();
   virtual void InvalidateLayoutBoxesOfNodeAndAncestors();
@@ -260,7 +260,7 @@
   virtual void InvalidateLayoutBoxCrossReferences() {}
   virtual void InvalidateLayoutBoxRenderTreeNodes() {}
 
-  void MarkDisplayNoneOnDescendants();
+  void MarkNotDisplayedOnDescendants();
   void PurgeCachedBackgroundImagesOfDescendants();
   void InvalidateComputedStylesOfDescendants();
   void InvalidateLayoutBoxesOfAncestors();
diff --git a/src/cobalt/layout/box.cc b/src/cobalt/layout/box.cc
index 521051f..403fc1a 100644
--- a/src/cobalt/layout/box.cc
+++ b/src/cobalt/layout/box.cc
@@ -706,6 +706,11 @@
     UpdateUiNavigationItem();
   }
 
+  // Update intersection observers for any targets represented by this box.
+  if (box_intersection_observer_module_) {
+    box_intersection_observer_module_->UpdateIntersectionObservations();
+  }
+
   // The painting order is:
   // - background color.
   // - background image.
@@ -2102,5 +2107,30 @@
   return true;
 }
 
+void Box::AddIntersectionObserverRootsAndTargets(
+    BoxIntersectionObserverModule::IntersectionObserverRootVector&& roots,
+    BoxIntersectionObserverModule::IntersectionObserverTargetVector&& targets) {
+  if (!box_intersection_observer_module_) {
+    box_intersection_observer_module_ =
+        std::unique_ptr<BoxIntersectionObserverModule>(
+            new BoxIntersectionObserverModule(this));
+  }
+
+  box_intersection_observer_module_->AddIntersectionObserverRoots(
+      std::move(roots));
+  box_intersection_observer_module_->AddIntersectionObserverTargets(
+      std::move(targets));
+}
+
+bool Box::ContainsIntersectionObserverRoot(
+    const scoped_refptr<IntersectionObserverRoot>& intersection_observer_root)
+    const {
+  if (box_intersection_observer_module_) {
+    return box_intersection_observer_module_
+        ->BoxContainsIntersectionObserverRoot(intersection_observer_root);
+  }
+  return false;
+}
+
 }  // namespace layout
 }  // namespace cobalt
diff --git a/src/cobalt/layout/box.h b/src/cobalt/layout/box.h
index 2368108..596b2b4 100644
--- a/src/cobalt/layout/box.h
+++ b/src/cobalt/layout/box.h
@@ -26,6 +26,7 @@
 #include "cobalt/cssom/css_style_declaration.h"
 #include "cobalt/dom/node.h"
 #include "cobalt/layout/base_direction.h"
+#include "cobalt/layout/box_intersection_observer_module.h"
 #include "cobalt/layout/insets_layout_unit.h"
 #include "cobalt/layout/layout_stat_tracker.h"
 #include "cobalt/layout/layout_unit.h"
@@ -643,6 +644,14 @@
     ui_nav_item_ = item;
   }
 
+  void AddIntersectionObserverRootsAndTargets(
+      BoxIntersectionObserverModule::IntersectionObserverRootVector&& roots,
+      BoxIntersectionObserverModule::IntersectionObserverTargetVector&&
+          targets);
+  bool ContainsIntersectionObserverRoot(
+      const scoped_refptr<IntersectionObserverRoot>& intersection_observer_root)
+      const;
+
  protected:
   UsedStyleProvider* used_style_provider() const {
     return used_style_provider_;
@@ -927,6 +936,9 @@
   // UI navigation items are used to help animate certain elements.
   scoped_refptr<ui_navigation::NavItem> ui_nav_item_;
 
+  std::unique_ptr<BoxIntersectionObserverModule>
+      box_intersection_observer_module_;
+
   // For write access to parent/containing_block members.
   friend class ContainerBox;
   friend class LayoutBoxes;
diff --git a/src/cobalt/layout/box_generator.cc b/src/cobalt/layout/box_generator.cc
index 2b85645..b9fe273 100644
--- a/src/cobalt/layout/box_generator.cc
+++ b/src/cobalt/layout/box_generator.cc
@@ -990,6 +990,13 @@
   container_box_before_split->SetUiNavItem(html_element->GetUiNavItem());
   boxes_.push_back(container_box_before_split);
 
+  BoxIntersectionObserverModule::IntersectionObserverRootVector roots =
+      html_element->GetLayoutIntersectionObserverRoots();
+  BoxIntersectionObserverModule::IntersectionObserverTargetVector targets =
+      html_element->GetLayoutIntersectionObserverTargets();
+  container_box_before_split->AddIntersectionObserverRootsAndTargets(
+      std::move(roots), std::move(targets));
+
   AppendPseudoElementToLine(html_element, dom::kBeforePseudoElementType);
 
   // Generate child boxes.
diff --git a/src/cobalt/layout/box_intersection_observer_module.cc b/src/cobalt/layout/box_intersection_observer_module.cc
new file mode 100644
index 0000000..d09d3b6
--- /dev/null
+++ b/src/cobalt/layout/box_intersection_observer_module.cc
@@ -0,0 +1,58 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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.
+
+#include "cobalt/layout/box_intersection_observer_module.h"
+
+#include "cobalt/layout/box.h"
+
+namespace cobalt {
+namespace layout {
+
+BoxIntersectionObserverModule::BoxIntersectionObserverModule(Box* box)
+    : box_(box) {}
+
+void BoxIntersectionObserverModule::AddIntersectionObserverRoots(
+    IntersectionObserverRootVector&& roots) {
+  intersection_observer_roots_ = std::move(roots);
+}
+
+void BoxIntersectionObserverModule::AddIntersectionObserverTargets(
+    IntersectionObserverTargetVector&& targets) {
+  intersection_observer_targets_ = std::move(targets);
+}
+
+bool BoxIntersectionObserverModule::BoxContainsIntersectionObserverRoot(
+    const scoped_refptr<IntersectionObserverRoot>& intersection_observer_root)
+    const {
+  for (auto it = intersection_observer_roots_.begin();
+       it != intersection_observer_roots_.end(); ++it) {
+    if (*it == intersection_observer_root) {
+      return true;
+    }
+  }
+  return false;
+}
+
+void BoxIntersectionObserverModule::UpdateIntersectionObservations() {
+  ContainerBox* container_box = box_->AsContainerBox();
+  if (!container_box) {
+    return;
+  }
+  for (const auto& target : intersection_observer_targets_) {
+    target->UpdateIntersectionObservationsForTarget(container_box);
+  }
+}
+
+}  // namespace layout
+}  // namespace cobalt
diff --git a/src/cobalt/layout/box_intersection_observer_module.h b/src/cobalt/layout/box_intersection_observer_module.h
new file mode 100644
index 0000000..f8668fa
--- /dev/null
+++ b/src/cobalt/layout/box_intersection_observer_module.h
@@ -0,0 +1,58 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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 COBALT_LAYOUT_BOX_INTERSECTION_OBSERVER_MODULE_H_
+#define COBALT_LAYOUT_BOX_INTERSECTION_OBSERVER_MODULE_H_
+
+#include <vector>
+
+#include "cobalt/layout/intersection_observer_root.h"
+#include "cobalt/layout/intersection_observer_target.h"
+
+namespace cobalt {
+namespace layout {
+
+class Box;
+
+// This helper class groups the methods and data related to the intersection
+// root and target elements produced by a particular box.
+class BoxIntersectionObserverModule {
+ public:
+  typedef std::vector<scoped_refptr<IntersectionObserverRoot>>
+      IntersectionObserverRootVector;
+  typedef std::vector<scoped_refptr<IntersectionObserverTarget>>
+      IntersectionObserverTargetVector;
+
+  explicit BoxIntersectionObserverModule(Box* box);
+  ~BoxIntersectionObserverModule() {}
+
+  void AddIntersectionObserverRoots(IntersectionObserverRootVector&& roots);
+  void AddIntersectionObserverTargets(
+      IntersectionObserverTargetVector&& targets);
+  bool BoxContainsIntersectionObserverRoot(
+      const scoped_refptr<IntersectionObserverRoot>& intersection_observer_root)
+      const;
+  void UpdateIntersectionObservations();
+
+ private:
+  Box* box_;
+
+  IntersectionObserverRootVector intersection_observer_roots_;
+  IntersectionObserverTargetVector intersection_observer_targets_;
+};
+
+}  // namespace layout
+}  // namespace cobalt
+
+#endif  // COBALT_LAYOUT_BOX_INTERSECTION_OBSERVER_MODULE_H_
diff --git a/src/cobalt/layout/intersection_observer_root.h b/src/cobalt/layout/intersection_observer_root.h
new file mode 100644
index 0000000..b7b6447
--- /dev/null
+++ b/src/cobalt/layout/intersection_observer_root.h
@@ -0,0 +1,59 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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 COBALT_LAYOUT_INTERSECTION_OBSERVER_ROOT_H_
+#define COBALT_LAYOUT_INTERSECTION_OBSERVER_ROOT_H_
+
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "cobalt/cssom/property_list_value.h"
+
+namespace cobalt {
+namespace layout {
+
+class Box;
+
+// An IntersectionObserverRoot holds information associated with the W3
+// IntersectionObserver object, such as rootMargin and thresholds.
+// IntersectionObserverTarget objects reference these objects, instead of the
+// other way around.
+class IntersectionObserverRoot
+    : public base::RefCountedThreadSafe<IntersectionObserverRoot> {
+ public:
+  IntersectionObserverRoot(
+      const scoped_refptr<cssom::PropertyListValue>& root_margin_property_value,
+      std::vector<double> thresholds_vector)
+      : root_margin_property_value_(root_margin_property_value),
+        thresholds_vector_(thresholds_vector) {}
+
+  ~IntersectionObserverRoot() {}
+
+  const scoped_refptr<cssom::PropertyListValue>& root_margin_property_value()
+      const {
+    return root_margin_property_value_;
+  }
+
+  std::vector<double> thresholds_vector() const { return thresholds_vector_; }
+
+ private:
+  // Properties associated with the IntersectionObserver object.
+  scoped_refptr<cssom::PropertyListValue> root_margin_property_value_;
+  std::vector<double> thresholds_vector_;
+};
+
+}  // namespace layout
+}  // namespace cobalt
+
+#endif  // COBALT_LAYOUT_INTERSECTION_OBSERVER_ROOT_H_
diff --git a/src/cobalt/layout/intersection_observer_target.cc b/src/cobalt/layout/intersection_observer_target.cc
new file mode 100644
index 0000000..19fd041
--- /dev/null
+++ b/src/cobalt/layout/intersection_observer_target.cc
@@ -0,0 +1,276 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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.
+
+#include "cobalt/layout/intersection_observer_target.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "cobalt/cssom/computed_style_utils.h"
+#include "cobalt/cssom/keyword_value.h"
+#include "cobalt/layout/box.h"
+#include "cobalt/layout/container_box.h"
+#include "cobalt/layout/intersection_observer_root.h"
+#include "cobalt/layout/used_style.h"
+
+namespace cobalt {
+namespace layout {
+
+void IntersectionObserverTarget::UpdateIntersectionObservationsForTarget(
+    ContainerBox* target_box) {
+  // Walk up the containing block chain looking for the box referencing the
+  // IntersectionObserverRoot corresponding to this IntersectionObserverTarget.
+  // Skip further processing for the target if it is not a descendant of the
+  // root in the containing block chain.
+  const ContainerBox* root_box = target_box->GetContainingBlock();
+  while (!root_box->ContainsIntersectionObserverRoot(
+      intersection_observer_root_)) {
+    if (!root_box->parent()) {
+      return;
+    }
+    root_box = root_box->GetContainingBlock();
+  }
+
+  // Let targetRect be target's bounding border box.
+  RectLayoutUnit target_transformed_border_box(
+      target_box->GetTransformedBorderBoxFromRoot());
+  math::RectF target_rect =
+      math::RectF(target_transformed_border_box.x().toFloat(),
+                  target_transformed_border_box.y().toFloat(),
+                  target_transformed_border_box.width().toFloat(),
+                  target_transformed_border_box.height().toFloat());
+
+  // Let intersectionRect be the result of running the compute the intersection
+  // algorithm on target.
+  math::RectF root_bounds = GetRootBounds(
+      root_box, intersection_observer_root_->root_margin_property_value());
+  math::RectF intersection_rect = ComputeIntersectionBetweenTargetAndRoot(
+      root_box, root_bounds, target_rect, target_box);
+
+  // Let targetArea be targetRect's area.
+  float target_area = target_rect.size().GetArea();
+
+  // Let intersectionArea be intersectionRect's area.
+  float intersection_area = intersection_rect.size().GetArea();
+
+  // Let isIntersecting be true if targetRect and rootBounds intersect or are
+  // edge-adjacent, even if the intersection has zero area (because rootBounds
+  // or targetRect have zero area); otherwise, let isIntersecting be false.
+  bool is_intersecting =
+      intersection_rect.width() != 0 || intersection_rect.height() != 0;
+
+  // If targetArea is non-zero, let intersectionRatio be intersectionArea
+  // divided by targetArea. Otherwise, let intersectionRatio be 1 if
+  // isIntersecting is true, or 0 if isIntersecting is false.
+  float intersection_ratio = is_intersecting ? 1.0f : 0.0f;
+  if (target_area != 0) {
+    intersection_ratio = intersection_area / target_area;
+  }
+
+  // Let thresholdIndex be the index of the first entry in observer.thresholds
+  // whose value is greater than intersectionRatio, or the length of
+  // observer.thresholds if intersectionRatio is greater than or equal to the
+  // last entry in observer.thresholds.
+  const std::vector<double>& thresholds =
+      intersection_observer_root_->thresholds_vector();
+  size_t threshold_index;
+  for (threshold_index = 0; threshold_index < thresholds.size();
+       ++threshold_index) {
+    if (thresholds.at(threshold_index) > intersection_ratio) {
+      break;
+    }
+  }
+
+  // If thresholdIndex does not equal previousThresholdIndex or if
+  // isIntersecting does not equal previousIsIntersecting, queue an
+  // IntersectionObserverEntry, passing in observer, time, rootBounds,
+  //         boundingClientRect, intersectionRect, isIntersecting, and target.
+  if (static_cast<int32>(threshold_index) != previous_threshold_index_ ||
+      is_intersecting != previous_is_intersecting_) {
+    on_intersection_callback_.Run(root_bounds, target_rect, intersection_rect,
+                                  is_intersecting, intersection_ratio);
+  }
+
+  // Update the previousThresholdIndex and previousIsIntersecting properties.
+  previous_threshold_index_ = static_cast<int32>(threshold_index);
+  previous_is_intersecting_ = is_intersecting;
+}
+
+bool IntersectionObserverTarget::IsInContainingBlockChain(
+    const ContainerBox* potential_containing_block,
+    const ContainerBox* target_box) {
+  const ContainerBox* containing_block = target_box->GetContainingBlock();
+  while (containing_block != potential_containing_block) {
+    if (!containing_block->parent()) {
+      return false;
+    }
+    containing_block = containing_block->GetContainingBlock();
+  }
+  return true;
+}
+
+math::RectF IntersectionObserverTarget::GetRootBounds(
+    const ContainerBox* root_box,
+    scoped_refptr<cssom::PropertyListValue> root_margin_property_value) {
+  math::RectF root_bounds_without_margins;
+  if (IsOverflowCropped(root_box->computed_style())) {
+    // If the intersection root has an overflow clip, it's the element's content
+    // area.
+    Vector2dLayoutUnit content_edge_offset =
+        root_box->GetContentBoxOffsetFromRoot(false /*transform_forms_root*/);
+    root_bounds_without_margins = math::RectF(
+        content_edge_offset.x().toFloat(), content_edge_offset.y().toFloat(),
+        root_box->width().toFloat(), root_box->height().toFloat());
+  } else {
+    // Otherwise, it's the result of running the getBoundingClientRect()
+    // algorithm on the intersection root.
+    RectLayoutUnit root_transformed_border_box(
+        root_box->GetTransformedBorderBoxFromRoot());
+    root_bounds_without_margins =
+        math::RectF(root_transformed_border_box.x().toFloat(),
+                    root_transformed_border_box.y().toFloat(),
+                    root_transformed_border_box.width().toFloat(),
+                    root_transformed_border_box.height().toFloat());
+  }
+  int32 top_margin = GetUsedLengthOfRootMarginPropertyValue(
+      root_margin_property_value->value()[0],
+      LayoutUnit(root_bounds_without_margins.height()));
+  int32 right_margin = GetUsedLengthOfRootMarginPropertyValue(
+      root_margin_property_value->value()[1],
+      LayoutUnit(root_bounds_without_margins.width()));
+  int32 bottom_margin = GetUsedLengthOfRootMarginPropertyValue(
+      root_margin_property_value->value()[2],
+      LayoutUnit(root_bounds_without_margins.height()));
+  int32 left_margin = GetUsedLengthOfRootMarginPropertyValue(
+      root_margin_property_value->value()[3],
+      LayoutUnit(root_bounds_without_margins.width()));
+
+  // Remember to grow or shrink the root intersection rectangle bounds based
+  // on the root margin property.
+  math::RectF root_bounds = math::RectF(
+      root_bounds_without_margins.x() - left_margin,
+      root_bounds_without_margins.y() - top_margin,
+      root_bounds_without_margins.width() + left_margin + right_margin,
+      root_bounds_without_margins.height() + top_margin + bottom_margin);
+  return root_bounds;
+}
+
+int32 IntersectionObserverTarget::GetUsedLengthOfRootMarginPropertyValue(
+    const scoped_refptr<cssom::PropertyValue>& length_property_value,
+    LayoutUnit percentage_base) {
+  UsedLengthValueProvider used_length_provider(percentage_base);
+  length_property_value->Accept(&used_length_provider);
+  // Not explicitly stated in web spec, but has been observed that Chrome
+  // truncates root margin decimal values.
+  return static_cast<int32>(
+      used_length_provider.used_length().value_or(LayoutUnit(0.0f)).toFloat());
+}
+
+math::RectF IntersectionObserverTarget::ComputeIntersectionBetweenTargetAndRoot(
+    const ContainerBox* root_box, const math::RectF& root_bounds,
+    const math::RectF& target_rect, const ContainerBox* target_box) {
+  // Let intersectionRect be target's bounding border box.
+  math::RectF intersection_rect = target_rect;
+
+  // Let container be the containing block of the target.
+  math::Vector2dF total_offset_from_containing_block =
+      target_box->GetBorderBoxOffsetFromContainingBlock();
+  const ContainerBox* prev_container = target_box;
+  const ContainerBox* container = prev_container->GetContainingBlock();
+
+  // While container is not the intersection root:
+  while (container != root_box) {
+    // Map intersectionRect to the coordinate space of container.
+    intersection_rect.set_x(total_offset_from_containing_block.x());
+    intersection_rect.set_y(total_offset_from_containing_block.y());
+
+    // If container has overflow clipping or a css clip-path property, update
+    // intersectionRect by applying container's clip. (Note: The containing
+    // block of an element with 'position: absolute' is formed by the padding
+    // edge of the ancestor. https://www.w3.org/TR/CSS2/visudet.html)
+    if (IsOverflowCropped(container->computed_style())) {
+      Vector2dLayoutUnit container_clip_dimensions =
+          prev_container->computed_style()->position() ==
+                  cssom::KeywordValue::GetAbsolute()
+              ? Vector2dLayoutUnit(container->GetPaddingBoxWidth(),
+                                   container->GetPaddingBoxHeight())
+              : Vector2dLayoutUnit(container->width(), container->height());
+      math::RectF container_clip(0.0f, 0.0f,
+                                 container_clip_dimensions.x().toFloat(),
+                                 container_clip_dimensions.y().toFloat());
+      intersection_rect =
+          IntersectIntersectionObserverRects(intersection_rect, container_clip);
+    }
+
+    // If container is the root element of a nested browsing context, update
+    // container to be the browsing context container of container, and update
+    // intersectionRect by clipping to the viewport of the nested browsing
+    // context. Otherwise, update container to be the containing block of
+    // container. (Note: The containing block of an element with 'position:
+    // absolute' is formed by the padding edge of the ancestor.
+    // https://www.w3.org/TR/CSS2/visudet.html)
+    math::Vector2dF next_offset_from_containing_block =
+        prev_container->computed_style()->position() ==
+                cssom::KeywordValue::GetAbsolute()
+            ? container->GetPaddingBoxOffsetFromContainingBlock()
+            : container->GetContentBoxOffsetFromContainingBlockContentBox(
+                  container->GetContainingBlock());
+    total_offset_from_containing_block += next_offset_from_containing_block;
+
+    prev_container = container;
+    container = prev_container->GetContainingBlock();
+  }
+
+  // Map intersectionRect to the coordinate space of the viewport of the
+  // Document containing the target.
+  // (Note: The containing block of an element with 'position: absolute'
+  // is formed by the padding edge of the ancestor.
+  // https://www.w3.org/TR/CSS2/visudet.html)
+  math::Vector2dF containing_block_offset_from_origin =
+      prev_container->computed_style()->position() ==
+                  cssom::KeywordValue::GetAbsolute() &&
+              !IsOverflowCropped(container->computed_style())
+          ? container->GetPaddingBoxOffsetFromRoot(
+                false /*transform_forms_root*/)
+          : container->GetContentBoxOffsetFromRoot(
+                false /*transform_forms_root*/);
+
+  intersection_rect.set_x(total_offset_from_containing_block.x() +
+                          containing_block_offset_from_origin.x());
+  intersection_rect.set_y(total_offset_from_containing_block.y() +
+                          containing_block_offset_from_origin.y());
+
+  // Update intersectionRect by intersecting it with the root intersection
+  // rectangle, which is already in this coordinate space.
+  intersection_rect =
+      IntersectIntersectionObserverRects(intersection_rect, root_bounds);
+  return intersection_rect;
+}
+
+math::RectF IntersectionObserverTarget::IntersectIntersectionObserverRects(
+    const math::RectF& a, const math::RectF& b) {
+  float rx = std::max(a.x(), b.x());
+  float ry = std::max(a.y(), b.y());
+  float rr = std::min(a.right(), b.right());
+  float rb = std::min(a.bottom(), b.bottom());
+
+  if (rx > rr || ry > rb) {
+    return math::RectF(0.0f, 0.0f, 0.0f, 0.0f);
+  }
+
+  return math::RectF(rx, ry, rr - rx, rb - ry);
+}
+
+}  // namespace layout
+}  // namespace cobalt
diff --git a/src/cobalt/layout/intersection_observer_target.h b/src/cobalt/layout/intersection_observer_target.h
new file mode 100644
index 0000000..a40d41d
--- /dev/null
+++ b/src/cobalt/layout/intersection_observer_target.h
@@ -0,0 +1,118 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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 COBALT_LAYOUT_INTERSECTION_OBSERVER_TARGET_H_
+#define COBALT_LAYOUT_INTERSECTION_OBSERVER_TARGET_H_
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "cobalt/cssom/property_list_value.h"
+#include "cobalt/layout/layout_unit.h"
+#include "cobalt/math/rect_f.h"
+
+namespace cobalt {
+namespace layout {
+
+class Box;
+class ContainerBox;
+class IntersectionObserverRoot;
+
+// A IntersectionObserverTarget roughly maps to |target| in a call to observe()
+// (https://www.w3.org/TR/intersection-observer/#dom-intersectionobserver-observe).
+// In lieu of having IntersectionObserverRegistration records, these objects can
+// keep track of the |previousThresholdIndex| and |previousIsIntersecting|
+// properties that determine whether IntersectionObserver objects need to be
+// notified of changes
+// (https://www.w3.org/TR/intersection-observer/#intersectionobserverregistration).
+// An IntersectionObserverTarget references the IntersectionObserverRoot that it
+// it associated with, not the other way around.
+class IntersectionObserverTarget
+    : public base::RefCountedThreadSafe<IntersectionObserverTarget> {
+ public:
+  // Callback that runs when an intersection change is observed, that goes back
+  // into DOM logic to notify the intersection observers.
+  typedef base::Callback<void(math::RectF root_bounds, math::RectF target_rect,
+                              math::RectF intersection_rect,
+                              bool is_intersecting, float intersection_ratio)>
+      OnIntersectionCallback;
+
+  IntersectionObserverTarget(
+      const OnIntersectionCallback& on_intersection_callback,
+      scoped_refptr<IntersectionObserverRoot> intersection_observer_root)
+      : on_intersection_callback_(on_intersection_callback),
+        intersection_observer_root_(intersection_observer_root),
+        previous_threshold_index_(-1),
+        previous_is_intersecting_(false) {}
+
+  ~IntersectionObserverTarget() {}
+
+  // The Intersection Observer spec describes a sequence of steps to update the
+  // target element of an observer in step 2.2 of the "Run the Update
+  // Intersection Observation Steps" algorithm.
+  // This function follows the algorithm in the web spec, with a few notable
+  // modifications: (1) All computations occur with respect to objects in the
+  // box tree, not the dom tree. (2) This class already keeps track of the
+  // previousThresholdIndex and previousIsIntersecting fields, so no
+  // intersection observer registration objects are used to determine whether
+  // IntersectionObserver objects need to be notified of a change.
+  // https://www.w3.org/TR/intersection-observer/#update-intersection-observations-algo
+  void UpdateIntersectionObservationsForTarget(ContainerBox* target_box);
+
+  scoped_refptr<IntersectionObserverRoot> intersection_observer_root() {
+    return intersection_observer_root_;
+  }
+
+ private:
+  // Walk up the containing block chain, as described in
+  // http://www.w3.org/TR/CSS2/visudet.html#containing-block-details
+  bool IsInContainingBlockChain(const ContainerBox* potential_containing_block,
+                                const ContainerBox* target_box);
+
+  int32 GetUsedLengthOfRootMarginPropertyValue(
+      const scoped_refptr<cssom::PropertyValue>& length_property_value,
+      LayoutUnit percentage_base);
+
+  // Rules for determining the root intersection rectangle bounds.
+  // https://www.w3.org/TR/intersection-observer/#intersectionobserver-root-intersection-rectangle
+  math::RectF GetRootBounds(
+      const ContainerBox* root_box,
+      scoped_refptr<cssom::PropertyListValue> root_margin_property_value);
+
+  // Compute the intersection between a target and the observer's intersection
+  // root.
+  // https://www.w3.org/TR/intersection-observer/#calculate-intersection-rect-algo
+  math::RectF ComputeIntersectionBetweenTargetAndRoot(
+      const ContainerBox* root_box, const math::RectF& root_bounds,
+      const math::RectF& target_rect, const ContainerBox* target_box);
+
+  // Similar to the IntersectRects function in math::RectF, but handles edge
+  // adjacent intersections as valid intersections (instead of returning a
+  // rectangle with zero dimensions)
+  math::RectF IntersectIntersectionObserverRects(const math::RectF& a,
+                                                 const math::RectF& b);
+
+  OnIntersectionCallback on_intersection_callback_;
+
+  scoped_refptr<IntersectionObserverRoot> intersection_observer_root_;
+
+  int32 previous_threshold_index_;
+
+  bool previous_is_intersecting_;
+};
+
+}  // namespace layout
+}  // namespace cobalt
+
+#endif  // COBALT_LAYOUT_INTERSECTION_OBSERVER_TARGET_H_
diff --git a/src/cobalt/layout/layout.cc b/src/cobalt/layout/layout.cc
index bcaaebf..b298940 100644
--- a/src/cobalt/layout/layout.cc
+++ b/src/cobalt/layout/layout.cc
@@ -93,6 +93,18 @@
         LayoutStatTracker::kStopWatchTypeBoxGeneration,
         base::StopWatch::kAutoStartOn, layout_stat_tracker);
 
+    // If the implicit root is a root for any observers, the initial containing
+    // block should reference the corresponding IntersectionObserverRoots.
+    dom::HTMLElement* html_element =
+        document->document_element()->AsHTMLElement();
+    BoxIntersectionObserverModule::IntersectionObserverRootVector roots =
+        html_element->GetLayoutIntersectionObserverRoots();
+    BoxIntersectionObserverModule::IntersectionObserverTargetVector targets =
+        html_element->GetLayoutIntersectionObserverTargets();
+    (*initial_containing_block)
+        ->AddIntersectionObserverRootsAndTargets(std::move(roots),
+                                                 std::move(targets));
+
     ScopedParagraph scoped_paragraph(
         new Paragraph(locale, (*initial_containing_block)->base_direction(),
                       Paragraph::DirectionalEmbeddingStack(),
diff --git a/src/cobalt/layout/layout.gyp b/src/cobalt/layout/layout.gyp
index 30b1717..884553b 100644
--- a/src/cobalt/layout/layout.gyp
+++ b/src/cobalt/layout/layout.gyp
@@ -38,6 +38,8 @@
         'box.h',
         'box_generator.cc',
         'box_generator.h',
+        'box_intersection_observer_module.cc',
+        'box_intersection_observer_module.h',
         'container_box.cc',
         'container_box.h',
         'flex_container_box.cc',
@@ -61,6 +63,9 @@
         'inline_level_replaced_box.h',
         'insets_layout_unit.cc',
         'insets_layout_unit.h',
+        'intersection_observer_root.h',
+        'intersection_observer_target.cc',
+        'intersection_observer_target.h',
         'layout.cc',
         'layout.h',
         'layout_unit.h',
diff --git a/src/cobalt/layout/layout_boxes.cc b/src/cobalt/layout/layout_boxes.cc
index b459474..da333da 100644
--- a/src/cobalt/layout/layout_boxes.cc
+++ b/src/cobalt/layout/layout_boxes.cc
@@ -89,11 +89,6 @@
   return GetBoundingBorderRectangle().height();
 }
 
-math::Vector2dF LayoutBoxes::GetBorderEdgeOffsetFromContainingBlock() const {
-  DCHECK(!boxes_.empty());
-  return boxes_.front()->GetBorderBoxOffsetFromContainingBlock();
-}
-
 float LayoutBoxes::GetBorderLeftWidth() const {
   DCHECK(!boxes_.empty());
   return boxes_.front()->border_left_width().toFloat();
@@ -130,33 +125,6 @@
   return boxes_.front()->GetPaddingBoxHeight().toFloat();
 }
 
-math::Vector2dF LayoutBoxes::GetPaddingEdgeOffsetFromContainingBlock() const {
-  DCHECK(!boxes_.empty());
-  return boxes_.front()->GetPaddingBoxOffsetFromContainingBlock();
-}
-
-math::Vector2dF LayoutBoxes::GetContentEdgeOffset() const {
-  DCHECK(!boxes_.empty());
-  return boxes_.front()->GetContentBoxOffsetFromRoot(
-      false /*transform_forms_root*/);
-}
-
-float LayoutBoxes::GetContentEdgeWidth() const {
-  DCHECK(!boxes_.empty());
-  return boxes_.front()->width().toFloat();
-}
-
-float LayoutBoxes::GetContentEdgeHeight() const {
-  DCHECK(!boxes_.empty());
-  return boxes_.front()->height().toFloat();
-}
-
-math::Vector2dF LayoutBoxes::GetContentEdgeOffsetFromContainingBlock() const {
-  DCHECK(!boxes_.empty());
-  return boxes_.front()->GetContentBoxOffsetFromContainingBlockContentBox(
-      boxes_.front()->GetContainingBlock());
-}
-
 math::RectF LayoutBoxes::GetScrollArea(dom::Directionality dir) const {
   // https://www.w3.org/TR/cssom-view-1/#scrolling-area
   // For rightward and downward:
diff --git a/src/cobalt/layout/layout_boxes.h b/src/cobalt/layout/layout_boxes.h
index 81114a1..0e838c0 100644
--- a/src/cobalt/layout/layout_boxes.h
+++ b/src/cobalt/layout/layout_boxes.h
@@ -45,7 +45,6 @@
   float GetBorderEdgeTop() const override;
   float GetBorderEdgeWidth() const override;
   float GetBorderEdgeHeight() const override;
-  math::Vector2dF GetBorderEdgeOffsetFromContainingBlock() const override;
 
   float GetBorderLeftWidth() const override;
   float GetBorderTopWidth() const override;
@@ -56,12 +55,6 @@
   math::Vector2dF GetPaddingEdgeOffset() const override;
   float GetPaddingEdgeWidth() const override;
   float GetPaddingEdgeHeight() const override;
-  math::Vector2dF GetPaddingEdgeOffsetFromContainingBlock() const override;
-
-  math::Vector2dF GetContentEdgeOffset() const override;
-  float GetContentEdgeWidth() const override;
-  float GetContentEdgeHeight() const override;
-  math::Vector2dF GetContentEdgeOffsetFromContainingBlock() const override;
 
   math::RectF GetScrollArea(dom::Directionality dir) const override;
 
diff --git a/src/cobalt/layout/layout_manager.cc b/src/cobalt/layout/layout_manager.cc
index c571f16..a913178 100644
--- a/src/cobalt/layout/layout_manager.cc
+++ b/src/cobalt/layout/layout_manager.cc
@@ -428,11 +428,6 @@
       is_render_tree_pending_ = false;
     }
 
-    // Run the Update Intersection Observation Steps
-    // https://www.w3.org/TR/intersection-observer/#update-intersection-observations-algo
-    document->intersection_observer_task_manager()
-        ->UpdateIntersectionObservations();
-
     TRACE_EVENT_END0("cobalt::layout", kBenchmarkStatLayout);
   }
 
diff --git a/src/cobalt/layout/used_style.cc b/src/cobalt/layout/used_style.cc
index a38110e..7a811f7 100644
--- a/src/cobalt/layout/used_style.cc
+++ b/src/cobalt/layout/used_style.cc
@@ -23,7 +23,6 @@
 
 #include "cobalt/base/polymorphic_downcast.h"
 #include "cobalt/cssom/absolute_url_value.h"
-#include "cobalt/cssom/calc_value.h"
 #include "cobalt/cssom/font_style_value.h"
 #include "cobalt/cssom/font_weight_value.h"
 #include "cobalt/cssom/keyword_value.h"
@@ -38,7 +37,6 @@
 #include "cobalt/cssom/string_value.h"
 #include "cobalt/cssom/transform_function_visitor.h"
 #include "cobalt/cssom/translate_function.h"
-#include "cobalt/cssom/used_style.h"
 #include "cobalt/loader/mesh/mesh_cache.h"
 #include "cobalt/math/transform_2d.h"
 #include "cobalt/render_tree/animations/animate_node.h"
@@ -53,8 +51,6 @@
 
 namespace {
 
-typedef cssom::UsedLengthValueProvider<LayoutUnit> UsedLengthValueProvider;
-
 struct BackgroundImageTransformData {
   BackgroundImageTransformData(
       const math::SizeF& image_node_size,
diff --git a/src/cobalt/layout/used_style.h b/src/cobalt/layout/used_style.h
index 07c543e..6aede9f 100644
--- a/src/cobalt/layout/used_style.h
+++ b/src/cobalt/layout/used_style.h
@@ -15,6 +15,7 @@
 #ifndef COBALT_LAYOUT_USED_STYLE_H_
 #define COBALT_LAYOUT_USED_STYLE_H_
 
+#include "cobalt/cssom/calc_value.h"
 #include "cobalt/cssom/css_computed_style_data.h"
 #include "cobalt/cssom/keyword_value.h"
 #include "cobalt/cssom/linear_gradient_value.h"
@@ -282,6 +283,50 @@
   DISALLOW_COPY_AND_ASSIGN(UsedBorderRadiusProvider);
 };
 
+class UsedLengthValueProvider : public cssom::NotReachedPropertyValueVisitor {
+ public:
+  explicit UsedLengthValueProvider(LayoutUnit percentage_base,
+                                   bool calc_permitted = false)
+      : percentage_base_(percentage_base), calc_permitted_(calc_permitted) {}
+
+  void VisitLength(cssom::LengthValue* length) override {
+    depends_on_containing_block_ = false;
+
+    DCHECK_EQ(cssom::kPixelsUnit, length->unit());
+    used_length_ = LayoutUnit(length->value());
+  }
+
+  void VisitPercentage(cssom::PercentageValue* percentage) override {
+    depends_on_containing_block_ = true;
+    used_length_ = percentage->value() * percentage_base_;
+  }
+
+  void VisitCalc(cssom::CalcValue* calc) override {
+    if (!calc_permitted_) {
+      NOTREACHED();
+    }
+    depends_on_containing_block_ = true;
+    used_length_ = LayoutUnit(calc->length_value()->value()) +
+                   calc->percentage_value()->value() * percentage_base_;
+  }
+
+  bool depends_on_containing_block() const {
+    return depends_on_containing_block_;
+  }
+  const base::Optional<LayoutUnit>& used_length() const { return used_length_; }
+
+ protected:
+  bool depends_on_containing_block_;
+
+ private:
+  const LayoutUnit percentage_base_;
+  const bool calc_permitted_;
+
+  base::Optional<LayoutUnit> used_length_;
+
+  DISALLOW_COPY_AND_ASSIGN(UsedLengthValueProvider);
+};
+
 class UsedLineHeightProvider : public cssom::NotReachedPropertyValueVisitor {
  public:
   explicit UsedLineHeightProvider(
diff --git a/src/cobalt/layout_tests/testdata/css3-transitions/3-transition-not-started-on-reattach-expected.png b/src/cobalt/layout_tests/testdata/css3-transitions/3-transition-not-started-on-reattach-expected.png
new file mode 100644
index 0000000..e7821e6
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-transitions/3-transition-not-started-on-reattach-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-transitions/3-transition-not-started-on-reattach.html b/src/cobalt/layout_tests/testdata/css3-transitions/3-transition-not-started-on-reattach.html
new file mode 100644
index 0000000..df8f7c0
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-transitions/3-transition-not-started-on-reattach.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<html>
+<!--
+ | Verify that the transition is not started when an element is removed then
+ | reattached to the DOM.
+ -->
+<head>
+  <style>
+    body {
+      background-color: #000000;
+    }
+
+    div {
+      width: 100px;
+      height: 100px;
+      background-color: #FF0000;
+      transition: background-color 5s;
+    }
+  </style>
+  <script>
+    if (window.testRunner) {
+      window.testRunner.waitUntilDone();
+    }
+
+    window.onload = function() {
+      var frame = 0;
+      var e = document.getElementById("animated");
+
+      function requestAnimationFrame(callback) {
+        window.requestAnimationFrame(callback);
+        if (window.testRunner) {
+          window.testRunner.DoNonMeasuredLayout();
+        }
+      }
+
+      function frameCallback() {
+        switch(++frame) {
+          case 1:
+          case 2:
+            requestAnimationFrame(frameCallback);
+            break;
+          case 3:
+            document.body.removeChild(e);
+            requestAnimationFrame(frameCallback);
+            break;
+          case 4:
+            requestAnimationFrame(frameCallback);
+            break;
+          case 5:
+            // Should not trigger a transition; color should change immediately.
+            e.style.backgroundColor = "#0000FF";
+            document.body.appendChild(e);
+            requestAnimationFrame(frameCallback);
+            break;
+          case 6:
+            requestAnimationFrame(frameCallback);
+            break;
+          case 7:
+            if (window.testRunner) {
+              window.testRunner.notifyDone();
+            }
+            break;
+        }
+      }
+
+      // Trigger initial transition.
+      e.style.backgroundColor = "#00FF00";
+      frameCallback();
+    }
+  </script>
+</head>
+<body>
+  <div id="animated"></div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css3-transitions/layout_tests.txt b/src/cobalt/layout_tests/testdata/css3-transitions/layout_tests.txt
index 3aa4d83..9313518 100644
--- a/src/cobalt/layout_tests/testdata/css3-transitions/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/css3-transitions/layout_tests.txt
@@ -6,6 +6,7 @@
 2-transition-start-during-display-none-does-not-transition
 2-transition-start-during-display-none-does-not-transition-from-ancestor
 2-transition-started-on-display-none-pseudoelement-does-not-dcheck
+3-transition-not-started-on-reattach
 5-multiple-transitions-should-fire-in-correct-order
 5-simple-transition
 5-simple-transition-for-pseudoelement
diff --git a/src/cobalt/layout_tests/testdata/intersection-observer/layout_tests.txt b/src/cobalt/layout_tests/testdata/intersection-observer/layout_tests.txt
index 1c4329b..8962d98 100644
--- a/src/cobalt/layout_tests/testdata/intersection-observer/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/intersection-observer/layout_tests.txt
@@ -2,6 +2,7 @@
 element-in-containing-block-chain-has-overflow-clip-without-padding-or-border
 multiple-observers-with-different-roots-and-targets
 no-intersection-when-root-is-not-in-containing-block-chain-of-target
+observers-should-update-when-elements-move
 previous-threshold-index-and-is-intersecting-fields-should-be-updated
 root-has-nonzero-padding-and-border
 root-has-nonzero-padding-and-border-and-overflow-clip
@@ -11,3 +12,4 @@
 target-has-nonzero-padding-and-border
 target-with-nonzero-area-is-edge-adjacent-to-root
 target-with-zero-area-is-edge-adjacent-to-root
+unobserved-targets-do-not-get-included-in-next-update
diff --git a/src/cobalt/layout_tests/testdata/intersection-observer/observers-should-update-when-elements-move-expected.png b/src/cobalt/layout_tests/testdata/intersection-observer/observers-should-update-when-elements-move-expected.png
new file mode 100644
index 0000000..7b242a2
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/intersection-observer/observers-should-update-when-elements-move-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/intersection-observer/observers-should-update-when-elements-move.html b/src/cobalt/layout_tests/testdata/intersection-observer/observers-should-update-when-elements-move.html
new file mode 100644
index 0000000..eb74022
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/intersection-observer/observers-should-update-when-elements-move.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<!--
+ | This test checks that the observer objects get updated when the elements
+ | themselves move around.
+ | The color of the target element is blue if the intersection ratio is above
+ | the threshold, and green if it is below. In this test, the intersection ratio
+ | crosses the threshold when the target element moves; the target element
+ | should initially be blue but end up green.
+ |   https://www.w3.org/TR/intersection-observer/
+ -->
+<html>
+<head>
+  <style>
+    div {
+      position: absolute;
+    }
+    #root {
+      margin: 100px;
+      background-color: red;
+      width: 250px;
+      height: 150px;
+    }
+    #target {
+      width: 150px;
+      height: 50px;
+      top: 50px;
+      left: 50px;
+    }
+  </style>
+</head>
+<body>
+  <div id="root">
+    <div id="target"></div>
+  </div>
+
+  <script>
+    if (window.testRunner) {
+      window.testRunner.waitUntilDone();
+    }
+
+    window.addEventListener("load", function() {
+      var rootElement = document.querySelector('#root');
+      var targetElement = document.querySelector('#target');
+
+      function handleIntersect(entries, observer) {
+        entries.forEach(function(entry) {
+          if (entry.intersectionRatio > 0.5) {
+            entry.target.style.backgroundColor = "blue";
+          } else {
+            entry.target.style.backgroundColor = "green";
+          }
+        });
+      }
+
+      var options = {
+        root: rootElement,
+        rootMargin: "0px",
+        threshold: 0.5
+      };
+
+      var observer = new IntersectionObserver(handleIntersect, options);
+      observer.observe(targetElement);
+
+      if (window.testRunner) {
+        window.testRunner.DoNonMeasuredLayout();
+      }
+
+      // Move the target element so that the intersection ratio now crosses
+      // the threshold value. An intersection observer update should be
+      // triggered and the callback should run.
+      targetElement.style.top = "-30px";
+
+      if (window.testRunner) {
+        window.testRunner.DoNonMeasuredLayout();
+        window.setTimeout(function() { window.testRunner.notifyDone(); }, 0);
+      }
+    });
+  </script>
+
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/intersection-observer/root-has-nonzero-padding-and-border-and-overflow-clip.html b/src/cobalt/layout_tests/testdata/intersection-observer/root-has-nonzero-padding-and-border-and-overflow-clip.html
index d3362a4..2966803 100644
--- a/src/cobalt/layout_tests/testdata/intersection-observer/root-has-nonzero-padding-and-border-and-overflow-clip.html
+++ b/src/cobalt/layout_tests/testdata/intersection-observer/root-has-nonzero-padding-and-border-and-overflow-clip.html
@@ -28,7 +28,7 @@
       position: absolute;
     }
     #target {
-      background-color: rgba(0, 128, 0, 255);
+      background-color: rgba(0, 128, 0, 0);
       width: 200px;
       height: 200px;
     }
diff --git a/src/cobalt/layout_tests/testdata/intersection-observer/root-has-nonzero-root-margin.html b/src/cobalt/layout_tests/testdata/intersection-observer/root-has-nonzero-root-margin.html
index 1617161..12cd034 100644
--- a/src/cobalt/layout_tests/testdata/intersection-observer/root-has-nonzero-root-margin.html
+++ b/src/cobalt/layout_tests/testdata/intersection-observer/root-has-nonzero-root-margin.html
@@ -18,7 +18,7 @@
       height: 150px;
     }
     #target {
-      background-color: rgba(0, 128, 0, 255);
+      background-color: rgba(0, 128, 0, 0);
       width: 150px;
       height: 250px;
       margin: 50px 150px;
diff --git a/src/cobalt/layout_tests/testdata/intersection-observer/unobserved-targets-do-not-get-included-in-next-update-expected.png b/src/cobalt/layout_tests/testdata/intersection-observer/unobserved-targets-do-not-get-included-in-next-update-expected.png
new file mode 100644
index 0000000..1015b8b
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/intersection-observer/unobserved-targets-do-not-get-included-in-next-update-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/intersection-observer/unobserved-targets-do-not-get-included-in-next-update.html b/src/cobalt/layout_tests/testdata/intersection-observer/unobserved-targets-do-not-get-included-in-next-update.html
new file mode 100644
index 0000000..e23d332
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/intersection-observer/unobserved-targets-do-not-get-included-in-next-update.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<!--
+ | This test checks that the observers no longer check the intersections
+ | for targets that have been unobserved.
+ | Observed targets become green if they intersect with the root (i.e. are
+ | visible on the screen). Unobserved targets do not change color.
+ |   https://www.w3.org/TR/intersection-observer/
+ -->
+<html>
+<head>
+  <style>
+    div {
+      background-color: yellow;
+      width: 50px;
+      height: 100px;
+      margin: 25px;
+      display: inline-block;
+    }
+  </style>
+</head>
+<body>
+  <div id="firsttarget"></div>
+  <div id="secondtarget"></div>
+  <div id="thirdtarget"></div>
+  <div id="fourthtarget"></div>
+  <div id="fifthtarget"></div>
+
+  <script>
+    if (window.testRunner) {
+      window.testRunner.waitUntilDone();
+    }
+
+    window.addEventListener("load", function() {
+
+      function handleIntersect(entries, observer) {
+        entries.forEach(function(entry) {
+          if (entry.isIntersecting) {
+            entry.target.style.backgroundColor = "green";
+          }
+        });
+      }
+
+      function createObserverAndUnobserveTargets() {
+        var firstTargetElement = document.querySelector('#firsttarget');
+        var secondTargetElement = document.querySelector('#secondtarget');
+        var thirdTargetElement = document.querySelector('#thirdtarget');
+        var fourthTargetElement = document.querySelector('#fourthtarget');
+        var fifthTargetElement = document.querySelector('#fifthtarget');
+
+        var options = {
+          root: null,
+          rootMargin: "0px",
+          threshold: 0.0
+        };
+
+        // Create observer and observe elements.
+        var observer = new IntersectionObserver(handleIntersect, options);
+        observer.observe(firstTargetElement);
+        observer.observe(secondTargetElement);
+        observer.observe(thirdTargetElement);
+        observer.observe(fourthTargetElement);
+        observer.observe(fifthTargetElement);
+
+        // Unobserve some of these targets. When the observer checks for
+        // intersections, these elements should not be included.
+        observer.unobserve(secondTargetElement);
+        observer.unobserve(fourthTargetElement);
+        observer.unobserve(fifthTargetElement);
+      }
+
+      createObserverAndUnobserveTargets();
+
+      if (window.testRunner) {
+        window.testRunner.DoNonMeasuredLayout();
+        window.setTimeout(function() { window.testRunner.notifyDone(); }, 0);
+      }
+    });
+  </script>
+
+</body>
+</html>
diff --git a/src/cobalt/network/url_request_context.cc b/src/cobalt/network/url_request_context.cc
index 5c20925..850eade 100644
--- a/src/cobalt/network/url_request_context.cc
+++ b/src/cobalt/network/url_request_context.cc
@@ -153,9 +153,6 @@
 
   storage_.set_job_factory(
       std::unique_ptr<net::URLRequestJobFactory>(job_factory));
-  if (!net::GetGlobalCertNetFetcher()) {
-    net::SetGlobalCertNetFetcher(net::CreateCertNetFetcher(this));
-  }
 }
 
 URLRequestContext::~URLRequestContext() {}
diff --git a/src/cobalt/site/docs/development/setup-raspi.md b/src/cobalt/site/docs/development/setup-raspi.md
index da7cc94..50faeb9 100644
--- a/src/cobalt/site/docs/development/setup-raspi.md
+++ b/src/cobalt/site/docs/development/setup-raspi.md
@@ -23,6 +23,18 @@
 Raspbian.
 </aside>
 
+Cobalt assumes the Raspberry Pi is configured to use non-default thread
+schedulers and priorities. Ensure that **/etc/security/limits.conf** sets
+**rtprio** and **nice** limits for the user. For example, if the user is **pi**,
+then limits.conf should have the following lines:
+
+```
+@pi hard rtprio 99
+@pi soft rtprio 99
+@pi hard nice -20
+@pi soft nice -20
+```
+
 The following commands update the package configuration on your Raspberry Pi
 so that Cobalt can run properly:
 
@@ -63,7 +75,7 @@
     1.  Run `mkdir -p $RASPI_HOME/sysroot`
     1.  Run:
 
-        ``` 
+        ```
         rsync -avzh --safe-links \
               --delete-after pi@$RASPI_ADDR:/{opt,lib,usr} \
               --exclude="lib/firmware" --exclude="lib/modules" \
diff --git a/src/cobalt/speech/google_speech_service.cc b/src/cobalt/speech/google_speech_service.cc
index f3dec39..550b541 100644
--- a/src/cobalt/speech/google_speech_service.cc
+++ b/src/cobalt/speech/google_speech_service.cc
@@ -35,7 +35,6 @@
 #include "cobalt/base/language.h"
 #include "cobalt/loader/fetcher_factory.h"
 #include "cobalt/network/network_module.h"
-#include "cobalt/speech/google_streaming_api.pb.h"
 #include "cobalt/speech/microphone.h"
 #include "cobalt/speech/speech_configuration.h"
 #include "cobalt/speech/speech_recognition_error.h"
@@ -85,7 +84,9 @@
 }
 
 SpeechRecognitionResultList::SpeechRecognitionResults
-ProcessProtoSuccessResults(const proto::SpeechRecognitionEvent& event) {
+ProcessProtoSuccessResults(proto::SpeechRecognitionEvent event) {
+  // This method handles wrappables and should run on the MainWebModule thread.
+
   DCHECK_EQ(event.status(), proto::SpeechRecognitionEvent::STATUS_SUCCESS);
 
   SpeechRecognitionResultList::SpeechRecognitionResults results;
@@ -117,8 +118,9 @@
 
 // TODO: Feed error messages when creating SpeechRecognitionError.
 void ProcessAndFireErrorEvent(
-    const proto::SpeechRecognitionEvent& event,
-    const GoogleSpeechService::EventCallback& event_callback) {
+    proto::SpeechRecognitionEvent event,
+    GoogleSpeechService::EventCallback event_callback) {
+  // This method handles wrappables and should run on the MainWebModule thread.
   scoped_refptr<dom::Event> error_event;
   switch (event.status()) {
     case proto::SpeechRecognitionEvent::STATUS_SUCCESS:
@@ -162,6 +164,12 @@
   event_callback.Run(error_event);
 }
 
+void HandleNetworkResponseFailure(
+    const GoogleSpeechService::EventCallback& event_callback) {
+  event_callback.Run(new SpeechRecognitionError(
+      kSpeechRecognitionErrorCodeNetwork, "Network response failure."));
+}
+
 bool IsResponseCodeSuccess(int response_code) {
   // NetFetcher only considers success to be if the network request
   // was successful *and* we get a 2xx response back.
@@ -181,7 +189,11 @@
       started_(false),
       event_callback_(event_callback),
       fetcher_creator_(fetcher_creator),
-      thread_("speech_recognizer") {
+      thread_("speech_recognizer"),
+      ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)),
+      ALLOW_THIS_IN_INITIALIZER_LIST(
+          weak_this_(weak_ptr_factory_.GetWeakPtr())),
+      wrappables_task_runner_(base::MessageLoop::current()->task_runner()) {
   thread_.StartWithOptions(
       base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
 }
@@ -240,14 +252,20 @@
         }
 
         if (event.status() == proto::SpeechRecognitionEvent::STATUS_SUCCESS) {
-          ProcessAndFireSuccessEvent(ProcessProtoSuccessResults(event));
+          wrappables_task_runner_->PostTask(
+              FROM_HERE,
+              base::Bind(&GoogleSpeechService::ProcessAndFireSuccessEvent,
+                         weak_this_, event));
         } else {
-          ProcessAndFireErrorEvent(event, event_callback_);
+          wrappables_task_runner_->PostTask(
+              FROM_HERE,
+              base::Bind(&ProcessAndFireErrorEvent, event, event_callback_));
         }
       }
     } else {
-      event_callback_.Run(new SpeechRecognitionError(
-          kSpeechRecognitionErrorCodeNetwork, "Network response failure."));
+      wrappables_task_runner_->PostTask(
+          FROM_HERE,
+          base::Bind(&HandleNetworkResponseFailure, event_callback_));
     }
   }
 }
@@ -358,12 +376,19 @@
   downstream_fetcher_.reset();
   encoder_.reset();
 
-  // Clear the final results.
-  final_results_.clear();
+  wrappables_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&GoogleSpeechService::ClearFinalResults, weak_this_));
   // Clear any remaining audio data.
   chunked_byte_buffer_.Clear();
 }
 
+void GoogleSpeechService::ClearFinalResults() {
+  // This method handles wrappables and should run on the MainWebModule thread.
+  DCHECK(wrappables_task_runner_->BelongsToCurrentThread());
+  final_results_.clear();
+}
+
 void GoogleSpeechService::UploadAudioDataInternal(
     std::unique_ptr<ShellAudioBus> audio_bus, bool is_last_chunk) {
   DCHECK_EQ(thread_.message_loop(), base::MessageLoop::current());
@@ -384,8 +409,11 @@
 }
 
 void GoogleSpeechService::ProcessAndFireSuccessEvent(
-    const SpeechRecognitionResults& new_results) {
-  DCHECK_EQ(thread_.message_loop(), base::MessageLoop::current());
+    proto::SpeechRecognitionEvent event) {
+  // This method handles wrappables and should run on the MainWebModule thread.
+  DCHECK(wrappables_task_runner_->BelongsToCurrentThread());
+
+  SpeechRecognitionResults new_results = ProcessProtoSuccessResults(event);
 
   SpeechRecognitionResults success_results;
   size_t total_size = final_results_.size() + new_results.size();
diff --git a/src/cobalt/speech/google_speech_service.h b/src/cobalt/speech/google_speech_service.h
index b6779cb..20179c6 100644
--- a/src/cobalt/speech/google_speech_service.h
+++ b/src/cobalt/speech/google_speech_service.h
@@ -24,6 +24,7 @@
 #include "cobalt/media/base/shell_audio_bus.h"
 #include "cobalt/network/network_module.h"
 #include "cobalt/speech/audio_encoder_flac.h"
+#include "cobalt/speech/google_streaming_api.pb.h"
 #include "cobalt/speech/speech_recognition_config.h"
 #include "cobalt/speech/speech_recognition_event.h"
 #include "content/browser/speech/chunked_byte_buffer.h"
@@ -78,9 +79,12 @@
  private:
   void StartInternal(const SpeechRecognitionConfig& config, int sample_rate);
   void StopInternal();
+  // This method handles wrappables and should run on the MainWebModule thread.
+  void ClearFinalResults();
   void UploadAudioDataInternal(std::unique_ptr<ShellAudioBus> audio_bus,
                                bool is_last_chunk);
-  void ProcessAndFireSuccessEvent(const SpeechRecognitionResults& new_results);
+  // This method handles wrappables, and so it must run on the MainWebModule.
+  void ProcessAndFireSuccessEvent(proto::SpeechRecognitionEvent event);
 
   // This is used for creating fetchers.
   network::NetworkModule* network_module_;
@@ -105,6 +109,11 @@
   base::Thread thread_;
   // Stores fetched response.
   CobaltURLFetcherStringWriter* download_data_writer_ = nullptr;
+
+  // Use a task runner to deal with all wrappables.
+  base::WeakPtrFactory<GoogleSpeechService> weak_ptr_factory_;
+  base::WeakPtr<GoogleSpeechService> weak_this_;
+  scoped_refptr<base::SingleThreadTaskRunner> const wrappables_task_runner_;
 };
 
 }  // namespace speech
diff --git a/src/net/cert/cert_verify_proc.cc b/src/net/cert/cert_verify_proc.cc
index 7f30d04..5b23772 100644
--- a/src/net/cert/cert_verify_proc.cc
+++ b/src/net/cert/cert_verify_proc.cc
@@ -48,10 +48,8 @@
 #elif defined(OS_WIN)
 #include "base/win/windows_version.h"
 #include "net/cert/cert_verify_proc_win.h"
-#elif defined(OS_FUCHSIA)
+#elif defined(OS_FUCHSIA) || defined(STARBOARD)
 #include "net/cert/cert_verify_proc_builtin.h"
-#elif defined(STARBOARD)
-#include "net/cert/cert_verify_proc_openssl.h"
 #else
 #error Implement certificate verification.
 #endif
@@ -480,10 +478,8 @@
   return new CertVerifyProcMac();
 #elif defined(OS_WIN)
   return new CertVerifyProcWin();
-#elif defined(OS_FUCHSIA)
+#elif defined(OS_FUCHSIA) || defined(STARBOARD)
   return CreateCertVerifyProcBuiltin();
-#elif defined(STARBOARD)
-  return new CertVerifyProcOpenSSL();
 #else
 #error Unsupported platform
 #endif
diff --git a/src/net/cert/cert_verify_proc_builtin.cc b/src/net/cert/cert_verify_proc_builtin.cc
index 9345b47..5129c90 100644
--- a/src/net/cert/cert_verify_proc_builtin.cc
+++ b/src/net/cert/cert_verify_proc_builtin.cc
@@ -454,6 +454,8 @@
   // |input_cert|.
   path_builder.AddCertIssuerSource(intermediates);
 
+// Cobalt does not need AIA fetching.
+#if !defined(STARBOARD)
   // Allow the path builder to discover intermediates through AIA fetching.
   std::unique_ptr<CertIssuerSourceAia> aia_cert_issuer_source;
   if (net_fetcher) {
@@ -462,6 +464,7 @@
   } else {
     LOG(ERROR) << "No net_fetcher for performing AIA chasing.";
   }
+#endif
 
   path_builder.Run();
 }
@@ -481,6 +484,13 @@
     // builder should always return some partial path (even if just containing
     // the target), then there is a CertErrors to test.
     verify_result->cert_status |= CERT_STATUS_AUTHORITY_INVALID;
+#if defined(STARBOARD)
+    // Cobalt only trusts root certificates that come with the binary,
+    // If the code reaches this point, it usually means the root certificate
+    // of the chain is not found in the content directory of trusted
+    // certificates.
+    DLOG(ERROR) << "Certificate Authority invalid!";
+#endif
     return ERR_CERT_AUTHORITY_INVALID;
   }
 
@@ -597,7 +607,13 @@
   }
 
   // Get the global dependencies.
+#if defined(STARBOARD)
+  // cert net fetcher is used for fetching AIA certs which Cobalt should not
+  // need.
+  CertNetFetcher* net_fetcher = nullptr;
+#else
   CertNetFetcher* net_fetcher = GetGlobalCertNetFetcher();
+#endif
   const EVRootCAMetadata* ev_metadata = EVRootCAMetadata::GetInstance();
 
   // This boolean tracks whether online revocation checking was performed for
diff --git a/src/net/cert/cert_verify_proc_openssl.cc b/src/net/cert/cert_verify_proc_openssl.cc
index a2520e7..e69de29 100644
--- a/src/net/cert/cert_verify_proc_openssl.cc
+++ b/src/net/cert/cert_verify_proc_openssl.cc
@@ -1,378 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/cert/cert_verify_proc_openssl.h"
-
-#include "third_party/boringssl/src/include/openssl/mem.h"
-#include "third_party/boringssl/src/include/openssl/ssl.h"
-#include "third_party/boringssl/src/include/openssl/x509_vfy.h"
-#include "third_party/boringssl/src/include/openssl/x509v3.h"
-
-#include <string>
-#include <vector>
-
-#include "base/base_paths.h"
-#include "base/logging.h"
-#include "base/memory/singleton.h"
-#include "base/path_service.h"
-#include "base/sha1.h"
-#include "crypto/openssl_util.h"
-#include "crypto/sha2.h"
-#include "net/base/net_errors.h"
-#include "net/cert/asn1_util.h"
-#include "net/cert/cert_status_flags.h"
-#include "net/cert/cert_verifier.h"
-#include "net/cert/cert_verify_result.h"
-#include "net/cert/x509_certificate.h"
-#include "net/cert/x509_util.h"
-
-namespace net {
-
-namespace {
-
-// Maps X509_STORE_CTX_get_error() return values to our cert status flags.
-CertStatus MapCertErrorToCertStatus(int err) {
-  switch (err) {
-    case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
-      return CERT_STATUS_COMMON_NAME_INVALID;
-    case X509_V_ERR_CERT_NOT_YET_VALID:
-    case X509_V_ERR_CERT_HAS_EXPIRED:
-    case X509_V_ERR_CRL_NOT_YET_VALID:
-    case X509_V_ERR_CRL_HAS_EXPIRED:
-    case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
-    case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
-    case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
-    case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
-      return CERT_STATUS_DATE_INVALID;
-    case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
-    case X509_V_ERR_UNABLE_TO_GET_CRL:
-    case X509_V_ERR_INVALID_CA:
-    case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER:
-    case X509_V_ERR_INVALID_NON_CA:
-    case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
-    case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
-    case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
-      return CERT_STATUS_AUTHORITY_INVALID;
-#if 0
-// TODO(bulach): what should we map to these status?
-      return CERT_STATUS_NO_REVOCATION_MECHANISM;
-      return CERT_STATUS_UNABLE_TO_CHECK_REVOCATION;
-#endif
-    case X509_V_ERR_CERT_REVOKED:
-      return CERT_STATUS_REVOKED;
-    // All these status are mapped to CERT_STATUS_INVALID.
-    case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
-    case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
-    case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
-    case X509_V_ERR_CERT_SIGNATURE_FAILURE:
-    case X509_V_ERR_CRL_SIGNATURE_FAILURE:
-    case X509_V_ERR_OUT_OF_MEM:
-    case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
-    case X509_V_ERR_CERT_CHAIN_TOO_LONG:
-    case X509_V_ERR_PATH_LENGTH_EXCEEDED:
-    case X509_V_ERR_INVALID_PURPOSE:
-    case X509_V_ERR_CERT_UNTRUSTED:
-    case X509_V_ERR_CERT_REJECTED:
-    case X509_V_ERR_AKID_SKID_MISMATCH:
-    case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
-    case X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION:
-    case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
-    case X509_V_ERR_KEYUSAGE_NO_CRL_SIGN:
-    case X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION:
-    case X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED:
-    case X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE:
-    case X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED:
-    case X509_V_ERR_INVALID_EXTENSION:
-    case X509_V_ERR_INVALID_POLICY_EXTENSION:
-    case X509_V_ERR_NO_EXPLICIT_POLICY:
-    case X509_V_ERR_UNNESTED_RESOURCE:
-    case X509_V_ERR_APPLICATION_VERIFICATION:
-      return CERT_STATUS_INVALID;
-    default:
-      NOTREACHED() << "Invalid X509 err " << err;
-      return CERT_STATUS_INVALID;
-  }
-}
-
-template <typename T, void (*destructor)(T*)>
-class ScopedOpenSSL {
- public:
-  ScopedOpenSSL() : ptr_(NULL) {}
-  explicit ScopedOpenSSL(T* ptr) : ptr_(ptr) {}
-  ~ScopedOpenSSL() { reset(NULL); }
-
-  T* get() const { return ptr_; }
-  void reset(T* ptr) {
-    if (ptr != ptr_) {
-      if (ptr_)
-        (*destructor)(ptr_);
-      ptr_ = ptr;
-    }
-  }
-
- private:
-  T* ptr_;
-
-  DISALLOW_COPY_AND_ASSIGN(ScopedOpenSSL);
-};
-
-// x509_to_buffer returns a |CRYPTO_BUFFER| that contains the serialised
-// contents of |x509|.
-static bssl::UniquePtr<CRYPTO_BUFFER> x509_to_buffer(X509* x509) {
-  uint8_t* buf = NULL;
-  int cert_len = i2d_X509(x509, &buf);
-  if (cert_len <= 0) {
-    return 0;
-  }
-
-  bssl::UniquePtr<CRYPTO_BUFFER> buffer(CRYPTO_BUFFER_new(buf, cert_len, NULL));
-  OPENSSL_free(buf);
-
-  return buffer;
-}
-
-struct DERCache {
-  unsigned char* data;
-  int data_length;
-};
-
-void DERCache_free(void* parent,
-                   void* ptr,
-                   CRYPTO_EX_DATA* ad,
-                   int idx,
-                   long argl,
-                   void* argp) {
-  DERCache* der_cache = static_cast<DERCache*>(ptr);
-  if (!der_cache)
-    return;
-  if (der_cache->data)
-    OPENSSL_free(der_cache->data);
-  OPENSSL_free(der_cache);
-}
-
-class X509InitSingleton {
- public:
-  static X509InitSingleton* GetInstance() {
-    // We allow the X509 store to leak, because it is used from a non-joinable
-    // worker that is not stopped on shutdown, hence may still be using
-    // OpenSSL library after the AtExit runner has completed.
-    return base::Singleton<X509InitSingleton, base::LeakySingletonTraits<
-                                                  X509InitSingleton>>::get();
-  }
-  int der_cache_ex_index() const { return der_cache_ex_index_; }
-  X509_STORE* store() const { return store_.get(); }
-
-  X509InitSingleton() {
-    crypto::EnsureOpenSSLInit();
-    der_cache_ex_index_ = X509_get_ex_new_index(0, 0, 0, 0, DERCache_free);
-    DCHECK_NE(der_cache_ex_index_, -1);
-    ResetCertStore();
-  }
-
-  void ResetCertStore() {
-    store_.reset(X509_STORE_new());
-    DCHECK(store_.get());
-    // Configure the SSL certs dir. We don't implement getenv() or hardcode
-    // the SSL_CERTS_DIR, which are the default methods OpenSSL uses to find
-    // the certs path.
-    base::FilePath cert_path;
-    base::PathService::Get(base::DIR_EXE, &cert_path);
-    cert_path = cert_path.Append("ssl").Append("certs");
-    X509_STORE_load_locations(store_.get(), NULL, cert_path.value().c_str());
-  }
-
- private:
-  int der_cache_ex_index_;
-  ScopedOpenSSL<X509_STORE, X509_STORE_free> store_;
-
-  DISALLOW_COPY_AND_ASSIGN(X509InitSingleton);
-};
-
-// Takes ownership of |data| (which must have been allocated by OpenSSL).
-DERCache* SetDERCache(X509* cert,
-                      int x509_der_cache_index,
-                      unsigned char* data,
-                      int data_length) {
-  DERCache* internal_cache =
-      static_cast<DERCache*>(OPENSSL_malloc(sizeof(*internal_cache)));
-  if (!internal_cache) {
-    // We took ownership of |data|, so we must free if we can't add it to
-    // |cert|.
-    OPENSSL_free(data);
-    return NULL;
-  }
-  internal_cache->data = data;
-  internal_cache->data_length = data_length;
-  X509_set_ex_data(cert, x509_der_cache_index, internal_cache);
-  return internal_cache;
-}
-
-// Returns true if |der_cache| points to valid data, false otherwise.
-// (note: the DER-encoded data in |der_cache| is owned by |cert|, callers should
-// not free it).
-bool GetDERAndCacheIfNeeded(X509* cert, DERCache* der_cache) {
-  int x509_der_cache_index =
-      X509InitSingleton::GetInstance()->der_cache_ex_index();
-
-  // Re-encoding the DER data via i2d_X509 is an expensive operation, but it's
-  // necessary for comparing two certificates. We re-encode at most once per
-  // certificate and cache the data within the X509 cert using X509_set_ex_data.
-  DERCache* internal_cache =
-      static_cast<DERCache*>(X509_get_ex_data(cert, x509_der_cache_index));
-  if (!internal_cache) {
-    unsigned char* data = NULL;
-    int data_length = i2d_X509(cert, &data);
-    if (data_length <= 0 || !data)
-      return false;
-    internal_cache = SetDERCache(cert, x509_der_cache_index, data, data_length);
-    if (!internal_cache)
-      return false;
-  }
-  *der_cache = *internal_cache;
-  return true;
-}
-
-// sk_X509_free is a function-style macro, so can't be used as a template
-// param directly.
-void sk_X509_free_fn(STACK_OF(X509) * st) {
-  sk_X509_pop_free(st, X509_free);
-}
-
-void GetCertChainInfo(X509_STORE_CTX* store_ctx,
-                      CertVerifyResult* verify_result) {
-  STACK_OF(X509)* chain = X509_STORE_CTX_get_chain(store_ctx);
-  bssl::UniquePtr<CRYPTO_BUFFER> verified_cert = NULL;
-  std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> verified_chain;
-  for (int i = 0; i < sk_X509_num(chain); ++i) {
-    X509* cert = sk_X509_value(chain, i);
-    auto DER_buffer = x509_to_buffer(cert);
-    auto x509_cert =
-        x509_util::CreateCryptoBuffer(CRYPTO_BUFFER_data(DER_buffer.get()),
-                                      CRYPTO_BUFFER_len(DER_buffer.get()));
-    if (i == 0) {
-      verified_cert = std::move(x509_cert);
-    } else {
-      verified_chain.push_back(std::move(x509_cert));
-    }
-
-    // Only check the algorithm status for certificates that are not in the
-    // trust store.
-    if (i < store_ctx->last_untrusted) {
-      int sig_alg = OBJ_obj2nid(cert->sig_alg->algorithm);
-      if (sig_alg == NID_md2WithRSAEncryption) {
-        verify_result->has_md2 = true;
-      } else if (sig_alg == NID_md4WithRSAEncryption) {
-        verify_result->has_md4 = true;
-      } else if (sig_alg == NID_md5WithRSAEncryption) {
-        verify_result->has_md5 = true;
-      } else if (sig_alg == NID_sha1WithRSAEncryption) {
-        verify_result->has_sha1 = true;
-      }
-    }
-  }
-
-  if (verified_cert) {
-    verify_result->verified_cert = X509Certificate::CreateFromBuffer(
-        std::move(verified_cert), std::move(verified_chain));
-  }
-}
-
-void AppendPublicKeyHashes(X509_STORE_CTX* store_ctx, HashValueVector* hashes) {
-  STACK_OF(X509)* chain = X509_STORE_CTX_get_chain(store_ctx);
-  for (int i = 0; i < sk_X509_num(chain); ++i) {
-    X509* cert = sk_X509_value(chain, i);
-
-    DERCache der_cache;
-    if (!GetDERAndCacheIfNeeded(cert, &der_cache))
-      continue;
-    std::string der_data;
-    der_data.assign(reinterpret_cast<const char*>(der_cache.data),
-                    der_cache.data_length);
-
-    base::StringPiece der_bytes(der_data);
-    base::StringPiece spki_bytes;
-    if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki_bytes))
-      continue;
-
-    HashValue sha256(HASH_VALUE_SHA256);
-    crypto::SHA256HashString(spki_bytes, sha256.data(), crypto::kSHA256Length);
-    hashes->push_back(sha256);
-  }
-}
-
-}  // namespace
-
-CertVerifyProcOpenSSL::CertVerifyProcOpenSSL() {}
-
-CertVerifyProcOpenSSL::~CertVerifyProcOpenSSL() {}
-
-int CertVerifyProcOpenSSL::VerifyInternal(
-    X509Certificate* cert,
-    const std::string& hostname,
-    const std::string& /*ocsp_response*/,
-    int flags,
-    CRLSet* crl_set,
-    const CertificateList& /*additional_trust_anchors*/,
-    CertVerifyResult* verify_result) {
-  crypto::EnsureOpenSSLInit();
-
-  if (!cert->VerifyNameMatch(hostname))
-    verify_result->cert_status |= CERT_STATUS_COMMON_NAME_INVALID;
-
-  ScopedOpenSSL<X509_STORE_CTX, X509_STORE_CTX_free> ctx(X509_STORE_CTX_new());
-
-  ScopedOpenSSL<STACK_OF(X509), sk_X509_free_fn> intermediates(
-      sk_X509_new_null());
-  if (!intermediates.get()) {
-    return ERR_OUT_OF_MEMORY;
-  }
-
-  const auto& cert_current_intermediates = cert->intermediate_buffers();
-  for (auto it = cert_current_intermediates.begin();
-       it != cert_current_intermediates.end(); ++it) {
-    X509* x509_intermediate = X509_parse_from_buffer(it->get());
-    if (!sk_X509_push(intermediates.get(), x509_intermediate)) {
-      return ERR_OUT_OF_MEMORY;
-    }
-  }
-
-  ScopedOpenSSL<X509, X509_free> cert_in_x509;
-  cert_in_x509.reset(X509_parse_from_buffer(cert->cert_buffer()));
-  if (X509_STORE_CTX_init(ctx.get(), X509InitSingleton::GetInstance()->store(),
-                          cert_in_x509.get(), intermediates.get()) != 1) {
-    NOTREACHED();
-    return ERR_FAILED;
-  }
-
-  if (X509_verify_cert(ctx.get()) != 1) {
-    int x509_error = X509_STORE_CTX_get_error(ctx.get());
-    // TODO[johnx]: replace this with net's map function.
-    CertStatus cert_status = MapCertErrorToCertStatus(x509_error);
-    LOG(ERROR) << "X509 Verification error "
-               << X509_verify_cert_error_string(x509_error) << " : "
-               << x509_error << " : "
-               << X509_STORE_CTX_get_error_depth(ctx.get()) << " : "
-               << cert_status;
-    verify_result->cert_status |= cert_status;
-  }
-
-  GetCertChainInfo(ctx.get(), verify_result);
-  AppendPublicKeyHashes(ctx.get(), &verify_result->public_key_hashes);
-
-  if (IsCertStatusError(verify_result->cert_status)) {
-    return MapCertStatusToNetError(verify_result->cert_status);
-  }
-
-  // Currently we only ues OpenSSL's default root CA paths, so treat all
-  // correctly verified certs as being from a known root.
-  // TODO(joth): if the motivations described in
-  // http://src.chromium.org/viewvc/chrome?view=rev&revision=80778 become an
-  // issue on OpenSSL builds, we will need to embed a hardcoded list of well
-  // known root CAs, as per the _mac and _win versions.
-  verify_result->is_issued_by_known_root = true;
-
-  return OK;
-}
-
-}  // namespace net
diff --git a/src/net/cert/cert_verify_proc_openssl.h b/src/net/cert/cert_verify_proc_openssl.h
deleted file mode 100644
index 0524db1..0000000
--- a/src/net/cert/cert_verify_proc_openssl.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_BASE_CERT_VERIFY_PROC_OPENSSL_H_
-#define NET_BASE_CERT_VERIFY_PROC_OPENSSL_H_
-
-#include "net/cert/cert_verify_proc.h"
-
-// Note: CertVerifyProcOpenSSL was copied from the old Chromium(m27) to support
-// verifying certs with openSSL which has been used by Cobalt since day 1.
-// This Method has somehow been deprecated by new Chromium(m70).
-// TODO[johnx]: Switch to Chromium's new CertVerifyProcBuiltin soon after the
-//              rebase.
-
-namespace net {
-
-// Performs certificate path construction and validation using OpenSSL.
-class CertVerifyProcOpenSSL : public CertVerifyProc {
- public:
-  CertVerifyProcOpenSSL();
-
- protected:
-  virtual ~CertVerifyProcOpenSSL();
-
- private:
-  virtual int VerifyInternal(X509Certificate* cert,
-                             const std::string& hostname,
-                             const std::string& ocsp_response,
-                             int flags,
-                             CRLSet* crl_set,
-                             const CertificateList& additional_trust_anchors,
-                             CertVerifyResult* verify_result) override;
-  virtual bool SupportsAdditionalTrustAnchors() const override { return false; }
-};
-
-}  // namespace net
-
-#endif  // NET_BASE_CERT_VERIFY_PROC_OPENSSL_H_
diff --git a/src/net/cert/internal/system_trust_store.cc b/src/net/cert/internal/system_trust_store.cc
index c5bb003..d0dbf8a 100644
--- a/src/net/cert/internal/system_trust_store.cc
+++ b/src/net/cert/internal/system_trust_store.cc
@@ -36,6 +36,8 @@
 #include "net/cert/x509_util_mac.h"
 #elif defined(OS_FUCHSIA)
 #include "third_party/boringssl/src/include/openssl/pool.h"
+#elif defined(STARBOARD)
+#include "net/cert/internal/trust_store_in_memory_starboard.h"
 #endif
 
 namespace net {
@@ -218,21 +220,43 @@
 
 #elif defined(STARBOARD)
 
-// Starboard does not currently provide support for system trust store access,
-// so we indicate that it should not be used.
+namespace {
+
+class StarboardSystemCerts {
+ public:
+  StarboardSystemCerts() {}
+
+  TrustStoreInMemoryStarboard* system_trust_store() {
+    return &system_trust_store_;
+  }
+
+ private:
+  TrustStoreInMemoryStarboard system_trust_store_;
+};
+
+base::LazyInstance<StarboardSystemCerts>::Leaky g_root_certs_starboard =
+    LAZY_INSTANCE_INITIALIZER;
+
+}  // namespace
+
+// Starboard does not use system certificate stores but Cobalt ships with a
+// set of trusted CA certificates that can be used for validations.
 class SystemTrustStoreStarboard : public BaseSystemTrustStore {
  public:
   SystemTrustStoreStarboard() {
+    trust_store_.AddTrustStore(
+        g_root_certs_starboard.Get().system_trust_store());
     if (TestRootCerts::HasInstance()) {
       trust_store_.AddTrustStore(
           TestRootCerts::GetInstance()->test_trust_store());
     }
   }
 
-  bool UsesSystemTrustStore() const override { return false; }
+  bool UsesSystemTrustStore() const override { return true; }
 
   bool IsKnownRoot(const ParsedCertificate* trust_anchor) const override {
-    return false;
+    return g_root_certs_starboard.Get().system_trust_store()->Contains(
+        trust_anchor);
   }
 };
 
diff --git a/src/net/cert/internal/trust_store_in_memory_starboard.cc b/src/net/cert/internal/trust_store_in_memory_starboard.cc
new file mode 100644
index 0000000..3d53b20
--- /dev/null
+++ b/src/net/cert/internal/trust_store_in_memory_starboard.cc
@@ -0,0 +1,178 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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.
+
+#include "net/cert/internal/trust_store_in_memory_starboard.h"
+
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "base/time/time.h"
+#include "net/cert/internal/cert_errors.h"
+#include "net/cert/pem_tokenizer.h"
+#include "net/cert/x509_certificate.h"
+#include "net/cert/x509_util.h"
+#include "starboard/directory.h"
+#include "starboard/file.h"
+#include "starboard/string.h"
+#include "third_party/boringssl/src/include/openssl/digest.h"
+#include "third_party/boringssl/src/include/openssl/sha.h"
+#include "third_party/boringssl/src/include/openssl/x509.h"
+
+namespace net {
+
+namespace {
+// PEM encoded DER cert is usually around or less than 2000 bytes long.
+const short kCertBufferSize = 8 * 1024;
+// Each certificate file name is 8 bit hash + ".0" suffix.
+const short kCertFileNameLength = 10;
+const char kCertificateHeader[] = "CERTIFICATE";
+const char kSSLDirName[] = "ssl";
+const char kCertsDirName[] = "certs";
+
+// Essentially an X509_NAME_hash without using X509_NAME. We did not use the
+// boringSSL function directly because net does not store certs with X509
+// struct anymore and converting binary certs to X509_NAME is slightly
+// expensive.
+unsigned long CertNameHash(const void* data, size_t length) {
+  unsigned long ret = 0;
+  unsigned char md[SHA_DIGEST_LENGTH];
+  if (!EVP_Digest(data, length, md, NULL, EVP_sha1(), NULL))
+    return 0;
+
+  ret = (((unsigned long)md[0]) | ((unsigned long)md[1] << 8L) |
+         ((unsigned long)md[2] << 16L) | ((unsigned long)md[3] << 24L)) &
+        0xffffffffL;
+  return ret;
+}
+
+base::FilePath GetCertificateDirPath() {
+  base::FilePath cert_path;
+  base::PathService::Get(base::DIR_EXE, &cert_path);
+  cert_path = cert_path.Append(kSSLDirName).Append(kCertsDirName);
+  return std::move(cert_path);
+}
+
+std::unordered_set<std::string> GetCertNamesOnDisk() {
+  auto sb_certs_directory =
+      SbDirectoryOpen(GetCertificateDirPath().value().c_str(), nullptr);
+  if (!SbDirectoryIsValid(sb_certs_directory)) {
+// Unit tests, for example, do not use production certificates.
+#if defined(STARBOARD_BUILD_TYPE_QA) || defined(STARBOARD_BUILD_TYPE_GOLD)
+    SB_CHECK(false);
+#else
+    DLOG(WARNING) << "ssl/certs directory is not valid, no root certificates"
+                     " will be loaded";
+#endif
+    return std::unordered_set<std::string>();
+  }
+  SbDirectoryEntry dir_entry;
+  std::unordered_set<std::string> trusted_certs_on_disk;
+
+  while (SbDirectoryGetNext(sb_certs_directory, &dir_entry)) {
+    if (SbStringGetLength(dir_entry.name) != kCertFileNameLength) {
+      continue;
+    }
+    trusted_certs_on_disk.emplace(dir_entry.name);
+  }
+  SbDirectoryClose(sb_certs_directory);
+  return std::move(trusted_certs_on_disk);
+}
+}  // namespace
+
+scoped_refptr<ParsedCertificate> TrustStoreInMemoryStarboard::TryLoadCert(
+    const base::StringPiece& cert_name) const {
+  auto hash = CertNameHash(cert_name.data(), cert_name.length());
+  char cert_file_name[256];
+  SbStringFormatF(cert_file_name, 256, "%08lx.%d", hash, 0);
+
+  if (trusted_cert_names_on_disk_.find(cert_file_name) ==
+      trusted_cert_names_on_disk_.end()) {
+    // The requested certificate is not found.
+    return nullptr;
+  }
+
+  SbFileError out_error;
+  char cert_buffer[kCertBufferSize];
+  base::FilePath cert_path = GetCertificateDirPath().Append(cert_file_name);
+  SbFile sb_cert_file =
+      SbFileOpen(cert_path.value().c_str(), kSbFileOpenOnly | kSbFileRead,
+                 nullptr, &out_error);
+  // The file was in certs directory when we iterated the directory at startup,
+  // opening it should not fail.
+  if (!SbFileIsValid(sb_cert_file)) {
+    NOTREACHED() << "ssl/certs/" << cert_path << " failed to open.";
+    return nullptr;
+  }
+  int cert_size = SbFileReadAll(sb_cert_file, cert_buffer, kCertBufferSize);
+  SbFileClose(sb_cert_file);
+  PEMTokenizer pem_tokenizer(base::StringPiece(cert_buffer, cert_size),
+                             {kCertificateHeader});
+  pem_tokenizer.GetNext();
+  std::string decoded(pem_tokenizer.data());
+  DCHECK(!pem_tokenizer.GetNext());
+  bssl::UniquePtr<CRYPTO_BUFFER> crypto_buffer =
+      X509Certificate::CreateCertBufferFromBytes(decoded.data(),
+                                                 decoded.length());
+  DCHECK(crypto_buffer);
+  CertErrors errors;
+  auto parsed = ParsedCertificate::Create(
+      bssl::UpRef(crypto_buffer.get()),
+      x509_util::DefaultParseCertificateOptions(), &errors);
+  CHECK(parsed) << errors.ToDebugString();
+  return parsed;
+}
+
+TrustStoreInMemoryStarboard::TrustStoreInMemoryStarboard()
+    : trusted_cert_names_on_disk_(std::move(GetCertNamesOnDisk())) {}
+
+TrustStoreInMemoryStarboard::~TrustStoreInMemoryStarboard() = default;
+
+void TrustStoreInMemoryStarboard::SyncGetIssuersOf(
+    const ParsedCertificate* cert,
+    ParsedCertificateList* issuers) {
+  DCHECK(issuers);
+  DCHECK(issuers->empty());
+  starboard::ScopedLock scoped_lock(load_mutex_);
+  // Look up the request certificate first in the trust store in memory.
+  underlying_trust_store_.SyncGetIssuersOf(cert, issuers);
+  if (issuers->empty()) {
+    // If the requested certificate is not found, compute certificate hash name
+    // and see if the certificate is stored on disk.
+    auto parsed_cert = TryLoadCert(cert->normalized_issuer().AsStringPiece());
+    if (parsed_cert.get()) {
+      issuers->push_back(parsed_cert);
+      underlying_trust_store_.AddTrustAnchor(parsed_cert);
+    }
+  }
+}
+
+void TrustStoreInMemoryStarboard::GetTrust(
+    const scoped_refptr<ParsedCertificate>& cert,
+    CertificateTrust* trust) const {
+  DCHECK(trust);
+  starboard::ScopedLock scoped_lock(load_mutex_);
+  // Loop up the request certificate first in the trust store in memory.
+  underlying_trust_store_.GetTrust(cert, trust);
+  if (trust->HasUnspecifiedTrust()) {
+    // If the requested certificate is not found, compute certificate hash name
+    // and see if the certificate is stored on disk.
+    auto parsed_cert = TryLoadCert(cert->normalized_subject().AsStringPiece());
+    if (parsed_cert.get()) {
+      *trust = CertificateTrust::ForTrustAnchor();
+      const_cast<TrustStoreInMemoryStarboard*>(this)
+          ->underlying_trust_store_.AddTrustAnchor(parsed_cert);
+    }
+  }
+}
+
+}  // namespace net
diff --git a/src/net/cert/internal/trust_store_in_memory_starboard.h b/src/net/cert/internal/trust_store_in_memory_starboard.h
new file mode 100644
index 0000000..bed671e
--- /dev/null
+++ b/src/net/cert/internal/trust_store_in_memory_starboard.h
@@ -0,0 +1,68 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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 NET_CERT_INTERNAL_TRUST_STORE_IN_MEMORY_STARBOARD_H_
+#define NET_CERT_INTERNAL_TRUST_STORE_IN_MEMORY_STARBOARD_H_
+
+#include <unordered_set>
+
+#include "base/strings/string_piece.h"
+#include "net/cert/internal/trust_store_in_memory.h"
+#include "starboard/common/mutex.h"
+
+namespace net {
+
+// Wrapper around TrustStoreInMemory to lazily load trusted root certificates.
+class NET_EXPORT TrustStoreInMemoryStarboard : public TrustStore {
+ public:
+  TrustStoreInMemoryStarboard();
+  ~TrustStoreInMemoryStarboard() override;
+
+  // TrustStore implementation:
+  void SyncGetIssuersOf(const ParsedCertificate* cert,
+                        ParsedCertificateList* issuers) override;
+  void GetTrust(const scoped_refptr<ParsedCertificate>& cert,
+                CertificateTrust* trust) const override;
+
+  // Returns true if the trust store contains the given ParsedCertificate
+  // (matches by DER).
+  bool Contains(const ParsedCertificate* cert) const {
+    starboard::ScopedLock scoped_lock(load_mutex_);
+    return underlying_trust_store_.Contains(cert);
+  }
+
+ private:
+  TrustStoreInMemoryStarboard(const TrustStoreInMemoryStarboard&) = delete;
+  TrustStoreInMemoryStarboard& operator=(const TrustStoreInMemoryStarboard&) =
+      delete;
+
+  TrustStoreInMemory underlying_trust_store_;
+
+  // Given a certificate's canonical name, try to load this cert from trusted
+  // certs on disk if it is found.
+  scoped_refptr<ParsedCertificate> TryLoadCert(
+      const base::StringPiece& cert_name) const;
+
+  // The memory trust store can be accessed by multiple threads, in Chromium,
+  // the synchronization issue is solved by initializing trust store at startup
+  // and passing constant reference to consumers. Cobalt loads certs lazily and
+  // therefore guards the underlying_trust_store_ with mutex.
+  starboard::Mutex load_mutex_;
+
+  const std::unordered_set<std::string> trusted_cert_names_on_disk_;
+};
+
+}  // namespace net
+
+#endif  // NET_CERT_INTERNAL_TRUST_STORE_IN_MEMORY_STARBOARD_H_
diff --git a/src/net/dns/host_resolver_impl_unittest.cc b/src/net/dns/host_resolver_impl_unittest.cc
index f8afb04..0bdd7b2 100644
--- a/src/net/dns/host_resolver_impl_unittest.cc
+++ b/src/net/dns/host_resolver_impl_unittest.cc
@@ -2625,7 +2625,11 @@
 // https://crbug.com/115051 is fixed.
 
 // Test the retry attempts simulating host resolver proc that takes too long.
+#if defined(STARBOARD)
+TEST_F(HostResolverImplTest, FLAKY_MultipleAttempts) {
+#else
 TEST_F(HostResolverImplTest, MultipleAttempts) {
+#endif
   // Total number of attempts would be 3 and we want the 3rd attempt to resolve
   // the host. First and second attempt will be forced to wait until they get
   // word that a resolution has completed. The 3rd resolution attempt will try
@@ -2636,7 +2640,13 @@
   // Add a little bit of extra fudge to the delay to allow reasonable
   // flexibility for time > vs >= etc.  We don't need to fail the test if we
   // retry at t=6001 instead of t=6000.
+#if defined(STARBOARD)
+  // The 1 millisecond delay is not enough on some of Cobalt's Linux platforms
+  // to ensure all delayed tasks are executed.
+  base::TimeDelta kSleepFudgeFactor = base::TimeDelta::FromMilliseconds(30);
+#else
   base::TimeDelta kSleepFudgeFactor = base::TimeDelta::FromMilliseconds(1);
+#endif
 
   scoped_refptr<LookupAttemptHostResolverProc> resolver_proc(
       new LookupAttemptHostResolverProc(
diff --git a/src/net/net.gyp b/src/net/net.gyp
index e87dbc9..f9d74c7 100644
--- a/src/net/net.gyp
+++ b/src/net/net.gyp
@@ -183,6 +183,8 @@
         'cert/internal/trust_store_collection.h',
         'cert/internal/trust_store_in_memory.cc',
         'cert/internal/trust_store_in_memory.h',
+        'cert/internal/trust_store_in_memory_starboard.cc',
+        'cert/internal/trust_store_in_memory_starboard.h',
         'cert/internal/verify_certificate_chain.cc',
         'cert/internal/verify_certificate_chain.h',
         'cert/internal/verify_name_match.cc',
@@ -431,10 +433,8 @@
         'cert/cert_verify_proc.h',
         # TODO[johnx]: Investigate why net deprecated openssl verifier and
         # if justified switch back to builtin verifier.
-        # 'cert/cert_verify_proc_builtin.cc',
-        # 'cert/cert_verify_proc_builtin.h',
-        'cert/cert_verify_proc_openssl.cc',
-        'cert/cert_verify_proc_openssl.h',
+        'cert/cert_verify_proc_builtin.cc',
+        'cert/cert_verify_proc_builtin.h',
         'cert/ct_log_response_parser.cc',
         'cert/ct_log_response_parser.h',
         'cert/ct_log_verifier.cc',
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioTrackBridge.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioTrackBridge.java
index 494f57b..733cf2b 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioTrackBridge.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioTrackBridge.java
@@ -35,6 +35,23 @@
   private AudioTimestamp audioTimestamp = new AudioTimestamp();
   private long maxFramePositionSoFar = 0;
 
+  private int GetFrameSize(int sampleType, int channelCount) {
+    switch (sampleType) {
+      case AudioFormat.ENCODING_PCM_16BIT:
+        {
+          return 2 * channelCount;
+        }
+      case AudioFormat.ENCODING_PCM_FLOAT:
+        {
+          return 4 * channelCount;
+        }
+      default:
+        {
+          throw new RuntimeException("Unsupported sample type: " + sampleType);
+        }
+    }
+  }
+
   public AudioTrackBridge(int sampleType, int sampleRate, int channelCount, int framesPerChannel) {
     int channelConfig;
     switch (channelCount) {
@@ -63,13 +80,12 @@
             .setChannelMask(channelConfig)
             .build();
 
-    int minBufferSizeBytes = AudioTrack.getMinBufferSize(sampleRate, channelConfig, sampleType);
-    int audioTrackBufferSize = minBufferSizeBytes;
-    // Use framesPerChannel to determine the buffer size.  To use a large buffer on a small
-    // framesPerChannel may lead to audio playback not able to start.
-    while (audioTrackBufferSize < framesPerChannel) {
-      audioTrackBufferSize *= 2;
-    }
+    // Try to create AudioTrack with the same size buffer as in renderer. But AudioTrack would not
+    // start playing until the buffer is fully filled once. A large buffer may cause
+    // AudioTrack not able to start. And we now pass no more than 1s of audio data to
+    // starboard player, limit the buffer size to store at most 0.5s of audio data.
+    int audioTrackBufferSize =
+        Math.min(framesPerChannel, sampleRate / 2) * GetFrameSize(sampleType, channelCount);
     while (audioTrackBufferSize > 0) {
       try {
         if (Build.VERSION.SDK_INT >= 26) {
@@ -105,7 +121,8 @@
         TAG,
         String.format(
             "AudioTrack created with buffer size %d.  The minimum buffer size is %d.",
-            audioTrackBufferSize, minBufferSizeBytes));
+            audioTrackBufferSize,
+            AudioTrack.getMinBufferSize(sampleRate, channelConfig, sampleType)));
   }
 
   public Boolean isAudioTrackValid() {
diff --git a/src/starboard/android/apk/build.id b/src/starboard/android/apk/build.id
new file mode 100644
index 0000000..80c1e89
--- /dev/null
+++ b/src/starboard/android/apk/build.id
@@ -0,0 +1 @@
+217501
\ No newline at end of file
diff --git a/src/starboard/android/shared/audio_track_audio_sink_type.cc b/src/starboard/android/shared/audio_track_audio_sink_type.cc
index a6584f2..24f1c5f 100644
--- a/src/starboard/android/shared/audio_track_audio_sink_type.cc
+++ b/src/starboard/android/shared/audio_track_audio_sink_type.cc
@@ -36,6 +36,11 @@
 
 const jint kNoOffset = 0;
 
+const size_t kSilenceFramesPerAppend = 1024;
+// 6 (max channels) * 4 (max sample size) * kSilenceFramesPerAppend, the number
+// is to ensure we always have at least 1024 frames silence to write.
+const uint8_t kSilenceBuffer[24 * kSilenceFramesPerAppend] = {0};
+
 // Helper function to compute the size of the two valid starboard audio sample
 // types.
 size_t GetSampleSize(SbMediaAudioSampleType sample_type) {
@@ -99,6 +104,8 @@
   static void* ThreadEntryPoint(void* context);
   void AudioThreadFunc();
 
+  int WriteData(JniEnvExt* env, const void* buffer, int size);
+
   Type* type_;
   int channels_;
   int sampling_frequency_hz_;
@@ -292,48 +299,26 @@
         std::min(expected_written_frames, kMaxFramesPerRequest);
     if (expected_written_frames == 0) {
       // It is possible that all the frames in buffer are written to the
-      // soundcard, but those are not being consumed.
+      // soundcard, but those are not being consumed. If eos is reached,
+      // write silence to make sure audio track start working and avoid
+      // underflow. Android audio track would not start working before
+      // its buffer is fully filled once.
+      if (is_eos_reached) {
+        // Currently AudioDevice and AudioRenderer will write tail silence.
+        // It should be reached only in tests.
+        WriteData(env, kSilenceBuffer, kSilenceFramesPerAppend);
+      }
       SbThreadSleep(10 * kSbTimeMillisecond);
       continue;
     }
     SB_DCHECK(expected_written_frames > 0);
-    bool written_fully = false;
-
-    if (sample_type_ == kSbMediaAudioSampleTypeFloat32) {
-      int expected_written_size = expected_written_frames * channels_;
-      env->SetFloatArrayRegion(
-          static_cast<jfloatArray>(j_audio_data_), kNoOffset,
-          expected_written_size,
-          static_cast<const float*>(IncrementPointerByBytes(
-              frame_buffer_,
-              start_position * channels_ * GetSampleSize(sample_type_))));
-      int written =
-          env->CallIntMethodOrAbort(j_audio_track_bridge_, "write", "([FI)I",
-                                    j_audio_data_, expected_written_size);
-      SB_DCHECK(written >= 0);
-      SB_DCHECK(written % channels_ == 0);
-      written_frames_ += written / channels_;
-      written_fully = (written == expected_written_frames);
-    } else if (sample_type_ == kSbMediaAudioSampleTypeInt16Deprecated) {
-      int expected_written_size =
-          expected_written_frames * channels_ * GetSampleSize(sample_type_);
-      env->SetByteArrayRegion(
-          static_cast<jbyteArray>(j_audio_data_), kNoOffset,
-          expected_written_size,
-          static_cast<const jbyte*>(IncrementPointerByBytes(
-              frame_buffer_,
-              start_position * channels_ * GetSampleSize(sample_type_))));
-      int written =
-          env->CallIntMethodOrAbort(j_audio_track_bridge_, "write", "([BI)I",
-                                    j_audio_data_, expected_written_size);
-      SB_DCHECK(written >= 0);
-      SB_DCHECK(written % (channels_ * GetSampleSize(sample_type_)) == 0);
-      written_frames_ += written / (channels_ * GetSampleSize(sample_type_));
-      written_fully = (written == expected_written_frames);
-    } else {
-      SB_NOTREACHED();
-    }
-
+    int written_frames = WriteData(
+        env,
+        IncrementPointerByBytes(frame_buffer_, start_position * channels_ *
+                                                   GetSampleSize(sample_type_)),
+        expected_written_frames);
+    written_frames_ += written_frames;
+    bool written_fully = (written_frames == expected_written_frames);
     auto unplayed_frames_in_time =
         written_frames_ * kSbTimeSecond / sampling_frequency_hz_ -
         (SbTimeGetMonotonicNow() - frames_consumed_at);
@@ -346,7 +331,7 @@
       SbThreadSleep(40 * kSbTimeMillisecond);
     } else if (!written_fully) {
       // Only sleep if the buffer is nearly full and the last write is partial.
-      SbThreadSleep(1 * kSbTimeMillisecond);
+      SbThreadSleep(10 * kSbTimeMillisecond);
     }
   }
 
@@ -358,6 +343,38 @@
   env->CallVoidMethodOrAbort(j_audio_track_bridge_, "flush", "()V");
 }
 
+int AudioTrackAudioSink::WriteData(JniEnvExt* env,
+                                   const void* buffer,
+                                   int expected_written_frames) {
+  if (sample_type_ == kSbMediaAudioSampleTypeFloat32) {
+    int expected_written_size = expected_written_frames * channels_;
+    env->SetFloatArrayRegion(static_cast<jfloatArray>(j_audio_data_), kNoOffset,
+                             expected_written_size,
+                             static_cast<const float*>(buffer));
+    int written =
+        env->CallIntMethodOrAbort(j_audio_track_bridge_, "write", "([FI)I",
+                                  j_audio_data_, expected_written_size);
+    SB_DCHECK(written >= 0);
+    SB_DCHECK(written % channels_ == 0);
+    return written / channels_;
+  }
+  if (sample_type_ == kSbMediaAudioSampleTypeInt16Deprecated) {
+    int expected_written_size =
+        expected_written_frames * channels_ * GetSampleSize(sample_type_);
+    env->SetByteArrayRegion(static_cast<jbyteArray>(j_audio_data_), kNoOffset,
+                            expected_written_size,
+                            static_cast<const jbyte*>(buffer));
+    int written =
+        env->CallIntMethodOrAbort(j_audio_track_bridge_, "write", "([BI)I",
+                                  j_audio_data_, expected_written_size);
+    SB_DCHECK(written >= 0);
+    SB_DCHECK(written % (channels_ * GetSampleSize(sample_type_)) == 0);
+    return written / (channels_ * GetSampleSize(sample_type_));
+  }
+  SB_NOTREACHED();
+  return 0;
+}
+
 void AudioTrackAudioSink::SetVolume(double volume) {
   auto* env = JniEnvExt::Get();
   jint status = env->CallIntMethodOrAbort(j_audio_track_bridge_, "setVolume",
diff --git a/src/starboard/android/shared/gyp_configuration.py b/src/starboard/android/shared/gyp_configuration.py
index e6d28d9..d674f72 100644
--- a/src/starboard/android/shared/gyp_configuration.py
+++ b/src/starboard/android/shared/gyp_configuration.py
@@ -298,13 +298,10 @@
           'VideoDecoderTests/VideoDecoderTest.SingleInput/1',
           'VideoDecoderTests/VideoDecoderTest.SingleInput/2',
           'VideoDecoderTests/VideoDecoderTest.SingleInput/3',
-
-          # On some platforms, and for some decoders (such as AVC), Android
-          # returns MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER for the test's
-          # invalid input frame instead of signaling an error, which the test is
-          # looking for.
-          'VideoDecoderTests/VideoDecoderTest.SingleInvalidInput/0',
-          'VideoDecoderTests/VideoDecoderTest.SingleInvalidInput/1',
+          'VideoDecoderTests/VideoDecoderTest.SingleInvalidInput/*',
+          'VideoDecoderTests/VideoDecoderTest'
+          '.MultipleValidInputsAfterInvalidKeyFrame/*',
+          'VideoDecoderTests/VideoDecoderTest.MultipleInvalidInput/*',
 
           # Android currently does not support multi-video playback, which
           # the following tests depend upon.
diff --git a/src/starboard/android/shared/system_get_property.cc b/src/starboard/android/shared/system_get_property.cc
index d4e6d84..d4412d4 100644
--- a/src/starboard/android/shared/system_get_property.cc
+++ b/src/starboard/android/shared/system_get_property.cc
@@ -93,7 +93,7 @@
 
   switch (property_id) {
     case kSbSystemPropertyBrandName:
-      return GetAndroidSystemProperty("ro.product.manufacturer", out_value,
+      return GetAndroidSystemProperty("ro.product.brand", out_value,
                                       value_length, kUnknownValue);
     case kSbSystemPropertyModelName:
       return GetAndroidSystemProperty("ro.product.model", out_value,
@@ -105,8 +105,11 @@
       return GetAndroidSystemProperty("ro.board.platform", out_value,
                                       value_length, kUnknownValue);
     case kSbSystemPropertyModelYear:
+       return false;
     case kSbSystemPropertyOriginalDesignManufacturerName:
-      return false;
+      return GetAndroidSystemProperty("ro.product.manufacturer", out_value,
+                                      value_length, kUnknownValue);
+
 
     case kSbSystemPropertyFriendlyName:
       return CopyStringAndTestIfSuccess(out_value, value_length, kFriendlyName);
diff --git a/src/starboard/common/common.gyp b/src/starboard/common/common.gyp
index c7cb5f6..4db427b 100644
--- a/src/starboard/common/common.gyp
+++ b/src/starboard/common/common.gyp
@@ -29,7 +29,7 @@
         'condition_variable.cc',
         'condition_variable.h',
         'flat_map.h',
-	'locked_ptr.h',
+        'locked_ptr.h',
         'log.cc',
         'log.h',
         'memory.cc',
diff --git a/src/starboard/cpu_features.h b/src/starboard/cpu_features.h
index 6aed047..166bd74 100644
--- a/src/starboard/cpu_features.h
+++ b/src/starboard/cpu_features.h
@@ -31,6 +31,10 @@
 
 #if SB_API_VERSION >= 11
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 typedef enum SbCPUFeaturesArchitecture {
   kSbCPUFeaturesArchitectureArm,
   kSbCPUFeaturesArchitectureArm64,
@@ -263,9 +267,12 @@
   // under the key "model name" or "Processor".
   const char* brand;
 
-  // Processor cache line size in bytes. Queried from /proc/cpuinfo or
+  // Processor cache line size in bytes of Level 1 instruction cache and data
+  // cache. Queried by sysconf(_SC_LEVEL1_ICACHE_LINESIZE) and
+  // sysconf(_SC_LEVEL1_DCACHE_LINESIZE), or from files /proc/cpuinfo,
   // /proc/self/auxv, or CPUID with CLFLUSH instruction.
-  int32_t cache_size;
+  int32_t icache_line_size;
+  int32_t dcache_line_size;
 
   // Processor has floating-point unit on-chip.
   bool has_fpu;
@@ -314,5 +321,9 @@
 // all fields in |features| are invalid.
 SB_EXPORT bool SbCPUFeaturesGet(SbCPUFeatures* features);
 
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
 #endif  // SB_API_VERSION >= 11
 #endif  // STARBOARD_CPU_FEATURES_H_
diff --git a/src/starboard/elf_loader/dynamic_section.cc b/src/starboard/elf_loader/dynamic_section.cc
new file mode 100644
index 0000000..b4d9422
--- /dev/null
+++ b/src/starboard/elf_loader/dynamic_section.cc
@@ -0,0 +1,203 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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.
+
+#include "starboard/elf_loader/dynamic_section.h"
+
+#include "starboard/common/log.h"
+
+namespace starboard {
+namespace elf_loader {
+
+DynamicSection::DynamicSection(Addr base_memory_address,
+                               Dyn* dynamic,
+                               size_t dynamic_count,
+                               Word dynamic_flags)
+    : base_memory_address_(base_memory_address),
+      soname_(NULL),
+      dynamic_(dynamic),
+      dynamic_count_(dynamic_count),
+      dynamic_flags_(dynamic_flags),
+      has_DT_SYMBOLIC_(false),
+      symbol_table_(NULL),
+      string_table_(NULL),
+      preinit_array_(NULL),
+      preinit_array_count_(0),
+      init_array_(NULL),
+      init_array_count_(0),
+      fini_array_(NULL),
+      fini_array_count_(0),
+      init_func_(NULL),
+      fini_func_(NULL) {}
+
+bool DynamicSection::InitDynamicSection() {
+  SB_LOG(INFO) << "Dynamic section count=" << dynamic_count_;
+  for (int i = 0; i < dynamic_count_; i++) {
+    Addr dyn_value = dynamic_[i].d_un.d_val;
+    uintptr_t dyn_addr = base_memory_address_ + dynamic_[i].d_un.d_ptr;
+    SB_LOG(INFO) << "Dynamic tag=" << dynamic_[i].d_tag;
+    switch (dynamic_[i].d_tag) {
+      case DT_DEBUG:
+        // TODO: implement.
+        break;
+      case DT_INIT:
+        SB_LOG(INFO) << "  DT_INIT addr=0x" << std::hex << dyn_addr;
+        init_func_ = reinterpret_cast<linker_function_t>(dyn_addr);
+        break;
+      case DT_FINI:
+        SB_LOG(INFO) << "  DT_FINI addr=0x" << std::hex << dyn_addr;
+        fini_func_ = reinterpret_cast<linker_function_t>(dyn_addr);
+        break;
+      case DT_INIT_ARRAY:
+        SB_LOG(INFO) << "  DT_INIT_ARRAY addr=0x" << std::hex << dyn_addr;
+        init_array_ = reinterpret_cast<linker_function_t*>(dyn_addr);
+        break;
+      case DT_INIT_ARRAYSZ:
+        init_array_count_ = dyn_value / sizeof(Addr);
+        SB_LOG(INFO) << "  DT_INIT_ARRAYSZ value=0x" << std::hex << dyn_value
+                     << " count=" << std::dec << init_array_count_;
+        break;
+      case DT_FINI_ARRAY:
+        SB_LOG(INFO) << "  DT_FINI_ARRAY addr=0x" << std::hex << dyn_addr;
+        fini_array_ = reinterpret_cast<linker_function_t*>(dyn_addr);
+        break;
+      case DT_FINI_ARRAYSZ:
+        fini_array_count_ = dyn_value / sizeof(Addr);
+        SB_LOG(INFO) << "  DT_FINI_ARRAYSZ value=0x" << std::hex << dyn_value
+                     << " count=" << fini_array_count_;
+        break;
+      case DT_PREINIT_ARRAY:
+        SB_LOG(INFO) << "  DT_PREINIT_ARRAY addr=0x" << std::hex << dyn_addr;
+        preinit_array_ = reinterpret_cast<linker_function_t*>(dyn_addr);
+        break;
+      case DT_PREINIT_ARRAYSZ:
+        preinit_array_count_ = dyn_value / sizeof(Addr);
+        SB_LOG(INFO) << "  DT_PREINIT_ARRAYSZ addr=" << dyn_addr
+                     << " count=" << preinit_array_count_;
+        break;
+      case DT_SYMBOLIC:
+        SB_LOG(INFO) << "  DT_SYMBOLIC";
+        has_DT_SYMBOLIC_ = true;
+        break;
+      case DT_FLAGS:
+        if (dyn_value & DF_SYMBOLIC)
+          has_DT_SYMBOLIC_ = true;
+        break;
+      case DT_SONAME:
+        soname_ = string_table_ + dyn_value;
+        break;
+      default:
+        break;
+    }
+  }
+  return true;
+}
+
+bool DynamicSection::InitDynamicSymbols() {
+  for (int i = 0; i < dynamic_count_; i++) {
+    Addr dyn_value = dynamic_[i].d_un.d_val;
+    uintptr_t dyn_addr = base_memory_address_ + dynamic_[i].d_un.d_ptr;
+    switch (dynamic_[i].d_tag) {
+      case DT_HASH:
+        SB_LOG(INFO) << "  DT_HASH addr=0x" << std::hex << dyn_addr;
+        elf_hash_.Init(dyn_addr);
+        break;
+      case DT_GNU_HASH:
+        SB_LOG(INFO) << "  DT_GNU_HASH addr=0x" << std::hex << dyn_addr;
+        gnu_hash_.Init(dyn_addr);
+        break;
+      case DT_STRTAB:
+        SB_LOG(INFO) << "  DT_STRTAB addr=0x" << std::hex << dyn_addr;
+        string_table_ = reinterpret_cast<const char*>(dyn_addr);
+        break;
+      case DT_SYMTAB:
+        SB_LOG(INFO) << "  DT_SYMTAB addr=0x" << std::hex << dyn_addr;
+        symbol_table_ = reinterpret_cast<Sym*>(dyn_addr);
+        break;
+      default:
+        break;
+    }
+  }
+  return true;
+}
+
+const Dyn* DynamicSection::GetDynamicTable() {
+  return dynamic_;
+}
+
+size_t DynamicSection::GetDynamicTableSize() {
+  return dynamic_count_;
+}
+
+void DynamicSection::CallConstructors() {
+  CallFunction(init_func_, "DT_INIT");
+  for (size_t n = 0; n < init_array_count_; ++n)
+    CallFunction(init_array_[n], "DT_INIT_ARRAY");
+}
+
+void DynamicSection::CallDestructors() {
+  for (size_t n = fini_array_count_; n > 0; --n) {
+    CallFunction(fini_array_[n - 1], "DT_FINI_ARRAY");
+  }
+  CallFunction(fini_func_, "DT_FINI");
+}
+
+void DynamicSection::CallFunction(linker_function_t func,
+                                  const char* func_type) {
+  uintptr_t func_address = reinterpret_cast<uintptr_t>(func);
+
+  // On some platforms  the entries in the array can be 0 or -1,
+  // and should  be ignored e.g. Android:
+  // https://android.googlesource.com/platform/bionic/+/android-4.2_r1/linker/README.TXT
+  if (func_address != 0 && func_address != uintptr_t(-1)) {
+    func();
+  }
+}
+
+const Sym* DynamicSection::LookupById(size_t symbol_id) const {
+  // TODO: Calculated the symbol_table size and validation check.
+  return &symbol_table_[symbol_id];
+}
+
+bool DynamicSection::IsWeakById(size_t symbol_id) const {
+  // TODO: Calculated the symbol_table size and validation check.
+  return ELF_ST_BIND(symbol_table_[symbol_id].st_info) == STB_WEAK;
+}
+
+const char* DynamicSection::LookupNameById(size_t symbol_id) const {
+  const Sym* sym = LookupById(symbol_id);
+  // TODO: Confirm that LookupById actually can return NULL.
+  if (!sym)
+    return NULL;
+  return string_table_ + sym->st_name;
+}
+
+const Sym* DynamicSection::LookupByName(const char* symbol_name) const {
+  const Sym* sym =
+      gnu_hash_.IsValid()
+          ? gnu_hash_.LookupByName(symbol_name, symbol_table_, string_table_)
+          : elf_hash_.LookupByName(symbol_name, symbol_table_, string_table_);
+
+  // Ignore undefined symbols or those that are not global or weak definitions.
+  if (!sym || sym->st_shndx == SHN_UNDEF)
+    return NULL;
+
+  uint8_t info = ELF_ST_BIND(sym->st_info);
+  if (info != STB_GLOBAL && info != STB_WEAK)
+    return NULL;
+
+  return sym;
+}
+
+}  // namespace elf_loader
+}  // namespace starboard
diff --git a/src/starboard/elf_loader/dynamic_section.h b/src/starboard/elf_loader/dynamic_section.h
new file mode 100644
index 0000000..eec44c2
--- /dev/null
+++ b/src/starboard/elf_loader/dynamic_section.h
@@ -0,0 +1,106 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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 STARBOARD_ELF_LOADER_DYNAMIC_SECTION_H_
+#define STARBOARD_ELF_LOADER_DYNAMIC_SECTION_H_
+
+#include "starboard/elf_loader/elf.h"
+#include "starboard/elf_loader/elf_hash_table.h"
+#include "starboard/elf_loader/exported_symbols.h"
+#include "starboard/elf_loader/gnu_hash_table.h"
+#include "starboard/elf_loader/program_table.h"
+
+namespace starboard {
+namespace elf_loader {
+
+typedef void (*linker_function_t)();
+
+// class representing the ELF dynamic
+// section with dynamic symbols and a
+// string tables.
+
+// The initialization requires calling:
+// 1. InitDynamicSection()
+// 2. InitDynamicSymbols()
+//
+class DynamicSection {
+ public:
+  DynamicSection(Addr base_memory_address,
+                 Dyn* dynamic,
+                 size_t dynamic_count,
+                 Word dynamic_flags);
+
+  // Initialize the dynamic section.
+  bool InitDynamicSection();
+
+  // Initialize all the dynamic symbol tables.
+  bool InitDynamicSymbols();
+
+  // Get pointer to the dynamic table.
+  const Dyn* GetDynamicTable();
+
+  // Get the size of the dynamic table
+  size_t GetDynamicTableSize();
+
+  // Call all the global constructors.
+  void CallConstructors();
+
+  // Call all the global destructors.
+  void CallDestructors();
+
+  // Call a function.
+  void CallFunction(linker_function_t func, const char* func_type);
+
+  // Lookup a symbol using its name.
+  const Sym* LookupByName(const char* symbol_name) const;
+
+  // Lookup a symbol using its id.
+  const Sym* LookupById(size_t symbol_id) const;
+
+  // Checks if a symbols is weak.
+  bool IsWeakById(size_t symbol_id) const;
+
+  // Lookup the name of a symbol by using its id.
+  const char* LookupNameById(size_t symbol_id) const;
+
+ private:
+  Addr base_memory_address_;
+  const char* soname_;
+
+  Dyn* dynamic_;
+  size_t dynamic_count_;
+  Word dynamic_flags_;
+  bool has_DT_SYMBOLIC_;
+
+  Sym* symbol_table_;
+  const char* string_table_;
+  ElfHashTable elf_hash_;
+  GnuHashTable gnu_hash_;
+
+  linker_function_t* preinit_array_;
+  size_t preinit_array_count_;
+  linker_function_t* init_array_;
+  size_t init_array_count_;
+  linker_function_t* fini_array_;
+  size_t fini_array_count_;
+  linker_function_t init_func_;
+  linker_function_t fini_func_;
+
+  SB_DISALLOW_COPY_AND_ASSIGN(DynamicSection);
+};
+
+}  // namespace elf_loader
+}  // namespace starboard
+
+#endif  // STARBOARD_ELF_LOADER_DYNAMIC_SECTION_H_
diff --git a/src/starboard/elf_loader/dynamic_section_test.cc b/src/starboard/elf_loader/dynamic_section_test.cc
new file mode 100644
index 0000000..9aa68be
--- /dev/null
+++ b/src/starboard/elf_loader/dynamic_section_test.cc
@@ -0,0 +1,38 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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.
+
+#include "starboard/elf_loader/elf_loader_impl.h"
+
+#include "starboard/common/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace elf_loader {
+
+namespace {
+
+// TODO: implement
+class DynamicSection : public ::testing::Test {
+ protected:
+  DynamicSection() {}
+  ~DynamicSection() {}
+};
+
+TEST_F(DynamicSection, Initialize) {
+  EXPECT_TRUE(true);
+}
+
+}  // namespace
+}  // namespace elf_loader
+}  // namespace starboard
diff --git a/src/starboard/elf_loader/elf.h b/src/starboard/elf_loader/elf.h
new file mode 100644
index 0000000..1ecd908
--- /dev/null
+++ b/src/starboard/elf_loader/elf.h
@@ -0,0 +1,651 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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 STARBOARD_ELF_LOADER_ELF_H_
+#define STARBOARD_ELF_LOADER_ELF_H_
+
+// Subset of the ELF specification for loading Dynamic Shared Libraries.
+// System V Application Binary Interface - DRAFT - 10 June 2013
+// http://www.sco.com/developers/gabi/latest/contents.html
+
+#ifndef __cplusplus
+#error "Only C++ files can include this header."
+#endif
+
+#include "starboard/types.h"
+
+namespace starboard {
+namespace elf_loader {
+
+// 32 bit data types
+
+// Unsigned program address - 4 bytes.
+typedef uint32_t Elf32_Addr;
+
+// Unsigned medium integer - 2 bytes.
+typedef uint16_t Elf32_Half;
+
+// Unsigned file offset - 4 bytes.
+typedef uint32_t Elf32_Off;
+
+// Signed large integer - 4 bytes.
+typedef int32_t Elf32_Sword;
+
+// Unsigned large integer - 4 bytes.
+typedef uint32_t Elf32_Word;
+
+// 64 bit data types
+
+// Unsigned program address - 8 bytes.
+typedef uint64_t Elf64_Addr;
+
+// Unsigned file offset - 8 bytes.
+typedef uint64_t Elf64_Off;
+
+// Unsigned medium intege 2 - bytes.
+typedef uint16_t Elf64_Half;
+
+// Unsigned integer - 4 bytes.
+typedef uint32_t Elf64_Word;
+
+// Signed integer - 4 bytes.
+typedef int32_t Elf64_Sword;
+
+// Unsigned long integer - 8 bytes.
+typedef uint64_t Elf64_Xword;
+
+// Signed long integer - 8 bytes.
+typedef int64_t Elf64_Sxword;
+
+#define EI_NIDENT (16)
+
+// Pack all the structs at 1 byte alignment.
+#pragma pack(push)
+#pragma pack(1)
+
+// 32 bit ELF file header.
+typedef struct {
+  // The initial bytes mark the file as an object file and provide
+  // machine-independent data.
+  unsigned char e_ident[EI_NIDENT];
+
+  // The object file type. We support only ET_DYN.
+  Elf32_Half e_type;
+
+  // Architecture of the file.
+  Elf32_Half e_machine;
+
+  // Object file version. The value should be 1.
+  Elf32_Word e_version;
+
+  // Virtual address to which the system first transfers
+  // control, thus starting the process.
+  Elf32_Addr e_entry;
+
+  // Program header table's file offset in bytes.
+  Elf32_Off e_phoff;
+
+  // Section header table's file offset in bytes.
+  Elf32_Off e_shoff;
+
+  // Processor-specific flags associated with the file.
+  Elf32_Word e_flags;
+
+  // ELF header's size in bytes.
+  Elf32_Half e_ehsize;
+
+  // Size in bytes of one entry in the file's program  header table
+  Elf32_Half e_phentsize;
+
+  // The number of entries in the program header table.
+  Elf32_Half e_phnum;
+
+  // Section header's size in bytes.
+  Elf32_Half e_shentsize;
+
+  // The number of entries in the section header table.
+  Elf32_Half e_shnum;
+
+  // The section header table index of the entry associated
+  // with the section name string table.
+  Elf32_Half e_shstrndx;
+} Elf32_Ehdr;
+
+// 64 bit ELF file header.
+typedef struct {
+  // The initial bytes mark the file as an object file and provide
+  // machine-independent data.
+  unsigned char e_ident[EI_NIDENT];
+
+  // The object file type. We support only ET_DYN.
+  Elf64_Half e_type;
+
+  // Architecture of the file.
+  Elf64_Half e_machine;
+
+  // Object file version. The value should be 1.
+  Elf64_Word e_version;
+
+  // Virtual address to which the system first transfers
+  // control, thus starting the process.
+  Elf64_Addr e_entry;
+
+  // Program header table's file offset in bytes.
+  Elf64_Off e_phoff;
+
+  // Section header table's file offset in bytes.
+  Elf64_Off e_shoff;
+
+  // Processor-specific flags associated with the file.
+  Elf64_Word e_flags;
+
+  // This member holds the ELF header's size in bytes.
+  Elf64_Half e_ehsize;
+
+  // Size in bytes of one entry in the file's program  header table
+  Elf64_Half e_phentsize;
+
+  // The number of entries in the section header table.
+  Elf64_Half e_phnum;
+
+  // Section header's size in bytes.
+  Elf64_Half e_shentsize;
+
+  // The number of entries in the section header table.
+  Elf64_Half e_shnum;
+
+  // The section header table index of the entry associated
+  // with the section name string table.
+  Elf64_Half e_shstrndx;
+} Elf64_Ehdr;
+
+// 32 bit Program header.
+typedef struct {
+  // The kind of segment this array element describes.
+  Elf32_Word p_type;
+
+  // The offset from the beginning of the file at which the
+  // first byte of the segment resides.
+  Elf32_Off p_offset;
+
+  // The virtual address at which the first byte of the
+  // segment resides in memory.
+  Elf32_Addr p_vaddr;
+
+  // Segment's physical address. Unused for shared libraries.
+  Elf32_Addr p_paddr;
+
+  // The number of bytes in the file image of the segment.  May be zero.
+  Elf32_Word p_filesz;
+
+  // The number of bytes in the memory image of the segment.  May be zero.
+  Elf32_Word p_memsz;
+
+  // Segment flags
+  Elf32_Word p_flags;
+
+  // Segment alignment constraint.
+  Elf32_Word p_align;
+} Elf32_Phdr;
+
+// 64 bit Program header.
+typedef struct {
+  // The kind of segment this array element describes.
+  Elf64_Word p_type;
+
+  // Segment flags
+  Elf64_Word p_flags;
+
+  // The offset from the beginning of the file at which the
+  // first byte of the segment resides.
+  Elf64_Off p_offset;
+
+  // The virtual address at which the first byte of the
+  // segment resides in memory.
+  Elf64_Addr p_vaddr;
+
+  // Segment's physical address. Unused for shared libraries.
+  Elf64_Addr p_paddr;
+
+  // The number of bytes in the file image of the segment. May be zero.
+  Elf64_Xword p_filesz;
+
+  // The number of bytes in the memory image of the segment.  May be zero.
+  Elf64_Xword p_memsz;
+
+  // Segment alignment constraint
+  Elf64_Xword p_align;
+} Elf64_Phdr;
+
+// 32 bit Dynamic Section Entry
+typedef struct {
+  // Controls the interpretation of d_un.
+  Elf32_Sword d_tag;
+  union {
+    // These objects represent integer values with various interpretations.
+    Elf32_Word d_val;
+    // These objects represent program virtual addresses.
+    Elf32_Addr d_ptr;
+  } d_un;
+} Elf32_Dyn;
+
+// 64 bit Dynamic Section Entry
+typedef struct {
+  // Controls the interpretation of d_un.
+  Elf64_Sxword d_tag;
+  union {
+    // These objects represent integer values with various interpretations.
+    Elf64_Xword d_val;
+    // These objects represent program virtual addresses.
+    Elf64_Addr d_ptr;
+  } d_un;
+} Elf64_Dyn;
+
+// 32 bit Symbol Table Entry
+typedef struct {
+  // An index into the object file's symbol string table,
+  // which holds the character representations of the symbol names. If the value
+  // is non-zero, it represents a string table index that gives the symbol name.
+  // Otherwise, the symbol table entry has no name.
+  Elf32_Word st_name;
+
+  // The value of the associated symbol. Depending on the
+  // context, this may be an absolute value, an address, and so on;
+  Elf32_Addr st_value;
+
+  // Many symbols have associated sizes. For example, a data object's size is
+  // the number of bytes contained in the object.
+  Elf32_Word st_size;
+
+  // The symbol's type and binding attributes.
+  unsigned char st_info;
+
+  // Symbol's visibility.
+  unsigned char st_other;
+
+  // Every symbol table entry is defined in relation to some section. This
+  // member holds the relevant section header table index.
+  Elf32_Half st_shndx;
+} Elf32_Sym;
+
+// 64 bit Symbol Table Entry
+typedef struct {
+  // An index into the object file's symbol string table,
+  // which holds the character representations of the symbol names. If the value
+  // is non-zero, it represents a string table index that gives the symbol name.
+  // Otherwise, the symbol table entry has no name.
+  Elf64_Word st_name;
+
+  // The symbol's type and binding attributes.
+  unsigned char st_info;
+
+  // Symbol's visibility.
+  unsigned char st_other;
+
+  // Every symbol table entry is defined in relation to some section. This
+  // member holds the relevant section header table index.
+  Elf64_Half st_shndx;
+
+  // The value of the associated symbol. Depending on the
+  // context, this may be an absolute value, an address, and so on;
+  Elf64_Addr st_value;
+
+  // Many symbols have associated sizes. For example, a data object's size is
+  // the number of bytes contained in the object.
+  Elf64_Xword st_size;
+} Elf64_Sym;
+
+// 32 bit Relocation Entry
+typedef struct {
+  // The location at which to apply the relocation action. For  a relocatable
+  // file, the value is the byte offset from the beginning of the  section to
+  // the storage unit affected by the relocation. For an executable file or a
+  // shared object, the value is the virtual address of the storage unit
+  // affected by the relocation.
+  Elf32_Addr r_offset;
+  // The symbol table index with respect to which the
+  // relocation must be made, and the type of relocation to apply.
+  Elf32_Word r_info;
+} Elf32_Rel;
+
+// 64 bit Relocation Entry
+typedef struct {
+  // The location at which to apply the relocation action. For
+  // a relocatable file, the value is the byte offset from the beginning of the
+  // section to the storage unit affected by the relocation. For an executable
+  // file or a shared object, the value is the virtual address of the storage
+  // unit affected by the relocation.
+  Elf64_Addr r_offset;
+
+  // The symbol table index with respect to which the
+  // relocation must be made, and the type of relocation to apply.
+  Elf64_Xword r_info;
+} Elf64_Rel;
+
+// 32 bit Relocation Entry with Addend.
+typedef struct {
+  // The location at which to apply the relocation action. For
+  // a relocatable file, the value is the byte offset from the beginning of the
+  // section to the storage unit affected by the relocation. For an executable
+  // file or a shared object, the value is the virtual address of the storage
+  // unit affected by the relocation.
+  Elf32_Addr r_offset;
+
+  // The symbol table index with respect to which the
+  // relocation must be made, and the type of relocation to apply.
+  Elf32_Word r_info;
+
+  // A constant addend used to compute the value to be stored into the
+  // relocatable field.
+  Elf32_Sword r_addend;
+} Elf32_Rela;
+
+// 64 bit Relocation Entry with Addend.
+typedef struct {
+  // The location at which to apply the relocation action. For  a relocatable
+  // file, the value is the byte offset from the beginning of the section to the
+  // storage unit affected by the relocation. For an executable file or a shared
+  // object, the value is the virtual address of the storage unit affected by
+  // the relocation.
+  Elf64_Addr r_offset;
+
+  // The symbol table index with respect to which the
+  // relocation must be made, and the type of relocation to apply.
+  Elf64_Xword r_info;
+
+  // A constant addend used to compute the value to be stored into the
+  // relocatable field.
+  Elf64_Sxword r_addend;
+} Elf64_Rela;
+
+#pragma pack(pop)
+
+#define EI_CLASS 4
+#define ELFCLASS32 1
+#define ELFCLASS64 2
+#define EI_DATA 5
+#define ELFDATA2LSB 1
+#define ET_DYN 3
+#define EV_CURRENT 1
+
+#if SB_HAS(32_BIT_POINTERS)
+typedef Elf32_Ehdr Ehdr;
+typedef Elf32_Phdr Phdr;
+typedef Elf32_Addr Addr;
+typedef Elf32_Dyn Dyn;
+typedef Elf32_Word Word;
+typedef Elf32_Sym Sym;
+typedef Elf32_Rel Rel;
+typedef Elf32_Rela Rela;
+typedef Elf32_Word Relr;
+typedef Elf32_Sword Sword;
+#define ELF_BITS 32
+#define ELF_R_TYPE ELF32_R_TYPE
+#define ELF_R_SYM ELF32_R_SYM
+#define ELF_CLASS_VALUE ELFCLASS32
+#elif SB_HAS(64_BIT_POINTERS)
+typedef Elf64_Ehdr Ehdr;
+typedef Elf64_Phdr Phdr;
+typedef Elf64_Addr Addr;
+typedef Elf64_Dyn Dyn;
+typedef Elf64_Word Word;
+typedef Elf64_Sym Sym;
+typedef Elf64_Rel Rel;
+typedef Elf64_Rela Rela;
+typedef Elf64_Word Relr;
+typedef Elf64_Sword Sword;
+#define ELF_BITS 64
+#define ELF_R_TYPE ELF64_R_TYPE
+#define ELF_R_SYM ELF64_R_SYM
+#define ELF_CLASS_VALUE ELFCLASS64
+#else
+#error "Unsupported pointer size"
+#endif
+
+#define ELF32_R_SYM(val) ((val) >> 8)
+#define ELF32_R_TYPE(val) ((val)&0xff)
+#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type)&0xff))
+
+#define ELF64_R_SYM(i) ((i) >> 32)
+#define ELF64_R_TYPE(i) ((i)&0xffffffff)
+#define ELF64_R_INFO(sym, type) ((((Elf64_Xword)(sym)) << 32) + (type))
+
+#define ELFMAG "\177ELF"
+#define SELFMAG 4
+
+// TODO: Refactor the code to detect it at runtime
+// using DT_PLTREL.
+#if (SB_IS(ARCH_ARM) || SB_IS(ARCH_X86)) && SB_IS(64_BIT)
+#define USE_RELA
+#endif
+
+#if defined(USE_RELA)
+typedef Rela rel_t;
+#else
+typedef Rel rel_t;
+#endif
+
+#if SB_IS(ARCH_ARM) && SB_IS(32_BIT)
+#define ELF_MACHINE 40
+#elif SB_IS(ARCH_X86) && SB_IS(32_BIT)
+#define ELF_MACHINE 3
+#elif SB_IS(ARCH_X86) && SB_IS(64_BIT)
+#define ELF_MACHINE 62
+#elif SB_IS(ARCH_ARM) && SB_IS(64_BIT)
+#define ELF_MACHINE 183
+#else
+#error "Unsupported target CPU architecture"
+#endif
+
+// Segment types.
+typedef enum SegmentTypes {
+  // Unused segment.
+  PT_NULL = 0,
+
+  // Loadable segment.
+  PT_LOAD = 1,
+
+  // Dynamic linking information.
+  PT_DYNAMIC = 2,
+
+  // Interpreter pathname.
+  PT_INTERP = 3,
+
+  // Auxiliary information.
+  PT_NOTE = 4,
+
+  // Reserved.
+  PT_SHLIB = 5,
+
+  // The program header table itself.
+  PT_PHDR = 6,
+
+  // The thread-local storage template.
+  PT_TLS = 7
+} SegmentTypes;
+
+// Symbol bindings.
+typedef enum SymbolBindings {
+  // Local symbol, not visible outside obj file containing def
+  STB_LOCAL = 0,
+
+  // Global symbol, visible to all object files being combined
+  STB_GLOBAL = 1,
+
+  // Weak symbol, like global but lower-precedence
+  STB_WEAK = 2,
+
+  STB_GNU_UNIQUE = 10,
+
+  // Lowest operating system-specific binding type
+  STB_LOOS = 10,
+
+  // Highest operating system-specific binding type
+  STB_HIOS = 12,
+
+  // Lowest processor-specific binding type
+  STB_LOPROC = 13,
+
+  // Highest processor-specific binding type
+  STB_HIPROC = 15
+} SymbolBindings;
+
+#define ELF_ST_BIND(x) ((x) >> 4)
+#define ELF32_ST_BIND(x) ELF_ST_BIND(x)
+#define ELF64_ST_BIND(x) ELF_ST_BIND(x)
+
+#define PF_X (1 << 0)
+#define PF_W (1 << 1)
+#define PF_R (1 << 2)
+#define PF_MASKOS 0x0ff00000
+#define PF_MASKPROC 0xf0000000
+
+// Dynamic table tags.
+typedef enum DynamicTags {
+  DT_NULL = 0,
+  DT_NEEDED = 1,
+  DT_PLTRELSZ = 2,
+  DT_PLTGOT = 3,
+  DT_HASH = 4,
+  DT_STRTAB = 5,
+  DT_SYMTAB = 6,
+  DT_RELA = 7,
+  DT_RELASZ = 8,
+  DT_RELAENT = 9,
+  DT_STRSZ = 10,
+  DT_SYMENT = 11,
+  DT_INIT = 12,
+  DT_FINI = 13,
+  DT_SONAME = 14,
+  DT_RPATH = 15,
+  DT_SYMBOLIC = 16,
+  DT_REL = 17,
+  DT_RELSZ = 18,
+  DT_RELENT = 19,
+  DT_PLTREL = 20,
+  DT_DEBUG = 21,
+  DT_TEXTREL = 22,
+  DT_JMPREL = 23,
+  DT_BIND_NOW = 24,
+  DT_INIT_ARRAY = 25,
+  DT_FINI_ARRAY = 26,
+  DT_INIT_ARRAYSZ = 27,
+  DT_FINI_ARRAYSZ = 28,
+  DT_RUNPATH = 29,
+  DT_FLAGS = 30,
+  DT_ENCODING = 32,
+  DT_PREINIT_ARRAY = 32,
+  DT_PREINIT_ARRAYSZ = 33,
+  DT_SYMTAB_SHNDX = 34,
+  DT_RELRSZ = 35,
+  DT_RELR = 36,
+  DT_RELRENT = 37,
+  DT_LOOS = 0x6000000D,
+  DT_ANDROID_REL = 0x6000000F,
+  DT_ANDROID_RELSZ = 0x60000010,
+  DT_ANDROID_RELA = 0x60000011,
+  DT_ANDROID_RELASZ = 0x60000012,
+  DT_HIOS = 0x6ffff000,
+  DT_ANDROID_RELR = 0x6fffe000,
+  DT_ANDROID_RELRSZ = 0x6fffe001,
+  DT_ANDROID_RELRENT = 0x6fffe003,
+  DT_GNU_HASH = 0x6ffffef5,
+  DT_LOPROC = 0x70000000,
+  DT_HIPROC = 0x7fffffff,
+} DynamicTags;
+
+typedef enum DynamicFlags {
+  // This flag signifies that the object being loaded may make reference to the
+  DF_ORIGIN = 0x00000001,
+
+  // If this flag is set in a shared object library, the dynamic linker's symbol
+  // resolution algorithm for references within the library is changed. Instead
+  // of starting a symbol search with the executable file, the dynamic linker
+  // starts from the shared object itself. If the shared object fails to supply
+  // the referenced symbol, the dynamic linker then searches the executable file
+  // and other shared objects as usual.
+  DF_SYMBOLIC = 0x00000002,
+
+  //  This flag is not set, no relocation entry should cause a modification to a
+  //  non-writable segment, as specified by the segment permissions in the
+  //  program header table.
+  DF_TEXTREL = 0x00000004,
+
+  // If set in a shared object or executable, this flag instructs the dynamic
+  // linker to process all relocations for the object containing this entry
+  // before transferring control to the program.
+  DF_BIND_NOW = 0x00000008,
+
+  // If set in a shared object or executable, this flag instructs the dynamic
+  // linker to reject attempts to load this file dynamically. It indicates that
+  // the shared object or executable contains code using a static thread-local
+  // storage scheme. Implementations need not support any form of thread-local
+  // storage.
+  DF_STATIC_TLS = 0x00000010,
+} DynamicFalgs;
+
+// Relocation types per CPU architecture
+#if SB_IS(ARCH_ARM) && SB_IS(32_BIT)
+typedef enum RelocationTypes {
+  R_ARM_ABS32 = 2,
+  R_ARM_REL32 = 3,
+  R_ARM_GLOB_DAT = 21,
+  R_ARM_JUMP_SLOT = 22,
+  R_ARM_COPY = 20,
+  R_ARM_RELATIVE = 23,
+} RelocationTypes;
+#elif SB_IS(ARCH_ARM) && SB_IS(64_BIT)
+typedef enum RelocationTypes {
+  R_AARCH64_ABS64 = 257,
+  R_AARCH64_COPY = 1024,
+  R_AARCH64_GLOB_DAT = 1025,
+  R_AARCH64_JUMP_SLOT = 1026,
+  R_AARCH64_RELATIVE = 1027,
+} RelocationTypes;
+#elif SB_IS(ARCH_X86) && SB_IS(32_BIT)
+typedef enum RelocationTypes {
+  R_386_32 = 1,
+  R_386_PC32 = 2,
+  R_386_GLOB_DAT = 6,
+  R_386_JMP_SLOT = 7,
+  R_386_RELATIVE = 8,
+} RelocationTypes;
+#elif SB_IS(ARCH_X86) && SB_IS(64_BIT)
+typedef enum RelocationTypes {
+  R_X86_64_64 = 1,
+  R_X86_64_PC32 = 2,
+  R_X86_64_GLOB_DAT = 6,
+  R_X86_64_JMP_SLOT = 7,
+  R_X86_64_RELATIVE = 8,
+} RelocationTypes;
+#else
+#error "Unsupported architecture for relocations."
+#endif
+
+// Helper macros for memory page computations.
+#ifndef PAGE_SIZE
+#define PAGE_SHIFT 12
+#define PAGE_SIZE (1UL << PAGE_SHIFT)
+#define PAGE_MASK (~(PAGE_SIZE - 1))
+#endif
+
+#define PAGE_START(x) ((x)&PAGE_MASK)
+#define PAGE_OFFSET(x) ((x) & ~PAGE_MASK)
+#define PAGE_END(x) PAGE_START((x) + (PAGE_SIZE - 1))
+
+#define SHN_UNDEF 0
+
+}  // namespace elf_loader
+}  // namespace starboard
+#endif  // STARBOARD_ELF_LOADER_ELF_H_
diff --git a/src/starboard/elf_loader/elf_hash_table.cc b/src/starboard/elf_loader/elf_hash_table.cc
new file mode 100644
index 0000000..b6b6c70
--- /dev/null
+++ b/src/starboard/elf_loader/elf_hash_table.cc
@@ -0,0 +1,69 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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.
+
+#include "starboard/elf_loader/elf_hash_table.h"
+#include "starboard/string.h"
+
+namespace starboard {
+namespace elf_loader {
+
+// Compute the ELF hash of a given symbol.
+// Defined in
+// https://refspecs.linuxfoundation.org/elf/gabi4+/ch5.dynamic.html#hash
+static unsigned ElfHash(const char* name) {
+  const uint8_t* ptr = reinterpret_cast<const uint8_t*>(name);
+  unsigned h = 0;
+  while (*ptr) {
+    h = (h << 4) + *ptr++;
+    unsigned g = h & 0xf0000000;
+    h ^= g;
+    h ^= g >> 24;
+  }
+  return h;
+}
+
+ElfHashTable::ElfHashTable()
+    : hash_bucket_(NULL),
+      hash_bucket_size_(0),
+      hash_chain_(NULL),
+      hash_chain_size_(0) {}
+void ElfHashTable::Init(uintptr_t dt_elf_hash) {
+  const Word* data = reinterpret_cast<const Word*>(dt_elf_hash);
+  hash_bucket_size_ = data[0];
+  hash_bucket_ = data + 2;
+  hash_chain_size_ = data[1];
+  hash_chain_ = hash_bucket_ + hash_bucket_size_;
+}
+
+bool ElfHashTable::IsValid() const {
+  return hash_bucket_size_ > 0;
+}
+
+const Sym* ElfHashTable::LookupByName(const char* symbol_name,
+                                      const Sym* symbol_table,
+                                      const char* string_table) const {
+  unsigned hash = ElfHash(symbol_name);
+
+  for (unsigned n = hash_bucket_[hash % hash_bucket_size_]; n != 0;
+       n = hash_chain_[n]) {
+    const Sym* symbol = &symbol_table[n];
+    // Check that the symbol has the appropriate name.
+    if (!SbStringCompareAll(string_table + symbol->st_name, symbol_name))
+      return symbol;
+  }
+  return NULL;
+}
+
+}  // namespace elf_loader
+}  // namespace starboard
diff --git a/src/starboard/elf_loader/elf_hash_table.h b/src/starboard/elf_loader/elf_hash_table.h
new file mode 100644
index 0000000..14bb9ca
--- /dev/null
+++ b/src/starboard/elf_loader/elf_hash_table.h
@@ -0,0 +1,62 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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 STARBOARD_ELF_LOADER_ELF_HASH_TABLE_H_
+#define STARBOARD_ELF_LOADER_ELF_HASH_TABLE_H_
+
+#include <stddef.h>
+#include "starboard/elf_loader/elf.h"
+
+namespace starboard {
+namespace elf_loader {
+
+// Models the hash table used to map symbol names to symbol entries using
+// the standard ELF format.
+class ElfHashTable {
+ public:
+  ElfHashTable();
+  // Initialize instance. |dt_elf_hash| should be the address that the
+  // DT_HASH entry points to in the input ELF dynamic section. Call IsValid()
+  // to determine whether the table was well-formed.
+  void Init(uintptr_t dt_elf_hash);
+
+  // Returns true iff the content of the table is valid.
+  bool IsValid() const;
+
+  // Index of the first dynamic symbol within the ELF symbol table.
+  size_t dyn_symbols_offset() const { return 1; }
+
+  // Number of dynamic symbols in the ELF symbol table.
+  size_t dyn_symbols_count() const { return hash_chain_size_ - 1; }
+
+  // Lookup |symbol_name| in the table. |symbol_table| should point to the
+  // ELF symbol table, and |string_table| to the start of its string table.
+  // Returns NULL on failure.
+  const Sym* LookupByName(const char* symbol_name,
+                          const Sym* symbol_table,
+                          const char* string_table) const;
+
+ private:
+  const Word* hash_bucket_;
+  size_t hash_bucket_size_;
+  const Word* hash_chain_;
+  size_t hash_chain_size_;
+
+  SB_DISALLOW_COPY_AND_ASSIGN(ElfHashTable);
+};
+
+}  // namespace elf_loader
+}  // namespace starboard
+
+#endif  // STARBOARD_ELF_LOADER_ELF_HASH_TABLE_H_
diff --git a/src/starboard/elf_loader/elf_header.cc b/src/starboard/elf_loader/elf_header.cc
new file mode 100644
index 0000000..bb3c07b
--- /dev/null
+++ b/src/starboard/elf_loader/elf_header.cc
@@ -0,0 +1,75 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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.
+
+#include "starboard/elf_loader/elf_header.h"
+
+#include "starboard/common/log.h"
+#include "starboard/memory.h"
+
+namespace starboard {
+namespace elf_loader {
+
+ElfHeader::ElfHeader() {
+  elf_header_.reset(new Ehdr());
+}
+
+bool ElfHeader::LoadElfHeader(File* file) {
+  SB_LOG(INFO) << "LoadElfHeader";
+  if (!file->ReadFromOffset(0, reinterpret_cast<char*>(elf_header_.get()),
+                            sizeof(Ehdr))) {
+    SB_LOG(ERROR) << "Failed to read file";
+    return false;
+  }
+
+  if (SbMemoryCompare(elf_header_->e_ident, ELFMAG, SELFMAG) != 0) {
+    SB_LOG(ERROR) << "Bad ELF magic: " << elf_header_->e_ident;
+    return false;
+  }
+
+  if (elf_header_->e_ident[EI_CLASS] != ELF_CLASS_VALUE) {
+    SB_LOG(ERROR) << "Not a " << ELF_BITS
+                  << "-bit class: " << elf_header_->e_ident[EI_CLASS];
+    return false;
+  }
+  if (elf_header_->e_ident[EI_DATA] != ELFDATA2LSB) {
+    SB_LOG(ERROR) << "Not little-endian class" << elf_header_->e_ident[EI_DATA];
+    return false;
+  }
+
+  if (elf_header_->e_type != ET_DYN) {
+    SB_LOG(ERROR) << "Not a shared library type:" << std::hex
+                  << elf_header_->e_type;
+    return false;
+  }
+
+  if (elf_header_->e_version != EV_CURRENT) {
+    SB_LOG(ERROR) << "Unexpected ELF version: " << elf_header_->e_version;
+    return false;
+  }
+
+  if (elf_header_->e_machine != ELF_MACHINE) {
+    SB_LOG(ERROR) << "Unexpected ELF machine type: " << std::hex
+                  << elf_header_->e_machine;
+    return false;
+  }
+
+  return true;
+}
+
+const Ehdr* ElfHeader::GetHeader() {
+  return elf_header_.get();
+}
+
+}  // namespace elf_loader
+}  // namespace starboard
diff --git a/src/starboard/elf_loader/elf_header.h b/src/starboard/elf_loader/elf_header.h
new file mode 100644
index 0000000..a8b666c
--- /dev/null
+++ b/src/starboard/elf_loader/elf_header.h
@@ -0,0 +1,45 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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 STARBOARD_ELF_LOADER_ELF_HEADER_H_
+#define STARBOARD_ELF_LOADER_ELF_HEADER_H_
+
+#include "starboard/elf_loader/elf.h"
+
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/elf_loader/file.h"
+
+namespace starboard {
+namespace elf_loader {
+
+// Class for loading, parsing and validating the ELF header section.
+class ElfHeader {
+ public:
+  ElfHeader();
+
+  // Load, parse and validate the ELF header.
+  bool LoadElfHeader(File* file);
+
+  // Get the actual header data.
+  const Ehdr* GetHeader();
+
+ private:
+  scoped_ptr<Ehdr> elf_header_;
+  SB_DISALLOW_COPY_AND_ASSIGN(ElfHeader);
+};
+
+}  // namespace elf_loader
+}  // namespace starboard
+
+#endif  // STARBOARD_ELF_LOADER_ELF_HEADER_H_
diff --git a/src/starboard/elf_loader/elf_header_test.cc b/src/starboard/elf_loader/elf_header_test.cc
new file mode 100644
index 0000000..c129af7
--- /dev/null
+++ b/src/starboard/elf_loader/elf_header_test.cc
@@ -0,0 +1,125 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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.
+
+#include "starboard/elf_loader/elf_header.h"
+
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/elf_loader/file.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace elf_loader {
+
+namespace {
+
+class DummyFile : public File {
+ public:
+  DummyFile(const char* buffer, int size) : buffer_(buffer), size_(size) {}
+
+  bool Open(const char* name) { return true; }
+  bool ReadFromOffset(int64_t offset, char* buffer, int size) {
+    SB_LOG(INFO) << "ReadFromOffset";
+    if (offset != 0) {
+      SB_LOG(ERROR) << "ReadFromOffset: Invalid offset " << offset;
+      return false;
+    }
+    if (size < size_) {
+      SB_LOG(ERROR) << "ReadFromOffset: Invalid size " << size;
+      return false;
+    }
+    SbMemoryCopy(buffer, buffer_, size);
+    return true;
+  }
+  void Close() {}
+
+ private:
+  const char* buffer_;
+  int size_;
+};
+
+class ElfHeaderTest : public ::testing::Test {
+ protected:
+  ElfHeaderTest() {
+    elf_header_.reset(new ElfHeader());
+    SbMemorySet(reinterpret_cast<char*>(&ehdr_data_), 0, sizeof(ehdr_data_));
+    ehdr_data_.e_machine = ELF_MACHINE;
+    ehdr_data_.e_ident[0] = 0x7F;
+    ehdr_data_.e_ident[1] = 'E';
+    ehdr_data_.e_ident[2] = 'L';
+    ehdr_data_.e_ident[3] = 'F';
+    ehdr_data_.e_ident[EI_CLASS] = ELF_CLASS_VALUE;
+    ehdr_data_.e_ident[EI_DATA] = ELFDATA2LSB;
+    ehdr_data_.e_type = ET_DYN;
+    ehdr_data_.e_version = EV_CURRENT;
+    ehdr_data_.e_machine = ELF_MACHINE;
+
+    dummy_file_.reset(new DummyFile(reinterpret_cast<const char*>(&ehdr_data_),
+                                    sizeof(ehdr_data_)));
+  }
+  ~ElfHeaderTest() {}
+
+  scoped_ptr<ElfHeader> elf_header_;
+  Ehdr ehdr_data_;
+  scoped_ptr<DummyFile> dummy_file_;
+};
+
+TEST_F(ElfHeaderTest, Initialize) {
+  EXPECT_TRUE(elf_header_->LoadElfHeader(dummy_file_.get()));
+}
+
+TEST_F(ElfHeaderTest, NegativeBadImage) {
+  ehdr_data_.e_ident[1] = 'F';
+  dummy_file_.reset(new DummyFile(reinterpret_cast<const char*>(&ehdr_data_),
+                                  sizeof(ehdr_data_)));
+  EXPECT_FALSE(elf_header_->LoadElfHeader(dummy_file_.get()));
+}
+
+TEST_F(ElfHeaderTest, NegativeBadClass) {
+  ehdr_data_.e_ident[EI_CLASS] = 0;
+  dummy_file_.reset(new DummyFile(reinterpret_cast<const char*>(&ehdr_data_),
+                                  sizeof(ehdr_data_)));
+  EXPECT_FALSE(elf_header_->LoadElfHeader(dummy_file_.get()));
+}
+
+TEST_F(ElfHeaderTest, NegativeWrongGulliverLilliput) {
+  ehdr_data_.e_type = 2;
+  dummy_file_.reset(new DummyFile(reinterpret_cast<const char*>(&ehdr_data_),
+                                  sizeof(ehdr_data_)));
+  EXPECT_FALSE(elf_header_->LoadElfHeader(dummy_file_.get()));
+}
+
+TEST_F(ElfHeaderTest, NegativeBadType) {
+  ehdr_data_.e_type = 2;
+  dummy_file_.reset(new DummyFile(reinterpret_cast<const char*>(&ehdr_data_),
+                                  sizeof(ehdr_data_)));
+  EXPECT_FALSE(elf_header_->LoadElfHeader(dummy_file_.get()));
+}
+
+TEST_F(ElfHeaderTest, NegativeBadVersion) {
+  ehdr_data_.e_version = 2;
+  dummy_file_.reset(new DummyFile(reinterpret_cast<const char*>(&ehdr_data_),
+                                  sizeof(ehdr_data_)));
+  EXPECT_FALSE(elf_header_->LoadElfHeader(dummy_file_.get()));
+}
+
+TEST_F(ElfHeaderTest, NegativeBadMachine) {
+  ehdr_data_.e_machine = 0;
+  dummy_file_.reset(new DummyFile(reinterpret_cast<const char*>(&ehdr_data_),
+                                  sizeof(ehdr_data_)));
+  EXPECT_FALSE(elf_header_->LoadElfHeader(dummy_file_.get()));
+}
+}  // namespace
+}  // namespace elf_loader
+}  // namespace starboard
diff --git a/src/starboard/elf_loader/elf_loader.cc b/src/starboard/elf_loader/elf_loader.cc
new file mode 100644
index 0000000..d62d07a
--- /dev/null
+++ b/src/starboard/elf_loader/elf_loader.cc
@@ -0,0 +1,38 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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.
+
+#include "starboard/elf_loader/elf_loader.h"
+#include "starboard/common/log.h"
+#include "starboard/elf_loader/elf_loader_impl.h"
+#include "starboard/elf_loader/file_impl.h"
+
+namespace starboard {
+namespace elf_loader {
+
+ElfLoader::~ElfLoader() {}
+
+bool ElfLoader::Load(const char* file_name) {
+  return impl_->Load(file_name);
+}
+
+void* ElfLoader::LookupSymbol(const char* symbol) {
+  return impl_->LookupSymbol(symbol);
+}
+
+ElfLoader::ElfLoader() {
+  impl_.reset(new ElfLoaderImpl());
+}
+
+}  // namespace elf_loader
+}  // namespace starboard
diff --git a/src/starboard/elf_loader/elf_loader.gyp b/src/starboard/elf_loader/elf_loader.gyp
new file mode 100644
index 0000000..7860e03
--- /dev/null
+++ b/src/starboard/elf_loader/elf_loader.gyp
@@ -0,0 +1,84 @@
+# Copyright 2019 The Cobalt Authors. All Rights Reserved.
+#
+# 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.
+
+{
+  'targets': [
+    {
+      'target_name': 'elf_loader',
+      'type': 'static_library',
+      'include_dirs': [
+        'src/include',
+        'src/src/',
+      ],
+      'dependencies': [
+        '<(DEPTH)/starboard/starboard.gyp:starboard',
+      ],
+      'sources': [
+        'elf_header.h',
+        'elf_header.cc',
+        'elf_hash_table.h',
+        'elf_hash_table.cc',
+        'elf_loader.h',
+        'elf_loader.cc',
+        'elf_loader_impl.h',
+        'elf_loader_impl.cc',
+        'exported_symbols.cc',
+        'file.h',
+        'file_impl.h',
+        'file_impl.cc',
+        'gnu_hash_table.h',
+        'gnu_hash_table.cc',
+        'dynamic_section.h',
+        'dynamic_section.cc',
+        'program_table.h',
+        'program_table.cc',
+        'relocations.h',
+        'relocations.cc',
+      ],
+    },
+    {
+      'target_name': 'elf_loader_sandbox',
+      'type': '<(final_executable_type)',
+      'include_dirs': [
+        'src/include',
+        'src/src/',
+      ],
+      'dependencies': [
+        'elf_loader',
+        '<(DEPTH)/starboard/starboard.gyp:starboard_full',
+      ],
+      'sources': [
+        'sandbox.cc',
+      ],
+    },
+    {
+      'target_name': 'elf_loader_test',
+      'type': '<(gtest_target_type)',
+      'sources': [
+        '<(DEPTH)/starboard/common/test_main.cc',
+        'elf_loader_test.cc',
+        'elf_header_test.cc',
+        'dynamic_section_test.cc',
+        'program_table_test.cc',
+        'relocations_test.cc',
+      ],
+      'dependencies': [
+        'elf_loader',
+        '<(DEPTH)/starboard/starboard.gyp:starboard_full',
+        '<(DEPTH)/testing/gmock.gyp:gmock',
+        '<(DEPTH)/testing/gtest.gyp:gtest',
+      ],
+    },
+  ]
+}
diff --git a/src/starboard/elf_loader/elf_loader.h b/src/starboard/elf_loader/elf_loader.h
new file mode 100644
index 0000000..7bda25d
--- /dev/null
+++ b/src/starboard/elf_loader/elf_loader.h
@@ -0,0 +1,47 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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 STARBOARD_ELF_LOADER_ELF_LOADER_H_
+#define STARBOARD_ELF_LOADER_ELF_LOADER_H_
+
+#include "starboard/common/scoped_ptr.h"
+
+namespace starboard {
+namespace elf_loader {
+
+class ElfLoaderImpl;
+
+// A loader for ELF dynamic shared library.
+class ElfLoader {
+ public:
+  ElfLoader();
+
+  // Loads the shared library
+  bool Load(const char* file_name);
+
+  // Looks up the symbol address in the
+  // shared library.
+  void* LookupSymbol(const char* symbol);
+
+  ~ElfLoader();
+
+ private:
+  scoped_ptr<ElfLoaderImpl> impl_;
+
+  SB_DISALLOW_COPY_AND_ASSIGN(ElfLoader);
+};
+
+}  // namespace elf_loader
+}  // namespace starboard
+#endif  // STARBOARD_ELF_LOADER_ELF_LOADER_H_
diff --git a/src/starboard/elf_loader/elf_loader_impl.cc b/src/starboard/elf_loader/elf_loader_impl.cc
new file mode 100644
index 0000000..f81db05
--- /dev/null
+++ b/src/starboard/elf_loader/elf_loader_impl.cc
@@ -0,0 +1,136 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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.
+
+#include "starboard/elf_loader/elf_loader_impl.h"
+#include "starboard/common/log.h"
+#include "starboard/elf_loader/elf.h"
+#include "starboard/elf_loader/file_impl.h"
+#include "starboard/memory.h"
+#include "starboard/string.h"
+
+namespace starboard {
+namespace elf_loader {
+
+ElfLoaderImpl::ElfLoaderImpl() {}
+
+bool ElfLoaderImpl::Load(const char* name) {
+  SB_LOG(INFO) << "Loading: " << name;
+  elf_file_.reset(new FileImpl());
+  elf_file_->Open(name);
+
+  elf_header_loader_.reset(new ElfHeader());
+  if (!elf_header_loader_->LoadElfHeader(elf_file_.get())) {
+    SB_LOG(ERROR) << "Failed to loaded ELF header";
+    return false;
+  }
+
+  SB_LOG(INFO) << "Loaded ELF header";
+
+  program_table_.reset(new ProgramTable());
+  program_table_->LoadProgramHeader(elf_header_loader_->GetHeader(),
+                                    elf_file_.get());
+
+  SB_LOG(INFO) << "Loaded Program header";
+
+  if (!program_table_->ReserveLoadMemory()) {
+    SB_LOG(ERROR) << "Failed to reserve memory space";
+    return false;
+  }
+
+  SB_LOG(INFO) << "Reserved address space";
+
+  if (!program_table_->LoadSegments(elf_file_.get())) {
+    SB_LOG(ERROR) << "Failed to load segments";
+    return false;
+  }
+  SB_LOG(INFO) << "Loaded segments";
+
+  Dyn* dynamic = NULL;
+  size_t dynamic_count = 0;
+  Word dynamic_flags = 0;
+  program_table_->GetDynamicSection(&dynamic, &dynamic_count, &dynamic_flags);
+  if (!dynamic) {
+    SB_LOG(ERROR) << "No PT_DYNAMIC section!";
+    return false;
+  }
+  dynamic_section_.reset(
+      new DynamicSection(program_table_->GetBaseMemoryAddress(), dynamic,
+                         dynamic_count, dynamic_flags));
+  if (!dynamic_section_->InitDynamicSection()) {
+    SB_LOG(ERROR) << "Failed to initialize dynamic section";
+    return false;
+  }
+  SB_LOG(INFO) << "Initialized dynamic section";
+  if (!dynamic_section_->InitDynamicSymbols()) {
+    SB_LOG(ERROR) << "Failed to load dynamic symbols";
+    return false;
+  }
+  SB_LOG(INFO) << "Initialized dynamic symbols";
+
+  exported_symbols_.reset(new ExportedSymbols());
+  relocations_.reset(new Relocations(program_table_->GetBaseMemoryAddress(),
+                                     dynamic_section_.get(),
+                                     exported_symbols_.get()));
+  if (!relocations_->InitRelocations()) {
+    SB_LOG(ERROR) << "Failed to initialize relocations";
+    return false;
+  }
+  if (relocations_->HasTextRelocations()) {
+    SB_LOG(INFO) << "HasTextRelocations";
+    // Adjust the memory protection to its to allow modifications.
+    if (program_table_->AdjustMemoryProtectionOfReadOnlySegments(
+            kSbMemoryMapProtectWrite) < 0) {
+      SB_LOG(ERROR) << "Unable to make segments writable";
+      return false;
+    }
+  }
+  SB_LOG(INFO) << "Loaded relocations";
+  if (!relocations_->ApplyAllRelocations()) {
+    SB_LOG(ERROR) << "Failed to apply relocations";
+    return false;
+  }
+
+  if (relocations_->HasTextRelocations()) {
+    // Restores the memory protection to its original state.
+    if (program_table_->AdjustMemoryProtectionOfReadOnlySegments(
+            kSbMemoryMapProtectReserved) < 0) {
+      SB_LOG(ERROR) << "Unable to restore segment protection";
+      return false;
+    }
+  }
+
+  SB_LOG(INFO) << "Applied relocations";
+
+  SB_LOG(INFO) << "Call constructors";
+  dynamic_section_->CallConstructors();
+
+  SB_LOG(INFO) << "Finished loading";
+
+  return true;
+}
+void* ElfLoaderImpl::LookupSymbol(const char* symbol) {
+  const Sym* sym = dynamic_section_->LookupByName(symbol);
+  void* address = NULL;
+  if (sym) {
+    address = reinterpret_cast<void*>(program_table_->GetBaseMemoryAddress() +
+                                      sym->st_value);
+  }
+  return address;
+}
+
+ElfLoaderImpl::~ElfLoaderImpl() {
+  dynamic_section_->CallDestructors();
+}
+}  // namespace elf_loader
+}  // namespace starboard
diff --git a/src/starboard/elf_loader/elf_loader_impl.h b/src/starboard/elf_loader/elf_loader_impl.h
new file mode 100644
index 0000000..b317ecf
--- /dev/null
+++ b/src/starboard/elf_loader/elf_loader_impl.h
@@ -0,0 +1,53 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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 STARBOARD_ELF_LOADER_ELF_LOADER_IMPL_H_
+#define STARBOARD_ELF_LOADER_ELF_LOADER_IMPL_H_
+
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/elf_loader/dynamic_section.h"
+#include "starboard/elf_loader/elf.h"
+#include "starboard/elf_loader/elf_hash_table.h"
+#include "starboard/elf_loader/elf_header.h"
+#include "starboard/elf_loader/exported_symbols.h"
+#include "starboard/elf_loader/file.h"
+#include "starboard/elf_loader/gnu_hash_table.h"
+#include "starboard/elf_loader/program_table.h"
+#include "starboard/elf_loader/relocations.h"
+
+namespace starboard {
+namespace elf_loader {
+
+// Implementation of the elf loader.
+class ElfLoaderImpl {
+ public:
+  ElfLoaderImpl();
+  bool Load(const char* file_name);
+  void* LookupSymbol(const char* symbol);
+  ~ElfLoaderImpl();
+
+ private:
+  scoped_ptr<File> elf_file_;
+  scoped_ptr<ElfHeader> elf_header_loader_;
+  scoped_ptr<ProgramTable> program_table_;
+  scoped_ptr<DynamicSection> dynamic_section_;
+  scoped_ptr<ExportedSymbols> exported_symbols_;
+  scoped_ptr<Relocations> relocations_;
+
+  SB_DISALLOW_COPY_AND_ASSIGN(ElfLoaderImpl);
+};
+
+}  // namespace elf_loader
+}  // namespace starboard
+#endif  // STARBOARD_ELF_LOADER_ELF_LOADER_IMPL_H_
diff --git a/src/starboard/elf_loader/elf_loader_test.cc b/src/starboard/elf_loader/elf_loader_test.cc
new file mode 100644
index 0000000..d3351f3
--- /dev/null
+++ b/src/starboard/elf_loader/elf_loader_test.cc
@@ -0,0 +1,38 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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.
+
+#include "starboard/elf_loader/elf_loader_impl.h"
+
+#include "starboard/common/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace elf_loader {
+
+namespace {
+
+// TODO: implement using real shared library fro the file system.
+class ElfLoaderTest : public ::testing::Test {
+ protected:
+  ElfLoaderTest() {}
+  ~ElfLoaderTest() {}
+};
+
+TEST_F(ElfLoaderTest, Initialize) {
+  EXPECT_TRUE(true);
+}
+
+}  // namespace
+}  // namespace elf_loader
+}  // namespace starboard
diff --git a/src/starboard/elf_loader/exported_symbols.cc b/src/starboard/elf_loader/exported_symbols.cc
new file mode 100644
index 0000000..a4c4539
--- /dev/null
+++ b/src/starboard/elf_loader/exported_symbols.cc
@@ -0,0 +1,512 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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.
+
+#include "starboard/elf_loader/exported_symbols.h"
+
+// TODO: Remove these once the API leaks are fixed.
+//#define LOCAL_TEST_WITH_API_LEAKS
+#ifdef LOCAL_TEST_WITH_API_LEAKS
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <nl_types.h>
+#include <setjmp.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+#include "starboard/accessibility.h"
+#include "starboard/audio_sink.h"
+#include "starboard/byte_swap.h"
+#include "starboard/character.h"
+#include "starboard/condition_variable.h"
+#include "starboard/cpu_features.h"
+#include "starboard/decode_target.h"
+#include "starboard/directory.h"
+#include "starboard/double.h"
+#include "starboard/egl.h"
+#include "starboard/event.h"
+#include "starboard/file.h"
+#include "starboard/gles.h"
+#include "starboard/image.h"
+#include "starboard/log.h"
+#include "starboard/memory.h"
+#include "starboard/microphone.h"
+#include "starboard/mutex.h"
+#include "starboard/once.h"
+#include "starboard/player.h"
+#include "starboard/socket.h"
+#include "starboard/socket_waiter.h"
+#include "starboard/speech_recognizer.h"
+#include "starboard/speech_synthesis.h"
+#include "starboard/storage.h"
+#include "starboard/string.h"
+#include "starboard/system.h"
+#include "starboard/thread.h"
+#include "starboard/time_zone.h"
+#include "starboard/ui_navigation.h"
+
+// TODO: cleanup these hack as we fix the API leaks
+
+void tmp_dl_iterate_phdr() {
+  SB_LOG(INFO) << "tmp_dl_iterate_phdr";
+}
+
+void tmp__cxa_thread_atexit_impl() {
+  SB_LOG(INFO) << "tmp__cxa_thread_atexit_impl";
+}
+
+namespace starboard {
+namespace elf_loader {
+
+ExportedSymbols::ExportedSymbols() {
+    map_["SbAccessibilityGetDisplaySettings"] =
+      reinterpret_cast<const void*>(SbAccessibilityGetDisplaySettings);
+  map_["SbAccessibilityGetTextToSpeechSettings"] =
+      reinterpret_cast<const void*>(SbAccessibilityGetTextToSpeechSettings);
+  map_["SbAudioSinkCreate"] = reinterpret_cast<const void*>(SbAudioSinkCreate);
+  map_["SbAudioSinkDestroy"] =
+      reinterpret_cast<const void*>(SbAudioSinkDestroy);
+  map_["SbAudioSinkGetNearestSupportedSampleFrequency"] =
+      reinterpret_cast<const void*>(
+          SbAudioSinkGetNearestSupportedSampleFrequency);
+  map_["SbAudioSinkIsAudioFrameStorageTypeSupported"] =
+      reinterpret_cast<const void*>(
+          SbAudioSinkIsAudioFrameStorageTypeSupported);
+  map_["SbAudioSinkIsAudioSampleTypeSupported"] =
+      reinterpret_cast<const void*>(SbAudioSinkIsAudioSampleTypeSupported);
+  map_["SbAudioSinkIsValid"] =
+      reinterpret_cast<const void*>(SbAudioSinkIsValid);
+  map_["SbByteSwapU16"] = reinterpret_cast<const void*>(SbByteSwapU16);
+  map_["SbByteSwapU32"] = reinterpret_cast<const void*>(SbByteSwapU32);
+  map_["SbByteSwapU64"] = reinterpret_cast<const void*>(SbByteSwapU64);
+  map_["SbCharacterIsDigit"] =
+      reinterpret_cast<const void*>(SbCharacterIsDigit);
+  map_["SbCharacterIsHexDigit"] =
+      reinterpret_cast<const void*>(SbCharacterIsHexDigit);
+  map_["SbCharacterIsSpace"] =
+      reinterpret_cast<const void*>(SbCharacterIsSpace);
+  map_["SbCharacterToLower"] =
+      reinterpret_cast<const void*>(SbCharacterToLower);
+  map_["SbCharacterToUpper"] =
+      reinterpret_cast<const void*>(SbCharacterToUpper);
+  map_["SbConditionVariableBroadcast"] =
+      reinterpret_cast<const void*>(SbConditionVariableBroadcast);
+  map_["SbConditionVariableCreate"] =
+      reinterpret_cast<const void*>(SbConditionVariableCreate);
+  map_["SbConditionVariableDestroy"] =
+      reinterpret_cast<const void*>(SbConditionVariableDestroy);
+  map_["SbConditionVariableSignal"] =
+      reinterpret_cast<const void*>(SbConditionVariableSignal);
+  map_["SbConditionVariableWait"] =
+      reinterpret_cast<const void*>(SbConditionVariableWait);
+  map_["SbConditionVariableWaitTimed"] =
+      reinterpret_cast<const void*>(SbConditionVariableWaitTimed);
+  map_["SbCPUFeaturesGet"] = reinterpret_cast<const void*>(SbCPUFeaturesGet);
+  map_["SbDecodeTargetGetInfo"] =
+      reinterpret_cast<const void*>(SbDecodeTargetGetInfo);
+  map_["SbDecodeTargetRelease"] =
+      reinterpret_cast<const void*>(SbDecodeTargetRelease);
+  map_["SbDirectoryCanOpen"] =
+      reinterpret_cast<const void*>(SbDirectoryCanOpen);
+  map_["SbDirectoryClose"] = reinterpret_cast<const void*>(SbDirectoryClose);
+  map_["SbDirectoryCreate"] = reinterpret_cast<const void*>(SbDirectoryCreate);
+  map_["SbDirectoryGetNext"] =
+      reinterpret_cast<const void*>(SbDirectoryGetNext);
+  map_["SbDirectoryOpen"] = reinterpret_cast<const void*>(SbDirectoryOpen);
+  map_["SbDoubleAbsolute"] = reinterpret_cast<const void*>(SbDoubleAbsolute);
+  map_["SbDoubleExponent"] = reinterpret_cast<const void*>(SbDoubleExponent);
+  map_["SbDoubleFloor"] = reinterpret_cast<const void*>(SbDoubleFloor);
+  map_["SbDoubleIsFinite"] = reinterpret_cast<const void*>(SbDoubleIsFinite);
+  map_["SbDoubleIsNan"] = reinterpret_cast<const void*>(SbDoubleIsNan);
+  map_["SbDrmCloseSession"] = reinterpret_cast<const void*>(SbDrmCloseSession);
+  map_["SbDrmCreateSystem"] = reinterpret_cast<const void*>(SbDrmCreateSystem);
+  map_["SbDrmDestroySystem"] =
+      reinterpret_cast<const void*>(SbDrmDestroySystem);
+  map_["SbDrmGenerateSessionUpdateRequest"] =
+      reinterpret_cast<const void*>(SbDrmGenerateSessionUpdateRequest);
+  map_["SbDrmIsServerCertificateUpdatable"] =
+      reinterpret_cast<const void*>(SbDrmIsServerCertificateUpdatable);
+  map_["SbDrmUpdateServerCertificate"] =
+      reinterpret_cast<const void*>(SbDrmUpdateServerCertificate);
+  map_["SbDrmUpdateSession"] =
+      reinterpret_cast<const void*>(SbDrmUpdateSession);
+  map_["SbEventCancel"] = reinterpret_cast<const void*>(SbEventCancel);
+  map_["SbEventSchedule"] = reinterpret_cast<const void*>(SbEventSchedule);
+  map_["SbFileCanOpen"] = reinterpret_cast<const void*>(SbFileCanOpen);
+  map_["SbFileClose"] = reinterpret_cast<const void*>(SbFileClose);
+  map_["SbFileDelete"] = reinterpret_cast<const void*>(SbFileDelete);
+  map_["SbFileExists"] = reinterpret_cast<const void*>(SbFileExists);
+  map_["SbFileFlush"] = reinterpret_cast<const void*>(SbFileFlush);
+  map_["SbFileGetInfo"] = reinterpret_cast<const void*>(SbFileGetInfo);
+  map_["SbFileGetPathInfo"] = reinterpret_cast<const void*>(SbFileGetPathInfo);
+  map_["SbFileModeStringToFlags"] =
+      reinterpret_cast<const void*>(SbFileModeStringToFlags);
+  map_["SbFileOpen"] = reinterpret_cast<const void*>(SbFileOpen);
+  map_["SbFileRead"] = reinterpret_cast<const void*>(SbFileRead);
+  map_["SbFileSeek"] = reinterpret_cast<const void*>(SbFileSeek);
+  map_["SbFileTruncate"] = reinterpret_cast<const void*>(SbFileTruncate);
+  map_["SbFileWrite"] = reinterpret_cast<const void*>(SbFileWrite);
+  map_["SbGetEglInterface"] = reinterpret_cast<const void*>(SbGetEglInterface);
+  map_["SbGetGlesInterface"] =
+      reinterpret_cast<const void*>(SbGetGlesInterface);
+  map_["SbImageDecode"] = reinterpret_cast<const void*>(SbImageDecode);
+  map_["SbImageIsDecodeSupported"] =
+      reinterpret_cast<const void*>(SbImageIsDecodeSupported);
+  map_["SbLog"] = reinterpret_cast<const void*>(SbLog);
+  map_["SbLogFlush"] = reinterpret_cast<const void*>(SbLogFlush);
+  map_["SbLogFormat"] = reinterpret_cast<const void*>(SbLogFormat);
+  map_["SbLogIsTty"] = reinterpret_cast<const void*>(SbLogIsTty);
+  map_["SbLogRaw"] = reinterpret_cast<const void*>(SbLogRaw);
+  map_["SbLogRawFormat"] = reinterpret_cast<const void*>(SbLogRawFormat);
+  map_["SbMediaCanPlayMimeAndKeySystem"] =
+      reinterpret_cast<const void*>(SbMediaCanPlayMimeAndKeySystem);
+  map_["SbMediaGetAudioBufferBudget"] =
+      reinterpret_cast<const void*>(SbMediaGetAudioBufferBudget);
+  map_["SbMediaGetBufferAlignment"] =
+      reinterpret_cast<const void*>(SbMediaGetBufferAlignment);
+  map_["SbMediaGetBufferAllocationUnit"] =
+      reinterpret_cast<const void*>(SbMediaGetBufferAllocationUnit);
+  map_["SbMediaGetBufferGarbageCollectionDurationThreshold"] =
+      reinterpret_cast<const void*>(
+          SbMediaGetBufferGarbageCollectionDurationThreshold);
+  map_["SbMediaGetBufferPadding"] =
+      reinterpret_cast<const void*>(SbMediaGetBufferPadding);
+  map_["SbMediaGetInitialBufferCapacity"] =
+      reinterpret_cast<const void*>(SbMediaGetInitialBufferCapacity);
+  map_["SbMediaGetMaxBufferCapacity"] =
+      reinterpret_cast<const void*>(SbMediaGetMaxBufferCapacity);
+  map_["SbMediaGetProgressiveBufferBudget"] =
+      reinterpret_cast<const void*>(SbMediaGetProgressiveBufferBudget);
+  map_["SbMediaGetVideoBufferBudget"] =
+      reinterpret_cast<const void*>(SbMediaGetVideoBufferBudget);
+  map_["SbMediaIsBufferPoolAllocateOnDemand"] =
+      reinterpret_cast<const void*>(SbMediaIsBufferPoolAllocateOnDemand);
+  map_["SbMediaIsBufferUsingMemoryPool"] =
+      reinterpret_cast<const void*>(SbMediaIsBufferUsingMemoryPool);
+  map_["SbMediaSetAudioWriteDuration"] =
+      reinterpret_cast<const void*>(SbMediaSetAudioWriteDuration);
+  map_["SbMemoryAllocateAlignedUnchecked"] =
+      reinterpret_cast<const void*>(SbMemoryAllocateAlignedUnchecked);
+  map_["SbMemoryAllocateUnchecked"] =
+      reinterpret_cast<const void*>(SbMemoryAllocateUnchecked);
+  map_["SbMemoryCompare"] = reinterpret_cast<const void*>(SbMemoryCompare);
+  map_["SbMemoryCopy"] = reinterpret_cast<const void*>(SbMemoryCopy);
+  map_["SbMemoryFindByte"] = reinterpret_cast<const void*>(SbMemoryFindByte);
+  map_["SbMemoryFree"] = reinterpret_cast<const void*>(SbMemoryFree);
+  map_["SbMemoryFreeAligned"] =
+      reinterpret_cast<const void*>(SbMemoryFreeAligned);
+  map_["SbMemoryMap"] = reinterpret_cast<const void*>(SbMemoryMap);
+  map_["SbMemoryMove"] = reinterpret_cast<const void*>(SbMemoryMove);
+  map_["SbMemoryProtect"] = reinterpret_cast<const void*>(SbMemoryProtect);
+  map_["SbMemoryReallocateUnchecked"] =
+      reinterpret_cast<const void*>(SbMemoryReallocateUnchecked);
+  map_["SbMemorySet"] = reinterpret_cast<const void*>(SbMemorySet);
+  map_["SbMemoryUnmap"] = reinterpret_cast<const void*>(SbMemoryUnmap);
+
+#if SB_HAS(MICROPHONE)
+  map_["SbMicrophoneClose"] = reinterpret_cast<const void*>(SbMicrophoneClose);
+  map_["SbMicrophoneCreate"] =
+      reinterpret_cast<const void*>(SbMicrophoneCreate);
+  map_["SbMicrophoneDestroy"] =
+      reinterpret_cast<const void*>(SbMicrophoneDestroy);
+  map_["SbMicrophoneGetAvailable"] =
+      reinterpret_cast<const void*>(SbMicrophoneGetAvailable);
+  map_["SbMicrophoneIsSampleRateSupported"] =
+      reinterpret_cast<const void*>(SbMicrophoneIsSampleRateSupported);
+  map_["SbMicrophoneOpen"] = reinterpret_cast<const void*>(SbMicrophoneOpen);
+  map_["SbMicrophoneRead"] = reinterpret_cast<const void*>(SbMicrophoneRead);
+#endif
+
+  map_["SbMutexAcquire"] = reinterpret_cast<const void*>(SbMutexAcquire);
+  map_["SbMutexAcquireTry"] = reinterpret_cast<const void*>(SbMutexAcquireTry);
+  map_["SbMutexCreate"] = reinterpret_cast<const void*>(SbMutexCreate);
+  map_["SbMutexDestroy"] = reinterpret_cast<const void*>(SbMutexDestroy);
+  map_["SbMutexRelease"] = reinterpret_cast<const void*>(SbMutexRelease);
+  map_["SbOnce"] = reinterpret_cast<const void*>(SbOnce);
+
+  map_["SbPlayerCreate"] = reinterpret_cast<const void*>(SbPlayerCreate);
+  map_["SbPlayerDestroy"] = reinterpret_cast<const void*>(SbPlayerDestroy);
+  map_["SbPlayerGetCurrentFrame"] =
+      reinterpret_cast<const void*>(SbPlayerGetCurrentFrame);
+  map_["SbPlayerGetInfo2"] = reinterpret_cast<const void*>(SbPlayerGetInfo2);
+  map_["SbPlayerGetMaximumNumberOfSamplesPerWrite"] =
+      reinterpret_cast<const void*>(SbPlayerGetMaximumNumberOfSamplesPerWrite);
+  map_["SbPlayerOutputModeSupported"] =
+      reinterpret_cast<const void*>(SbPlayerOutputModeSupported);
+  map_["SbPlayerSeek2"] = reinterpret_cast<const void*>(SbPlayerSeek2);
+  map_["SbPlayerSetBounds"] = reinterpret_cast<const void*>(SbPlayerSetBounds);
+  map_["SbPlayerSetPlaybackRate"] =
+      reinterpret_cast<const void*>(SbPlayerSetPlaybackRate);
+  map_["SbPlayerSetVolume"] = reinterpret_cast<const void*>(SbPlayerSetVolume);
+  map_["SbPlayerWriteEndOfStream"] =
+      reinterpret_cast<const void*>(SbPlayerWriteEndOfStream);
+  map_["SbPlayerWriteSample2"] =
+      reinterpret_cast<const void*>(SbPlayerWriteSample2);
+  map_["SbSocketAccept"] = reinterpret_cast<const void*>(SbSocketAccept);
+  map_["SbSocketBind"] = reinterpret_cast<const void*>(SbSocketBind);
+  map_["SbSocketClearLastError"] = reinterpret_cast<const void*>(SbSocketClearLastError);
+  map_["SbSocketConnect"] = reinterpret_cast<const void*>(SbSocketConnect);
+  map_["SbSocketCreate"] = reinterpret_cast<const void*>(SbSocketCreate);
+  map_["SbSocketDestroy"] = reinterpret_cast<const void*>(SbSocketDestroy);
+  map_["SbSocketFreeResolution"] =
+      reinterpret_cast<const void*>(SbSocketFreeResolution);
+  map_["SbSocketGetInterfaceAddress"] =
+      reinterpret_cast<const void*>(SbSocketGetInterfaceAddress);
+  map_["SbSocketGetLastError"] =
+      reinterpret_cast<const void*>(SbSocketGetLastError);
+  map_["SbSocketGetLocalAddress"] =
+      reinterpret_cast<const void*>(SbSocketGetLocalAddress);
+  map_["SbSocketIsConnected"] =
+      reinterpret_cast<const void*>(SbSocketIsConnected);
+  map_["SbSocketIsConnectedAndIdle"] =
+      reinterpret_cast<const void*>(SbSocketIsConnectedAndIdle);
+  map_["SbSocketJoinMulticastGroup"] =
+      reinterpret_cast<const void*>(SbSocketJoinMulticastGroup);
+  map_["SbSocketListen"] = reinterpret_cast<const void*>(SbSocketListen);
+  map_["SbSocketReceiveFrom"] =
+      reinterpret_cast<const void*>(SbSocketReceiveFrom);
+  map_["SbSocketResolve"] = reinterpret_cast<const void*>(SbSocketResolve);
+  map_["SbSocketSendTo"] = reinterpret_cast<const void*>(SbSocketSendTo);
+  map_["SbSocketSetBroadcast"] =
+      reinterpret_cast<const void*>(SbSocketSetBroadcast);
+  map_["SbSocketSetReceiveBufferSize"] =
+      reinterpret_cast<const void*>(SbSocketSetReceiveBufferSize);
+  map_["SbSocketSetReuseAddress"] =
+      reinterpret_cast<const void*>(SbSocketSetReuseAddress);
+  map_["SbSocketSetSendBufferSize"] =
+      reinterpret_cast<const void*>(SbSocketSetSendBufferSize);
+  map_["SbSocketSetTcpKeepAlive"] =
+      reinterpret_cast<const void*>(SbSocketSetTcpKeepAlive);
+  map_["SbSocketSetTcpNoDelay"] =
+      reinterpret_cast<const void*>(SbSocketSetTcpNoDelay);
+  map_["SbSocketSetTcpWindowScaling"] =
+      reinterpret_cast<const void*>(SbSocketSetTcpWindowScaling);
+  map_["SbSocketWaiterAdd"] = reinterpret_cast<const void*>(SbSocketWaiterAdd);
+  map_["SbSocketWaiterCreate"] =
+      reinterpret_cast<const void*>(SbSocketWaiterCreate);
+  map_["SbSocketWaiterDestroy"] =
+      reinterpret_cast<const void*>(SbSocketWaiterDestroy);
+  map_["SbSocketWaiterRemove"] =
+      reinterpret_cast<const void*>(SbSocketWaiterRemove);
+  map_["SbSocketWaiterWait"] =
+      reinterpret_cast<const void*>(SbSocketWaiterWait);
+  map_["SbSocketWaiterWaitTimed"] =
+      reinterpret_cast<const void*>(SbSocketWaiterWaitTimed);
+  map_["SbSocketWaiterWakeUp"] =
+      reinterpret_cast<const void*>(SbSocketWaiterWakeUp);
+
+#if SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
+  map_["SbSpeechRecognizerCreate"] =
+      reinterpret_cast<const void*>(SbSpeechRecognizerCreate);
+  map_["SbSpeechRecognizerDestroy"] =
+      reinterpret_cast<const void*>(SbSpeechRecognizerDestroy);
+  map_["SbSpeechRecognizerStart"] =
+      reinterpret_cast<const void*>(SbSpeechRecognizerStart);
+  map_["SbSpeechRecognizerStop"] =
+      reinterpret_cast<const void*>(SbSpeechRecognizerStop);
+  map_["SbSpeechSynthesisCancel"] =
+      reinterpret_cast<const void*>(SbSpeechSynthesisCancel);
+  map_["SbSpeechSynthesisSpeak"] =
+      reinterpret_cast<const void*>(SbSpeechSynthesisSpeak);
+#endif
+
+  map_["SbStorageCloseRecord"] =
+      reinterpret_cast<const void*>(SbStorageCloseRecord);
+  map_["SbStorageDeleteRecord"] =
+      reinterpret_cast<const void*>(SbStorageDeleteRecord);
+  map_["SbStorageGetRecordSize"] =
+      reinterpret_cast<const void*>(SbStorageGetRecordSize);
+  map_["SbStorageOpenRecord"] =
+      reinterpret_cast<const void*>(SbStorageOpenRecord);
+  map_["SbStorageReadRecord"] =
+      reinterpret_cast<const void*>(SbStorageReadRecord);
+  map_["SbStorageWriteRecord"] =
+      reinterpret_cast<const void*>(SbStorageWriteRecord);
+  map_["SbStringCompare"] = reinterpret_cast<const void*>(SbStringCompare);
+  map_["SbStringCompareAll"] =
+      reinterpret_cast<const void*>(SbStringCompareAll);
+  map_["SbStringCompareNoCase"] =
+      reinterpret_cast<const void*>(SbStringCompareNoCase);
+  map_["SbStringCompareNoCaseN"] =
+      reinterpret_cast<const void*>(SbStringCompareNoCaseN);
+  map_["SbStringConcat"] = reinterpret_cast<const void*>(SbStringConcat);
+  map_["SbStringCopy"] = reinterpret_cast<const void*>(SbStringCopy);
+  map_["SbStringDuplicate"] = reinterpret_cast<const void*>(SbStringDuplicate);
+  map_["SbStringFindCharacter"] =
+      reinterpret_cast<const void*>(SbStringFindCharacter);
+  map_["SbStringFindLastCharacter"] =
+      reinterpret_cast<const void*>(SbStringFindLastCharacter);
+  map_["SbStringFindString"] =
+      reinterpret_cast<const void*>(SbStringFindString);
+  map_["SbStringFormat"] = reinterpret_cast<const void*>(SbStringFormat);
+  map_["SbStringFormatWide"] =
+      reinterpret_cast<const void*>(SbStringFormatWide);
+  map_["SbStringGetLength"] = reinterpret_cast<const void*>(SbStringGetLength);
+  map_["SbStringParseDouble"] =
+      reinterpret_cast<const void*>(SbStringParseDouble);
+  map_["SbStringParseSignedInteger"] =
+      reinterpret_cast<const void*>(SbStringParseSignedInteger);
+  map_["SbStringParseUInt64"] =
+      reinterpret_cast<const void*>(SbStringParseUInt64);
+  map_["SbStringParseUnsignedInteger"] =
+      reinterpret_cast<const void*>(SbStringParseUnsignedInteger);
+  map_["SbStringScan"] = reinterpret_cast<const void*>(SbStringScan);
+  map_["SbSystemBinarySearch"] =
+      reinterpret_cast<const void*>(SbSystemBinarySearch);
+  map_["SbSystemBreakIntoDebugger"] =
+      reinterpret_cast<const void*>(SbSystemBreakIntoDebugger);
+  map_["SbSystemClearLastError"] =
+      reinterpret_cast<const void*>(SbSystemClearLastError);
+  map_["SbSystemGetConnectionType"] =
+      reinterpret_cast<const void*>(SbSystemGetConnectionType);
+  map_["SbSystemGetDeviceType"] =
+      reinterpret_cast<const void*>(SbSystemGetDeviceType);
+  map_["SbSystemGetErrorString"] =
+      reinterpret_cast<const void*>(SbSystemGetErrorString);
+  map_["SbSystemGetExtension"] =
+      reinterpret_cast<const void*>(SbSystemGetExtension);
+  map_["SbSystemGetLastError"] =
+      reinterpret_cast<const void*>(SbSystemGetLastError);
+  map_["SbSystemGetLocaleId"] =
+      reinterpret_cast<const void*>(SbSystemGetLocaleId);
+  map_["SbSystemGetNumberOfProcessors"] =
+      reinterpret_cast<const void*>(SbSystemGetNumberOfProcessors);
+  map_["SbSystemGetPath"] = reinterpret_cast<const void*>(SbSystemGetPath);
+  map_["SbSystemGetProperty"] =
+      reinterpret_cast<const void*>(SbSystemGetProperty);
+  map_["SbSystemGetRandomData"] =
+      reinterpret_cast<const void*>(SbSystemGetRandomData);
+  map_["SbSystemGetRandomUInt64"] =
+      reinterpret_cast<const void*>(SbSystemGetRandomUInt64);
+  map_["SbSystemGetStack"] = reinterpret_cast<const void*>(SbSystemGetStack);
+  map_["SbSystemGetTotalCPUMemory"] =
+      reinterpret_cast<const void*>(SbSystemGetTotalCPUMemory);
+  map_["SbSystemGetTotalGPUMemory"] =
+      reinterpret_cast<const void*>(SbSystemGetTotalGPUMemory);
+  map_["SbSystemGetUsedCPUMemory"] =
+      reinterpret_cast<const void*>(SbSystemGetUsedCPUMemory);
+  map_["SbSystemGetUsedGPUMemory"] =
+      reinterpret_cast<const void*>(SbSystemGetUsedGPUMemory);
+  map_["SbSystemHasCapability"] =
+      reinterpret_cast<const void*>(SbSystemHasCapability);
+  map_["SbSystemHideSplashScreen"] =
+      reinterpret_cast<const void*>(SbSystemHideSplashScreen);
+  map_["SbSystemIsDebuggerAttached"] =
+      reinterpret_cast<const void*>(SbSystemIsDebuggerAttached);
+  map_["SbSystemRaisePlatformError"] =
+      reinterpret_cast<const void*>(SbSystemRaisePlatformError);
+  map_["SbSystemRequestPause"] =
+      reinterpret_cast<const void*>(SbSystemRequestPause);
+  map_["SbSystemRequestStop"] =
+      reinterpret_cast<const void*>(SbSystemRequestStop);
+  map_["SbSystemRequestSuspend"] =
+      reinterpret_cast<const void*>(SbSystemRequestSuspend);
+  map_["SbSystemRequestUnpause"] =
+      reinterpret_cast<const void*>(SbSystemRequestUnpause);
+  map_["SbSystemSort"] = reinterpret_cast<const void*>(SbSystemSort);
+  map_["SbSystemSupportsResume"] =
+      reinterpret_cast<const void*>(SbSystemSupportsResume);
+  map_["SbSystemSymbolize"] = reinterpret_cast<const void*>(SbSystemSymbolize);
+  map_["SbThreadContextGetPointer"] =
+      reinterpret_cast<const void*>(SbThreadContextGetPointer);
+  map_["SbThreadCreate"] = reinterpret_cast<const void*>(SbThreadCreate);
+  map_["SbThreadCreateLocalKey"] =
+      reinterpret_cast<const void*>(SbThreadCreateLocalKey);
+  map_["SbThreadDestroyLocalKey"] =
+      reinterpret_cast<const void*>(SbThreadDestroyLocalKey);
+  map_["SbThreadDetach"] = reinterpret_cast<const void*>(SbThreadDetach);
+  map_["SbThreadGetCurrent"] =
+      reinterpret_cast<const void*>(SbThreadGetCurrent);
+  map_["SbThreadGetId"] = reinterpret_cast<const void*>(SbThreadGetId);
+  map_["SbThreadGetLocalValue"] =
+      reinterpret_cast<const void*>(SbThreadGetLocalValue);
+  map_["SbThreadIsEqual"] = reinterpret_cast<const void*>(SbThreadIsEqual);
+  map_["SbThreadJoin"] = reinterpret_cast<const void*>(SbThreadJoin);
+  map_["SbThreadSamplerCreate"] =
+      reinterpret_cast<const void*>(SbThreadSamplerCreate);
+  map_["SbThreadSamplerDestroy"] =
+      reinterpret_cast<const void*>(SbThreadSamplerDestroy);
+  map_["SbThreadSamplerFreeze"] =
+      reinterpret_cast<const void*>(SbThreadSamplerFreeze);
+  map_["SbThreadSamplerThaw"] =
+      reinterpret_cast<const void*>(SbThreadSamplerThaw);
+  map_["SbThreadSetLocalValue"] =
+      reinterpret_cast<const void*>(SbThreadSetLocalValue);
+  map_["SbThreadSetName"] = reinterpret_cast<const void*>(SbThreadSetName);
+  map_["SbThreadSleep"] = reinterpret_cast<const void*>(SbThreadSleep);
+  map_["SbThreadYield"] = reinterpret_cast<const void*>(SbThreadYield);
+  map_["SbTimeGetMonotonicNow"] =
+      reinterpret_cast<const void*>(SbTimeGetMonotonicNow);
+  map_["SbTimeGetMonotonicThreadNow"] =
+      reinterpret_cast<const void*>(SbTimeGetMonotonicThreadNow);
+  map_["SbTimeGetNow"] = reinterpret_cast<const void*>(SbTimeGetNow);
+  map_["SbTimeZoneGetCurrent"] =
+      reinterpret_cast<const void*>(SbTimeZoneGetCurrent);
+  map_["SbTimeZoneGetName"] = reinterpret_cast<const void*>(SbTimeZoneGetName);
+#if SB_API_VERSION >= SB_UI_NAVIGATION_VERSION
+  map_["SbUiNavGetInterface"] =
+      reinterpret_cast<const void*>(SbUiNavGetInterface);
+#endif
+  map_["SbUserGetCurrent"] = reinterpret_cast<const void*>(SbUserGetCurrent);
+  map_["SbUserGetProperty"] = reinterpret_cast<const void*>(SbUserGetProperty);
+  map_["SbUserGetPropertySize"] =
+      reinterpret_cast<const void*>(SbUserGetPropertySize);
+  map_["SbWindowCreate"] = reinterpret_cast<const void*>(SbWindowCreate);
+  map_["SbWindowDestroy"] = reinterpret_cast<const void*>(SbWindowDestroy);
+  map_["SbWindowGetDiagonalSizeInInches"] =
+      reinterpret_cast<const void*>(SbWindowGetDiagonalSizeInInches);
+  map_["SbWindowGetPlatformHandle"] =
+      reinterpret_cast<const void*>(SbWindowGetPlatformHandle);
+  map_["SbWindowGetSize"] = reinterpret_cast<const void*>(SbWindowGetSize);
+  map_["SbWindowSetDefaultOptions"] =
+      reinterpret_cast<const void*>(SbWindowSetDefaultOptions);
+#ifdef LOCAL_TEST_WITH_API_LEAKS
+  map_["atexit"] = reinterpret_cast<const void*>(atexit);
+  map_["btowc"] = reinterpret_cast<const void*>(btowc);
+  map_["__ctype_get_mb_cur_max"] =
+  map_["__cxa_thread_atexit_impl"] =
+      reinterpret_cast<const void*>(tmp__cxa_thread_atexit_impl);
+  map_["dladdr"] = reinterpret_cast<const void*>(dladdr);
+  map_["dl_iterate_phdr"] = reinterpret_cast<const void*>(tmp_dl_iterate_phdr);
+  map_["longjmp"] = reinterpret_cast<const void*>(longjmp);
+  map_["mbrlen"] = reinterpret_cast<const void*>(mbrlen);
+  map_["mbrtowc"] = reinterpret_cast<const void*>(mbrtowc);
+  map_["mbsnrtowcs"] = reinterpret_cast<const void*>(mbsnrtowcs);
+  map_["mbsrtowcs"] = reinterpret_cast<const void*>(mbsrtowcs);
+  map_["mbtowc"] = reinterpret_cast<const void*>(mbtowc);
+
+  map_["setjmp"] = reinterpret_cast<const void*>(setjmp);
+  map_["wcrtomb"] = reinterpret_cast<const void*>(wcrtomb);
+  map_["wcsnrtombs"] = reinterpret_cast<const void*>(wcsnrtombs);
+  map_["wcstod"] = reinterpret_cast<const void*>(wcstod);
+  map_["wcstof"] = reinterpret_cast<const void*>(wcstof);
+  map_["wcstol"] = reinterpret_cast<const void*>(wcstol);
+  map_["wcstold"] = reinterpret_cast<const void*>(wcstold);
+  map_["wcstoll"] = reinterpret_cast<const void*>(wcstoll);
+  map_["wcstoul"] = reinterpret_cast<const void*>(wcstoul);
+  map_["wcstoull"] = reinterpret_cast<const void*>(wcstoull);
+  map_["wcsxfrm_l"] = reinterpret_cast<const void*>(wcsxfrm_l);
+  map_["wctob"] = reinterpret_cast<const void*>(wctob);
+#endif
+}
+
+const void* ExportedSymbols::Lookup(const char* name) {
+  const void* ret = map_[name];
+  SB_CHECK(ret);
+  return ret;
+}
+}  // namespace elf_loader
+}  // namespace starboard
diff --git a/src/starboard/elf_loader/exported_symbols.h b/src/starboard/elf_loader/exported_symbols.h
new file mode 100644
index 0000000..a28c583
--- /dev/null
+++ b/src/starboard/elf_loader/exported_symbols.h
@@ -0,0 +1,46 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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 STARBOARD_ELF_LOADER_EXPORTED_SYMBOLS_H_
+#define STARBOARD_ELF_LOADER_EXPORTED_SYMBOLS_H_
+
+#include <map>
+#include <string>
+
+#include "starboard/elf_loader/elf_hash_table.h"
+#include "starboard/elf_loader/gnu_hash_table.h"
+#include "starboard/file.h"
+
+namespace starboard {
+namespace elf_loader {
+
+// class representing all exported symbols
+// by the starboard layer.
+
+// The elf_loader will not use any other symbols
+// outside of the set represented in this class.
+class ExportedSymbols {
+ public:
+  ExportedSymbols();
+  const void* Lookup(const char* name);
+
+ private:
+  std::map<std::string, const void*> map_;
+
+  SB_DISALLOW_COPY_AND_ASSIGN(ExportedSymbols);
+};
+
+}  // namespace elf_loader
+}  // namespace starboard
+#endif  // STARBOARD_ELF_LOADER_EXPORTED_SYMBOLS_H_
diff --git a/src/starboard/elf_loader/file.h b/src/starboard/elf_loader/file.h
new file mode 100644
index 0000000..90296e7
--- /dev/null
+++ b/src/starboard/elf_loader/file.h
@@ -0,0 +1,44 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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 STARBOARD_ELF_LOADER_FILE_H_
+#define STARBOARD_ELF_LOADER_FILE_H_
+
+#include "starboard/elf_loader/elf.h"
+
+namespace starboard {
+namespace elf_loader {
+
+// File abstraction to be used by the ELF loader.
+// The main reason to introduce this class is to allow for
+// easy testing.
+class File {
+ public:
+  // Opens the file specified for reading.
+  virtual bool Open(const char* name) = 0;
+
+  // Reads a buffer from the file using the specified offset from the beginning
+  // of the file.
+  virtual bool ReadFromOffset(int64_t offset, char* buffer, int size) = 0;
+
+  // Closes the underlying file.
+  virtual void Close() = 0;
+
+  virtual ~File() {}
+};
+
+}  // namespace elf_loader
+}  // namespace starboard
+
+#endif  // STARBOARD_ELF_LOADER_FILE_H_
diff --git a/src/starboard/elf_loader/file_impl.cc b/src/starboard/elf_loader/file_impl.cc
new file mode 100644
index 0000000..8250e8f
--- /dev/null
+++ b/src/starboard/elf_loader/file_impl.cc
@@ -0,0 +1,71 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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.
+
+#include "starboard/elf_loader/file_impl.h"
+
+#include "starboard/common/log.h"
+
+namespace {
+void LogLastError(const char* msg) {
+  const int kErrorMessageBufferSize = 256;
+  char msgbuf[kErrorMessageBufferSize];
+  SbSystemError error_code = SbSystemGetLastError();
+  if (SbSystemGetErrorString(error_code, msgbuf, kErrorMessageBufferSize) > 0) {
+    SB_LOG(ERROR) << msg << ": " << msgbuf;
+  }
+}
+}  // namespace
+
+namespace starboard {
+namespace elf_loader {
+
+FileImpl::FileImpl() : file_(NULL) {}
+
+bool FileImpl::Open(const char* name) {
+  SB_LOG(INFO) << "Loading: " << name;
+  file_ = SbFileOpen(name, kSbFileOpenOnly | kSbFileRead, NULL, NULL);
+  if (!file_) {
+    return false;
+  }
+  return true;
+}
+
+bool FileImpl::ReadFromOffset(int64_t offset, char* buffer, int size) {
+  if (!file_) {
+    return false;
+  }
+  int64_t ret = SbFileSeek(file_, kSbFileFromBegin, offset);
+  SB_LOG(INFO) << "SbFileSeek: ret=" << ret;
+  if (ret == -1) {
+    SB_LOG(INFO) << "SbFileSeek: failed";
+    return false;
+  }
+
+  int count = SbFileReadAll(file_, buffer, size);
+  SB_LOG(INFO) << "SbFileReadAll: count=" << count;
+  if (count == -1) {
+    LogLastError("SbFileReadAll failed");
+    return false;
+  }
+  return true;
+}
+
+void FileImpl::Close() {
+  if (file_) {
+    SbFileClose(file_);
+  }
+}
+
+}  // namespace elf_loader
+}  // namespace starboard
diff --git a/src/starboard/elf_loader/file_impl.h b/src/starboard/elf_loader/file_impl.h
new file mode 100644
index 0000000..2a923be
--- /dev/null
+++ b/src/starboard/elf_loader/file_impl.h
@@ -0,0 +1,43 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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 STARBOARD_ELF_LOADER_FILE_IMPL_H_
+#define STARBOARD_ELF_LOADER_FILE_IMPL_H_
+
+#include "starboard/elf_loader/elf.h"
+
+#include "starboard/elf_loader/file.h"
+#include "starboard/file.h"
+
+namespace starboard {
+namespace elf_loader {
+
+// Starboard implementation for reading a file.
+class FileImpl : public File {
+ public:
+  FileImpl();
+  bool Open(const char* name);
+  bool ReadFromOffset(int64_t offset, char* buffer, int size);
+  void Close();
+
+ private:
+  SbFile file_;
+
+  SB_DISALLOW_COPY_AND_ASSIGN(FileImpl);
+};
+
+}  // namespace elf_loader
+}  // namespace starboard
+
+#endif  // STARBOARD_ELF_LOADER_FILE_IMPL_H_
diff --git a/src/starboard/elf_loader/gnu_hash_table.cc b/src/starboard/elf_loader/gnu_hash_table.cc
new file mode 100644
index 0000000..37db118
--- /dev/null
+++ b/src/starboard/elf_loader/gnu_hash_table.cc
@@ -0,0 +1,142 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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.
+
+#include "starboard/elf_loader/gnu_hash_table.h"
+#include "starboard/common/log.h"
+#include "starboard/string.h"
+
+namespace starboard {
+namespace elf_loader {
+
+// Compute the GNU hash of a given symbol.
+// For more details on the hash function:
+//  https://blogs.oracle.com/solaris/gnu-hash-elf-sections-v2
+static uint32_t GnuHash(const char* name) {
+  uint32_t h = 5381;
+  const uint8_t* ptr = reinterpret_cast<const uint8_t*>(name);
+  while (*ptr) {
+    h = h * 33 + *ptr++;
+  }
+  return h;
+}
+
+GnuHashTable::GnuHashTable()
+    : num_buckets_(0),
+      sym_offset_(0),
+      sym_count_(0),
+      bloom_word_mask_(0),
+      bloom_shift_(0),
+      bloom_filter_(NULL),
+      buckets_(NULL),
+      chain_(NULL) {}
+void GnuHashTable::Init(uintptr_t dt_gnu_hash) {
+  SB_LOG(INFO) << "GnuHashTable::Init 0x" << std::hex << dt_gnu_hash;
+  sym_count_ = 0;
+
+  const uint32_t* data = reinterpret_cast<const uint32_t*>(dt_gnu_hash);
+  num_buckets_ = data[0];
+  sym_offset_ = data[1];
+
+  SB_LOG(INFO) << "GnuHashTable::Init num_buckets_=" << num_buckets_
+               << " sym_offset_" << sym_offset_;
+  if (!num_buckets_)
+    return;
+
+  const uint32_t bloom_size = data[2];
+  SB_LOG(INFO) << "GnuHashTable::Init bloom_size=" << bloom_size;
+  if ((bloom_size & (bloom_size - 1U)) != 0)  // must be a power of 2
+    return;
+
+  bloom_word_mask_ = bloom_size - 1U;
+  bloom_shift_ = data[3];
+
+  SB_LOG(INFO) << "GnuHashTable::Init bloom_word_mask_=" << bloom_word_mask_;
+  SB_LOG(INFO) << "GnuHashTable::Init bloom_shift_=" << bloom_shift_;
+  bloom_filter_ = reinterpret_cast<const Addr*>(data + 4);
+  SB_LOG(INFO) << "GnuHashTable::Init bloom_filter_=0x" << std::hex
+               << bloom_filter_;
+  buckets_ = reinterpret_cast<const uint32_t*>(bloom_filter_ + bloom_size);
+
+  SB_LOG(INFO) << "GnuHashTable::Init buckets_=0x" << std::hex << buckets_;
+  chain_ = buckets_ + num_buckets_;
+
+  // Compute number of dynamic symbols by parsing the table.
+  if (num_buckets_ > 0) {
+    // First find the maximum index in the buckets table.
+    uint32_t max_index = buckets_[0];
+    for (size_t n = 1; n < num_buckets_; ++n) {
+      uint32_t sym_index = buckets_[n];
+      if (sym_index > max_index)
+        max_index = sym_index;
+    }
+    // Now start to look at the chain_ table from (max_index - sym_offset_)
+    // until there is a value with LSB set to 1, indicating the end of the
+    // last chain.
+    while ((chain_[max_index - sym_offset_] & 1) == 0)
+      max_index++;
+
+    sym_count_ = (max_index - sym_offset_) + 1;
+  }
+}
+
+bool GnuHashTable::IsValid() const {
+  return sym_count_ > 0;
+}
+
+const Sym* GnuHashTable::LookupByName(const char* symbol_name,
+                                      const Sym* symbol_table,
+                                      const char* string_table) const {
+  SB_LOG(INFO) << "GnuHashTable::LookupByName: " << symbol_name;
+  uint32_t hash = GnuHash(symbol_name);
+
+  SB_LOG(INFO) << "GnuHashTable::LookupByName: hash=" << hash;
+  SB_LOG(INFO) << "GnuHashTable::LookupByName: ELF_BITS=" << ELF_BITS;
+
+  // First, bloom filter test.
+  Addr word = bloom_filter_[(hash / ELF_BITS) & bloom_word_mask_];
+
+  SB_LOG(INFO) << "GnuHashTable::LookupByName: word=" << word;
+  Addr mask = (Addr(1) << (hash % ELF_BITS)) |
+              (Addr(1) << ((hash >> bloom_shift_) % ELF_BITS));
+
+  SB_LOG(INFO) << "GnuHashTable::LookupByName: mask=" << mask;
+  if ((word & mask) != mask)
+    return NULL;
+
+  uint32_t sym_index = buckets_[hash % num_buckets_];
+  if (sym_index < sym_offset_)
+    return NULL;
+
+  // TODO: add validations of the syn_index
+  while (true) {
+    const Sym* sym = symbol_table + sym_index;
+    const uint32_t sym_hash = chain_[sym_index - sym_offset_];
+    const char* sym_name = string_table + sym->st_name;
+
+    if ((sym_hash | 1) == (hash | 1) &&
+        !SbStringCompareAll(sym_name, symbol_name)) {
+      return sym;
+    }
+
+    if (sym_hash & 1)
+      break;
+
+    sym_index++;
+  }
+
+  return NULL;
+}
+
+}  // namespace elf_loader
+}  // namespace starboard
diff --git a/src/starboard/elf_loader/gnu_hash_table.h b/src/starboard/elf_loader/gnu_hash_table.h
new file mode 100644
index 0000000..5bdf199
--- /dev/null
+++ b/src/starboard/elf_loader/gnu_hash_table.h
@@ -0,0 +1,66 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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 STARBOARD_ELF_LOADER_GNU_HASH_TABLE_H_
+#define STARBOARD_ELF_LOADER_GNU_HASH_TABLE_H_
+
+#include <stddef.h>
+#include "starboard/elf_loader/elf.h"
+
+namespace starboard {
+namespace elf_loader {
+
+// Models the hash table used to map symbol names to symbol entries using
+// the GNU format. This one is smaller and faster than the standard ELF one.
+class GnuHashTable {
+ public:
+  GnuHashTable();
+  // Initialize instance. |dt_gnu_hash| should be the address that the
+  // DT_GNU_HASH entry points to in the input ELF dynamic section. Call
+  // IsValid() to determine whether the table was well-formed.
+  void Init(uintptr_t dt_gnu_hash);
+
+  // Returns true iff the content of the table is valid.
+  bool IsValid() const;
+
+  // Return the index of the first dynamic symbol within the ELF symbol table.
+  size_t dyn_symbols_offset() const { return sym_offset_; }
+
+  // Number of dynamic symbols in the ELF symbol table.
+  size_t dyn_symbols_count() const { return sym_count_; }
+
+  // Lookup |symbol_name| in the table. |symbol_table| should point to the
+  // ELF symbol table, and |string_table| to the start of its string table.
+  // Returns NULL on failure.
+  const Sym* LookupByName(const char* symbol_name,
+                          const Sym* symbol_table,
+                          const char* string_table) const;
+
+ private:
+  uint32_t num_buckets_;
+  uint32_t sym_offset_;
+  uint32_t sym_count_;
+  uint32_t bloom_word_mask_;
+  uint32_t bloom_shift_;
+  const Addr* bloom_filter_;
+  const uint32_t* buckets_;
+  const uint32_t* chain_;
+
+  SB_DISALLOW_COPY_AND_ASSIGN(GnuHashTable);
+};
+
+}  // namespace elf_loader
+}  // namespace starboard
+
+#endif  // STARBOARD_ELF_LOADER_GNU_HASH_TABLE_H_
diff --git a/src/starboard/elf_loader/program_table.cc b/src/starboard/elf_loader/program_table.cc
new file mode 100644
index 0000000..7542604
--- /dev/null
+++ b/src/starboard/elf_loader/program_table.cc
@@ -0,0 +1,329 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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.
+
+#include "starboard/elf_loader/program_table.h"
+
+#include "starboard/common/log.h"
+#include "starboard/memory.h"
+
+#define MAYBE_MAP_FLAG(x, from, to) (((x) & (from)) ? (to) : 0)
+#define PFLAGS_TO_PROT(x)                               \
+  (MAYBE_MAP_FLAG((x), PF_X, kSbMemoryMapProtectExec) | \
+   MAYBE_MAP_FLAG((x), PF_R, kSbMemoryMapProtectRead) | \
+   MAYBE_MAP_FLAG((x), PF_W, kSbMemoryMapProtectWrite))
+
+#define MAP_FAILED ((void*)-1)
+
+namespace starboard {
+namespace elf_loader {
+
+ProgramTable::ProgramTable()
+    : phdr_num_(0),
+      phdr_mmap_(NULL),
+      phdr_table_(NULL),
+      phdr_size_(0),
+      load_start_(NULL),
+      load_size_(0),
+      base_memory_address_(0) {}
+
+bool ProgramTable::LoadProgramHeader(const Ehdr* elf_header, File* elf_file) {
+  if (!elf_header) {
+    SB_LOG(ERROR) << "Ehdr is required";
+    return false;
+  }
+  if (!elf_file) {
+    SB_LOG(ERROR) << "File is required";
+    return false;
+  }
+  phdr_num_ = elf_header->e_phnum;
+
+  SB_LOG(INFO) << "Program Header count=" << phdr_num_;
+  // Like the kernel, only accept program header tables smaller than 64 KB.
+  if (phdr_num_ < 1 || phdr_num_ > 65536 / elf_header->e_phentsize) {
+    SB_LOG(ERROR) << "Invalid program header count: " << phdr_num_;
+    return false;
+  }
+
+  SB_LOG(INFO) << "elf_header->e_phoff=" << elf_header->e_phoff;
+  SB_LOG(INFO) << "elf_header->e_phnum=" << elf_header->e_phnum;
+
+  Addr page_min = PAGE_START(elf_header->e_phoff);
+  Addr page_max = PAGE_END(elf_header->e_phoff + (phdr_num_ * elf_header->e_phentsize));
+  Addr page_offset = PAGE_OFFSET(elf_header->e_phoff);
+
+  SB_LOG(INFO) << "page_min=" << page_min;
+  SB_LOG(INFO) << "page_max=" << page_max;
+
+  phdr_size_ = page_max - page_min;
+
+  SB_LOG(INFO) << "page_max - page_min=" << page_max - page_min;
+
+  phdr_mmap_ =
+      SbMemoryMap(phdr_size_, kSbMemoryMapProtectWrite, "program_header");
+  if (!phdr_mmap_) {
+    SB_LOG(ERROR) << "Failed to allocate memory";
+    return false;
+  }
+
+  SB_LOG(INFO) << "Allocated address=" << phdr_mmap_;
+
+  if (!elf_file->ReadFromOffset(page_min, reinterpret_cast<char*>(phdr_mmap_),
+                                phdr_size_)) {
+    SB_LOG(ERROR) << "Failed to read program header from file offset: "
+                  << page_min;
+    return false;
+  }
+
+  bool mp_result =
+      SbMemoryProtect(phdr_mmap_, phdr_size_, kSbMemoryMapProtectRead);
+  SB_LOG(INFO) << "mp_result=" << mp_result;
+  if (!mp_result) {
+    SB_LOG(ERROR) << "Failed to protect program header";
+    return false;
+  }
+  phdr_table_ = reinterpret_cast<Phdr*>(reinterpret_cast<char*>(phdr_mmap_) +
+                                        page_offset);
+
+  return true;
+}
+
+bool ProgramTable::LoadSegments(File* elf_file) {
+  for (size_t i = 0; i < phdr_num_; ++i) {
+    const Phdr* phdr = &phdr_table_[i];
+
+    if (phdr->p_type != PT_LOAD) {
+      continue;
+    }
+
+    // Segment byte addresses in memory.
+    Addr seg_start = phdr->p_vaddr + base_memory_address_;
+    Addr seg_end = seg_start + phdr->p_memsz;
+
+    // Segment page addresses in memory.
+    Addr seg_page_start = PAGE_START(seg_start);
+    Addr seg_page_end = PAGE_END(seg_end);
+
+    // File offsets.
+    Addr seg_file_end = seg_start + phdr->p_filesz;
+    Addr file_start = phdr->p_offset;
+    Addr file_end = file_start + phdr->p_filesz;
+
+    SB_LOG(INFO) << " phdr->p_offset=" << phdr->p_offset
+                 << " phdr->p_filesz=" << phdr->p_filesz;
+
+    Addr file_page_start = PAGE_START(file_start);
+    Addr file_length = file_end - file_page_start;
+
+    SB_LOG(INFO) << "Mapping segment: "
+                 << " file_page_start=" << file_page_start
+                 << " file_length=" << file_length << " seg_page_start=0x"
+                 << std::hex << seg_page_start;
+
+    if (file_length != 0) {
+      const int prot_flags = PFLAGS_TO_PROT(phdr->p_flags);
+      SB_LOG(INFO) << "segment prot_flags=" << std::hex << prot_flags;
+
+      void* seg_addr = reinterpret_cast<void*>(seg_page_start);
+      bool mp_ret =
+          SbMemoryProtect(seg_addr, file_length, kSbMemoryMapProtectWrite);
+      SB_LOG(INFO) << "segment vaddress=" << seg_addr;
+
+      if (!mp_ret) {
+        SB_LOG(ERROR) << "Failed to unprotect segment";
+        return false;
+      }
+      if (!elf_file->ReadFromOffset(file_page_start,
+                                    reinterpret_cast<char*>(seg_addr),
+                                    file_length)) {
+        SB_LOG(INFO) << "Failed to read segment from file offset: "
+                     << file_page_start;
+        return false;
+      }
+
+      mp_ret = SbMemoryProtect(seg_addr, file_length, prot_flags);
+      SB_LOG(INFO) << "mp_ret=" << mp_ret;
+      if (!mp_ret) {
+        SB_LOG(ERROR) << "Failed to protect segment";
+        return false;
+      }
+      if (!seg_addr) {
+        SB_LOG(ERROR) << "Could not map segment " << i;
+        return false;
+      }
+    }
+
+    // if the segment is writable, and does not end on a page boundary,
+    // zero-fill it until the page limit.
+    if ((phdr->p_flags & PF_W) != 0 && PAGE_OFFSET(seg_file_end) > 0) {
+      SbMemorySet(reinterpret_cast<void*>(seg_file_end), 0,
+                  PAGE_SIZE - PAGE_OFFSET(seg_file_end));
+    }
+
+    seg_file_end = PAGE_END(seg_file_end);
+
+    // seg_file_end is now the first page address after the file
+    // content. If seg_page_end is larger, we need to zero anything
+    // between them. This is done by using a private anonymous
+    // map for all extra pages.
+    if (seg_page_end > seg_file_end) {
+      bool mprotect_fix = SbMemoryProtect(reinterpret_cast<void*>(seg_file_end),
+                                          seg_page_end - seg_file_end,
+                                          kSbMemoryMapProtectWrite);
+      SB_LOG(INFO) << "mprotect_fix=" << mprotect_fix;
+      if (!mprotect_fix) {
+        SB_LOG(ERROR) << "Failed to unprotect end of segment";
+        return false;
+      }
+      SbMemorySet(reinterpret_cast<void*>(seg_file_end), 0,
+                  seg_page_end - seg_file_end);
+      SbMemoryProtect(reinterpret_cast<void*>(seg_file_end),
+                      seg_page_end - seg_file_end,
+                      PFLAGS_TO_PROT(phdr->p_flags));
+      SB_LOG(INFO) << "mprotect_fix=" << mprotect_fix;
+      if (!mprotect_fix) {
+        SB_LOG(ERROR) << "Failed to protect end of segment";
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+size_t ProgramTable::GetLoadMemorySize() {
+  Addr min_vaddr = ~static_cast<Addr>(0);
+  Addr max_vaddr = 0x00000000U;
+
+  bool found_pt_load = false;
+  for (size_t i = 0; i < phdr_num_; ++i) {
+    const Phdr* phdr = &phdr_table_[i];
+
+    if (phdr->p_type != PT_LOAD) {
+      SB_LOG(INFO) << "GetLoadMemorySize: ignoring segment with type: "
+                   << phdr->p_type;
+      continue;
+    }
+    found_pt_load = true;
+
+    if (phdr->p_vaddr < min_vaddr) {
+      SB_LOG(INFO) << "p_vaddr=" << std::hex << phdr->p_vaddr;
+      min_vaddr = phdr->p_vaddr;
+    }
+
+    if (phdr->p_vaddr + phdr->p_memsz > max_vaddr) {
+      max_vaddr = phdr->p_vaddr + phdr->p_memsz;
+      SB_LOG(INFO) << "phdr->p_vaddr=" << phdr->p_vaddr
+                   << " phdr->p_memsz=" << phdr->p_memsz;
+      SB_LOG(INFO) << " max_vaddr=0x" << std::hex << max_vaddr;
+    }
+  }
+  if (!found_pt_load) {
+    min_vaddr = 0x00000000U;
+  }
+
+  min_vaddr = PAGE_START(min_vaddr);
+  max_vaddr = PAGE_END(max_vaddr);
+
+  return max_vaddr - min_vaddr;
+}
+
+void ProgramTable::GetDynamicSection(Dyn** dynamic,
+                                     size_t* dynamic_count,
+                                     Word* dynamic_flags) {
+  const Phdr* phdr = phdr_table_;
+  const Phdr* phdr_limit = phdr + phdr_num_;
+
+  for (phdr = phdr_table_; phdr < phdr_limit; phdr++) {
+    if (phdr->p_type != PT_DYNAMIC) {
+      SB_LOG(INFO) << "Ignore section with type: " << phdr->p_type;
+      continue;
+    }
+
+    SB_LOG(INFO) << "Reading at vaddr: " << phdr->p_vaddr;
+    *dynamic = reinterpret_cast<Dyn*>(base_memory_address_ + phdr->p_vaddr);
+    if (dynamic_count) {
+      *dynamic_count = (size_t)(phdr->p_memsz / sizeof(Dyn));
+    }
+    if (dynamic_flags) {
+      *dynamic_flags = phdr->p_flags;
+    }
+    return;
+  }
+  *dynamic = NULL;
+  if (dynamic_count) {
+    *dynamic_count = 0;
+  }
+}
+
+int ProgramTable::AdjustMemoryProtectionOfReadOnlySegments(
+    int extra_prot_flags) {
+  const Phdr* phdr = phdr_table_;
+  const Phdr* phdr_limit = phdr + phdr_num_;
+
+  for (; phdr < phdr_limit; phdr++) {
+    if (phdr->p_type != PT_LOAD || (phdr->p_flags & PF_W) != 0)
+      continue;
+
+    Addr seg_page_start = PAGE_START(phdr->p_vaddr) + base_memory_address_;
+    Addr seg_page_end =
+        PAGE_END(phdr->p_vaddr + phdr->p_memsz) + base_memory_address_;
+
+    int ret = SbMemoryProtect(reinterpret_cast<void*>(seg_page_start),
+                              seg_page_end - seg_page_start,
+                              PFLAGS_TO_PROT(phdr->p_flags) | extra_prot_flags);
+    if (ret < 0) {
+      return -1;
+    }
+  }
+  return 0;
+}
+
+bool ProgramTable::ReserveLoadMemory() {
+  load_size_ = GetLoadMemorySize();
+  if (load_size_ == 0) {
+    SB_LOG(ERROR) << "No loadable segments";
+    return false;
+  }
+
+  SB_LOG(INFO) << "Load size=" << load_size_;
+
+  load_start_ =
+      SbMemoryMap(load_size_, kSbMemoryMapProtectReserved, "reserved_mem");
+  if (load_start_ == MAP_FAILED) {
+    SB_LOG(ERROR) << "Could not reserve " << load_size_
+                  << " bytes of address space";
+    return false;
+  }
+
+  base_memory_address_ = reinterpret_cast<Addr>(load_start_);
+
+  SB_LOG(INFO) << "Load start=" << std::hex << load_start_
+               << " base_memory_address=0x" << base_memory_address_;
+  return true;
+}
+
+Addr ProgramTable::GetBaseMemoryAddress() {
+  return base_memory_address_;
+}
+
+ProgramTable::~ProgramTable() {
+  if (load_start_) {
+    SbMemoryUnmap(load_start_, load_size_);
+  }
+  if (phdr_mmap_) {
+    SbMemoryUnmap(phdr_mmap_, phdr_size_);
+  }
+}
+
+}  // namespace elf_loader
+}  // namespace starboard
diff --git a/src/starboard/elf_loader/program_table.h b/src/starboard/elf_loader/program_table.h
new file mode 100644
index 0000000..c4229e7
--- /dev/null
+++ b/src/starboard/elf_loader/program_table.h
@@ -0,0 +1,91 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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 STARBOARD_ELF_LOADER_PROGRAM_TABLE_H_
+#define STARBOARD_ELF_LOADER_PROGRAM_TABLE_H_
+
+#include "starboard/elf_loader/elf.h"
+#include "starboard/elf_loader/file.h"
+
+namespace starboard {
+namespace elf_loader {
+
+// Loads the ELF's binary program table and memory maps
+// the loadable segments.
+//
+// To properly initialize the program table and the segments
+// the following calls are required:
+//  1. LoadProgramHeader()
+//  2. LoadSegments()
+//
+// After those calls the ProgramTable class is fully functional and
+// the segments properly loaded.
+
+class ProgramTable {
+ public:
+  ProgramTable();
+
+  // Loads the program header.
+  bool LoadProgramHeader(const Ehdr* elf_header, File* elf_file);
+
+  // Loads the segments.
+  bool LoadSegments(File* elf_file);
+
+  // Retrieves the dynamic section table.
+  void GetDynamicSection(Dyn** dynamic,
+                         size_t* dynamic_count,
+                         Word* dynamic_flags);
+
+  // Adjusts the memory protection of read only segments.
+  // This call is used to make text segments writable in order
+  // to apply relocations. After the relocations are done the
+  // protection is restored to its original read only state.
+  int AdjustMemoryProtectionOfReadOnlySegments(int extra_prot_flags);
+
+  // Reserves a contiguous block of memory, page aligned for mapping all
+  // the segments of the binary.
+  bool ReserveLoadMemory();
+
+  // Retrieves the base load address for the binary.
+  Addr GetBaseMemoryAddress();
+
+  ~ProgramTable();
+
+ private:
+  // Calculates the memory size of the binary.
+  size_t GetLoadMemorySize();
+
+ private:
+  size_t phdr_num_;
+  void* phdr_mmap_;
+  Phdr* phdr_table_;
+  Addr phdr_size_;
+
+  // First page of reserved address space.
+  void* load_start_;
+
+  // Size in bytes of reserved address space.
+  Addr load_size_;
+
+  // The base memory address. All virtual addresses
+  // from the ELF file are offsets from this address.
+  Addr base_memory_address_;
+
+  SB_DISALLOW_COPY_AND_ASSIGN(ProgramTable);
+};
+
+}  // namespace elf_loader
+}  // namespace starboard
+
+#endif  // STARBOARD_ELF_LOADER_PROGRAM_TABLE_H_
diff --git a/src/starboard/elf_loader/program_table_test.cc b/src/starboard/elf_loader/program_table_test.cc
new file mode 100644
index 0000000..da15cae
--- /dev/null
+++ b/src/starboard/elf_loader/program_table_test.cc
@@ -0,0 +1,182 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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.
+
+#include "starboard/elf_loader/program_table.h"
+
+#include <vector>
+
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/elf_loader/file.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace elf_loader {
+
+namespace {
+
+class DummyFile : public File {
+ public:
+  typedef struct FileChunk {
+    FileChunk(int file_offset, const char* buffer, int size)
+        : file_offset_(file_offset), buffer_(buffer), size_(size) {}
+    int file_offset_;
+    const char* buffer_;
+    int size_;
+  } FileChunk;
+
+  explicit DummyFile(const std::vector<FileChunk>& file_chunks)
+      : file_chunks_(file_chunks), read_index_(0) {}
+
+  bool Open(const char* name) { return true; }
+  bool ReadFromOffset(int64_t offset, char* buffer, int size) {
+    SB_LOG(INFO) << "ReadFromOffset offset=" << offset << " size=" << size
+                 << " read_index_=" << read_index_;
+    if (read_index_ >= file_chunks_.size()) {
+      SB_LOG(INFO) << "ReadFromOffset EOF";
+      return false;
+    }
+    const FileChunk& file_chunk = file_chunks_[read_index_++];
+    if (offset != file_chunk.file_offset_) {
+      SB_LOG(ERROR) << "ReadFromOffset: Invalid offset " << offset
+                    << " expected " << file_chunk.file_offset_;
+      return false;
+    }
+    if (size > file_chunk.size_) {
+      SB_LOG(ERROR) << "ReadFromOffset: Invalid size " << size << " expected < "
+                    << file_chunk.size_;
+      return false;
+    }
+    SbMemoryCopy(buffer, file_chunk.buffer_, size);
+    return true;
+  }
+  void Close() {}
+
+ private:
+  int file_offset_;
+  const char* buffer_;
+  int size_;
+  std::vector<FileChunk> file_chunks_;
+  int read_index_;
+};
+
+class ProgramTableTest : public ::testing::Test {
+ protected:
+  ProgramTableTest() { program_table_.reset(new ProgramTable()); }
+  ~ProgramTableTest() {}
+
+  void HelperMethod() {}
+
+ protected:
+  scoped_ptr<ProgramTable> program_table_;
+};
+
+TEST_F(ProgramTableTest, LoadSegments) {
+  // File structure
+  // [Phdr1]
+  // [Phdr2]
+  // [200, 300) sement for phdr1
+  // [250, 300) dyanmic section in segment for phdr1
+  // [400, 500) sement for phdr2
+  Ehdr ehdr;
+  ehdr.e_phnum = 3;
+  ehdr.e_phoff = 0;
+
+  Phdr ent1;
+  Phdr ent2;
+  Phdr ent3;
+  SbMemorySet(&ent1, 0, sizeof(Phdr));
+  SbMemorySet(&ent2, 0, sizeof(Phdr));
+  SbMemorySet(&ent3, 0, sizeof(Phdr));
+
+  ent1.p_type = PT_LOAD;
+  ent1.p_vaddr = 0;
+  ent1.p_memsz = 2 * PAGE_SIZE;
+  ent1.p_offset = 200;
+  ent1.p_filesz = 100;
+  ent1.p_flags = kSbMemoryMapProtectRead;
+
+  ent2.p_type = PT_LOAD;
+  ent2.p_vaddr = 2 * PAGE_SIZE;
+  ent2.p_memsz = 3 * PAGE_SIZE;
+  ent2.p_offset = 400;
+  ent2.p_filesz = 100;
+  ent1.p_flags = kSbMemoryMapProtectRead | kSbMemoryMapProtectExec;
+
+  ent3.p_type = PT_DYNAMIC;
+  ent3.p_vaddr = 250;
+  ent3.p_memsz = 3 * sizeof(Dyn);
+  ent3.p_offset = 250;
+  ent3.p_filesz = 5 * sizeof(Dyn);
+  ent3.p_flags = 0x42;
+
+  Phdr program_table_data[3];
+  program_table_data[0] = ent1;
+  program_table_data[1] = ent2;
+  program_table_data[2] = ent3;
+
+  Dyn dynamic_table_data[3];
+  dynamic_table_data[0].d_tag = DT_DEBUG;
+  dynamic_table_data[1].d_tag = DT_DEBUG;
+  dynamic_table_data[2].d_tag = DT_DEBUG;
+
+  char program_table_page[PAGE_SIZE];
+  SbMemorySet(program_table_page, 0, sizeof(program_table_page));
+  SbMemoryCopy(program_table_page, program_table_data,
+               sizeof(program_table_data));
+
+  char segment_file_data1[2 * PAGE_SIZE];
+  char segment_file_data2[3 * PAGE_SIZE];
+
+  SbMemoryCopy(segment_file_data1 + 250, dynamic_table_data,
+               sizeof(dynamic_table_data));
+
+  std::vector<DummyFile::FileChunk> file_chunks;
+  file_chunks.push_back(
+      DummyFile::FileChunk(0, program_table_page, sizeof(program_table_page)));
+  file_chunks.push_back(
+      DummyFile::FileChunk(0, segment_file_data1, sizeof(segment_file_data1)));
+  file_chunks.push_back(
+      DummyFile::FileChunk(0, segment_file_data2, sizeof(segment_file_data2)));
+
+  DummyFile file(file_chunks);
+
+  EXPECT_TRUE(program_table_->LoadProgramHeader(&ehdr, &file));
+
+  EXPECT_EQ(program_table_->GetBaseMemoryAddress(), 0);
+
+  EXPECT_TRUE(program_table_->ReserveLoadMemory());
+
+  EXPECT_NE(program_table_->GetBaseMemoryAddress(), 0);
+
+  EXPECT_TRUE(program_table_->LoadSegments(&file));
+
+  Dyn* dynamic = NULL;
+  size_t dynamic_count = 0;
+  Word dynamic_flags = 0;
+
+  program_table_->GetDynamicSection(&dynamic, &dynamic_count, &dynamic_flags);
+  Dyn* expected_dyn = reinterpret_cast<Dyn*>(
+      program_table_->GetBaseMemoryAddress() + ent3.p_vaddr);
+  EXPECT_TRUE(dynamic != NULL);
+  EXPECT_EQ(dynamic[0].d_tag, DT_DEBUG);
+  EXPECT_EQ(dynamic[1].d_tag, DT_DEBUG);
+  EXPECT_EQ(dynamic[2].d_tag, DT_DEBUG);
+  EXPECT_EQ(dynamic_count, 3);
+  EXPECT_EQ(dynamic_flags, 0x42);
+}
+
+}  // namespace
+}  // namespace elf_loader
+}  // namespace starboard
diff --git a/src/starboard/elf_loader/relocations.cc b/src/starboard/elf_loader/relocations.cc
new file mode 100644
index 0000000..4b7131c
--- /dev/null
+++ b/src/starboard/elf_loader/relocations.cc
@@ -0,0 +1,551 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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.
+
+#include "starboard/elf_loader/relocations.h"
+
+#include "starboard/common/log.h"
+
+namespace starboard {
+namespace elf_loader {
+
+Relocations::Relocations(Addr base_memory_address,
+                         DynamicSection* dynamic_section,
+                         ExportedSymbols* exported_symbols)
+    : base_memory_address_(base_memory_address),
+      dynamic_section_(dynamic_section),
+      plt_relocations_(0),
+      plt_relocations_size_(0),
+      plt_got_(NULL),
+      relocations_(0),
+      relocations_size_(0),
+      android_relocations_(NULL),
+      android_relocations_size_(0),
+      has_text_relocations_(false),
+      has_symbolic_(false),
+      exported_symbols_(exported_symbols) {}
+
+bool Relocations::HasTextRelocations() {
+  return has_text_relocations_;
+}
+
+bool Relocations::InitRelocations() {
+  SB_LOG(INFO) << "InitRelocations: dynamic_count="
+               << dynamic_section_->GetDynamicTableSize();
+  const Dyn* dynamic = dynamic_section_->GetDynamicTable();
+  for (int i = 0; i < dynamic_section_->GetDynamicTableSize(); i++) {
+    Addr dyn_value = dynamic[i].d_un.d_val;
+    uintptr_t dyn_addr = base_memory_address_ + dynamic[i].d_un.d_ptr;
+    Addr tag = dynamic[i].d_tag;
+    SB_LOG(INFO) << "InitRelocations: tag=" << tag;
+    switch (tag) {
+#if defined(USE_RELA)
+      case DT_REL:
+      case DT_RELSZ:
+      case DT_ANDROID_REL:
+      case DT_ANDROID_RELSZ:
+#else
+      case DT_RELA:
+      case DT_RELASZ:
+      case DT_ANDROID_RELA:
+      case DT_ANDROID_RELASZ:
+#endif
+        SB_LOG(ERROR) << "unsupported relocation type";
+        return false;
+      case DT_PLTREL:
+        SB_LOG(INFO) << "  DT_PLTREL value=" << dyn_value;
+#if defined(USE_RELA)
+        if (dyn_value != DT_RELA) {
+          SB_LOG(ERROR) << "unsupported DT_PLTREL  expected DT_RELA";
+          return false;
+        }
+#else
+        if (dyn_value != DT_REL) {
+          SB_LOG(ERROR) << "unsupported DT_PLTREL expected DT_REL";
+          return false;
+        }
+#endif
+        break;
+      case DT_JMPREL:
+        SB_LOG(INFO) << "  DT_JMPREL addr=0x" << std::hex
+                     << (dyn_addr - base_memory_address_);
+        plt_relocations_ = dyn_addr;
+        break;
+      case DT_PLTRELSZ:
+        plt_relocations_size_ = dyn_value;
+        SB_LOG(INFO) << "  DT_PLTRELSZ size=" << dyn_value;
+        break;
+#if defined(USE_RELA)
+      case DT_RELA:
+#else
+      case DT_REL:
+#endif
+        SB_LOG(INFO) << "  " << ((tag == DT_RELA) ? "DT_RELA" : "DT_REL")
+                     << " addr=" << std::hex
+                     << (dyn_addr - base_memory_address_);
+        if (relocations_) {
+          SB_LOG(ERROR)
+              << "Unsupported DT_RELA/DT_REL combination in dynamic section";
+          return false;
+        }
+        relocations_ = dyn_addr;
+        break;
+#if defined(USE_RELA)
+      case DT_RELASZ:
+#else
+      case DT_RELSZ:
+#endif
+        SB_LOG(INFO) << "  " << ((tag == DT_RELASZ) ? "DT_RELASZ" : "DT_RELSZ")
+                     << " size=" << dyn_value;
+        relocations_size_ = dyn_value;
+        break;
+#if defined(USE_RELA)
+      case DT_ANDROID_RELA:
+#else
+      case DT_ANDROID_REL:
+#endif
+        SB_LOG(INFO) << "  "
+                     << ((tag == DT_ANDROID_REL) ? "DT_ANDROID_REL"
+                                                 : "DT_ANDROID_RELA")
+                     << " addr=" << std::hex
+                     << (dyn_addr - base_memory_address_);
+        if (android_relocations_) {
+          SB_LOG(ERROR) << "Multiple DT_ANDROID_* sections defined.";
+          return false;
+        }
+        android_relocations_ = reinterpret_cast<uint8_t*>(dyn_addr);
+        break;
+#if defined(USE_RELA)
+      case DT_ANDROID_RELASZ:
+#else
+      case DT_ANDROID_RELSZ:
+#endif
+        SB_LOG(ERROR) << "  DT_ANDROID_RELSZ NOT IMPELMENTED";
+        android_relocations_size_ = dyn_value;
+        break;
+      case DT_RELR:
+      case DT_ANDROID_RELR:
+        SB_LOG(ERROR) << "  DT_RELR NOT IMPELMENTED";
+        break;
+      case DT_ANDROID_RELRSZ:
+      case DT_RELRSZ:
+        SB_LOG(ERROR) << "  DT_RELRSZ NOT IMPELMENTED";
+        break;
+      case DT_RELRENT:
+      case DT_ANDROID_RELRENT:
+        if (dyn_value != sizeof(Relr)) {
+          SB_LOG(ERROR) << "Invalid DT_RELRENT value=" << std::hex
+                        << static_cast<int>(dyn_value)
+                        << " expected=" << static_cast<int>(sizeof(Relr));
+          return false;
+        }
+        break;
+      case DT_PLTGOT:
+        SB_LOG(INFO) << "DT_PLTGOT addr=0x" << std::hex
+                     << (dyn_addr - base_memory_address_);
+        plt_got_ = reinterpret_cast<Addr*>(dyn_addr);
+        break;
+      case DT_TEXTREL:
+        SB_LOG(INFO) << "  DT_TEXTREL";
+        has_text_relocations_ = true;
+        break;
+      case DT_SYMBOLIC:
+        SB_LOG(INFO) << "  DT_SYMBOLIC";
+        has_symbolic_ = true;
+        break;
+      case DT_FLAGS:
+        if (dyn_value & DF_TEXTREL)
+          has_text_relocations_ = true;
+        if (dyn_value & DF_SYMBOLIC)
+          has_symbolic_ = true;
+
+        SB_LOG(INFO) << "  DT_FLAGS has_text_relocations="
+                     << has_text_relocations_
+                     << " has_symbolic=" << has_symbolic_;
+
+        break;
+      default:
+        break;
+    }
+  }
+
+  return true;
+}
+
+bool Relocations::ApplyAllRelocations() {
+  SB_LOG(INFO) << "Applying regular relocations";
+  if (!ApplyRelocations(reinterpret_cast<rel_t*>(relocations_),
+                        relocations_size_ / sizeof(rel_t))) {
+    SB_LOG(ERROR) << "regular relocations failed";
+    return false;
+  }
+
+  SB_LOG(INFO) << "Applying PLT relocations";
+  if (!ApplyRelocations(reinterpret_cast<rel_t*>(plt_relocations_),
+                        plt_relocations_size_ / sizeof(rel_t))) {
+    SB_LOG(ERROR) << "PLT relocations failed";
+    return false;
+  }
+  return true;
+}
+
+bool Relocations::ApplyRelocations(const rel_t* rel, size_t rel_count) {
+  SB_LOG(INFO) << "rel=" << std::hex << rel << std::dec
+               << " rel_count=" << rel_count;
+
+  if (!rel)
+    return true;
+
+  for (size_t rel_n = 0; rel_n < rel_count; rel++, rel_n++) {
+    SB_LOG(INFO) << "  Relocation " << rel_n + 1 << " of " << rel_count;
+
+    if (!ApplyRelocation(rel))
+      return false;
+  }
+
+  return true;
+}
+
+bool Relocations::ApplyRelocation(const rel_t* rel) {
+  const Word rel_type = ELF_R_TYPE(rel->r_info);
+  const Word rel_symbol = ELF_R_SYM(rel->r_info);
+
+  Addr sym_addr = 0;
+  Addr reloc = static_cast<Addr>(rel->r_offset + base_memory_address_);
+  SB_LOG(INFO) << "  offset=0x" << std::hex << rel->r_offset
+               << " type=" << std::dec << rel_type << " reloc=0x" << std::hex
+               << reloc << " symbol=" << rel_symbol;
+
+  if (rel_type == 0)
+    return true;
+
+  if (rel_symbol != 0) {
+    if (!ResolveSymbol(rel_type, rel_symbol, reloc, &sym_addr)) {
+      SB_LOG(ERROR) << "Failed to resolve symbol: " << rel_symbol;
+      return false;
+    }
+  }
+
+  return ApplyResolvedReloc(rel, sym_addr);
+}
+
+#if defined(USE_RELA)
+bool Relocations::ApplyResolvedReloc(const Rela* rela, Addr sym_addr) {
+  const Word rela_type = ELF_R_TYPE(rela->r_info);
+  const Word rela_symbol = ELF_R_SYM(rela->r_info);
+  const Sword addend = rela->r_addend;
+  const Addr reloc = static_cast<Addr>(rela->r_offset + base_memory_address_);
+
+  SB_LOG(INFO) << "  rela reloc=0x" << std::hex << reloc << " offset=0x"
+               << rela->r_offset << " type=" << std::dec << rela_type
+               << " addend=0x" << std::hex << addend;
+  Addr* target = reinterpret_cast<Addr*>(reloc);
+  switch (rela_type) {
+#if SB_IS(ARCH_ARM) && SB_IS(64_BIT)
+    case R_AARCH64_JUMP_SLOT:
+      SB_LOG(INFO) << "  R_AARCH64_JUMP_SLOT target=" << std::hex << target
+                   << " addr=" << (sym_addr + addend);
+      *target = sym_addr + addend;
+      break;
+
+    case R_AARCH64_GLOB_DAT:
+      SB_LOG(INFO) << " R_AARCH64_GLOB_DAT target=" << std::hex << target
+                   << " addr=" << (sym_addr + addend);
+      *target = sym_addr + addend;
+      break;
+
+    case R_AARCH64_ABS64:
+      SB_LOG(INFO) << "  R_AARCH64_ABS64 target=" << std::hex << target << " "
+                   << *target << " addr=" << sym_addr + addend;
+      *target += sym_addr + addend;
+      break;
+
+    case R_AARCH64_RELATIVE:
+      SB_LOG(INFO) << "  R_AARCH64_RELATIVE target=" << std::hex << target
+                   << " " << *target
+                   << " bias=" << base_memory_address_ + addend;
+      if (!rela_symbol) {
+        SB_LOG(ERROR) << "Invalid relative relocation with symbol";
+        return false;
+      }
+      *target = base_memory_address_ + addend;
+      break;
+
+    case R_AARCH64_COPY:
+      // NOTE: These relocations are forbidden in shared libraries.
+      SB_LOG(ERROR) << "Invalid R_AARCH64_COPY relocation in shared library";
+      return false;
+#endif
+
+#if SB_IS(ARCH_X86) && SB_IS(64_BIT)
+    case R_X86_64_JMP_SLOT:
+      SB_LOG(INFO) << "  R_X86_64_JMP_SLOT target=" << std::hex << target
+                   << " addr=" << (sym_addr + addend);
+      *target = sym_addr + addend;
+      break;
+
+    case R_X86_64_GLOB_DAT:
+      SB_LOG(INFO) << "  R_X86_64_GLOB_DAT target=" << std::hex << target
+                   << " addr=" << (sym_addr + addend);
+
+      *target = sym_addr + addend;
+      break;
+
+    case R_X86_64_RELATIVE:
+      if (rela_symbol) {
+        SB_LOG(ERROR) << "Invalid R_X86_64_RELATIVE";
+        return false;
+      }
+
+      SB_LOG(INFO) << "  R_X86_64_RELATIVE target=" << std::hex << target << " "
+                   << *target << " bias=" << base_memory_address_ + addend;
+      *target = base_memory_address_ + addend;
+      break;
+
+    case R_X86_64_64:
+      *target = sym_addr + addend;
+      break;
+
+    case R_X86_64_PC32:
+      *target = sym_addr + (addend - reloc);
+      break;
+#endif
+
+    default:
+      SB_LOG(ERROR) << "Invalid relocation type: " << rela_type;
+      return false;
+  }
+
+  return true;
+}
+#else
+bool Relocations::ApplyResolvedReloc(const Rel* rel, Addr sym_addr) {
+  const Word rel_type = ELF_R_TYPE(rel->r_info);
+  const Word rel_symbol = ELF_R_SYM(rel->r_info);
+
+  const Addr reloc = static_cast<Addr>(rel->r_offset + base_memory_address_);
+
+  SB_LOG(INFO) << "  rel reloc=0x" << std::hex << reloc << " offset=0x"
+               << rel->r_offset << " type=" << std::dec << rel_type;
+
+  Addr* target = reinterpret_cast<Addr*>(reloc);
+  switch (rel_type) {
+#if SB_IS(ARCH_ARM) && SB_IS(32_BIT)
+    case R_ARM_JUMP_SLOT:
+      SB_LOG(INFO) << "  R_ARM_JUMP_SLOT target=" << std::hex << target
+                   << " addr=" << sym_addr;
+      *target = sym_addr;
+      break;
+
+    case R_ARM_GLOB_DAT:
+      SB_LOG(INFO) << "  R_ARM_GLOB_DAT target=" << std::hex << target
+                   << " addr=" << sym_addr;
+      *target = sym_addr;
+      break;
+
+    case R_ARM_ABS32:
+      SB_LOG(INFO) << "  R_ARM_ABS32 target=" << std::hex << target << " "
+                   << *target << " addr=" << sym_addr;
+      *target += sym_addr;
+      break;
+
+    case R_ARM_REL32:
+      SB_LOG(INFO) << "  R_ARM_REL32 target=" << std::hex << target << " "
+                   << *target << " addr=" << sym_addr
+                   << " offset=" << rel->r_offset;
+      *target += sym_addr - rel->r_offset;
+      break;
+
+    case R_ARM_RELATIVE:
+      SB_LOG(INFO) << "  RR_ARM_RELATIVE target=" << std::hex << target << " "
+                   << *target << " bias=" << base_memory_address_;
+      if (!rel_symbol) {
+        SB_LOG(ERROR) << "Invalid relative relocation with symbol";
+        return false;
+      }
+      *target += base_memory_address_;
+      break;
+
+    case R_ARM_COPY:
+      // NOTE: These relocations are forbidden in shared libraries.
+      // The Android linker has special code to deal with this, which
+      // is not needed here.
+      SB_LOG(ERROR) << "Invalid R_ARM_COPY relocation in shared library";
+
+      return false;
+#endif
+
+#if SB_IS(ARCH_X86) && SB_IS(32_BIT)
+    case R_386_JMP_SLOT:
+      SB_LOG(INFO) << "  R_386_JMP_SLOT target=" << std::hex << target
+                   << " addr=" << sym_addr;
+
+      *target = sym_addr;
+      break;
+
+    case R_386_GLOB_DAT:
+      SB_LOG(INFO) << "  R_386_GLOB_DAT target=" << std::hex << target
+                   << " addr=" << sym_addr;
+      *target = sym_addr;
+
+      break;
+
+    case R_386_RELATIVE:
+      if (rel_symbol) {
+        SB_LOG(ERROR) << "Invalid relative relocation with symbol";
+        return false;
+      }
+      SB_LOG(INFO) << "  R_386_RELATIVE target=" << std::hex << target << " "
+                   << *target << " bias=" << base_memory_address_;
+
+      *target += base_memory_address_;
+      break;
+
+    case R_386_32:
+      SB_LOG(INFO) << "  R_386_32 target=" << std::hex << target << " "
+                   << *target << " addr=" << sym_addr;
+      *target += sym_addr;
+      break;
+
+    case R_386_PC32:
+      SB_LOG(INFO) << "  R_386_PC32 target=" << std::hex << target << " "
+                   << *target << " addr=" << sym_addr << " reloc=" << reloc;
+      *target += (sym_addr - reloc);
+      break;
+#endif
+
+    default:
+      SB_LOG(ERROR) << "Invalid relocation type: " << rel_type;
+      return false;
+  }
+
+  return true;
+}
+#endif
+
+RelocationType Relocations::GetRelocationType(Word r_type) {
+  switch (r_type) {
+#if SB_IS(ARCH_ARM) && SB_IS(32_BIT)
+    case R_ARM_JUMP_SLOT:
+    case R_ARM_GLOB_DAT:
+    case R_ARM_ABS32:
+      return RELOCATION_TYPE_ABSOLUTE;
+
+    case R_ARM_REL32:
+    case R_ARM_RELATIVE:
+      return RELOCATION_TYPE_RELATIVE;
+
+    case R_ARM_COPY:
+      return RELOCATION_TYPE_COPY;
+#endif
+
+#if SB_IS(ARCH_ARM) && SB_IS(64_BIT)
+    case R_AARCH64_JUMP_SLOT:
+    case R_AARCH64_GLOB_DAT:
+    case R_AARCH64_ABS64:
+      return RELOCATION_TYPE_ABSOLUTE;
+
+    case R_AARCH64_RELATIVE:
+      return RELOCATION_TYPE_RELATIVE;
+
+    case R_AARCH64_COPY:
+      return RELOCATION_TYPE_COPY;
+#endif
+
+#if SB_IS(ARCH_X86) && SB_IS(32_BIT)
+    case R_386_JMP_SLOT:
+    case R_386_GLOB_DAT:
+    case R_386_32:
+      return RELOCATION_TYPE_ABSOLUTE;
+
+    case R_386_RELATIVE:
+      return RELOCATION_TYPE_RELATIVE;
+
+    case R_386_PC32:
+      return RELOCATION_TYPE_PC_RELATIVE;
+#endif
+
+#if SB_IS(ARCH_X86) && SB_IS(64_BIT)
+    case R_X86_64_JMP_SLOT:
+    case R_X86_64_GLOB_DAT:
+    case R_X86_64_64:
+      return RELOCATION_TYPE_ABSOLUTE;
+
+    case R_X86_64_RELATIVE:
+      return RELOCATION_TYPE_RELATIVE;
+
+    case R_X86_64_PC32:
+      return RELOCATION_TYPE_PC_RELATIVE;
+#endif
+    default:
+      return RELOCATION_TYPE_UNKNOWN;
+  }
+}
+
+bool Relocations::ResolveSymbol(Word rel_type,
+                                Word rel_symbol,
+                                Addr reloc,
+                                Addr* sym_addr) {
+  const char* sym_name = dynamic_section_->LookupNameById(rel_symbol);
+  SB_LOG(INFO) << "Resolve: " << sym_name;
+  const void* address = NULL;
+
+  const Sym* sym = dynamic_section_->LookupByName(sym_name);
+  if (sym) {
+    address = reinterpret_cast<void*>(base_memory_address_ + sym->st_value);
+  } else {
+    address = exported_symbols_->Lookup(sym_name);
+  }
+
+  SB_LOG(INFO) << "Resolve: address=0x" << std::hex << address;
+
+  if (address) {
+    // The symbol was found, so compute its address.
+    *sym_addr = reinterpret_cast<Addr>(address);
+    return true;
+  }
+
+  // The symbol was not found. Normally this is an error except
+  // if this is a weak reference.
+  if (!dynamic_section_->IsWeakById(rel_symbol)) {
+    SB_LOG(ERROR) << "Could not find symbol: " << sym_name;
+    return false;
+  }
+
+  // IHI0044C AAELF 4.5.1.1:
+  // Libraries are not searched to resolve weak references.
+  // It is not an error for a weak reference to remain
+  // unsatisfied.
+  //
+  // During linking, the value of an undefined weak reference is:
+  // - Zero if the relocation type is absolute
+  // - The address of the place if the relocation is pc-relative
+  // - The address of nominal base address if the relocation
+  //   type is base-relative.
+  RelocationType r = GetRelocationType(rel_type);
+  if (r == RELOCATION_TYPE_ABSOLUTE || r == RELOCATION_TYPE_RELATIVE) {
+    *sym_addr = 0;
+    return true;
+  }
+
+  if (r == RELOCATION_TYPE_PC_RELATIVE) {
+    *sym_addr = reloc;
+    return true;
+  }
+
+  SB_LOG(ERROR) << "Invalid weak relocation type (" << r
+                << ") for unknown symbol '" << sym_name << "'";
+  return false;
+}
+}  // namespace elf_loader
+}  // namespace starboard
diff --git a/src/starboard/elf_loader/relocations.h b/src/starboard/elf_loader/relocations.h
new file mode 100644
index 0000000..b763c1b
--- /dev/null
+++ b/src/starboard/elf_loader/relocations.h
@@ -0,0 +1,96 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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 STARBOARD_ELF_LOADER_RELOCATIONS_H_
+#define STARBOARD_ELF_LOADER_RELOCATIONS_H_
+
+#include "starboard/elf_loader/elf.h"
+
+#include "starboard/elf_loader/dynamic_section.h"
+#include "starboard/elf_loader/program_table.h"
+
+namespace starboard {
+namespace elf_loader {
+
+enum RelocationType {
+  RELOCATION_TYPE_UNKNOWN = 0,
+  RELOCATION_TYPE_ABSOLUTE = 1,
+  RELOCATION_TYPE_RELATIVE = 2,
+  RELOCATION_TYPE_PC_RELATIVE = 3,
+  RELOCATION_TYPE_COPY = 4,
+};
+
+// class representing the ELF relocations.
+class Relocations {
+ public:
+  Relocations(Addr base_memory_adddress,
+              DynamicSection* dynamic_section,
+              ExportedSymbols* exported_symbols);
+
+  // Initialize the relocation tables.
+  bool InitRelocations();
+
+  // Apply all the relocations.
+  bool ApplyAllRelocations();
+
+  // Apply a set of relocations.
+  bool ApplyRelocations(const rel_t* rel, size_t rel_count);
+
+  // Apply an individual relocation.
+  bool ApplyRelocation(const rel_t* rel);
+
+// Apply a resolved symbol relocation.
+#if defined(USE_RELA)
+  bool ApplyResolvedReloc(const Rela* rela, Addr sym_addr);
+#else
+  bool ApplyResolvedReloc(const Rel* rel, Addr sym_addr);
+#endif
+
+  // Convert an ELF relocation type info a RelocationType value.
+  RelocationType GetRelocationType(Word r_type);
+
+  // Resolve a symbol address.
+  bool ResolveSymbol(Word rel_type,
+                     Word rel_symbol,
+                     Addr reloc,
+                     Addr* sym_addr);
+
+  // Checks if there are any text relocations.
+  bool HasTextRelocations();
+
+ private:
+  Addr base_memory_address_;
+  DynamicSection* dynamic_section_;
+  Addr plt_relocations_;
+  size_t plt_relocations_size_;
+  Addr* plt_got_;
+
+  Addr relocations_;
+  size_t relocations_size_;
+
+  uint8_t* android_relocations_;
+  size_t android_relocations_size_;
+
+  bool has_text_relocations_;
+  bool has_symbolic_;
+
+  ExportedSymbols* exported_symbols_;
+
+  SB_DISALLOW_COPY_AND_ASSIGN(Relocations);
+};
+
+}  // namespace elf_loader
+}  // namespace starboard
+
+#endif  // STARBOARD_ELF_LOADER_RELOCATIONS_H_
diff --git a/src/starboard/elf_loader/relocations_test.cc b/src/starboard/elf_loader/relocations_test.cc
new file mode 100644
index 0000000..a95d7e5
--- /dev/null
+++ b/src/starboard/elf_loader/relocations_test.cc
@@ -0,0 +1,74 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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.
+
+#include "starboard/elf_loader/relocations.h"
+
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/elf_loader/elf.h"
+#include "starboard/elf_loader/file_impl.h"
+#include "starboard/string.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace elf_loader {
+
+namespace {
+
+// TODO: implement
+class RelocationsTest : public ::testing::Test {
+ protected:
+  RelocationsTest() {}
+  ~RelocationsTest() {}
+
+  scoped_ptr<Relocations> relocations_;
+};
+
+#if SB_IS(ARCH_X86) && SB_IS(64_BIT)
+TEST_F(RelocationsTest, Initialize_X86_64) {
+  char buff[1024] = "AAAAAAAAAAAAAAAAAAA";
+  Addr load_bias = reinterpret_cast<Addr>(&buff);
+  Dyn dynamic_table[10];
+  Dyn entry1;
+  entry1.d_tag = DT_REL;
+
+  dynamic_table[0] = entry1;
+  Dyn* dyn = dynamic_table;
+  size_t dyn_count = 1;
+  Word dyn_flags = 0;
+
+  scoped_ptr<DynamicSection> dynamic_section(
+      new DynamicSection(load_bias, dyn, dyn_count, dyn_flags));
+  dynamic_section->InitDynamicSection();
+  dynamic_section->InitDynamicSymbols();
+
+  scoped_ptr<ExportedSymbols> exported_symbols(new ExportedSymbols());
+  relocations_.reset(new Relocations(load_bias, dynamic_section.get(),
+                                     exported_symbols.get()));
+  Rela rela;
+  rela.r_offset = 2;
+  rela.r_info = R_X86_64_JMP_SLOT;
+  rela.r_addend = 5;
+  Addr sym_addr = 34;
+
+  Addr target = rela.r_offset + load_bias;
+  SB_LOG(INFO) << "target= " << reinterpret_cast<char*>(target);
+  relocations_->ApplyResolvedReloc(&rela, sym_addr);
+  EXPECT_EQ(39, *reinterpret_cast<Elf64_Sxword*>(buff + 2));
+  SB_LOG(INFO) << "buffer= " << *reinterpret_cast<Elf64_Sxword*>(buff + 2);
+}
+#endif
+
+}  // namespace
+}  // namespace elf_loader
+}  // namespace starboard
diff --git a/src/starboard/elf_loader/sandbox.cc b/src/starboard/elf_loader/sandbox.cc
new file mode 100644
index 0000000..86e0476
--- /dev/null
+++ b/src/starboard/elf_loader/sandbox.cc
@@ -0,0 +1,52 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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.
+
+#include "starboard/common/log.h"
+#include "starboard/event.h"
+
+#include "starboard/elf_loader/elf_loader.h"
+
+starboard::elf_loader::ElfLoader g_elfLoader;
+
+void (*g_sb_event_func)(const SbEvent*) = NULL;
+
+void SbEventHandle(const SbEvent* event) {
+  switch (event->type) {
+    case kSbEventTypeStart: {
+      SbEventStartData* data = static_cast<SbEventStartData*>(event->data);
+      if (!g_sb_event_func && data->argument_count == 2) {
+        if (!g_elfLoader.Load(data->argument_values[1])) {
+          SB_LOG(INFO) << "Failed to load library";
+          return;
+        }
+
+        SB_LOG(INFO) << "Successfully loaded library\n";
+        void* p = g_elfLoader.LookupSymbol("SbEventHandle");
+        if (p != NULL) {
+          SB_LOG(INFO) << "Symbol Lookup succeeded address=0x" << std::hex << p;
+          g_sb_event_func = (void (*)(const SbEvent*))p;
+          g_sb_event_func(event);
+        } else {
+          SB_LOG(INFO) << "Symbol Lookup failed\n";
+        }
+      }
+      break;
+    }
+    default: {
+      if (g_sb_event_func) {
+        g_sb_event_func(event);
+      }
+    }
+  }
+}
diff --git a/src/starboard/linux/shared/audio_sink_type_dispatcher.cc b/src/starboard/linux/shared/audio_sink_type_dispatcher.cc
index 22bc79b..2a0b581 100644
--- a/src/starboard/linux/shared/audio_sink_type_dispatcher.cc
+++ b/src/starboard/linux/shared/audio_sink_type_dispatcher.cc
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "starboard/common/log.h"
 #include "starboard/shared/alsa/alsa_audio_sink_type.h"
 #include "starboard/shared/pulse/pulse_audio_sink_type.h"
 #include "starboard/shared/starboard/audio_sink/audio_sink_internal.h"
@@ -23,7 +24,10 @@
 // static
 void SbAudioSinkPrivate::PlatformInitialize() {
   starboard::shared::pulse::PlatformInitialize();
-  if (!GetPrimaryType()) {
+  if (GetPrimaryType()) {
+    SB_LOG(INFO) << "Use PulseAudio";
+  } else {
+    SB_LOG(INFO) << "Use ALSA";
     starboard::shared::alsa::PlatformInitialize();
     is_fallback_to_alsa = true;
   }
diff --git a/src/starboard/nplb/audio_sink_test.cc b/src/starboard/nplb/audio_sink_test.cc
index 326fb8d..caa7870 100644
--- a/src/starboard/nplb/audio_sink_test.cc
+++ b/src/starboard/nplb/audio_sink_test.cc
@@ -38,9 +38,10 @@
   AudioSinkTestEnvironment environment(frame_buffers);
   ASSERT_TRUE(environment.is_valid());
 
+  // Audio sink need to be fully filled once to ensure it can start working.
   int frames_to_append = frame_buffers.frames_per_channel();
+  environment.AppendFrame(frames_to_append);
 
-  environment.AppendFrame(frames_to_append / 2);
   EXPECT_TRUE(environment.WaitUntilSomeFramesAreConsumed());
 }
 
@@ -50,8 +51,8 @@
   ASSERT_TRUE(environment.is_valid());
 
   int frames_to_append = frame_buffers.frames_per_channel();
-
   environment.AppendFrame(frames_to_append / 2);
+
   EXPECT_TRUE(environment.WaitUntilAllFramesAreConsumed());
 }
 
@@ -60,11 +61,13 @@
   AudioSinkTestEnvironment environment(frame_buffers);
   ASSERT_TRUE(environment.is_valid());
 
+  // Audio sink need to be fully filled once to ensure it can start working.
   int frames_to_append = frame_buffers.frames_per_channel();
+  environment.AppendFrame(frames_to_append);
 
-  environment.AppendFrame(frames_to_append / 2);
   EXPECT_TRUE(environment.WaitUntilSomeFramesAreConsumed());
-  environment.AppendFrame(frames_to_append / 2);
+  ASSERT_GT(environment.GetFrameBufferFreeSpaceAmount(), 0);
+  environment.AppendFrame(environment.GetFrameBufferFreeSpaceAmount());
   EXPECT_TRUE(environment.WaitUntilAllFramesAreConsumed());
 }
 
@@ -75,9 +78,10 @@
 
   environment.SetIsPlaying(false);
 
+  // Audio sink need to be fully filled once to ensure it can start working.
   int frames_to_append = frame_buffers.frames_per_channel();
+  environment.AppendFrame(frames_to_append);
 
-  environment.AppendFrame(frames_to_append / 2);
   int free_space = environment.GetFrameBufferFreeSpaceAmount();
   EXPECT_TRUE(environment.WaitUntilUpdateStatusCalled());
   EXPECT_TRUE(environment.WaitUntilUpdateStatusCalled());
@@ -91,12 +95,14 @@
   AudioSinkTestEnvironment environment(frame_buffers);
   ASSERT_TRUE(environment.is_valid());
 
+  // Audio sink need to be fully filled once to ensure it can start working.
   int frames_to_append = frame_buffers.frames_per_channel();
+  environment.AppendFrame(frames_to_append);
 
-  environment.AppendFrame(frames_to_append / 2);
   EXPECT_TRUE(environment.WaitUntilSomeFramesAreConsumed());
   SbThreadSleep(250 * kSbTimeMillisecond);
-  environment.AppendFrame(frames_to_append / 2);
+  ASSERT_GT(environment.GetFrameBufferFreeSpaceAmount(), 0);
+  environment.AppendFrame(environment.GetFrameBufferFreeSpaceAmount());
   EXPECT_TRUE(environment.WaitUntilAllFramesAreConsumed());
 }
 
diff --git a/src/starboard/nplb/cpu_features_get_test.cc b/src/starboard/nplb/cpu_features_get_test.cc
index 87fa1ba..97c2271 100644
--- a/src/starboard/nplb/cpu_features_get_test.cc
+++ b/src/starboard/nplb/cpu_features_get_test.cc
@@ -100,7 +100,8 @@
     EXPECT_EQ(kSbCPUFeaturesArchitectureUnknown, features.architecture);
     EXPECT_NE(nullptr, features.brand);
     EXPECT_EQ(0, strlen(features.brand));
-    EXPECT_EQ(kFeatureValueInvalid, features.cache_size);
+    EXPECT_EQ(kFeatureValueInvalid, features.icache_line_size);
+    EXPECT_EQ(kFeatureValueInvalid, features.dcache_line_size);
     EXPECT_FALSE(features.has_fpu);
     EXPECT_EQ(0, features.hwcap);
     EXPECT_EQ(0, features.hwcap2);
diff --git a/src/starboard/raspi/shared/gyp_configuration.py b/src/starboard/raspi/shared/gyp_configuration.py
index 21e3c72..e7668ea 100644
--- a/src/starboard/raspi/shared/gyp_configuration.py
+++ b/src/starboard/raspi/shared/gyp_configuration.py
@@ -106,6 +106,10 @@
       'player_filter_tests': [
           # TODO: debug these failures.
           'VideoDecoderTests/VideoDecoderTest.EndOfStreamWithoutAnyInput/0',
-          'VideoDecoderTests/VideoDecoderTest.SingleInvalidInput/0',
+          'VideoDecoderTests/VideoDecoderTest.MultipleResets/0',
+          'VideoDecoderTests/VideoDecoderTest.SingleInvalidInput/*',
+          'VideoDecoderTests/VideoDecoderTest'
+          '.MultipleValidInputsAfterInvalidKeyFrame/*',
+          'VideoDecoderTests/VideoDecoderTest.MultipleInvalidInput/*',
       ],
   }
diff --git a/src/starboard/raspi/shared/starboard_platform.gypi b/src/starboard/raspi/shared/starboard_platform.gypi
index 4a8b07e..b20edc6 100644
--- a/src/starboard/raspi/shared/starboard_platform.gypi
+++ b/src/starboard/raspi/shared/starboard_platform.gypi
@@ -140,6 +140,7 @@
         '<(DEPTH)/starboard/shared/libevent/socket_waiter_wait_timed.cc',
         '<(DEPTH)/starboard/shared/libevent/socket_waiter_wake_up.cc',
         '<(DEPTH)/starboard/shared/linux/byte_swap.cc',
+        '<(DEPTH)/starboard/shared/linux/cpu_features_get.cc',
         '<(DEPTH)/starboard/shared/linux/dev_input/dev_input.cc',
         '<(DEPTH)/starboard/shared/linux/get_home_directory.cc',
         '<(DEPTH)/starboard/shared/linux/memory_get_stack_bounds.cc',
@@ -352,7 +353,6 @@
         '<(DEPTH)/starboard/shared/starboard/window_set_default_options.cc',
         '<(DEPTH)/starboard/shared/stub/accessibility_get_display_settings.cc',
         '<(DEPTH)/starboard/shared/stub/accessibility_get_text_to_speech_settings.cc',
-        '<(DEPTH)/starboard/shared/stub/cpu_features_get.cc',
         '<(DEPTH)/starboard/shared/stub/cryptography_create_transformer.cc',
         '<(DEPTH)/starboard/shared/stub/cryptography_destroy_transformer.cc',
         '<(DEPTH)/starboard/shared/stub/cryptography_get_tag.cc',
diff --git a/src/starboard/raspi/shared/thread_create_priority.cc b/src/starboard/raspi/shared/thread_create_priority.cc
index b068feb..981c9d6 100644
--- a/src/starboard/raspi/shared/thread_create_priority.cc
+++ b/src/starboard/raspi/shared/thread_create_priority.cc
@@ -27,6 +27,19 @@
 // This is the maximum priority that will be passed to SetRoundRobinScheduler().
 const int kMaxRoundRobinPriority = 2;
 
+// Permissions are needed to allow usage non-default thread schedulers and
+// thread priorities. Specifically, /etc/security/limits.conf should set
+// "rtprio" and "nice" limits. "rtprio" will allow use of the real-time
+// schedulers, and "nice" will allow use of nice priorities as well as allow
+// SCHED_IDLE threads to increase their priority. If the user is 'pi', then
+// limits.conf should have the following lines:
+//   @pi hard rtprio 99
+//   @pi soft rtprio 99
+//   @pi hard nice -20
+//   @pi soft nice -20
+const char kSchedulerErrorMessage[] = "Unable to set scheduler. Please update "
+    "limits.conf to set 'rtprio' limit to 99 and 'nice' limit to -20.";
+
 // Note that use of sched_setscheduler() has been found to be more reliably
 // supported than pthread_setschedparam(), so we are using that.
 
@@ -34,14 +47,14 @@
   struct sched_param thread_sched_param;
   thread_sched_param.sched_priority = 0;
   int result = sched_setscheduler(0, SCHED_IDLE, &thread_sched_param);
-  SB_CHECK(result == 0);
+  SB_CHECK(result == 0) << kSchedulerErrorMessage;
 }
 
 void SetOtherScheduler() {
   struct sched_param thread_sched_param;
   thread_sched_param.sched_priority = 0;
   int result = sched_setscheduler(0, SCHED_OTHER, &thread_sched_param);
-  SB_CHECK(result == 0);
+  SB_CHECK(result == 0) << kSchedulerErrorMessage;
 }
 
 // Here |priority| is a number >= 0, where the higher the number, the
@@ -83,7 +96,7 @@
       std::min(min_priority + priority,
                static_cast<int>(rlimit_rtprio.rlim_cur));
   int result = sched_setscheduler(0, SCHED_RR, &thread_sched_param);
-  SB_CHECK(result == 0);
+  SB_CHECK(result == 0) << kSchedulerErrorMessage;
 }
 
 void ThreadSetPriority(SbThreadPriority priority) {
diff --git a/src/starboard/shared/blittergles/blitter_create_default_device.cc b/src/starboard/shared/blittergles/blitter_create_default_device.cc
index e7becb8..abec352 100644
--- a/src/starboard/shared/blittergles/blitter_create_default_device.cc
+++ b/src/starboard/shared/blittergles/blitter_create_default_device.cc
@@ -22,6 +22,8 @@
 #include "starboard/common/optional.h"
 #include "starboard/shared/blittergles/blitter_context.h"
 #include "starboard/shared/blittergles/blitter_internal.h"
+#include "starboard/shared/blittergles/blitter_surface.h"
+#include "starboard/shared/blittergles/color_shader_program.h"
 #include "starboard/shared/gles/gl_call.h"
 #include "starboard/shared/x11/application_x11.h"
 #include "starboard/window.h"
@@ -79,6 +81,21 @@
   return starboard::nullopt;
 }
 
+// When using Xvfb, the selected drivers will leak memory on the 1st call to
+// glDrawArrays(). We get that draw out of the way with a dummy draw here.
+void DummyDraw(SbBlitterContext context) {
+  SbBlitterSurface dummy_surface = SbBlitterCreateRenderTargetSurface(
+      context->device, 1, 1, kSbBlitterSurfaceFormatRGBA8);
+  SbBlitterSetRenderTarget(context, dummy_surface->render_target);
+  SbBlitterContextPrivate::ScopedCurrentContext scoped_current_context(context);
+  const starboard::shared::blittergles::ColorShaderProgram&
+      color_shader_program = context->GetColorShaderProgram();
+  color_shader_program.DummyDraw(dummy_surface->render_target);
+  context->current_render_target = kSbBlitterInvalidRenderTarget;
+  context->scissor = SbBlitterMakeRect(0, 0, 0, 0);
+  SbBlitterDestroySurface(dummy_surface);
+}
+
 }  // namespace
 
 SbBlitterDevice SbBlitterCreateDefaultDevice() {
@@ -97,8 +114,17 @@
     SB_DLOG(ERROR) << ": Failed to get EGL display connection.";
     return kSbBlitterInvalidDevice;
   }
-  eglInitialize(device->display, NULL, NULL);
-  if (eglGetError() != EGL_SUCCESS) {
+
+  // When running on Xvfb, sometimes ANGLE fails to open the default X display.
+  // By retrying, we increase the chances that eglInitialize() will succeed.
+  // This is a temporary fix.
+  int max_tries = 3, num_tries = 0;
+  bool initialized = false;
+  do {
+    initialized = eglInitialize(device->display, NULL, NULL);
+    ++num_tries;
+  } while (!initialized && num_tries < max_tries);
+  if (!initialized) {
     SB_DLOG(ERROR) << ": Failed to initialize device.";
     return kSbBlitterInvalidDevice;
   }
@@ -120,5 +146,7 @@
   starboard::ScopedLock context_lock(context_registry->mutex);
   context_registry->context = context.release();
 
+  DummyDraw(context_registry->context);
+
   return device_registry->default_device;
 }
diff --git a/src/starboard/shared/blittergles/blitter_create_render_target_surface.cc b/src/starboard/shared/blittergles/blitter_create_render_target_surface.cc
index 7f5d2bf..6ed0927 100644
--- a/src/starboard/shared/blittergles/blitter_create_render_target_surface.cc
+++ b/src/starboard/shared/blittergles/blitter_create_render_target_surface.cc
@@ -46,7 +46,9 @@
   surface->info.height = height;
   surface->info.format = surface_format;
   surface->color_texture_handle = 0;
-  surface->SetTexture(NULL);
+  if (!surface->SetTexture(NULL)) {
+    return kSbBlitterInvalidSurface;
+  }
   std::unique_ptr<SbBlitterRenderTargetPrivate> render_target(
       new SbBlitterRenderTargetPrivate());
   render_target->swap_chain = kSbBlitterInvalidSwapChain;
@@ -56,7 +58,9 @@
   render_target->device = device;
   render_target->framebuffer_handle = 0;
   surface->render_target = render_target.release();
-  surface->render_target->SetFramebuffer();
+  if (!surface->render_target->SetFramebuffer()) {
+    return kSbBlitterInvalidSurface;
+  }
 
   return surface.release();
 }
diff --git a/src/starboard/shared/blittergles/blitter_download_surface_pixels.cc b/src/starboard/shared/blittergles/blitter_download_surface_pixels.cc
index af006fb..2757b65 100644
--- a/src/starboard/shared/blittergles/blitter_download_surface_pixels.cc
+++ b/src/starboard/shared/blittergles/blitter_download_surface_pixels.cc
@@ -101,7 +101,9 @@
   dummy_render_target->width = surface->info.width;
   dummy_render_target->height = surface->info.height;
   dummy_render_target->device = surface->device;
-  dummy_render_target->SetFramebuffer();
+  if (!dummy_render_target->SetFramebuffer()) {
+    return false;
+  }
 
   starboard::shared::blittergles::SbBlitterContextRegistry* context_registry =
       starboard::shared::blittergles::GetBlitterContextRegistry();
diff --git a/src/starboard/shared/blittergles/blitter_internal.cc b/src/starboard/shared/blittergles/blitter_internal.cc
index fec01db..6661cbc 100644
--- a/src/starboard/shared/blittergles/blitter_internal.cc
+++ b/src/starboard/shared/blittergles/blitter_internal.cc
@@ -150,22 +150,22 @@
 }  // namespace shared
 }  // namespace starboard
 
-void SbBlitterRenderTargetPrivate::SetFramebuffer() {
+bool SbBlitterRenderTargetPrivate::SetFramebuffer() {
   if (surface->color_texture_handle == 0) {
-    return;
+    return false;
   }
   SbBlitterContextPrivate::ScopedCurrentContext scoped_current_context;
   glGenFramebuffers(1, &framebuffer_handle);
   if (framebuffer_handle == 0) {
     SB_DLOG(ERROR) << ": Error creating new framebuffer.";
-    return;
+    return false;
   }
   glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_handle);
   if (glGetError() != GL_NO_ERROR) {
     GL_CALL(glDeleteFramebuffers(1, &framebuffer_handle));
     framebuffer_handle = 0;
     SB_DLOG(ERROR) << ": Error binding framebuffer.";
-    return;
+    return false;
   }
   GL_CALL(glBindTexture(GL_TEXTURE_2D, surface->color_texture_handle));
   glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
@@ -176,7 +176,7 @@
     framebuffer_handle = 0;
     surface->color_texture_handle = 0;
     SB_DLOG(ERROR) << ": Error drawing empty image to framebuffer.";
-    return;
+    return false;
   }
 
   GL_CALL(glBindTexture(GL_TEXTURE_2D, 0));
@@ -186,7 +186,8 @@
     framebuffer_handle = 0;
     surface->color_texture_handle = 0;
     SB_DLOG(ERROR) << ": Failed to create framebuffer.";
-    return;
+    return false;
   }
   GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
+  return true;
 }
diff --git a/src/starboard/shared/blittergles/blitter_internal.h b/src/starboard/shared/blittergles/blitter_internal.h
index f62d717..2cc517a 100644
--- a/src/starboard/shared/blittergles/blitter_internal.h
+++ b/src/starboard/shared/blittergles/blitter_internal.h
@@ -91,8 +91,8 @@
   GLuint framebuffer_handle;
 
   // Sets framebuffer_handle and binds the texture from the surface field to it.
-  // On failure, sets framebuffer_handle to 0.
-  void SetFramebuffer();
+  // On failure, sets framebuffer_handle to 0 and returns false.
+  bool SetFramebuffer();
 };
 
 struct SbBlitterSwapChainPrivate {
diff --git a/src/starboard/shared/blittergles/blitter_surface.cc b/src/starboard/shared/blittergles/blitter_surface.cc
index 25054fd..06edbd6 100644
--- a/src/starboard/shared/blittergles/blitter_surface.cc
+++ b/src/starboard/shared/blittergles/blitter_surface.cc
@@ -29,22 +29,22 @@
   }
 }
 
-void SbBlitterSurfacePrivate::SetTexture(void* pixel_data) {
+bool SbBlitterSurfacePrivate::SetTexture(void* pixel_data) {
   SbBlitterContextPrivate::ScopedCurrentContext scoped_current_context;
   if (scoped_current_context.InitializationError()) {
-    return;
+    return false;
   }
   glGenTextures(1, &color_texture_handle);
   if (color_texture_handle == 0) {
     SB_DLOG(ERROR) << ": Error creating new texture.";
-    return;
+    return false;
   }
   glBindTexture(GL_TEXTURE_2D, color_texture_handle);
   if (glGetError() != GL_NO_ERROR) {
     GL_CALL(glDeleteTextures(1, &color_texture_handle));
     color_texture_handle = 0;
     SB_DLOG(ERROR) << ": Error binding new texture.";
-    return;
+    return false;
   }
   GL_CALL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
   GL_CALL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
@@ -59,8 +59,9 @@
     GL_CALL(glDeleteTextures(1, &color_texture_handle));
     color_texture_handle = 0;
     SB_DLOG(ERROR) << ": Error allocating new texture backing.";
-    return;
+    return false;
   }
 
   GL_CALL(glBindTexture(GL_TEXTURE_2D, 0));
+  return true;
 }
diff --git a/src/starboard/shared/blittergles/blitter_surface.h b/src/starboard/shared/blittergles/blitter_surface.h
index c1d734b..cf0f29f 100644
--- a/src/starboard/shared/blittergles/blitter_surface.h
+++ b/src/starboard/shared/blittergles/blitter_surface.h
@@ -36,8 +36,8 @@
   GLuint color_texture_handle;
 
   // Sets the color_texture_handle field using given pixel_data. On failure,
-  // resets color_texture_handle to 0.
-  void SetTexture(void* pixel_data);
+  // resets color_texture_handle to 0 and returns false.
+  bool SetTexture(void* pixel_data);
 };
 
 #endif  // STARBOARD_SHARED_BLITTERGLES_BLITTER_SURFACE_H_
diff --git a/src/starboard/shared/blittergles/color_shader_program.cc b/src/starboard/shared/blittergles/color_shader_program.cc
index d3978d0..38bbd83 100644
--- a/src/starboard/shared/blittergles/color_shader_program.cc
+++ b/src/starboard/shared/blittergles/color_shader_program.cc
@@ -15,6 +15,18 @@
 #include "starboard/shared/blittergles/color_shader_program.h"
 
 #include <GLES2/gl2.h>
+#if defined(ADDRESS_SANITIZER)
+// By default, Leak Sanitizer and Address Sanitizer is expected to exist
+// together. However, this is not true for all platforms.
+// HAS_LEAK_SANTIZIER=0 explicitly removes the Leak Sanitizer from code.
+#ifndef HAS_LEAK_SANITIZER
+#define HAS_LEAK_SANITIZER 1
+#endif  // HAS_LEAK_SANITIZER
+#endif  // defined(ADDRESS_SANITIZER)
+
+#if HAS_LEAK_SANITIZER
+#include <sanitizer/lsan_interface.h>
+#endif  // HAS_LEAK_SANITIZER
 
 #include "starboard/blitter.h"
 #include "starboard/shared/blittergles/blitter_internal.h"
@@ -75,6 +87,29 @@
   return success;
 }
 
+void ColorShaderProgram::DummyDraw(SbBlitterRenderTarget render_target) const {
+  GL_CALL(glUseProgram(GetProgramHandle()));
+
+  float vertices[8];
+  SetNDC(SbBlitterMakeRect(0, 0, 1, 1), render_target->width,
+         render_target->height, vertices);
+  GL_CALL(glVertexAttribPointer(kPositionAttribute, 2, GL_FLOAT, GL_FALSE, 0,
+                                vertices));
+  GL_CALL(glEnableVertexAttribArray(kPositionAttribute));
+  GL_CALL(glVertexAttrib4f(kColorAttribute, 0.0f, 0.0f, 0.0f, 0.0f));
+
+#if HAS_LEAK_SANITIZER
+  __lsan_disable();
+#endif  // HAS_LEAK_SANITIZER
+  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+#if HAS_LEAK_SANITIZER
+  __lsan_enable();
+#endif  // HAS_LEAK_SANITIZER
+
+  GL_CALL(glDisableVertexAttribArray(kPositionAttribute));
+  GL_CALL(glUseProgram(0));
+}
+
 }  // namespace blittergles
 }  // namespace shared
 }  // namespace starboard
diff --git a/src/starboard/shared/blittergles/color_shader_program.h b/src/starboard/shared/blittergles/color_shader_program.h
index a682a5a..e99dfdd 100644
--- a/src/starboard/shared/blittergles/color_shader_program.h
+++ b/src/starboard/shared/blittergles/color_shader_program.h
@@ -33,6 +33,10 @@
             float (&color_rgba)[4],
             SbBlitterRect rect) const;
 
+  // Method that draws a 1x1 transparent rectangle and disables leak sanitizer
+  // around glDrawArrays().
+  void DummyDraw(SbBlitterRenderTarget render_target) const;
+
  private:
   // Location of the shader attribute "a_position" for the color shader.
   static const int kPositionAttribute = 0;
diff --git a/src/starboard/shared/linux/cpu_features_get.cc b/src/starboard/shared/linux/cpu_features_get.cc
new file mode 100644
index 0000000..d8ce245
--- /dev/null
+++ b/src/starboard/shared/linux/cpu_features_get.cc
@@ -0,0 +1,515 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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.
+
+#include "starboard/cpu_features.h"
+
+#include <dlfcn.h>         // dlsym, dlclose, dlopen
+#include <linux/auxvec.h>  // AT_HWCAP
+#include <stdio.h>         // fopen, fclose
+#include <string.h>
+#include <strings.h>  // strcasecmp
+#include <sys/auxv.h>
+#include <unistd.h>  // sysconf()
+#include <memory>
+
+#include <cstdlib>
+
+#include "starboard/common/log.h"
+#include "starboard/shared/starboard/cpu_features.h"
+
+#if SB_API_VERSION >= 11
+
+namespace {
+
+// See <asm/hwcap.h> kernel header.
+#define HWCAP_VFP (1 << 6)
+#define HWCAP_IWMMXT (1 << 9)
+#define HWCAP_NEON (1 << 12)
+#define HWCAP_VFPv3 (1 << 13)
+#define HWCAP_VFPv3D16 (1 << 14) /* also set for VFPv4-D16 */
+#define HWCAP_VFPv4 (1 << 16)
+#define HWCAP_IDIVA (1 << 17)
+#define HWCAP_IDIVT (1 << 18)
+#define HWCAP_VFPD32 (1 << 19) /* set if VFP has 32 regs (not 16) */
+#define HWCAP_IDIV (HWCAP_IDIVA | HWCAP_IDIVT)
+
+// see <uapi/asm/hwcap.h> kernel header
+#define HWCAP2_AES (1 << 0)
+#define HWCAP2_PMULL (1 << 1)
+#define HWCAP2_SHA1 (1 << 2)
+#define HWCAP2_SHA2 (1 << 3)
+#define HWCAP2_CRC32 (1 << 4)
+
+// Preset hwcap for Armv8
+#define HWCAP_SET_FOR_ARMV8 \
+  (HWCAP_VFP | HWCAP_NEON | HWCAP_VFPv3 | HWCAP_VFPv4 | HWCAP_IDIV)
+
+using starboard::shared::SetX86FeaturesInvalid;
+using starboard::shared::SetArmFeaturesInvalid;
+using starboard::shared::SetGeneralFeaturesInvalid;
+
+// Checks if a space-separated list of items |list|, in the form of a string,
+// contains one given item |item|.
+bool HasItemInList(const char* list, const char* flag) {
+  ssize_t flag_length = strlen(flag);
+  const char* list_ptr = list;
+  if (list_ptr == nullptr) {
+    return false;
+  }
+  while (*list_ptr != '\0') {
+    // Skip whitespace.
+    while (isspace(*list_ptr))
+      ++list_ptr;
+
+    // Find end of current list flag.
+    const char* end_ptr = list_ptr;
+    while (*end_ptr != '\0' && !isspace(*end_ptr))
+      ++end_ptr;
+
+    if (flag_length == end_ptr - list_ptr &&
+        memcmp(list_ptr, flag, flag_length) == 0) {
+      return true;
+    }
+
+    // Continue to the next flag.
+    list_ptr = end_ptr;
+  }
+  return false;
+}
+
+// Class that holds the information in system file /proc/cpuinfo.
+class ProcCpuInfo {
+  // Raw data of the file /proc/cpuinfo
+  std::unique_ptr<char[]> file_data_;
+  // Size of the raw data
+  size_t file_data_size_;
+
+ public:
+  explicit ProcCpuInfo(const char* file_path = "/proc/cpuinfo") {
+    file_data_size_ = 0;
+    // Get the size of the cpuinfo file by reading it until the end. This is
+    // required because files under /proc do not always return a valid size
+    // when using fseek(0, SEEK_END) + ftell(). Nor can the be mmap()-ed.
+    FILE* file = fopen(file_path, "r");
+    if (file != nullptr) {
+      for (;;) {
+        char file_buffer[256];
+        size_t data_size = fread(file_buffer, 1, sizeof(file_buffer), file);
+        if (data_size == 0) {
+          break;
+        }
+        file_data_size_ += data_size;
+      }
+      fclose(file);
+    }
+
+    // Read the contents of the cpuinfo file.
+    file_data_ = std::unique_ptr<char[]>(new char[file_data_size_ + 1]);
+    char* file_data_ptr = file_data_.get();
+    memset(file_data_ptr, 0, file_data_size_ + 1);
+
+    file = fopen(file_path, "r");
+    if (file != nullptr) {
+      for (size_t offset = 0; offset < file_data_size_;) {
+        size_t data_size =
+            fread(file_data_ptr + offset, 1, file_data_size_ - offset, file);
+        if (data_size == 0) {
+          break;
+        }
+        offset += data_size;
+      }
+      fclose(file);
+    }
+
+    // Zero-terminate the data.
+    file_data_ptr[file_data_size_] = '\0';
+  }
+  ~ProcCpuInfo() { file_data_.reset(); }
+
+  // Extract the string feature data named by |feature| and store in
+  // |out_feature| whose size is |out_feature_size|.
+  bool ExtractStringFeature(const char* feature,
+                            char* out_feature,
+                            size_t out_feature_size) {
+    if (feature == nullptr) {
+      return false;
+    }
+
+    // Look for first feature occurrence, and ensure it starts the line.
+    size_t feature_name_size = strlen(feature);
+    const char* feature_ptr = nullptr;
+    char* file_data_ptr = file_data_.get();
+
+    const char* line_start_ptr = file_data_.get();
+    while (line_start_ptr < file_data_ptr + file_data_size_) {
+      // Find the end of the line.
+      const char* line_end_ptr = strchr(line_start_ptr, '\n');
+      if (line_end_ptr == nullptr) {
+        line_end_ptr = file_data_ptr + file_data_size_;
+      }
+
+      char line_buffer[line_end_ptr - line_start_ptr +
+                       1];  // NOLINT(runtime/arrays)
+      memset(line_buffer, 0, sizeof(line_buffer));
+      memcpy(line_buffer, line_start_ptr, line_end_ptr - line_start_ptr);
+      line_buffer[line_end_ptr - line_start_ptr] = '\0';
+
+      // Find the colon
+      const char* colon_ptr = strchr(line_buffer, ':');
+      if (colon_ptr == nullptr || !isspace(colon_ptr[1])) {
+        line_start_ptr = line_end_ptr + 1;
+        continue;
+      }
+
+      line_buffer[colon_ptr - line_buffer] = '\0';
+
+      // Trim trailing white space of the line before colon
+      const char* feature_end_ptr = colon_ptr - 1;
+      while (feature_end_ptr >= line_buffer &&
+             isspace(line_buffer[feature_end_ptr - line_buffer])) {
+        line_buffer[feature_end_ptr - line_buffer] = '\0';
+        feature_end_ptr--;
+      }
+      // Out of boundary
+      if (feature_end_ptr < line_buffer) {
+        line_start_ptr = line_end_ptr + 1;
+        continue;
+      }
+      // Trim leading white space of the line
+      const char* feature_start_ptr = line_buffer;
+      while (feature_start_ptr <= feature_end_ptr &&
+             isspace(line_buffer[feature_start_ptr - line_buffer])) {
+        line_buffer[feature_start_ptr - line_buffer] = '\0';
+        feature_start_ptr++;
+      }
+      // There is no need to check feature_start_ptr out of boundary, because if
+      // feature_start_ptr > feature_end_ptr, it means the line before colon is
+      // all white space, and feature_end_ptr will be out of boundary already.
+
+      if (strcmp(feature_start_ptr, feature) != 0) {
+        line_start_ptr = line_end_ptr + 1;
+        continue;
+      }
+
+      feature_ptr = colon_ptr + 2 - line_buffer + line_start_ptr;
+      break;
+    }
+
+    if (feature_ptr == nullptr) {
+      return false;
+    }
+
+    // Find the end of the line.
+    const char* line_end_ptr = strchr(feature_ptr, '\n');
+    if (line_end_ptr == nullptr) {
+      line_end_ptr = file_data_ptr + file_data_size_;
+    }
+
+    // Get the size of the feature data
+    int feature_size = line_end_ptr - feature_ptr;
+
+    if (out_feature_size < feature_size + 1) {
+      SB_LOG(WARNING) << "CPU Feature " << feature << " is truncated.";
+      feature_size = out_feature_size - 1;
+    }
+    memcpy(out_feature, feature_ptr, feature_size);
+    out_feature[feature_size] = '\0';
+
+    return true;
+  }
+
+  // Extract a integer feature field identified by |feature| from /proc/cpuinfo
+  int ExtractIntegerFeature(const char* feature) {
+    int feature_data = -1;
+    // Allocate 128 bytes for an integer field.
+    char feature_buffer[128] = {0};
+    if (!ExtractStringFeature(
+            feature, feature_buffer,
+            sizeof(feature_buffer) / sizeof(feature_buffer[0]))) {
+      return feature_data;
+    }
+
+    char* end;
+    feature_data = static_cast<int>(strtol(feature_buffer, &end, 0));
+    if (end == feature_buffer) {
+      feature_data = -1;
+    }
+    return feature_data;
+  }
+};
+
+// Check if getauxval() is supported
+bool IsGetauxvalSupported() {
+  // TODO: figure out which linking flags are needed to use
+  // dl* functions. Currently the linker complains symbols
+  // like "__dlopen" undefined, even though "-ldl" and "-lc"
+  // are added to linker flags.
+
+  // dlerror();
+  // void* libc_handle = dlopen("libc.so", RTLD_NOW);
+  // if (!libc_handle) {
+  //   printf("Could not dlopen() C library: %s\n", dlerror());
+  //   return false;
+  // }
+
+  // typedef unsigned long getauxval_func_t(unsigned long);
+  // getauxval_func_t* func = (getauxval_func_t*)
+  //         dlsym(libc_handle, "getauxval");
+  // if (!func) {
+  //   printf("Could not find getauxval() in C library\n");
+  //   return false;
+  // }
+  // dlclose(libc_handle);
+  return true;
+}
+
+// Get hwcap bitmask by getauxval() or by reading /proc/self/auxv
+uint32_t ReadElfHwcaps(uint32_t hwcap_type) {
+  uint32_t hwcap = 0;
+  if (IsGetauxvalSupported()) {
+    hwcap = static_cast<uint32_t>(getauxval(hwcap_type));
+  } else {
+    // Read the ELF HWCAP flags by parsing /proc/self/auxv.
+    FILE* file_ptr = fopen("/proc/self/auxv", "r");
+    if (file_ptr == nullptr) {
+      return hwcap;
+    }
+    struct {
+      uint32_t tag;
+      uint32_t value;
+    } entry;
+    for (;;) {
+      size_t n = fread(&entry, sizeof(entry), 1, file_ptr);
+      if (n == 0 || (entry.tag == 0 && entry.value == 0)) {
+        break;
+      }
+      if (entry.tag == hwcap_type) {
+        hwcap = entry.value;
+        break;
+      }
+    }
+    fclose(file_ptr);
+  }
+  return hwcap;
+}
+
+// Construct hwcap bitmask by the feature flags in /proc/cpuinfo
+uint32_t ConstructHwcapFromCPUInfo(ProcCpuInfo* cpu_info,
+                                   int16_t architecture_generation,
+                                   uint32_t hwcap_type) {
+  if (hwcap_type == AT_HWCAP && architecture_generation >= 8) {
+    // This is a 32-bit ARM binary running on a 64-bit ARM64 kernel.
+    // The 'Features' line only lists the optional features that the
+    // device's CPU supports, compared to its reference architecture
+    // which are of no use for this process.
+    SB_LOG(INFO) << "Faking 32-bit ARM HWCaps on ARMv"
+                 << architecture_generation;
+    return HWCAP_SET_FOR_ARMV8;
+  }
+
+  uint32_t hwcap_value = 0;
+
+  // Allocate 1024 bytes for "Features", which is a list of flags.
+  char flags_buffer[1024] = {0};
+  if (!cpu_info->ExtractStringFeature(
+          "Features", flags_buffer,
+          sizeof(flags_buffer) / sizeof(flags_buffer[0]))) {
+    return hwcap_value;
+  }
+
+  if (hwcap_type == AT_HWCAP) {
+    hwcap_value |= HasItemInList(flags_buffer, "vfp") ? HWCAP_VFP : 0;
+    hwcap_value |= HasItemInList(flags_buffer, "vfpv3") ? HWCAP_VFPv3 : 0;
+    hwcap_value |= HasItemInList(flags_buffer, "vfpv3d16") ? HWCAP_VFPv3D16 : 0;
+    hwcap_value |= HasItemInList(flags_buffer, "vfpv4") ? HWCAP_VFPv4 : 0;
+    hwcap_value |= HasItemInList(flags_buffer, "neon") ? HWCAP_NEON : 0;
+    hwcap_value |= HasItemInList(flags_buffer, "idiva") ? HWCAP_IDIVA : 0;
+    hwcap_value |= HasItemInList(flags_buffer, "idivt") ? HWCAP_IDIVT : 0;
+    hwcap_value |=
+        HasItemInList(flags_buffer, "idiv") ? (HWCAP_IDIVA | HWCAP_IDIVT) : 0;
+    hwcap_value |= HasItemInList(flags_buffer, "iwmmxt") ? HWCAP_IWMMXT : 0;
+  } else if (hwcap_type == AT_HWCAP2) {
+    hwcap_value |= HasItemInList(flags_buffer, "aes") ? HWCAP2_AES : 0;
+    hwcap_value |= HasItemInList(flags_buffer, "pmull") ? HWCAP2_PMULL : 0;
+    hwcap_value |= HasItemInList(flags_buffer, "sha1") ? HWCAP2_SHA1 : 0;
+    hwcap_value |= HasItemInList(flags_buffer, "sha2") ? HWCAP2_SHA2 : 0;
+    hwcap_value |= HasItemInList(flags_buffer, "crc32") ? HWCAP2_CRC32 : 0;
+  }
+  return hwcap_value;
+}
+
+bool SbCPUFeaturesGet_ARM(SbCPUFeatures* features) {
+  memset(features, 0, sizeof(*features));
+
+  // Raspi is a 32-bit ARM platform.
+  features->architecture = kSbCPUFeaturesArchitectureArm;
+
+  // Set the default value of the features to be invalid, then fill them in
+  // if appropriate.
+  SetGeneralFeaturesInvalid(features);
+  SetArmFeaturesInvalid(features);
+  SetX86FeaturesInvalid(features);
+
+  ProcCpuInfo cpu_info;
+
+  // Extract CPU implementor, variant, revision and part information, which
+  // are all integers.
+  features->arm.implementer = cpu_info.ExtractIntegerFeature("CPU implementer");
+  features->arm.variant = cpu_info.ExtractIntegerFeature("CPU variant");
+  features->arm.revision = cpu_info.ExtractIntegerFeature("CPU revision");
+  features->arm.part = cpu_info.ExtractIntegerFeature("CPU part");
+
+  // Extract CPU architecture generation from the "CPU Architecture" field.
+  // Allocate 128 bytes for "CPU architecture", which is an integer field.
+  char architecture_buffer[128] = {0};
+  if (cpu_info.ExtractStringFeature(
+          "CPU architecture", architecture_buffer,
+          sizeof(architecture_buffer) / sizeof(architecture_buffer[0]))) {
+    char* end;
+    features->arm.architecture_generation =
+        static_cast<int>(strtol(architecture_buffer, &end, 10));
+    if (end == architecture_buffer) {
+      // Kernels older than 3.18 report "CPU architecture: AArch64" on ARMv8.
+      if (strcasecmp(architecture_buffer, "AArch64") == 0) {
+        features->arm.architecture_generation = 8;
+      } else {
+        features->arm.architecture_generation = -1;
+      }
+    }
+
+    // Unfortunately, it seems that certain ARMv6-based CPUs
+    // report an incorrect architecture number of 7!
+    //
+    // See http://code.google.com/p/android/issues/detail?id=10812
+    //
+    // We try to correct this by looking at the 'elf_platform'
+    // feature reported by the 'Processor' field or 'model name'
+    // field in Linux v3.8, which is of the form of "(v7l)" for an
+    // ARMv7-based CPU, and "(v6l)" for an ARMv6-one. For example,
+    // the Raspberry Pi is one popular ARMv6 device that reports
+    // architecture 7. The 'Processor' or 'model name' fields
+    // also contain processor brand information.
+
+    // Allocate 256 bytes for "Processor"/"model name" field.
+    static char brand_buffer[256] = {0};
+    if (!cpu_info.ExtractStringFeature(
+            "Processor", brand_buffer,
+            sizeof(brand_buffer) / sizeof(brand_buffer[0]))) {
+      if (cpu_info.ExtractStringFeature(
+              "model name", brand_buffer,
+              sizeof(brand_buffer) / sizeof(brand_buffer[0]))) {
+        features->brand = brand_buffer;
+        if (features->arm.architecture_generation == 7 &&
+            HasItemInList(features->brand, "(v6l)")) {
+          features->arm.architecture_generation = 6;
+        }
+      }
+    }
+  }
+
+  // Get hwcap bitmask and extract the CPU feature flags from it.
+  features->hwcap = ReadElfHwcaps(AT_HWCAP);
+  if (features->hwcap == 0) {
+    features->hwcap = ConstructHwcapFromCPUInfo(
+        &cpu_info, features->arm.architecture_generation, AT_HWCAP);
+  }
+
+  features->arm.has_idiva = (features->hwcap & HWCAP_IDIVA) != 0;
+  features->arm.has_neon = (features->hwcap & HWCAP_NEON) != 0;
+  features->arm.has_vfp = (features->hwcap & HWCAP_VFP) != 0;
+  features->arm.has_vfp3 = (features->hwcap & (HWCAP_VFPv3 | HWCAP_VFPv3D16 |
+                                               HWCAP_VFPv4 | HWCAP_NEON)) != 0;
+  features->arm.has_vfp3_d32 =
+      (features->arm.has_vfp3 && ((features->hwcap & HWCAP_VFPv3D16) == 0 ||
+                                  (features->hwcap & HWCAP_VFPD32) != 0));
+  features->arm.has_vfp3_d32 =
+      features->arm.has_vfp3_d32 || features->arm.has_neon;
+
+  // Some old kernels will report vfp not vfpv3. Here we make an attempt
+  // to detect vfpv3 by checking for vfp *and* neon, since neon is only
+  // available on architectures with vfpv3. Checking neon on its own is
+  // not enough as it is possible to have neon without vfp.
+  if (features->arm.has_vfp && features->arm.has_neon) {
+    features->arm.has_vfp3 = true;
+  }
+
+  // VFPv3 implies ARMv7, see ARM DDI 0406B, page A1-6.
+  if (features->arm.architecture_generation < 7 && features->arm.has_vfp3) {
+    features->arm.architecture_generation = 7;
+  }
+
+  // ARMv7 implies Thumb2.
+  if (features->arm.architecture_generation >= 7) {
+    features->arm.has_thumb2 = true;
+  }
+
+  // The earliest architecture with Thumb2 is ARMv6T2.
+  if (features->arm.has_thumb2 && features->arm.architecture_generation < 6) {
+    features->arm.architecture_generation = 6;
+  }
+
+  // We don't support any FPUs other than VFP.
+  features->has_fpu = features->arm.has_vfp;
+
+  // The following flags are always supported by ARMv8, as mandated by the ARM
+  // Architecture Reference Manual.
+  if (features->arm.architecture_generation >= 8) {
+    features->arm.has_idiva = true;
+    features->arm.has_neon = true;
+    features->arm.has_thumb2 = true;
+    features->arm.has_vfp = true;
+    features->arm.has_vfp3 = true;
+  }
+
+  // Read hwcaps2 bitmask and extract the CPU feature flags from it.
+  features->hwcap2 = ReadElfHwcaps(AT_HWCAP2);
+  if (features->hwcap2 == 0) {
+    features->hwcap2 = ConstructHwcapFromCPUInfo(
+        &cpu_info, features->arm.architecture_generation, AT_HWCAP2);
+  }
+
+  features->arm.has_aes = (features->hwcap2 & HWCAP2_AES) != 0;
+  features->arm.has_pmull = (features->hwcap2 & HWCAP2_PMULL) != 0;
+  features->arm.has_sha1 = (features->hwcap2 & HWCAP2_PMULL) != 0;
+  features->arm.has_sha2 = (features->hwcap2 & HWCAP2_SHA2) != 0;
+  features->arm.has_crc32 = (features->hwcap2 & HWCAP2_CRC32) != 0;
+
+  // Get L1 ICACHE and DCACHE line size.
+  features->icache_line_size = sysconf(_SC_LEVEL1_ICACHE_LINESIZE);
+  features->dcache_line_size = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
+
+  return true;
+}
+
+}  // namespace
+
+// TODO: Only ARM is currently implemented and tested
+bool SbCPUFeaturesGet(SbCPUFeatures* features) {
+#if SB_IS(ARCH_ARM)
+  return SbCPUFeaturesGet_ARM(features);
+#else
+  SB_NOTIMPLEMENTED();
+
+  memset(features, 0, sizeof(*features));
+  features->architecture = kSbCPUFeaturesArchitectureUnknown;
+
+  SetGeneralFeaturesInvalid(features);
+  SetArmFeaturesInvalid(features);
+  SetX86FeaturesInvalid(features);
+
+  return false;
+#endif
+}
+
+#endif  // SB_API_VERSION >= 11
diff --git a/src/starboard/shared/starboard/audio_sink/audio_sink_create.cc b/src/starboard/shared/starboard/audio_sink/audio_sink_create.cc
index 36fa34b..00605fc 100644
--- a/src/starboard/shared/starboard/audio_sink/audio_sink_create.cc
+++ b/src/starboard/shared/starboard/audio_sink/audio_sink_create.cc
@@ -93,10 +93,14 @@
   }
 
   if (auto type = SbAudioSinkPrivate::GetFallbackType()) {
+    SB_LOG(WARNING) << "Primary audio sink failed to create, use fallback.";
     return type->Create(channels, sampling_frequency_hz, audio_sample_type,
                         audio_frame_storage_type, frame_buffers,
                         frame_buffers_size_in_frames, update_source_status_func,
                         consume_frames_func, context);
+  } else {
+    SB_LOG(WARNING) << "Primary audio sink failed to create,"
+                    << " fallback is not enabled.";
   }
 
   return kSbAudioSinkInvalid;
diff --git a/src/starboard/shared/starboard/cpu_features.h b/src/starboard/shared/starboard/cpu_features.h
new file mode 100644
index 0000000..6641ee8
--- /dev/null
+++ b/src/starboard/shared/starboard/cpu_features.h
@@ -0,0 +1,65 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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.
+
+// Module Overview: Starboard CPU features API
+
+// Provide helper classes, macros and functions useful to the implementations of
+// Starboard CPU features API on all platforms
+
+#ifndef STARBOARD_SHARED_STARBOARD_CPU_FEATURES_H_
+#define STARBOARD_SHARED_STARBOARD_CPU_FEATURES_H_
+
+#include "starboard/cpu_features.h"
+
+#if SB_API_VERSION >= 11
+
+namespace starboard {
+namespace shared {
+
+// Set the general features of SbCPUFeatures to be invalid
+inline void SetGeneralFeaturesInvalid(SbCPUFeatures* features) {
+  features->brand = "";
+  features->icache_line_size = -1;
+  features->dcache_line_size = -1;
+  features->hwcap = 0;
+  features->hwcap2 = 0;
+}
+
+// Set the features of SbCPUFeatures.x86 to be invalid
+inline void SetX86FeaturesInvalid(SbCPUFeatures* features) {
+  features->x86.vendor = "";
+  features->x86.family = -1;
+  features->x86.ext_family = -1;
+  features->x86.model = -1;
+  features->x86.ext_model = -1;
+  features->x86.stepping = -1;
+  features->x86.type = -1;
+  features->x86.signature = -1;
+}
+
+// Set the features of SbCPUFeatures.arm to be invalid
+inline void SetArmFeaturesInvalid(SbCPUFeatures* features) {
+  features->arm.implementer = -1;
+  features->arm.variant = -1;
+  features->arm.revision = -1;
+  features->arm.architecture_generation = -1;
+  features->arm.part = -1;
+}
+
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // SB_API_VERSION >= 11
+
+#endif  // STARBOARD_SHARED_STARBOARD_CPU_FEATURES_H_
diff --git a/src/starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.cc b/src/starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.cc
index d85f33e..8b06d66 100644
--- a/src/starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.cc
+++ b/src/starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.cc
@@ -14,6 +14,8 @@
 
 #include "starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.h"
 
+#include "starboard/audio_sink.h"
+#include "starboard/common/log.h"
 #include "starboard/common/reset_and_return.h"
 
 namespace starboard {
@@ -25,6 +27,33 @@
 using common::ResetAndReturn;
 
 #if SB_API_VERSION >= 11
+SbMediaAudioSampleType GetDefaultSupportedAudioSampleType() {
+  if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)) {
+    return kSbMediaAudioSampleTypeFloat32;
+  }
+  if (SbAudioSinkIsAudioSampleTypeSupported(
+          kSbMediaAudioSampleTypeInt16Deprecated)) {
+    return kSbMediaAudioSampleTypeInt16Deprecated;
+  }
+  SB_NOTREACHED();
+  return kSbMediaAudioSampleTypeFloat32;
+}
+
+SbMediaAudioFrameStorageType GetDefaultSupportedAudioFrameStorageType() {
+  if (SbAudioSinkIsAudioFrameStorageTypeSupported(
+          kSbMediaAudioFrameStorageTypeInterleaved)) {
+    return kSbMediaAudioFrameStorageTypeInterleaved;
+  }
+  SB_NOTREACHED();
+  return kSbMediaAudioFrameStorageTypeInterleaved;
+}
+
+int GetDefaultSupportedAudioSamplesPerSecond() {
+  const int kDefaultOutputSamplesPerSecond = 48000;
+  return SbAudioSinkGetNearestSupportedSampleFrequency(
+      kDefaultOutputSamplesPerSecond);
+}
+
 bool IsResetDecoderNecessary(const SbMediaAudioSampleInfo& current_info,
                              const SbMediaAudioSampleInfo& new_info) {
   if (current_info.codec != new_info.codec) {
@@ -118,6 +147,16 @@
   if (audio_decoder_) {
     audio_decoder_->WriteEndOfStream();
   } else {
+    // It's possible that WriteEndOfStream() is called without any
+    // other input. In that case, we need to give |output_sample_type_|,
+    // |output_storage_type_| and |output_samples_per_second_| default
+    // value.
+    if (!first_output_received_) {
+      first_output_received_ = true;
+      output_sample_type_ = GetDefaultSupportedAudioSampleType();
+      output_storage_type_ = GetDefaultSupportedAudioFrameStorageType();
+      output_samples_per_second_ = GetDefaultSupportedAudioSamplesPerSecond();
+    }
     decoded_audios_.push(new DecodedAudio);
     Schedule(output_cb_);
   }
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc
index 479ce16..996f9aa 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc
@@ -518,6 +518,7 @@
       max_cached_frames_, this);
   if (!audio_renderer_sink_->HasStarted()) {
 #if SB_HAS(PLAYER_ERROR_MESSAGE)
+    SB_LOG(ERROR) << "Failed to start audio sink.";
     error_cb_(kSbPlayerErrorDecode, "failed to start audio sink");
 #else   // SB_HAS(PLAYER_ERROR_MESSAGE)
     error_cb_();
@@ -721,7 +722,8 @@
                     << " time since last check, which is too frequently.";
   }
 
-  sink_callbacks_since_last_check_.store(0);
+  auto sink_callbacks_since_last_check =
+      sink_callbacks_since_last_check_.exchange(0);
 
   if (paused_ || playback_rate_ == 0.0) {
     return;
@@ -735,7 +737,9 @@
                      << elapsed / kSbTimeSecond << " seconds, with "
                      << total_frames_sent_to_sink_ -
                             total_frames_consumed_by_sink_
-                     << " frames in sink.";
+                     << " frames in sink, " << (underflow_ ? "underflow, " : "")
+                     << sink_callbacks_since_last_check
+                     << " callbacks since last check.";
   }
   Schedule(std::bind(&AudioRenderer::CheckAudioSinkStatus, this),
            kCheckAudioSinkStatusInterval);
diff --git a/src/starboard/shared/starboard/player/filter/player_components.cc b/src/starboard/shared/starboard/player/filter/player_components.cc
new file mode 100644
index 0000000..74e3cb3
--- /dev/null
+++ b/src/starboard/shared/starboard/player/filter/player_components.cc
@@ -0,0 +1,123 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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.
+
+#include "starboard/shared/starboard/player/filter/player_components.h"
+
+#include "starboard/shared/starboard/application.h"
+#include "starboard/shared/starboard/command_line.h"
+#include "starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.h"
+#include "starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h"
+#include "starboard/shared/starboard/player/filter/punchout_video_renderer_sink.h"
+#include "starboard/shared/starboard/player/filter/stub_audio_decoder.h"
+#include "starboard/shared/starboard/player/filter/stub_video_decoder.h"
+#include "starboard/shared/starboard/player/filter/video_render_algorithm_impl.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+namespace filter {
+
+scoped_ptr<AudioRenderer> PlayerComponents::CreateAudioRenderer(
+    const AudioParameters& audio_parameters) {
+  scoped_ptr<AudioDecoder> audio_decoder;
+  scoped_ptr<AudioRendererSink> audio_renderer_sink;
+
+  auto command_line = shared::starboard::Application::Get()->GetCommandLine();
+  if (command_line->HasSwitch("use_stub_audio_decoder")) {
+    CreateStubAudioComponents(audio_parameters, &audio_decoder,
+                              &audio_renderer_sink);
+  } else {
+    CreateAudioComponents(audio_parameters, &audio_decoder,
+                          &audio_renderer_sink);
+  }
+  if (!audio_decoder || !audio_renderer_sink) {
+    return scoped_ptr<AudioRenderer>();
+  }
+  int max_cached_frames, max_frames_per_append;
+  GetAudioRendererParams(&max_cached_frames, &max_frames_per_append);
+  return make_scoped_ptr(
+      new AudioRenderer(audio_decoder.Pass(), audio_renderer_sink.Pass(),
+                        audio_parameters.audio_sample_info, max_cached_frames,
+                        max_frames_per_append));
+}
+
+scoped_ptr<VideoRenderer> PlayerComponents::CreateVideoRenderer(
+    const VideoParameters& video_parameters,
+    MediaTimeProvider* media_time_provider) {
+  scoped_ptr<VideoDecoder> video_decoder;
+  scoped_ptr<VideoRenderAlgorithm> video_render_algorithm;
+  scoped_refptr<VideoRendererSink> video_renderer_sink;
+
+  auto command_line = shared::starboard::Application::Get()->GetCommandLine();
+  if (command_line->HasSwitch("use_stub_video_decoder")) {
+    CreateStubVideoComponents(video_parameters, &video_decoder,
+                              &video_render_algorithm, &video_renderer_sink);
+  } else {
+    CreateVideoComponents(video_parameters, &video_decoder,
+                          &video_render_algorithm, &video_renderer_sink);
+  }
+  if (!video_decoder || !video_render_algorithm) {
+    return scoped_ptr<VideoRenderer>();
+  }
+  return make_scoped_ptr(
+      new VideoRenderer(video_decoder.Pass(), media_time_provider,
+                        video_render_algorithm.Pass(), video_renderer_sink));
+}
+
+void PlayerComponents::CreateStubAudioComponents(
+    const AudioParameters& audio_parameters,
+    scoped_ptr<AudioDecoder>* audio_decoder,
+    scoped_ptr<AudioRendererSink>* audio_renderer_sink) {
+  SB_DCHECK(audio_decoder);
+  SB_DCHECK(audio_renderer_sink);
+
+#if SB_API_VERSION >= 11
+  auto decoder_creator = [](const SbMediaAudioSampleInfo& audio_sample_info,
+                            SbDrmSystem drm_system) {
+    return scoped_ptr<AudioDecoder>(
+        new StubAudioDecoder(audio_sample_info.codec, audio_sample_info));
+  };
+  audio_decoder->reset(
+      new AdaptiveAudioDecoder(audio_parameters.audio_sample_info,
+                               audio_parameters.drm_system, decoder_creator));
+#else   // SB_API_VERSION >= 11
+  audio_decoder->reset(new StubAudioDecoder(
+      audio_parameters.audio_codec, audio_parameters.audio_sample_info));
+#endif  // SB_API_VERISON >= 11
+  audio_renderer_sink->reset(new AudioRendererSinkImpl);
+}
+
+void PlayerComponents::CreateStubVideoComponents(
+    const VideoParameters& video_parameters,
+    scoped_ptr<VideoDecoder>* video_decoder,
+    scoped_ptr<VideoRenderAlgorithm>* video_render_algorithm,
+    scoped_refptr<VideoRendererSink>* video_renderer_sink) {
+  const SbTime kVideoSinkRenderInterval = 10 * kSbTimeMillisecond;
+
+  SB_DCHECK(video_decoder);
+  SB_DCHECK(video_render_algorithm);
+  SB_DCHECK(video_renderer_sink);
+
+  video_decoder->reset(new StubVideoDecoder);
+  video_render_algorithm->reset(new VideoRenderAlgorithmImpl);
+  *video_renderer_sink = new PunchoutVideoRendererSink(
+      video_parameters.player, kVideoSinkRenderInterval);
+}
+
+}  // namespace filter
+}  // namespace player
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
diff --git a/src/starboard/shared/starboard/player/filter/player_components.h b/src/starboard/shared/starboard/player/filter/player_components.h
index 983f207..a73ac46 100644
--- a/src/starboard/shared/starboard/player/filter/player_components.h
+++ b/src/starboard/shared/starboard/player/filter/player_components.h
@@ -22,22 +22,14 @@
 #include "starboard/media.h"
 #include "starboard/player.h"
 #include "starboard/shared/internal_only.h"
-#include "starboard/shared/starboard/application.h"
-#include "starboard/shared/starboard/command_line.h"
 #include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
 #include "starboard/shared/starboard/player/filter/audio_renderer_internal.h"
 #include "starboard/shared/starboard/player/filter/audio_renderer_sink.h"
-#include "starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h"
 #include "starboard/shared/starboard/player/filter/media_time_provider.h"
-#include "starboard/shared/starboard/player/filter/punchout_video_renderer_sink.h"
-#include "starboard/shared/starboard/player/filter/stub_audio_decoder.h"
-#include "starboard/shared/starboard/player/filter/stub_video_decoder.h"
 #include "starboard/shared/starboard/player/filter/video_decoder_internal.h"
 #include "starboard/shared/starboard/player/filter/video_render_algorithm.h"
-#include "starboard/shared/starboard/player/filter/video_render_algorithm_impl.h"
 #include "starboard/shared/starboard/player/filter/video_renderer_internal.h"
 #include "starboard/shared/starboard/player/filter/video_renderer_sink.h"
-#include "starboard/shared/starboard/player/job_queue.h"
 
 namespace starboard {
 namespace shared {
@@ -71,50 +63,11 @@
   static scoped_ptr<PlayerComponents> Create();
 
   scoped_ptr<AudioRenderer> CreateAudioRenderer(
-      const AudioParameters& audio_parameters) {
-    scoped_ptr<AudioDecoder> audio_decoder;
-    scoped_ptr<AudioRendererSink> audio_renderer_sink;
+      const AudioParameters& audio_parameters);
 
-    auto command_line = shared::starboard::Application::Get()->GetCommandLine();
-    if (command_line->HasSwitch("use_stub_audio_decoder")) {
-      CreateStubAudioComponents(audio_parameters, &audio_decoder,
-                                &audio_renderer_sink);
-    } else {
-      CreateAudioComponents(audio_parameters, &audio_decoder,
-                            &audio_renderer_sink);
-    }
-    if (!audio_decoder || !audio_renderer_sink) {
-      return scoped_ptr<AudioRenderer>();
-    }
-    int max_cached_frames, max_frames_per_append;
-    GetAudioRendererParams(&max_cached_frames, &max_frames_per_append);
-    return make_scoped_ptr(
-        new AudioRenderer(audio_decoder.Pass(), audio_renderer_sink.Pass(),
-                          audio_parameters.audio_sample_info, max_cached_frames,
-                          max_frames_per_append));
-  }
   scoped_ptr<VideoRenderer> CreateVideoRenderer(
       const VideoParameters& video_parameters,
-      MediaTimeProvider* media_time_provider) {
-    scoped_ptr<VideoDecoder> video_decoder;
-    scoped_ptr<VideoRenderAlgorithm> video_render_algorithm;
-    scoped_refptr<VideoRendererSink> video_renderer_sink;
-
-    auto command_line = shared::starboard::Application::Get()->GetCommandLine();
-    if (command_line->HasSwitch("use_stub_video_decoder")) {
-      CreateStubVideoComponents(video_parameters, &video_decoder,
-                                &video_render_algorithm, &video_renderer_sink);
-    } else {
-      CreateVideoComponents(video_parameters, &video_decoder,
-                            &video_render_algorithm, &video_renderer_sink);
-    }
-    if (!video_decoder || !video_render_algorithm) {
-      return scoped_ptr<VideoRenderer>();
-    }
-    return make_scoped_ptr(
-        new VideoRenderer(video_decoder.Pass(), media_time_provider,
-                          video_render_algorithm.Pass(), video_renderer_sink));
-  }
+      MediaTimeProvider* media_time_provider);
 
 #if COBALT_BUILD_TYPE_GOLD
  private:
@@ -143,31 +96,13 @@
   void CreateStubAudioComponents(
       const AudioParameters& audio_parameters,
       scoped_ptr<AudioDecoder>* audio_decoder,
-      scoped_ptr<AudioRendererSink>* audio_renderer_sink) {
-    SB_DCHECK(audio_decoder);
-    SB_DCHECK(audio_renderer_sink);
-
-    audio_decoder->reset(
-        new StubAudioDecoder(audio_parameters.audio_sample_info));
-    audio_renderer_sink->reset(new AudioRendererSinkImpl);
-  }
+      scoped_ptr<AudioRendererSink>* audio_renderer_sink);
 
   void CreateStubVideoComponents(
       const VideoParameters& video_parameters,
       scoped_ptr<VideoDecoder>* video_decoder,
       scoped_ptr<VideoRenderAlgorithm>* video_render_algorithm,
-      scoped_refptr<VideoRendererSink>* video_renderer_sink) {
-    const SbTime kVideoSinkRenderInterval = 10 * kSbTimeMillisecond;
-
-    SB_DCHECK(video_decoder);
-    SB_DCHECK(video_render_algorithm);
-    SB_DCHECK(video_renderer_sink);
-
-    video_decoder->reset(new StubVideoDecoder);
-    video_render_algorithm->reset(new VideoRenderAlgorithmImpl);
-    *video_renderer_sink = new PunchoutVideoRendererSink(
-        video_parameters.player, kVideoSinkRenderInterval);
-  }
+      scoped_refptr<VideoRendererSink>* video_renderer_sink);
 
  private:
   SB_DISALLOW_COPY_AND_ASSIGN(PlayerComponents);
diff --git a/src/starboard/shared/starboard/player/filter/player_filter.gypi b/src/starboard/shared/starboard/player/filter/player_filter.gypi
index a3f40ba..b67a371 100644
--- a/src/starboard/shared/starboard/player/filter/player_filter.gypi
+++ b/src/starboard/shared/starboard/player/filter/player_filter.gypi
@@ -42,6 +42,7 @@
       '<(DEPTH)/starboard/shared/starboard/player/filter/media_time_provider_impl.h',
       '<(DEPTH)/starboard/shared/starboard/player/filter/mock_audio_decoder.h',
       '<(DEPTH)/starboard/shared/starboard/player/filter/mock_audio_renderer_sink.h',
+      '<(DEPTH)/starboard/shared/starboard/player/filter/player_components.cc',
       '<(DEPTH)/starboard/shared/starboard/player/filter/player_components.h',
       '<(DEPTH)/starboard/shared/starboard/player/filter/punchout_video_renderer_sink.cc',
       '<(DEPTH)/starboard/shared/starboard/player/filter/punchout_video_renderer_sink.h',
@@ -61,4 +62,4 @@
       '<(DEPTH)/starboard/shared/starboard/player/filter/wsola_internal.h',
     ],
   },
-}
\ No newline at end of file
+}
diff --git a/src/starboard/shared/starboard/player/filter/stub_audio_decoder.cc b/src/starboard/shared/starboard/player/filter/stub_audio_decoder.cc
index 5358ae3..b5888c1 100644
--- a/src/starboard/shared/starboard/player/filter/stub_audio_decoder.cc
+++ b/src/starboard/shared/starboard/player/filter/stub_audio_decoder.cc
@@ -35,8 +35,10 @@
 }  // namespace
 
 StubAudioDecoder::StubAudioDecoder(
+    SbMediaAudioCodec audio_codec,
     const SbMediaAudioSampleInfo& audio_sample_info)
     : sample_type_(GetSupportedSampleType()),
+      audio_codec_(audio_codec),
       audio_sample_info_(audio_sample_info),
       stream_ended_(false) {}
 
@@ -67,10 +69,15 @@
     size_t size = diff * GetSamplesPerSecond() * sample_size *
                   audio_sample_info_.number_of_channels / kSbTimeSecond;
     size -= size % (sample_size * audio_sample_info_.number_of_channels);
+    if (audio_codec_ == kSbMediaAudioCodecAac) {
+      // Frame size for AAC is fixed at 1024, so fake the output size such that
+      // number of frames matches up.
+      size = sample_size * audio_sample_info_.number_of_channels * 1024;
+    }
 
-    decoded_audios_.push(new DecodedAudio(audio_sample_info_.number_of_channels,
-                                          GetSampleType(), GetStorageType(),
-                                          input_buffer->timestamp(), size));
+    decoded_audios_.push(new DecodedAudio(
+        audio_sample_info_.number_of_channels, GetSampleType(),
+        GetStorageType(), last_input_buffer_->timestamp(), size));
 
     if (fill_type == kSilence) {
       SbMemorySet(decoded_audios_.back()->buffer(), 0, size);
@@ -100,9 +107,13 @@
     size_t fake_size = 4 * last_input_buffer_->size();
     size_t sample_size =
         GetSampleType() == kSbMediaAudioSampleTypeInt16Deprecated ? 2 : 4;
-    fake_size +=
+    fake_size -=
         fake_size % (sample_size * audio_sample_info_.number_of_channels);
-
+    if (audio_codec_ == kSbMediaAudioCodecAac) {
+      // Frame size for AAC is fixed at 1024, so fake the output size such that
+      // number of frames matches up.
+      fake_size = sample_size * audio_sample_info_.number_of_channels * 1024;
+    }
     decoded_audios_.push(new DecodedAudio(
         audio_sample_info_.number_of_channels, GetSampleType(),
         GetStorageType(), last_input_buffer_->timestamp(), fake_size));
diff --git a/src/starboard/shared/starboard/player/filter/stub_audio_decoder.h b/src/starboard/shared/starboard/player/filter/stub_audio_decoder.h
index bcfdf8f..d42ecd0 100644
--- a/src/starboard/shared/starboard/player/filter/stub_audio_decoder.h
+++ b/src/starboard/shared/starboard/player/filter/stub_audio_decoder.h
@@ -30,7 +30,8 @@
 
 class StubAudioDecoder : public AudioDecoder, private JobQueue::JobOwner {
  public:
-  explicit StubAudioDecoder(const SbMediaAudioSampleInfo& audio_sample_info);
+  StubAudioDecoder(SbMediaAudioCodec audio_codec,
+                   const SbMediaAudioSampleInfo& audio_sample_info);
 
   void Initialize(const OutputCB& output_cb, const ErrorCB& error_cb) override;
 
@@ -52,6 +53,7 @@
  private:
   OutputCB output_cb_;
   SbMediaAudioSampleType sample_type_;
+  SbMediaAudioCodec audio_codec_;
   SbMediaAudioSampleInfo audio_sample_info_;
   bool stream_ended_;
   std::queue<scoped_refptr<DecodedAudio> > decoded_audios_;
diff --git a/src/starboard/shared/starboard/player/filter/stub_player_components_impl.cc b/src/starboard/shared/starboard/player/filter/stub_player_components_impl.cc
index baa83fa..2947df4 100644
--- a/src/starboard/shared/starboard/player/filter/stub_player_components_impl.cc
+++ b/src/starboard/shared/starboard/player/filter/stub_player_components_impl.cc
@@ -12,7 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/common/log.h"
+#include "starboard/shared/starboard/player/filter/stub_player_components_impl.h"
+
 #include "starboard/shared/starboard/player/filter/player_components.h"
 
 namespace starboard {
@@ -21,37 +22,9 @@
 namespace player {
 namespace filter {
 
-class PlayerComponentsImpl : public PlayerComponents {
-  void CreateAudioComponents(
-      const AudioParameters& audio_parameters,
-      scoped_ptr<AudioDecoder>* audio_decoder,
-      scoped_ptr<AudioRendererSink>* audio_renderer_sink) override {
-    CreateStubAudioComponents(audio_parameters, audio_decoder,
-                              audio_renderer_sink);
-  }
-
-  void CreateVideoComponents(
-      const VideoParameters& video_parameters,
-      scoped_ptr<VideoDecoder>* video_decoder,
-      scoped_ptr<VideoRenderAlgorithm>* video_render_algorithm,
-      scoped_refptr<VideoRendererSink>* video_renderer_sink) override {
-    CreateStubVideoComponents(video_parameters, video_decoder,
-                              video_render_algorithm, video_renderer_sink);
-  }
-
-  void GetAudioRendererParams(int* max_cached_frames,
-                              int* max_frames_per_append) const override {
-    SB_DCHECK(max_cached_frames);
-    SB_DCHECK(max_frames_per_append);
-
-    *max_cached_frames = 128 * 1024;
-    *max_frames_per_append = 16384;
-  }
-};
-
 // static
 scoped_ptr<PlayerComponents> PlayerComponents::Create() {
-  return make_scoped_ptr<PlayerComponents>(new PlayerComponentsImpl);
+  return make_scoped_ptr<PlayerComponents>(new StubPlayerComponentsImpl);
 }
 
 // static
diff --git a/src/starboard/shared/starboard/player/filter/stub_player_components_impl.h b/src/starboard/shared/starboard/player/filter/stub_player_components_impl.h
new file mode 100644
index 0000000..3e2013a
--- /dev/null
+++ b/src/starboard/shared/starboard/player/filter/stub_player_components_impl.h
@@ -0,0 +1,61 @@
+// Copyright 2017 The Cobalt Authors. All Rights Reserved.
+//
+// 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 STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_STUB_PLAYER_COMPONENTS_IMPL_H_
+#define STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_STUB_PLAYER_COMPONENTS_IMPL_H_
+
+#include "starboard/shared/starboard/player/filter/player_components.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+namespace filter {
+
+class StubPlayerComponentsImpl : public PlayerComponents {
+ public:
+  void CreateAudioComponents(
+      const AudioParameters& audio_parameters,
+      scoped_ptr<AudioDecoder>* audio_decoder,
+      scoped_ptr<AudioRendererSink>* audio_renderer_sink) override {
+    CreateStubAudioComponents(audio_parameters, audio_decoder,
+                              audio_renderer_sink);
+  }
+
+  void CreateVideoComponents(
+      const VideoParameters& video_parameters,
+      scoped_ptr<VideoDecoder>* video_decoder,
+      scoped_ptr<VideoRenderAlgorithm>* video_render_algorithm,
+      scoped_refptr<VideoRendererSink>* video_renderer_sink) override {
+    CreateStubVideoComponents(video_parameters, video_decoder,
+                              video_render_algorithm, video_renderer_sink);
+  }
+
+  void GetAudioRendererParams(int* max_cached_frames,
+                              int* max_frames_per_append) const override {
+    SB_DCHECK(max_cached_frames);
+    SB_DCHECK(max_frames_per_append);
+
+    *max_cached_frames = 128 * 1024;
+    *max_frames_per_append = 16384;
+  }
+};
+
+}  // namespace filter
+}  // namespace player
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_STUB_PLAYER_COMPONENTS_IMPL_H_
diff --git a/src/starboard/shared/starboard/player/filter/stub_video_decoder.cc b/src/starboard/shared/starboard/player/filter/stub_video_decoder.cc
index 344d364..38b84a6 100644
--- a/src/starboard/shared/starboard/player/filter/stub_video_decoder.cc
+++ b/src/starboard/shared/starboard/player/filter/stub_video_decoder.cc
@@ -52,17 +52,40 @@
         video_sample_info_.value() != video_sample_info) {
       SB_LOG(INFO) << "New video sample info: " << video_sample_info;
       video_sample_info_ = video_sample_info;
+#if SB_API_VERSION < 11
+      color_metadata_ = *video_sample_info.color_metadata;
+      video_sample_info_->color_metadata = &color_metadata_;
+#endif  // SB_API_VERSION < 11
     }
   }
 
-  decoder_status_cb_(kNeedMoreInput, new VideoFrame(input_buffer->timestamp()));
+  output_event_frame_times_.insert(input_buffer->timestamp());
+  // Defer sending frames out until we've accumulated a reasonable number.
+  // This allows for input buffers to be out of order, and we expect that
+  // after buffering 8 (arbitrarily chosen) that the first timestamp in the
+  // sorted buffer will be the "correct" timestamp to send out.
+  const int kMaxFramesToDelay = 8;
+  scoped_refptr<VideoFrame> output_frame = NULL;
+  if (output_event_frame_times_.size() > kMaxFramesToDelay) {
+    output_frame = new VideoFrame(*output_event_frame_times_.begin());
+    output_event_frame_times_.erase(output_event_frame_times_.begin());
+  }
+  decoder_status_cb_(kNeedMoreInput, output_frame);
 }
 
 void StubVideoDecoder::WriteEndOfStream() {
+  // If there are any remaining frames we need to output, send them all out
+  // before writing EOS.
+  for (const auto& time : output_event_frame_times_) {
+    decoder_status_cb_(kBufferFull, new VideoFrame(time));
+  }
   decoder_status_cb_(kBufferFull, VideoFrame::CreateEOSFrame());
 }
 
-void StubVideoDecoder::Reset() {}
+void StubVideoDecoder::Reset() {
+  output_event_frame_times_.clear();
+  video_sample_info_ = nullopt;
+}
 
 SbDecodeTarget StubVideoDecoder::GetCurrentDecodeTarget() {
   return kSbDecodeTargetInvalid;
diff --git a/src/starboard/shared/starboard/player/filter/stub_video_decoder.h b/src/starboard/shared/starboard/player/filter/stub_video_decoder.h
index fd64fdc..b320b32 100644
--- a/src/starboard/shared/starboard/player/filter/stub_video_decoder.h
+++ b/src/starboard/shared/starboard/player/filter/stub_video_decoder.h
@@ -15,6 +15,8 @@
 #ifndef STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_STUB_VIDEO_DECODER_H_
 #define STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_STUB_VIDEO_DECODER_H_
 
+#include <set>
+
 #include "starboard/common/optional.h"
 #include "starboard/shared/internal_only.h"
 #include "starboard/shared/starboard/player/filter/video_decoder_internal.h"
@@ -50,6 +52,12 @@
  private:
   DecoderStatusCB decoder_status_cb_;
   optional<SbMediaVideoSampleInfo> video_sample_info_;
+#if SB_API_VERSION < 11
+  SbMediaColorMetadata color_metadata_;
+#endif  // SB_API_VERSION < 11
+
+  // std::set<> keeps frame timestamps sorted in ascending order.
+  std::set<SbTime> output_event_frame_times_;
 };
 
 }  // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/testing/adaptive_audio_decoder_test.cc b/src/starboard/shared/starboard/player/filter/testing/adaptive_audio_decoder_test.cc
index 4730e34..c68e925 100644
--- a/src/starboard/shared/starboard/player/filter/testing/adaptive_audio_decoder_test.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/adaptive_audio_decoder_test.cc
@@ -21,6 +21,7 @@
 #include "starboard/shared/starboard/media/media_support_internal.h"
 #include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
 #include "starboard/shared/starboard/player/filter/player_components.h"
+#include "starboard/shared/starboard/player/filter/stub_player_components_impl.h"
 #include "starboard/shared/starboard/player/video_dmp_reader.h"
 #include "starboard/thread.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -37,6 +38,8 @@
 namespace testing {
 namespace {
 
+using ::testing::Bool;
+using ::testing::Combine;
 using ::testing::ValuesIn;
 using std::vector;
 using std::string;
@@ -88,13 +91,14 @@
 
 // TODO: Avoid reading same dmp file repeatly.
 class AdaptiveAudioDecoderTest
-    : public ::testing::TestWithParam<vector<const char*>> {
+    : public ::testing::TestWithParam<std::tuple<vector<const char*>, bool>> {
  protected:
   enum Event { kConsumed, kOutput, kError };
 
-  AdaptiveAudioDecoderTest() {
-    vector<const char*> params = GetParam();
-    for (auto filename : params) {
+  AdaptiveAudioDecoderTest()
+      : test_filenames_(std::get<0>(GetParam())),
+        using_stub_decoder_(std::get<1>(GetParam())) {
+    for (auto filename : test_filenames_) {
       dmp_readers_.emplace_back(
           new VideoDmpReader(ResolveTestFileName(filename).c_str()));
     }
@@ -105,9 +109,11 @@
       }
       return std::move(accumulated) + "->" + str;
     };
-    string description = std::accumulate(params.begin(), params.end(), string(),
-                                         accumulate_operation);
-    SB_LOG(INFO) << "Testing: " << description;
+    string description =
+        std::accumulate(test_filenames_.begin(), test_filenames_.end(),
+                        string(), accumulate_operation);
+    SB_LOG(INFO) << "Testing: " << description
+                 << (using_stub_decoder_ ? " (with stub decoder)." : ".");
   }
 
   void SetUp() override {
@@ -121,8 +127,14 @@
         dmp_readers_[0]->audio_codec(), dmp_readers_[0]->audio_sample_info(),
         kSbDrmSystemInvalid};
 
-    scoped_ptr<PlayerComponents> components = PlayerComponents::Create();
     scoped_ptr<AudioRendererSink> audio_renderer_sink;
+    scoped_ptr<PlayerComponents> components;
+    if (using_stub_decoder_) {
+      components = make_scoped_ptr<StubPlayerComponentsImpl>(
+          new StubPlayerComponentsImpl);
+    } else {
+      components = PlayerComponents::Create();
+    }
     components->CreateAudioComponents(audio_parameters, &audio_decoder_,
                                       &audio_renderer_sink);
     ASSERT_TRUE(audio_decoder_);
@@ -202,6 +214,16 @@
     }
   }
 
+  void AssertExpectedAndOutputFramesMatch(int expected_output_frames) {
+    if (using_stub_decoder_) {
+      // The number of output frames is not applicable in the case of the
+      // StubAudioDecoder, because it is not actually doing any decoding work.
+      return;
+    }
+    ASSERT_LE(abs(expected_output_frames - num_of_output_frames_),
+              dmp_readers_.size());
+  }
+
   vector<std::unique_ptr<VideoDmpReader>> dmp_readers_;
   scoped_refptr<DecodedAudio> last_decoded_audio_;
   int num_of_output_frames_ = 0;
@@ -262,6 +284,13 @@
     num_of_output_frames_ += last_decoded_audio_->frames();
   }
 
+  // Test parameter for the filenames to load with the VideoDmpReader.
+  std::vector<const char*> test_filenames_;
+
+  // Test parameter to configure whether the test is run with the
+  // StubAudioDecoder, or the platform-specific AudioDecoderImpl.
+  bool using_stub_decoder_;
+
   JobQueue job_queue_;
   scoped_ptr<AudioDecoder> audio_decoder_;
 
@@ -300,8 +329,7 @@
   // |expected_output_frames|. Each time to switch decoder, it may have one
   // sample difference in output due to integer conversion. The total difference
   // should not exceed the length of |dmp_readers_|.
-  ASSERT_LE(abs(expected_output_frames - num_of_output_frames_),
-            dmp_readers_.size());
+  AssertExpectedAndOutputFramesMatch(expected_output_frames);
 }
 
 TEST_P(AdaptiveAudioDecoderTest, MultipleInput) {
@@ -334,8 +362,7 @@
   // |expected_output_frames|. Each time to switch decoder, it may have one
   // sample difference in ouput due to integer conversion. The total difference
   // should not exceed the length of |dmp_readers_|.
-  ASSERT_LE(abs(expected_output_frames - num_of_output_frames_),
-            dmp_readers_.size());
+  AssertExpectedAndOutputFramesMatch(expected_output_frames);
 }
 
 vector<vector<const char*>> GetSupportedTests() {
@@ -391,7 +418,7 @@
 
 INSTANTIATE_TEST_CASE_P(AdaptiveAudioDecoderTests,
                         AdaptiveAudioDecoderTest,
-                        ValuesIn(GetSupportedTests()));
+                        Combine(ValuesIn(GetSupportedTests()), Bool()));
 
 }  // namespace
 }  // namespace testing
diff --git a/src/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc b/src/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc
index cd73cfc..9b8e5ce 100644
--- a/src/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc
@@ -25,6 +25,7 @@
 #include "starboard/shared/starboard/media/media_support_internal.h"
 #include "starboard/shared/starboard/media/media_util.h"
 #include "starboard/shared/starboard/player/filter/player_components.h"
+#include "starboard/shared/starboard/player/filter/stub_player_components_impl.h"
 #include "starboard/shared/starboard/player/video_dmp_reader.h"
 #include "starboard/thread.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -40,6 +41,8 @@
 namespace testing {
 namespace {
 
+using ::testing::Bool;
+using ::testing::Combine;
 using ::testing::ValuesIn;
 using video_dmp::VideoDmpReader;
 
@@ -74,10 +77,15 @@
   return GetTestInputDirectory() + SB_FILE_SEP_CHAR + filename;
 }
 
-class AudioDecoderTest : public ::testing::TestWithParam<const char*> {
+class AudioDecoderTest
+    : public ::testing::TestWithParam<std::tuple<const char*, bool> > {
  public:
-  AudioDecoderTest() : dmp_reader_(ResolveTestFileName(GetParam()).c_str()) {
-    SB_LOG(INFO) << "Testing " << GetParam();
+  AudioDecoderTest()
+      : test_filename_(std::get<0>(GetParam())),
+        using_stub_decoder_(std::get<1>(GetParam())),
+        dmp_reader_(ResolveTestFileName(test_filename_).c_str()) {
+    SB_LOG(INFO) << "Testing " << test_filename_
+                 << (using_stub_decoder_ ? " with stub audio decoder." : ".");
   }
   void SetUp() override {
     ASSERT_NE(dmp_reader_.audio_codec(), kSbMediaAudioCodecNone);
@@ -87,7 +95,13 @@
         dmp_reader_.audio_codec(), dmp_reader_.audio_sample_info(),
         kSbDrmSystemInvalid};
 
-    scoped_ptr<PlayerComponents> components = PlayerComponents::Create();
+    scoped_ptr<PlayerComponents> components;
+    if (using_stub_decoder_) {
+      components = make_scoped_ptr<StubPlayerComponentsImpl>(
+          new StubPlayerComponentsImpl);
+    } else {
+      components = PlayerComponents::Create();
+    }
     components->CreateAudioComponents(audio_parameters, &audio_decoder_,
                                       &audio_renderer_sink_);
     ASSERT_TRUE(audio_decoder_);
@@ -151,6 +165,8 @@
 
     last_input_buffer_ = GetAudioInputBuffer(index);
 
+    outstanding_inputs_.insert(last_input_buffer_->timestamp());
+
     audio_decoder_->Decode(last_input_buffer_, consumed_cb());
   }
 
@@ -265,6 +281,8 @@
     can_accept_more_input_ = true;
     last_input_buffer_ = NULL;
     last_decoded_audio_ = NULL;
+    outstanding_inputs_.clear();
+    eos_written_ = false;
   }
 
   void WaitForDecodedAudio() {
@@ -296,9 +314,38 @@
 #endif  // SB_API_VERSION >= 11
   }
 
+  void WriteEndOfStream() {
+    SB_DCHECK(!eos_written_);
+    audio_decoder_->WriteEndOfStream();
+    eos_written_ = true;
+  }
+
+  void AssertInvalidOutputFormat() {
+    SbMediaAudioSampleType output_sample_type = audio_decoder_->GetSampleType();
+    ASSERT_TRUE(output_sample_type == kSbMediaAudioSampleTypeFloat32 ||
+                output_sample_type == kSbMediaAudioSampleTypeInt16Deprecated);
+
+    SbMediaAudioFrameStorageType output_storage_type =
+        audio_decoder_->GetStorageType();
+    ASSERT_TRUE(output_storage_type ==
+                    kSbMediaAudioFrameStorageTypeInterleaved ||
+                output_storage_type == kSbMediaAudioFrameStorageTypePlanar);
+
+    int output_samples_per_second = audio_decoder_->GetSamplesPerSecond();
+    ASSERT_TRUE(output_samples_per_second > 0 &&
+                output_samples_per_second <= 480000);
+  }
+
   Mutex event_queue_mutex_;
   std::deque<Event> event_queue_;
 
+  // Test parameter for the filename to load with the VideoDmpReader.
+  const char* test_filename_;
+
+  // Test parameter to configure whether the test is run with the
+  // StubAudioDecoder, or the platform-specific AudioDecoderImpl
+  bool using_stub_decoder_;
+
   JobQueue job_queue_;
   VideoDmpReader dmp_reader_;
   scoped_ptr<AudioDecoder> audio_decoder_;
@@ -307,6 +354,12 @@
   bool can_accept_more_input_ = true;
   scoped_refptr<InputBuffer> last_input_buffer_;
   scoped_refptr<DecodedAudio> last_decoded_audio_;
+
+  // List of timestamps of InputBuffers provided to the Decoder so far, sorted
+  // in ascending order.
+  std::set<SbTime> outstanding_inputs_;
+
+  bool eos_written_ = false;
 };
 
 TEST_P(AudioDecoderTest, ThreeMoreDecoders) {
@@ -332,10 +385,11 @@
 
 TEST_P(AudioDecoderTest, SingleInput) {
   ASSERT_NO_FATAL_FAILURE(WriteSingleInput(0));
-  audio_decoder_->WriteEndOfStream();
+  WriteEndOfStream();
 
   ASSERT_NO_FATAL_FAILURE(DrainOutputs());
   ASSERT_TRUE(last_decoded_audio_);
+  ASSERT_NO_FATAL_FAILURE(AssertInvalidOutputFormat());
 }
 
 TEST_P(AudioDecoderTest, SingleInputHEAAC) {
@@ -346,10 +400,11 @@
   }
 
   ASSERT_NO_FATAL_FAILURE(WriteSingleInput(0));
-  audio_decoder_->WriteEndOfStream();
+  WriteEndOfStream();
 
   ASSERT_NO_FATAL_FAILURE(DrainOutputs());
   ASSERT_TRUE(last_decoded_audio_);
+  ASSERT_NO_FATAL_FAILURE(AssertInvalidOutputFormat());
   if (last_decoded_audio_->frames() == kAacFrameSize) {
     return;
   }
@@ -367,7 +422,7 @@
                                           static_cast<int>(content.size()));
   audio_decoder_->Decode(last_input_buffer_, consumed_cb());
 
-  audio_decoder_->WriteEndOfStream();
+  WriteEndOfStream();
 
   bool error_occurred = false;
   ASSERT_NO_FATAL_FAILURE(DrainOutputs(&error_occurred));
@@ -381,20 +436,22 @@
 }
 
 TEST_P(AudioDecoderTest, EndOfStreamWithoutAnyInput) {
-  audio_decoder_->WriteEndOfStream();
+  WriteEndOfStream();
 
   ASSERT_NO_FATAL_FAILURE(DrainOutputs());
   ASSERT_FALSE(last_decoded_audio_);
+  ASSERT_NO_FATAL_FAILURE(AssertInvalidOutputFormat());
 }
 
 TEST_P(AudioDecoderTest, ResetBeforeInput) {
   ResetDecoder();
 
   ASSERT_NO_FATAL_FAILURE(WriteSingleInput(0));
-  audio_decoder_->WriteEndOfStream();
+  WriteEndOfStream();
 
   ASSERT_NO_FATAL_FAILURE(DrainOutputs());
   ASSERT_TRUE(last_decoded_audio_);
+  ASSERT_NO_FATAL_FAILURE(AssertInvalidOutputFormat());
 }
 
 TEST_P(AudioDecoderTest, MultipleInputs) {
@@ -404,10 +461,11 @@
 
   ASSERT_NO_FATAL_FAILURE(WriteMultipleInputs(0, number_of_inputs_to_write));
 
-  audio_decoder_->WriteEndOfStream();
+  WriteEndOfStream();
 
   ASSERT_NO_FATAL_FAILURE(DrainOutputs());
   ASSERT_TRUE(last_decoded_audio_);
+  ASSERT_NO_FATAL_FAILURE(AssertInvalidOutputFormat());
 }
 
 #if SB_API_VERSION >= 11
@@ -421,7 +479,7 @@
   ASSERT_NO_FATAL_FAILURE(WriteTimeLimitedInputs(&start_index, duration));
 
   if (start_index >= dmp_reader_.number_of_audio_buffers()) {
-    audio_decoder_->WriteEndOfStream();
+    WriteEndOfStream();
   }
 
   // Wait for decoded audio.
@@ -456,9 +514,10 @@
       ASSERT_FALSE(decoded_audio->is_end_of_stream());
     }
   }
-  audio_decoder_->WriteEndOfStream();
+  WriteEndOfStream();
   ASSERT_NO_FATAL_FAILURE(DrainOutputs());
   ASSERT_TRUE(last_decoded_audio_);
+  ASSERT_NO_FATAL_FAILURE(AssertInvalidOutputFormat());
 }
 
 #endif  // SB_API_VERSION >= 11
@@ -488,7 +547,7 @@
 
 INSTANTIATE_TEST_CASE_P(AudioDecoderTests,
                         AudioDecoderTest,
-                        ValuesIn(GetSupportedTests()));
+                        Combine(ValuesIn(GetSupportedTests()), Bool()));
 
 }  // namespace
 }  // namespace testing
diff --git a/src/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc b/src/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc
index 19f8fcc..c977332 100644
--- a/src/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc
@@ -17,6 +17,7 @@
 #include <algorithm>
 #include <deque>
 #include <functional>
+#include <map>
 #include <set>
 
 #include "starboard/common/condition_variable.h"
@@ -28,7 +29,7 @@
 #include "starboard/memory.h"
 #include "starboard/shared/starboard/media/media_support_internal.h"
 #include "starboard/shared/starboard/media/media_util.h"
-#include "starboard/shared/starboard/player/filter/player_components.h"
+#include "starboard/shared/starboard/player/filter/stub_player_components_impl.h"
 #include "starboard/shared/starboard/player/job_queue.h"
 #include "starboard/shared/starboard/player/video_dmp_reader.h"
 #include "starboard/testing/fake_graphics_context_provider.h"
@@ -55,6 +56,8 @@
 using ::testing::AssertionFailure;
 using ::testing::AssertionResult;
 using ::testing::AssertionSuccess;
+using ::testing::Bool;
+using ::testing::Combine;
 using ::testing::ValuesIn;
 using video_dmp::VideoDmpReader;
 
@@ -102,11 +105,16 @@
          << "time " << time1 << " doesn't match with time " << time2;
 }
 
-class VideoDecoderTest : public ::testing::TestWithParam<TestParam> {
+class VideoDecoderTest
+    : public ::testing::TestWithParam<std::tuple<TestParam, bool>> {
  public:
   VideoDecoderTest()
-      : dmp_reader_(ResolveTestFileName(GetParam().filename).c_str()) {
-    SB_LOG(INFO) << "Testing " << GetParam().filename;
+      : test_filename_(std::get<0>(GetParam()).filename),
+        output_mode_(std::get<0>(GetParam()).output_mode),
+        using_stub_decoder_(std::get<1>(GetParam())),
+        dmp_reader_(ResolveTestFileName(test_filename_).c_str()) {
+    SB_LOG(INFO) << "Testing " << test_filename_
+                 << (using_stub_decoder_ ? " with stub video decoder." : ".");
   }
 
   ~VideoDecoderTest() { video_decoder_->Reset(); }
@@ -116,7 +124,7 @@
     ASSERT_GT(dmp_reader_.number_of_video_buffers(), 0);
     ASSERT_TRUE(GetVideoInputBuffer(0)->video_sample_info().is_key_frame);
 
-    SbPlayerOutputMode output_mode = GetParam().output_mode;
+    SbPlayerOutputMode output_mode = output_mode_;
     ASSERT_TRUE(VideoDecoder::OutputModeSupported(
         output_mode, dmp_reader_.video_codec(), kSbDrmSystemInvalid));
 
@@ -127,7 +135,13 @@
         output_mode,
         fake_graphics_context_provider_.decoder_target_provider()};
 
-    scoped_ptr<PlayerComponents> components = PlayerComponents::Create();
+    scoped_ptr<PlayerComponents> components;
+    if (using_stub_decoder_) {
+      components = make_scoped_ptr<StubPlayerComponentsImpl>(
+          new StubPlayerComponentsImpl);
+    } else {
+      components = PlayerComponents::Create();
+    }
     components->CreateVideoComponents(video_parameters, &video_decoder_,
                                       &video_render_algorithm_,
                                       &video_renderer_sink_);
@@ -172,7 +186,7 @@
 
 #if SB_HAS(GLES2)
   void AssertInvalidDecodeTarget() {
-    if (GetParam().output_mode == kSbPlayerOutputModeDecodeToTexture) {
+    if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) {
       auto decode_target = video_decoder_->GetCurrentDecodeTarget();
       ASSERT_FALSE(SbDecodeTargetIsValid(decode_target));
       fake_graphics_context_provider_.ReleaseDecodeTarget(decode_target);
@@ -239,10 +253,20 @@
     return !event_queue_.empty();
   }
 
+  void GetDecodeTargetWhenSupported() {
+#if SB_HAS(GLES2)
+    if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) {
+      SbDecodeTarget decode_target = video_decoder_->GetCurrentDecodeTarget();
+      fake_graphics_context_provider_.ReleaseDecodeTarget(decode_target);
+    }
+#endif  // SB_HAS(GLES2)
+  }
+
   void AssertValidDecodeTargetWhenSupported() {
 #if SB_HAS(GLES2)
-    if (GetParam().output_mode == kSbPlayerOutputModeDecodeToTexture) {
-      auto decode_target = video_decoder_->GetCurrentDecodeTarget();
+    if (output_mode_ == kSbPlayerOutputModeDecodeToTexture &&
+        !using_stub_decoder_) {
+      SbDecodeTarget decode_target = video_decoder_->GetCurrentDecodeTarget();
       ASSERT_TRUE(SbDecodeTargetIsValid(decode_target));
       fake_graphics_context_provider_.ReleaseDecodeTarget(decode_target);
     }
@@ -290,6 +314,14 @@
         ASSERT_NO_FATAL_FAILURE(WriteSingleInput(start_index));
         ++start_index;
         --number_of_inputs_to_write;
+      } else if (event.status == kError) {
+        // Assume that the caller does't expect an error when |event_cb| isn't
+        // provided.
+        ASSERT_TRUE(event_cb);
+        bool continue_process = true;
+        event_cb(event, &continue_process);
+        ASSERT_FALSE(continue_process);
+        return;
       } else {
         ASSERT_EQ(event.status, kBufferFull);
       }
@@ -335,7 +367,16 @@
       if (event.frame) {
         if (event.frame->is_end_of_stream()) {
           end_of_stream_decoded = true;
-          ASSERT_TRUE(outstanding_inputs_.empty());
+          if (!outstanding_inputs_.empty()) {
+            if (error_occurred) {
+              *error_occurred = true;
+            } else {
+              // |error_occurred| is NULL indicates that the caller doesn't
+              // expect an error, use the following redundant ASSERT to trigger
+              // a failure.
+              ASSERT_TRUE(outstanding_inputs_.empty());
+            }
+          }
         } else {
           if (!decoded_frames_.empty()) {
             ASSERT_LT(decoded_frames_.back()->timestamp(),
@@ -371,11 +412,24 @@
     auto video_sample_info =
         dmp_reader_.GetPlayerSampleInfo(kSbMediaTypeVideo, index);
 #if SB_API_VERSION >= 11
-    return new InputBuffer(DeallocateSampleFunc, NULL, NULL, video_sample_info);
+    auto input_buffer =
+        new InputBuffer(DeallocateSampleFunc, NULL, NULL, video_sample_info);
 #else   // SB_API_VERSION >= 11
-    return new InputBuffer(kSbMediaTypeVideo, DeallocateSampleFunc, NULL, NULL,
-                           video_sample_info, NULL);
+    auto input_buffer = new InputBuffer(kSbMediaTypeVideo, DeallocateSampleFunc,
+                                        NULL, NULL, video_sample_info, NULL);
 #endif  // SB_API_VERSION >= 11
+    auto iter = invalid_inputs_.find(index);
+    if (iter != invalid_inputs_.end()) {
+      std::vector<uint8_t> content(input_buffer->size(), iter->second);
+      // Replace the content with invalid data.
+      input_buffer->SetDecryptedContent(content.data(),
+                                        static_cast<int>(content.size()));
+    }
+    return input_buffer;
+  }
+
+  void UseInvalidDataForInput(size_t index, uint8_t byte_to_fill) {
+    invalid_inputs_[index] = byte_to_fill;
   }
 
   JobQueue job_queue_;
@@ -383,6 +437,16 @@
   Mutex mutex_;
   std::deque<Event> event_queue_;
 
+  // Test parameter filename for the VideoDmpReader to load and test with.
+  const char* test_filename_;
+
+  // Test parameter for OutputMode.
+  SbPlayerOutputMode output_mode_;
+
+  // Test parameter for whether or not to use the StubVideoDecoder, or the
+  // platform-specific VideoDecoderImpl.
+  bool using_stub_decoder_;
+
   FakeGraphicsContextProvider fake_graphics_context_provider_;
   VideoDmpReader dmp_reader_;
   scoped_ptr<VideoDecoder> video_decoder_;
@@ -397,6 +461,8 @@
   scoped_refptr<VideoRendererSink> video_renderer_sink_;
 
   bool end_of_stream_written_ = false;
+
+  std::map<size_t, uint8_t> invalid_inputs_;
 };
 
 TEST_P(VideoDecoderTest, PrerollFrameCount) {
@@ -440,8 +506,11 @@
 
 #if SB_HAS(GLES2)
 TEST_P(VideoDecoderTest, GetCurrentDecodeTargetBeforeWriteInputBuffer) {
-  if (GetParam().output_mode == kSbPlayerOutputModeDecodeToTexture) {
+  if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) {
     AssertInvalidDecodeTarget();
+    SbDecodeTarget decode_target = video_decoder_->GetCurrentDecodeTarget();
+    EXPECT_FALSE(SbDecodeTargetIsValid(decode_target));
+    fake_graphics_context_provider_.ReleaseDecodeTarget(decode_target);
   }
 }
 #endif  // SB_HAS(GLES2)
@@ -529,28 +598,81 @@
   ASSERT_FALSE(error_occurred);
 }
 
-TEST_P(VideoDecoderTest, SingleInvalidInput) {
-  need_more_input_ = false;
-  auto input_buffer = GetVideoInputBuffer(0);
-  outstanding_inputs_.insert(input_buffer->timestamp());
-  std::vector<uint8_t> content(input_buffer->size(), 0xab);
-  // Replace the content with invalid data.
-  input_buffer->SetDecryptedContent(content.data(),
-                                    static_cast<int>(content.size()));
-  video_decoder_->WriteInputBuffer(input_buffer);
+TEST_P(VideoDecoderTest, SingleInvalidKeyFrame) {
+  UseInvalidDataForInput(0, 0xab);
 
+  WriteSingleInput(0);
   WriteEndOfStream();
 
   bool error_occurred = true;
   ASSERT_NO_FATAL_FAILURE(DrainOutputs(&error_occurred));
-  if (error_occurred) {
-    ASSERT_TRUE(decoded_frames_.empty());
-  } else {
-    // We don't expect the video decoder to recover from a bad input but some
-    // decoders may just return an empty frame.
-    ASSERT_FALSE(decoded_frames_.empty());
-    AssertValidDecodeTargetWhenSupported();
+  // We don't expect the video decoder can always recover from a bad key frame
+  // and to raise an error, but it shouldn't crash or hang.
+  GetDecodeTargetWhenSupported();
+}
+
+TEST_P(VideoDecoderTest, MultipleValidInputsAfterInvalidKeyFrame) {
+  const size_t kMaxNumberOfInputToWrite = 10;
+  const size_t number_of_input_to_write =
+      std::min(kMaxNumberOfInputToWrite, dmp_reader_.number_of_video_buffers());
+
+  UseInvalidDataForInput(0, 0xab);
+
+  bool error_occurred = false;
+
+  // Write first few frames.  The first one is invalid and the rest are valid.
+  WriteMultipleInputs(0, number_of_input_to_write,
+                      [&](const Event& event, bool* continue_process) {
+                        if (event.status == kError) {
+                          error_occurred = true;
+                          *continue_process = false;
+                          return;
+                        }
+
+                        *continue_process = event.status != kBufferFull;
+                      });
+
+  if (!error_occurred) {
+    GetDecodeTargetWhenSupported();
+    WriteEndOfStream();
+    ASSERT_NO_FATAL_FAILURE(DrainOutputs(&error_occurred));
   }
+  // We don't expect the video decoder can always recover from a bad key frame
+  // and to raise an error, but it shouldn't crash or hang.
+  GetDecodeTargetWhenSupported();
+}
+
+TEST_P(VideoDecoderTest, MultipleInvalidInput) {
+  const size_t kMaxNumberOfInputToWrite = 128;
+  const size_t number_of_input_to_write =
+      std::min(kMaxNumberOfInputToWrite, dmp_reader_.number_of_video_buffers());
+  // Replace the content of the first few input buffers with invalid data.
+  // Every test instance loads its own copy of data so this won't affect other
+  // tests.
+  for (size_t i = 0; i < number_of_input_to_write; ++i) {
+    UseInvalidDataForInput(i, static_cast<uint8_t>(0xab + i));
+  }
+
+  bool error_occurred = false;
+  WriteMultipleInputs(0, number_of_input_to_write,
+                      [&](const Event& event, bool* continue_process) {
+                        if (event.status == kError) {
+                          error_occurred = true;
+                          *continue_process = false;
+                          return;
+                        }
+
+                        *continue_process = event.status != kBufferFull;
+                      });
+
+  if (!error_occurred) {
+    GetDecodeTargetWhenSupported();
+    WriteEndOfStream();
+    ASSERT_NO_FATAL_FAILURE(DrainOutputs(&error_occurred));
+  }
+  // We don't expect the video decoder can always recover from a bad key frame
+  // and to raise an error, but it shouldn't crash or hang.
+  GetDecodeTargetWhenSupported();
 }
 
 TEST_P(VideoDecoderTest, EndOfStreamWithoutAnyInput) {
@@ -744,7 +866,7 @@
 
 INSTANTIATE_TEST_CASE_P(VideoDecoderTests,
                         VideoDecoderTest,
-                        ValuesIn(GetSupportedTests()));
+                        Combine(ValuesIn(GetSupportedTests()), Bool()));
 
 }  // namespace
 }  // namespace testing
diff --git a/src/starboard/shared/starboard/player/testdata/beneath_the_canopy_avc_aac.dmp b/src/starboard/shared/starboard/player/testdata/beneath_the_canopy_avc_aac.dmp
deleted file mode 100644
index bec6752..0000000
--- a/src/starboard/shared/starboard/player/testdata/beneath_the_canopy_avc_aac.dmp
+++ /dev/null
Binary files differ
diff --git a/src/starboard/shared/starboard/player/testdata/beneath_the_canopy_vp9_opus.dmp b/src/starboard/shared/starboard/player/testdata/beneath_the_canopy_vp9_opus.dmp
deleted file mode 100644
index 4d663f9..0000000
--- a/src/starboard/shared/starboard/player/testdata/beneath_the_canopy_vp9_opus.dmp
+++ /dev/null
Binary files differ
diff --git a/src/starboard/shared/starboard/player/testdata/heaac.dmp b/src/starboard/shared/starboard/player/testdata/heaac.dmp
deleted file mode 100644
index 133a9c1..0000000
--- a/src/starboard/shared/starboard/player/testdata/heaac.dmp
+++ /dev/null
Binary files differ
diff --git a/src/starboard/shared/starboard/player/video_dmp_reader.cc b/src/starboard/shared/starboard/player/video_dmp_reader.cc
index 2ae52a0..250a0f7 100644
--- a/src/starboard/shared/starboard/player/video_dmp_reader.cc
+++ b/src/starboard/shared/starboard/player/video_dmp_reader.cc
@@ -120,13 +120,13 @@
   switch (type) {
     case kSbMediaTypeAudio: {
       SB_DCHECK(index < audio_access_units_.size());
-      const AudioAccessUnit& aau = audio_access_units_[index];
-      return ConvertToPlayerSampleInfo(aau);
+      const AudioAccessUnit& audio_au = audio_access_units_[index];
+      return ConvertToPlayerSampleInfo(audio_au);
     }
     case kSbMediaTypeVideo: {
       SB_DCHECK(index < video_access_units_.size());
-      const VideoAccessUnit& vau = video_access_units_[index];
-      return ConvertToPlayerSampleInfo(vau);
+      const VideoAccessUnit& video_au = video_access_units_[index];
+      return ConvertToPlayerSampleInfo(video_au);
     }
   }
   SB_NOTREACHED() << "Unhandled SbMediaType";
diff --git a/src/starboard/shared/stub/cpu_features_get.cc b/src/starboard/shared/stub/cpu_features_get.cc
index 21c289f..a550da2 100644
--- a/src/starboard/shared/stub/cpu_features_get.cc
+++ b/src/starboard/shared/stub/cpu_features_get.cc
@@ -13,34 +13,23 @@
 // limitations under the License.
 
 #include "starboard/cpu_features.h"
+#include "starboard/shared/starboard/cpu_features.h"
 
 #include <string.h>
 
 #if SB_API_VERSION >= 11
 
+using starboard::shared::SetArmFeaturesInvalid;
+using starboard::shared::SetGeneralFeaturesInvalid;
+using starboard::shared::SetX86FeaturesInvalid;
+
 bool SbCPUFeaturesGet(SbCPUFeatures* features) {
   memset(features, 0, sizeof(*features));
   features->architecture = kSbCPUFeaturesArchitectureUnknown;
-  features->brand = "";
-  features->cache_size = -1;
-  features->has_fpu = false;
-  features->hwcap = 0;
-  features->hwcap2 = 0;
 
-  features->arm.implementer = -1;
-  features->arm.variant = -1;
-  features->arm.revision = -1;
-  features->arm.architecture_generation = -1;
-  features->arm.part = -1;
-
-  features->x86.vendor = "";
-  features->x86.family = -1;
-  features->x86.ext_family = -1;
-  features->x86.model = -1;
-  features->x86.ext_model = -1;
-  features->x86.stepping = -1;
-  features->x86.type = -1;
-  features->x86.signature = -1;
+  SetGeneralFeaturesInvalid(features);
+  SetArmFeaturesInvalid(features);
+  SetX86FeaturesInvalid(features);
 
   return false;
 }
diff --git a/src/starboard/tools/testing/test_runner.py b/src/starboard/tools/testing/test_runner.py
index f5af1ca..ee3b7d7 100755
--- a/src/starboard/tools/testing/test_runner.py
+++ b/src/starboard/tools/testing/test_runner.py
@@ -473,6 +473,11 @@
     total_flaky_failed_count = 0
     total_filtered_count = 0
 
+    print  # Explicit print for empty formatting line.
+    logging.info("TEST RUN COMPLETE.")
+    if results:
+      print  # Explicit print for empty formatting line.
+
     # If the number of run tests from a test binary cannot be
     # determined, assume an error occurred while running it.
     error = False
@@ -513,8 +518,7 @@
         for test_case in flaky_failed_tests:
           for retry in range(_FLAKY_RETRY_LIMIT):
             retry_result = self._RunTest(target_name, test_case)
-            # Explicit print used to have an empty newline for formatting.
-            print
+            print  # Explicit print for empty formatting line.
             if retry_result[2] == 1:
               flaky_passed_tests.append(test_case)
               logging.info("%s succeeded on run #%d!\n", test_case, retry + 2)
@@ -529,20 +533,17 @@
       else:
         logging.info("")  # formatting newline.
 
-      logging.info("TEST RUN COMPLETE. RESULTS BELOW:")
-      logging.info("")  # formatting newline.
-
       test_status = "SUCCEEDED"
-      # If |return_code| is non-zero, the tests either crashed or failed.
-      if return_code != 0:
-        # If |run_count| is zero the tests crashed.
-        if run_count == 0 or actual_failed_count > 0 or flaky_failed_count > 0:
-          error = True
-          test_status = "FAILED"
+
+      # Always mark as FAILED if we have a non-zero return code, or failing
+      # test.
+      if return_code != 0 or actual_failed_count > 0 or flaky_failed_count > 0:
+        error = True
+        test_status = "FAILED"
         failed_test_groups.append(target_name)
 
       logging.info("%s: %s.", target_name, test_status)
-      if return_code != 0 and run_count == 0:
+      if return_code != 0 and run_count == 0 and filtered_count == 0:
         logging.info("  Results not available.  Did the test crash?")
         logging.info("")  # formatting newline.
         continue
@@ -579,6 +580,8 @@
         for line in filtered_tests:
           logging.info("    %s", line)
       logging.info("")  # formatting newline.
+      logging.info("  RETURN CODE: %d", return_code)
+      logging.info("")  # formatting newline.
 
     overall_status = "SUCCEEDED"
     result = True
diff --git a/src/third_party/boringssl/boringssl.gyp b/src/third_party/boringssl/boringssl.gyp
index 1edb8cd..a6cd7c8 100644
--- a/src/third_party/boringssl/boringssl.gyp
+++ b/src/third_party/boringssl/boringssl.gyp
@@ -44,6 +44,7 @@
         ],
         'sources': [
           '<(boringssl_root)/crypto/rand_extra/starboard.c',
+          '<(boringssl_root)/crypto/cpu-starboard.c',
         ],
         'sources!': [
           '<(boringssl_root)/crypto/bio/connect.c',
diff --git a/src/third_party/boringssl/boringssl.gypi b/src/third_party/boringssl/boringssl.gypi
index e8c5445..c1e4e2e 100644
--- a/src/third_party/boringssl/boringssl.gypi
+++ b/src/third_party/boringssl/boringssl.gypi
@@ -105,6 +105,7 @@
       '<(boringssl_root)/crypto/chacha/chacha.c',
       '<(boringssl_root)/crypto/cipher_extra/cipher_extra.c',
       '<(boringssl_root)/crypto/cipher_extra/derive_key.c',
+      '<(boringssl_root)/crypto/cipher_extra/e_aesccm.c',
       '<(boringssl_root)/crypto/cipher_extra/e_aesctrhmac.c',
       '<(boringssl_root)/crypto/cipher_extra/e_aesgcmsiv.c',
       '<(boringssl_root)/crypto/cipher_extra/e_chacha20poly1305.c',
diff --git a/src/third_party/boringssl/boringssl_tool.gyp b/src/third_party/boringssl/boringssl_tool.gyp
new file mode 100644
index 0000000..8397b22
--- /dev/null
+++ b/src/third_party/boringssl/boringssl_tool.gyp
@@ -0,0 +1,47 @@
+# Copyright 2019 Google Inc. All Rights Reserved.
+{
+  'variables': {
+    'boringssl_root%': '<(DEPTH)/third_party/boringssl/src',
+  },
+  'targets': [
+    {
+      'target_name': 'crypto_tool',
+      'type': '<(final_executable_type)',
+      'include_dirs': [
+        '<(boringssl_root)/include',
+      ],
+      'defines': [
+        'OPENSSL_NO_SOCK'
+      ],
+      'sources' : [
+        '<(boringssl_root)/tool/args.cc',
+        '<(boringssl_root)/tool/ciphers.cc',
+        '<(boringssl_root)/tool/const.cc',
+        '<(boringssl_root)/tool/digest.cc',
+        '<(boringssl_root)/tool/file.cc',
+        '<(boringssl_root)/tool/generate_ed25519.cc',
+        '<(boringssl_root)/tool/genrsa.cc',
+        '<(boringssl_root)/tool/pkcs12.cc',
+        '<(boringssl_root)/tool/rand.cc',
+        '<(boringssl_root)/tool/sign.cc',
+        '<(boringssl_root)/tool/speed.cc',
+        '<(boringssl_root)/tool/tool.cc',
+      ],
+      'dependencies' : [
+          '<(DEPTH)/third_party/boringssl/boringssl.gyp:crypto',
+          '<(DEPTH)/starboard/starboard.gyp:starboard'
+      ]
+    },
+    {
+      'target_name': 'crypto_tool_deploy',
+      'type': 'none',
+      'dependencies': [
+        'crypto_tool',
+      ],
+      'variables': {
+        'executable_name': 'crypto_tool',
+      },
+      'includes': [ '../../starboard/build/deploy.gypi' ],
+    },
+  ],
+}
diff --git a/src/third_party/boringssl/src/config/starboard/openssl/opensslconf.h b/src/third_party/boringssl/src/config/starboard/openssl/opensslconf.h
index 3734c4d..6edf989 100644
--- a/src/third_party/boringssl/src/config/starboard/openssl/opensslconf.h
+++ b/src/third_party/boringssl/src/config/starboard/openssl/opensslconf.h
@@ -36,9 +36,10 @@
 # define OPENSSL_NO_POSIX_IO
 #endif
 
-#ifndef OPENSSL_NO_FP_API
-# define OPENSSL_NO_FP_API
-#endif
+// Benchmarking tool requires FP API to read certs and keys
+//#ifndef OPENSSL_NO_FP_API
+// # define OPENSSL_NO_FP_API
+//#endif
 
 #ifndef OPENSSL_NO_DSO
 # define OPENSSL_NO_DSO
diff --git a/src/third_party/boringssl/src/crypto/cpu-starboard.c b/src/third_party/boringssl/src/crypto/cpu-starboard.c
new file mode 100644
index 0000000..2f7609c
--- /dev/null
+++ b/src/third_party/boringssl/src/crypto/cpu-starboard.c
@@ -0,0 +1,124 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// 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.
+
+#include <openssl/opensslconf.h>
+#include <openssl/cpu.h>
+#include "../../crypto/internal.h"
+#include <starboard/cpu_features.h>
+#include <starboard/string.h>
+
+#if defined(STARBOARD) && (SB_API_VERSION >= 11)
+
+#if defined(OPENSSL_X86) || defined(OPENSSL_X86_64)
+
+#define CPUID_01_REG_EDX        0
+#define CPUID_01_REG_ECX        1
+#define CPUID_07_REG_EBX        2
+#define CPUID_07_REG_ECX        3
+
+#define ID_01_EDX_IS_INTEL_BIT 30
+#define ID_01_ECX_SSSE3_BIT     9
+#define ID_01_ECX_AESNI_BIT    25
+#define ID_01_ECX_AVX_BIT      28
+
+static bool starboard_cpuid_setup_x86(void) {
+    OPENSSL_ia32cap_P[CPUID_01_REG_EDX] = 0;
+    OPENSSL_ia32cap_P[CPUID_01_REG_ECX] = 0;
+    OPENSSL_ia32cap_P[CPUID_07_REG_EBX] = 0;
+    OPENSSL_ia32cap_P[CPUID_07_REG_ECX] = 0;
+
+    SbCPUFeatures features;
+    if (!SbCPUFeaturesGet(&features)) {
+        return false;
+    }
+
+    if(features.architecture != kSbCPUFeaturesArchitectureX86_64 &&
+       features.architecture != kSbCPUFeaturesArchitectureX86) {
+        return false;
+    }
+
+    // OpenSSL has optimized AVX codepath enabled only on Intel CPUs
+    // The flag is passed as a "synthetic" value in reserved bit, i.e. CPUID
+    // never returns it. See https://github.com/openssl/openssl/commit/0c14980
+    if (!SbStringCompareAll(features.x86.vendor,"GenuineIntel"))  {
+        OPENSSL_ia32cap_P[CPUID_01_REG_EDX] |= (1 << ID_01_EDX_IS_INTEL_BIT);
+    }
+
+    // Enables AESNI acceleration
+    if (features.x86.has_aesni)
+        OPENSSL_ia32cap_P[CPUID_01_REG_ECX] |= (1 << ID_01_ECX_AESNI_BIT);
+    // Enables AESNI and SHAx acceleration
+    if (features.x86.has_avx)
+        OPENSSL_ia32cap_P[CPUID_01_REG_ECX] |= (1 << ID_01_ECX_AVX_BIT);
+    // Enables alternate SHA acceleration
+    if (features.x86.has_ssse3)
+        OPENSSL_ia32cap_P[CPUID_01_REG_ECX] |= (1 << ID_01_ECX_SSSE3_BIT);
+
+    return true;
+}
+
+#else
+static bool starboard_cpuid_setup_x86(void) { return true; }
+#endif // defined(OPENSSL_X86 || OPENSSL_X86_64)
+
+#if defined(OPENSSL_ARM)
+
+#include <openssl/arm_arch.h>
+
+// This global provides ARM assembly routines capability flags
+extern uint32_t OPENSSL_armcap_P;
+
+static bool starboard_cpuid_setup_arm() {
+    OPENSSL_armcap_P = 0; // Reset the flags
+
+    SbCPUFeatures features;
+    if (!SbCPUFeaturesGet(&features)) {
+        return false;
+    }
+
+    if(features.architecture != kSbCPUFeaturesArchitectureArm64 &&
+       features.architecture != kSbCPUFeaturesArchitectureArm) {
+        return false;
+    }
+
+    if (features.arm.has_aes)
+        OPENSSL_armcap_P |= ARMV8_AES;
+    if (features.arm.has_sha1)
+        OPENSSL_armcap_P |= ARMV8_SHA1;
+    if (features.arm.has_sha2)
+        OPENSSL_armcap_P |= ARMV8_SHA256;
+    if (features.arm.has_pmull)
+        OPENSSL_armcap_P |= ARMV8_PMULL;
+    if (features.arm.has_neon)
+        OPENSSL_armcap_P |= ARMV7_NEON;
+
+    return true;
+}
+
+#else
+static bool starboard_cpuid_setup_arm(void) { return true; }
+#endif // OPENSSL_ARM
+
+void OPENSSL_cpuid_setup_starboard(void) {
+    if (!starboard_cpuid_setup_arm() ||
+        !starboard_cpuid_setup_x86() ) {
+#if !SB_IS(EVERGREEN)
+            // Fall back on original implementation if the platform
+            // does not yet support Starboard CPU detection
+            OPENSSL_cpuid_setup();
+#endif
+        }
+}
+
+#endif // STARBOARD
diff --git a/src/third_party/boringssl/src/crypto/crypto.c b/src/third_party/boringssl/src/crypto/crypto.c
index 93e2f82..4e9d316 100644
--- a/src/third_party/boringssl/src/crypto/crypto.c
+++ b/src/third_party/boringssl/src/crypto/crypto.c
@@ -148,8 +148,12 @@
  // WARNING: this function may only configure the capability variables. See the
  // note above about the linker bug.
 #if defined(NEED_CPUID)
+#if defined(STARBOARD) && (SB_API_VERSION >= 11)
+  OPENSSL_cpuid_setup_starboard();
+#else
   OPENSSL_cpuid_setup();
 #endif
+#endif
 }
 
 void CRYPTO_library_init(void) {
diff --git a/src/third_party/boringssl/src/crypto/internal.h b/src/third_party/boringssl/src/crypto/internal.h
index 643c389..4b88bcf 100644
--- a/src/third_party/boringssl/src/crypto/internal.h
+++ b/src/third_party/boringssl/src/crypto/internal.h
@@ -155,6 +155,9 @@
     defined(OPENSSL_AARCH64) || defined(OPENSSL_PPC64LE)
 // OPENSSL_cpuid_setup initializes the platform-specific feature cache.
 void OPENSSL_cpuid_setup(void);
+#if defined(STARBOARD)
+void OPENSSL_cpuid_setup_starboard(void);
+#endif
 #endif
 
 
diff --git a/src/third_party/boringssl/src/tool/digest.cc b/src/third_party/boringssl/src/tool/digest.cc
index 7b6c88b..6020c99 100644
--- a/src/third_party/boringssl/src/tool/digest.cc
+++ b/src/third_party/boringssl/src/tool/digest.cc
@@ -37,8 +37,10 @@
 OPENSSL_MSVC_PRAGMA(warning(pop))
 #include <io.h>
 #define PATH_MAX MAX_PATH
+#if !defined(STARBOARD)
 typedef int ssize_t;
 #endif
+#endif
 
 #include <openssl/digest.h>
 
diff --git a/src/third_party/boringssl/src/tool/pkcs12.cc b/src/third_party/boringssl/src/tool/pkcs12.cc
index a8ddb0e..8440096 100644
--- a/src/third_party/boringssl/src/tool/pkcs12.cc
+++ b/src/third_party/boringssl/src/tool/pkcs12.cc
@@ -111,7 +111,7 @@
             off < sizeof(password) - 1) ||
            (n == -1 && errno == EINTR));
 
-  char *newline = reinterpret_cast<char *>(OPENSSL_memchr(password, '\n', off));
+  char *newline = const_cast<char *>(reinterpret_cast<const char *>(OPENSSL_memchr(password, '\n', off)));
   if (newline == NULL) {
     return false;
   }
diff --git a/src/third_party/boringssl/src/tool/speed.cc b/src/third_party/boringssl/src/tool/speed.cc
index 2175baa..194b618 100644
--- a/src/third_party/boringssl/src/tool/speed.cc
+++ b/src/third_party/boringssl/src/tool/speed.cc
@@ -32,6 +32,7 @@
 #include <openssl/ecdsa.h>
 #include <openssl/ec_key.h>
 #include <openssl/evp.h>
+#include <openssl/mem.h>
 #include <openssl/nid.h>
 #include <openssl/rand.h>
 #include <openssl/rsa.h>
diff --git a/src/third_party/boringssl/src/tool/tool.cc b/src/third_party/boringssl/src/tool/tool.cc
index 670d4e7..313da33 100644
--- a/src/third_party/boringssl/src/tool/tool.cc
+++ b/src/third_party/boringssl/src/tool/tool.cc
@@ -19,12 +19,18 @@
 #include <openssl/err.h>
 #include <openssl/ssl.h>
 
+#ifdef STARBOARD
+#include <starboard/client_porting/wrap_main/wrap_main.h>
+#endif
+
 #if defined(OPENSSL_WINDOWS)
 #include <fcntl.h>
 #include <io.h>
 #else
+#if defined(_POSIX_C_SOURCE) || defined(__ANDROID_API_)
 #include <libgen.h>
 #endif
+#endif
 
 #include "internal.h"
 
@@ -43,16 +49,20 @@
 
 static const Tool kTools[] = {
   { "ciphers", Ciphers },
+#ifndef OPENSSL_NO_SOCK
   { "client", Client },
+#endif
   { "isfips", IsFIPS },
   { "generate-ed25519", GenerateEd25519Key },
   { "genrsa", GenerateRSAKey },
   { "md5sum", MD5Sum },
   { "pkcs12", DoPKCS12 },
   { "rand", Rand },
+#ifndef OPENSSL_NO_SOCK
   { "s_client", Client },
   { "s_server", Server },
   { "server", Server },
+#endif
   { "sha1sum", SHA1Sum },
   { "sha224sum", SHA224Sum },
   { "sha256sum", SHA256Sum },
@@ -86,7 +96,11 @@
   }
 }
 
+#ifdef STARBOARD
+int crypto_tool_main(int argc, char **argv) {
+#else
 int main(int argc, char **argv) {
+#endif
 #if defined(OPENSSL_WINDOWS)
   // Read and write in binary mode. This makes bssl on Windows consistent with
   // bssl on other platforms, and also makes it consistent with MSYS's commands
@@ -111,8 +125,10 @@
   int starting_arg = 1;
   tool_func_t tool = nullptr;
 #if !defined(OPENSSL_WINDOWS)
+#if defined(_POSIX_C_SOURCE) || defined(__ANDROID_API_)
   tool = FindTool(basename(argv[0]));
 #endif
+#endif
   if (tool == nullptr) {
     starting_arg++;
     if (argc > 1) {
@@ -136,3 +152,7 @@
 
   return 0;
 }
+
+#ifdef STARBOARD
+STARBOARD_WRAP_SIMPLE_MAIN(crypto_tool_main);
+#endif
diff --git a/src/third_party/llvm-project/libcxxabi/libcxxabi.gyp b/src/third_party/llvm-project/libcxxabi/libcxxabi.gyp
index 7133871..bcf2e83 100644
--- a/src/third_party/llvm-project/libcxxabi/libcxxabi.gyp
+++ b/src/third_party/llvm-project/libcxxabi/libcxxabi.gyp
@@ -47,9 +47,6 @@
         '-Wno-unused-command-line-argument',
       ],
       'defines' : [
-        # If not defined, this symbol is used to detect this function's presence in the C library
-        # at runtime.
-        'HAVE___CXA_THREAD_ATEXIT_IMPL',
         # This macro is used to disable extern template declarations in the libc++
         # headers. The intended use case is for clients who wish to use the libc++
         # headers without taking a dependency on the libc++ library itself.
@@ -75,7 +72,6 @@
         'src/cxa_handlers.cpp',
         'src/cxa_handlers.hpp',
         'src/cxa_personality.cpp',
-        'src/cxa_thread_atexit.cpp',
         'src/cxa_unexpected.cpp',
         'src/cxa_vector.cpp',
         'src/cxa_virtual.cpp',
@@ -96,6 +92,9 @@
       'sources!': [
         # We utilize exception handling and the following file breaks the build.
         'src/cxa_noexception.cpp',
+
+        # Not needed and leaks __cxa_thread_atexit_impl.
+        'src/cxa_thread_atexit.cpp',
       ],
       'all_dependent_settings': {
         'defines': [
diff --git a/src/third_party/llvm-project/libunwind/libunwind.gyp b/src/third_party/llvm-project/libunwind/libunwind.gyp
index 0802c24..833ca4a 100644
--- a/src/third_party/llvm-project/libunwind/libunwind.gyp
+++ b/src/third_party/llvm-project/libunwind/libunwind.gyp
@@ -53,6 +53,9 @@
         # Build libunwind with concurrency built upon Starboard mutexes and
         # condition variables.
         '_LIBUNWIND_HAS_STARBOARD_THREADS',
+        'LIBUNWIND_PRINT_APIS',
+        'LIBUNWIND_PRINT_DWARF',
+        'LIBUNWIND_PRINT_UNWINDING',
         'LLVM_PATH="<(DEPTH)/third_party/llvm-project/llvm/"',
       ],
       'sources': [
diff --git a/src/third_party/llvm-project/libunwind/src/libunwind.cpp b/src/third_party/llvm-project/libunwind/src/libunwind.cpp
index 3bd8ef1..a0fdecb 100644
--- a/src/third_party/llvm-project/libunwind/src/libunwind.cpp
+++ b/src/third_party/llvm-project/libunwind/src/libunwind.cpp
@@ -374,7 +374,12 @@
   static bool checked = false;
   static bool log = false;
   if (!checked) {
-    log = (getenv("LIBUNWIND_PRINT_APIS") != NULL);
+    log =
+#if defined(LIBUNWIND_PRINT_APIS)
+        true;
+#else  // !defined(LIBUNWIND_PRINT_APIS)
+        false;
+#endif // defined(LIBUNWIND_PRINT_APIS)
     checked = true;
   }
   return log;
@@ -386,7 +391,12 @@
   static bool checked = false;
   static bool log = false;
   if (!checked) {
-    log = (getenv("LIBUNWIND_PRINT_UNWINDING") != NULL);
+    log =
+#if defined(LIBUNWIND_PRINT_UNWINDING)
+        true;
+#else  // !defined(LIBUNWIND_PRINT_UNWINDING)
+        false;
+#endif // defined(LIBUNWIND_PRINT_UNWINDING)
     checked = true;
   }
   return log;
@@ -398,7 +408,12 @@
   static bool checked = false;
   static bool log = false;
   if (!checked) {
-    log = (getenv("LIBUNWIND_PRINT_DWARF") != NULL);
+    log =
+#if defined(LIBUNWIND_PRINT_DWARF)
+        true;
+#else  // !defined(LIBUNWIND_PRINT_DWARF)
+        false;
+#endif // defined(LIBUNWIND_PRINT_DWARF)
     checked = true;
   }
   return log;
diff --git a/src/third_party/musl/musl.gyp b/src/third_party/musl/musl.gyp
index f2ac88a..58afe2f 100644
--- a/src/third_party/musl/musl.gyp
+++ b/src/third_party/musl/musl.gyp
@@ -116,6 +116,7 @@
           'include_dirs+': [
             'src/starboard/internal',
             'src/errno',
+            'src/multibyte',
             'src/time',
           ],
           'sources': [
@@ -144,8 +145,13 @@
             'src/starboard/time/__tz.c',
             'src/starboard/time/clock_gettime.c',
 
+            'src/ctype/iswspace.c',
             'src/errno/strerror.c',
             'src/exit/assert.c',
+            'src/exit/atexit.c',
+            'src/internal/floatscan.c',
+            'src/internal/intscan.c',
+            'src/internal/shgetc.c',
             'src/locale/strcoll.c',
             'src/locale/strcoll.c',
             'src/locale/strxfrm.c',
@@ -154,7 +160,24 @@
             'src/locale/wcscoll.c',
             'src/locale/wcsxfrm.c',
             'src/locale/wcsxfrm.c',
+            'src/math/copysignl.c',
+            'src/math/scalbnl.c',
+            'src/multibyte/btowc.c',
+            'src/multibyte/internal.c',
+            'src/multibyte/mbrlen.c',
+            'src/multibyte/mbrtowc.c',
+            'src/multibyte/mbsnrtowcs.c',
+            'src/multibyte/mbsrtowcs.c',
+            'src/multibyte/mbtowc.c',
+            'src/multibyte/wcrtomb.c',
+            'src/multibyte/wcsnrtombs.c',
+            'src/multibyte/wcsrtombs.c',
+            'src/multibyte/wcstombs.c',
+            'src/multibyte/wctob.c',
+            'src/stdio/__toread.c',
+            'src/stdio/__uflow.c',
             'src/stdio/fprintf.c',
+            'src/stdio/fwrite.c',
             'src/stdio/printf.c',
             'src/stdio/snprintf.c',
             'src/stdio/sscanf.c',
@@ -164,6 +187,8 @@
             'src/stdlib/abs.c',
             'src/stdlib/labs.c',
             'src/stdlib/llabs.c',
+            'src/stdlib/wcstod.c',
+            'src/stdlib/wcstol.c',
             'src/string/strerror_r.c',
             'src/time/__month_to_secs.c',
             'src/time/__tm_to_secs.c',
diff --git a/src/third_party/musl/src/exit/atexit.c b/src/third_party/musl/src/exit/atexit.c
index cd3b0a6..5b51b4b 100644
--- a/src/third_party/musl/src/exit/atexit.c
+++ b/src/third_party/musl/src/exit/atexit.c
@@ -2,6 +2,13 @@
 #include <stdint.h>
 #include "libc.h"
 
+#ifdef STARBOARD
+#include "starboard/common/log.h"
+#include "starboard/mutex.h"
+#include "starboard/thread_types.h"
+#include "starboard/types.h"
+#endif  // STARBOARD
+
 /* Ensure that at least 32 atexit handlers can be registered without malloc */
 #define COUNT 32
 
@@ -13,7 +20,19 @@
 } builtin, *head;
 
 static int slot;
+#ifdef STARBOARD
+static SbMutex lock = SB_MUTEX_INITIALIZER;
+#define LOCK(x)                                          \
+    do {                                                 \
+      SB_DCHECK(SbMutexAcquire(&x) == kSbMutexAcquired); \
+    } while (0)
+#define UNLOCK(x)                    \
+    do {                             \
+      SB_DCHECK(SbMutexRelease(&x)); \
+    } while (0)
+#else   // !STARBOARD
 static volatile int lock[1];
+#endif  // STARBOARD
 
 void __funcs_on_exit()
 {
diff --git a/src/third_party/musl/src/internal/libc.h b/src/third_party/musl/src/internal/libc.h
index 5e14518..1fa9243 100644
--- a/src/third_party/musl/src/internal/libc.h
+++ b/src/third_party/musl/src/internal/libc.h
@@ -51,8 +51,10 @@
 void __unlock(volatile int *) ATTR_LIBC_VISIBILITY;
 int __lockfile(FILE *) ATTR_LIBC_VISIBILITY;
 void __unlockfile(FILE *) ATTR_LIBC_VISIBILITY;
+#ifndef STARBOARD
 #define LOCK(x) __lock(x)
 #define UNLOCK(x) __unlock(x)
+#endif // STARBOARD
 
 void __synccall(void (*)(void *), void *);
 int __setxid(int, int, int, int);
diff --git a/src/third_party/musl/src/internal/stdio_impl.h b/src/third_party/musl/src/internal/stdio_impl.h
index 1127a49..d3ce332 100644
--- a/src/third_party/musl/src/internal/stdio_impl.h
+++ b/src/third_party/musl/src/internal/stdio_impl.h
@@ -2,14 +2,20 @@
 #define _STDIO_IMPL_H
 
 #include <stdio.h>
-#include "syscall.h"
 #include "libc.h"
 
+// When STARBOARD is defined we only want to include the definition of _IO_FILE,
+// a.k.a. FILE, along with a few constants used when working with wide strings.
+
+#ifndef STARBOARD
+#include "syscall.h"
+
 #define UNGET 8
 
 #define FFINALLOCK(f) ((f)->lock>=0 ? __lockfile((f)) : 0)
 #define FLOCK(f) int __need_unlock = ((f)->lock>=0 ? __lockfile((f)) : 0)
 #define FUNLOCK(f) do { if (__need_unlock) __unlockfile((f)); } while (0)
+#endif  // STARBOARD
 
 #define F_PERM 1
 #define F_NORD 4
@@ -50,6 +56,7 @@
 	struct __locale_struct *locale;
 };
 
+#ifndef STARBOARD
 size_t __stdio_read(FILE *, unsigned char *, size_t);
 size_t __stdio_write(FILE *, const unsigned char *, size_t);
 size_t __stdout_write(FILE *, const unsigned char *, size_t);
@@ -93,5 +100,6 @@
 /* Caller-allocated FILE * operations */
 FILE *__fopen_rb_ca(const char *, FILE *, unsigned char *, size_t);
 int __fclose_ca(FILE *);
+#endif  // STARBOARD
 
 #endif
diff --git a/src/third_party/musl/src/starboard/internal/stdio_impl.h b/src/third_party/musl/src/starboard/internal/stdio_impl.h
deleted file mode 100644
index 57133a2..0000000
--- a/src/third_party/musl/src/starboard/internal/stdio_impl.h
+++ /dev/null
@@ -1,41 +0,0 @@
-#ifndef _STDIO_IMPL_H
-#define _STDIO_IMPL_H
-
-#include <stdio.h>
-#include "libc.h"
-
-// Slimmed version of stdio_impl.h that only provides the definition of
-// _IO_FILE, a.k.a. FILE.
-
-struct _IO_FILE {
-  unsigned flags;
-  unsigned char *rpos, *rend;
-  int (*close)(FILE *);
-  unsigned char *wend, *wpos;
-  unsigned char *mustbezero_1;
-  unsigned char *wbase;
-  size_t (*read)(FILE *, unsigned char *, size_t);
-  size_t (*write)(FILE *, const unsigned char *, size_t);
-  off_t (*seek)(FILE *, off_t, int);
-  unsigned char *buf;
-  size_t buf_size;
-  FILE *prev, *next;
-  int fd;
-  int pipe_pid;
-  long lockcount;
-  short dummy3;
-  signed char mode;
-  signed char lbf;
-  volatile int lock;
-  volatile int waiters;
-  void *cookie;
-  off_t off;
-  char *getln_buf;
-  void *mustbezero_2;
-  unsigned char *shend;
-  off_t shlim, shcnt;
-  FILE *prev_locked, *next_locked;
-  struct __locale_struct *locale;
-};
-
-#endif
diff --git a/src/third_party/musl/src/stdio/__toread.c b/src/third_party/musl/src/stdio/__toread.c
index 35f67b8..20fef0a 100644
--- a/src/third_party/musl/src/stdio/__toread.c
+++ b/src/third_party/musl/src/stdio/__toread.c
@@ -13,9 +13,12 @@
 	return (f->flags & F_EOF) ? EOF : 0;
 }
 
+// This function is unused, and leaks __stdio_exit_needed.
+#ifndef STARBOARD
 void __stdio_exit_needed(void);
 
 void __toread_needs_stdio_exit()
 {
 	__stdio_exit_needed();
 }
+#endif  // STARBOARD
diff --git a/src/third_party/musl/src/stdio/fwrite.c b/src/third_party/musl/src/stdio/fwrite.c
index 7a567b2..8a5d9c5 100644
--- a/src/third_party/musl/src/stdio/fwrite.c
+++ b/src/third_party/musl/src/stdio/fwrite.c
@@ -1,6 +1,11 @@
 #include "stdio_impl.h"
 #include <string.h>
 
+#ifdef STARBOARD
+#include "starboard/common/log.h"
+#endif  // STARBOARD
+
+#ifndef STARBOARD
 size_t __fwritex(const unsigned char *restrict s, size_t l, FILE *restrict f)
 {
 	size_t i=0;
@@ -24,15 +29,21 @@
 	f->wpos += l;
 	return l+i;
 }
+#endif
 
 size_t fwrite(const void *restrict src, size_t size, size_t nmemb, FILE *restrict f)
 {
+#ifdef STARBOARD
+  SB_NOTREACHED();
+  return 0;
+#else   // !STARBOARD
 	size_t k, l = size*nmemb;
 	if (!size) nmemb = 0;
 	FLOCK(f);
 	k = __fwritex(src, l, f);
 	FUNLOCK(f);
 	return k==l ? nmemb : k/size;
+#endif  // STARBOARD
 }
 
 weak_alias(fwrite, fwrite_unlocked);
diff --git a/src/third_party/openssl/openssl/Makefile b/src/third_party/openssl/openssl/Makefile
deleted file mode 100644
index d5db11b..0000000
--- a/src/third_party/openssl/openssl/Makefile
+++ /dev/null
@@ -1,685 +0,0 @@
-### Generated automatically from Makefile.org by Configure.
-
-##
-## Makefile for OpenSSL
-##
-
-VERSION=1.0.1c
-MAJOR=1
-MINOR=0.1
-SHLIB_VERSION_NUMBER=1.0.0
-SHLIB_VERSION_HISTORY=
-SHLIB_MAJOR=1
-SHLIB_MINOR=0.0
-SHLIB_EXT=
-PLATFORM=dist
-OPTIONS= no-ec_nistp_64_gcc_128 no-gmp no-jpake no-krb5 no-md2 no-rc5 no-rfc3779 no-sctp no-shared no-store no-zlib no-zlib-dynamic static-engine
-CONFIGURE_ARGS=dist
-SHLIB_TARGET=
-
-# HERE indicates where this Makefile lives.  This can be used to indicate
-# where sub-Makefiles are expected to be.  Currently has very limited usage,
-# and should probably not be bothered with at all.
-HERE=.
-
-# INSTALL_PREFIX is for package builders so that they can configure
-# for, say, /usr/ and yet have everything installed to /tmp/somedir/usr/.
-# Normally it is left empty.
-INSTALL_PREFIX=
-INSTALLTOP=/usr/local/ssl
-
-# Do not edit this manually. Use Configure --openssldir=DIR do change this!
-OPENSSLDIR=/usr/local/ssl
-
-# NO_IDEA - Define to build without the IDEA algorithm
-# NO_RC4  - Define to build without the RC4 algorithm
-# NO_RC2  - Define to build without the RC2 algorithm
-# THREADS - Define when building with threads, you will probably also need any
-#           system defines as well, i.e. _REENTERANT for Solaris 2.[34]
-# TERMIO  - Define the termio terminal subsystem, needed if sgtty is missing.
-# TERMIOS - Define the termios terminal subsystem, Silicon Graphics.
-# LONGCRYPT - Define to use HPUX 10.x's long password modification to crypt(3).
-# DEVRANDOM - Give this the value of the 'random device' if your OS supports
-#           one.  32 bytes will be read from this when the random
-#           number generator is initalised.
-# SSL_FORBID_ENULL - define if you want the server to be not able to use the
-#           NULL encryption ciphers.
-#
-# LOCK_DEBUG - turns on lots of lock debug output :-)
-# REF_CHECK - turn on some xyz_free() assertions.
-# REF_PRINT - prints some stuff on structure free.
-# CRYPTO_MDEBUG - turns on my 'memory leak' detecting stuff
-# MFUNC - Make all Malloc/Free/Realloc calls call
-#       CRYPTO_malloc/CRYPTO_free/CRYPTO_realloc which can be setup to
-#       call application defined callbacks via CRYPTO_set_mem_functions()
-# MD5_ASM needs to be defined to use the x86 assembler for MD5
-# SHA1_ASM needs to be defined to use the x86 assembler for SHA1
-# RMD160_ASM needs to be defined to use the x86 assembler for RIPEMD160
-# Do not define B_ENDIAN or L_ENDIAN if 'unsigned long' == 8.  It must
-# equal 4.
-# PKCS1_CHECK - pkcs1 tests.
-
-CC= cc
-CFLAG= -O
-DEPFLAG= -DOPENSSL_NO_EC_NISTP_64_GCC_128 -DOPENSSL_NO_GMP -DOPENSSL_NO_JPAKE -DOPENSSL_NO_MD2 -DOPENSSL_NO_RC5 -DOPENSSL_NO_RFC3779 -DOPENSSL_NO_SCTP -DOPENSSL_NO_STORE
-PEX_LIBS= 
-EX_LIBS= 
-EXE_EXT= 
-ARFLAGS= 
-AR= ar $(ARFLAGS) r
-RANLIB= /usr/bin/ranlib
-NM= nm
-PERL= /usr/bin/perl
-TAR= tar
-TARFLAGS= --no-recursion
-MAKEDEPPROG=makedepend
-LIBDIR=lib
-
-# We let the C compiler driver to take care of .s files. This is done in
-# order to be excused from maintaining a separate set of architecture
-# dependent assembler flags. E.g. if you throw -mcpu=ultrasparc at SPARC
-# gcc, then the driver will automatically translate it to -xarch=v8plus
-# and pass it down to assembler.
-AS=$(CC) -c
-ASFLAG=$(CFLAG)
-
-# For x86 assembler: Set PROCESSOR to 386 if you want to support
-# the 80386.
-PROCESSOR= 
-
-# CPUID module collects small commonly used assembler snippets
-CPUID_OBJ= mem_clr.o
-BN_ASM= bn_asm.o
-DES_ENC= des_enc.o fcrypt_b.o
-AES_ENC= aes_core.o aes_cbc.o
-BF_ENC= bf_enc.o
-CAST_ENC= c_enc.o
-RC4_ENC= rc4_enc.o rc4_skey.o
-RC5_ENC= rc5_enc.o
-MD5_ASM_OBJ= 
-SHA1_ASM_OBJ= 
-RMD160_ASM_OBJ= 
-WP_ASM_OBJ= wp_block.o
-CMLL_ENC= camellia.o cmll_misc.o cmll_cbc.o
-MODES_ASM_OBJ= 
-ENGINES_ASM_OBJ= 
-PERLASM_SCHEME= 
-
-# KRB5 stuff
-KRB5_INCLUDES=
-LIBKRB5=
-
-# Zlib stuff
-ZLIB_INCLUDE=
-LIBZLIB=
-
-# TOP level FIPS install directory.
-FIPSDIR=/usr/local/ssl/fips-2.0
-
-# This is the location of fipscanister.o and friends.
-# The FIPS module build will place it $(INSTALLTOP)/lib
-# but since $(INSTALLTOP) can only take the default value
-# when the module is built it will be in /usr/local/ssl/lib
-# $(INSTALLTOP) for this build may be different so hard
-# code the path.
-
-FIPSLIBDIR=
-
-# The location of the library which contains fipscanister.o
-# normally it will be libcrypto unless fipsdso is set in which
-# case it will be libfips. If not compiling in FIPS mode at all
-# this is empty making it a useful test for a FIPS compile.
-
-FIPSCANLIB=
-
-# Shared library base address. Currently only used on Windows.
-#
-
-BASEADDR=0xFB00000
-
-DIRS=   crypto ssl engines apps test tools
-ENGDIRS= ccgost
-SHLIBDIRS= crypto ssl
-
-# dirs in crypto to build
-SDIRS=  \
-	objects \
-	md4 md5 sha mdc2 hmac ripemd whrlpool \
-	des aes rc2 rc4 idea bf cast camellia seed modes \
-	bn ec rsa dsa ecdsa dh ecdh dso engine \
-	buffer bio stack lhash rand err \
-	evp asn1 pem x509 x509v3 conf txt_db pkcs7 pkcs12 comp ocsp ui krb5 \
-	cms pqueue ts srp cmac
-# keep in mind that the above list is adjusted by ./Configure
-# according to no-xxx arguments...
-
-# tests to perform.  "alltests" is a special word indicating that all tests
-# should be performed.
-TESTS = alltests
-
-MAKEFILE= Makefile
-
-MANDIR=$(OPENSSLDIR)/man
-MAN1=1
-MAN3=3
-MANSUFFIX=
-HTMLSUFFIX=html
-HTMLDIR=$(OPENSSLDIR)/html
-SHELL=/bin/sh
-
-TOP=    .
-ONEDIRS=out tmp
-EDIRS=  times doc bugs util include certs ms shlib mt demos perl sf dep VMS
-WDIRS=  windows
-LIBS=   libcrypto.a libssl.a
-SHARED_CRYPTO=libcrypto$(SHLIB_EXT)
-SHARED_SSL=libssl$(SHLIB_EXT)
-SHARED_LIBS=
-SHARED_LIBS_LINK_EXTS=
-SHARED_LDFLAGS=
-
-GENERAL=        Makefile
-BASENAME=       openssl
-NAME=           $(BASENAME)-$(VERSION)
-TARFILE=        $(NAME).tar
-WTARFILE=       $(NAME)-win.tar
-EXHEADER=       e_os2.h
-HEADER=         e_os.h
-
-all: Makefile build_all openssl.pc libssl.pc libcrypto.pc
-
-# as we stick to -e, CLEARENV ensures that local variables in lower
-# Makefiles remain local and variable. $${VAR+VAR} is tribute to Korn
-# shell, which [annoyingly enough] terminates unset with error if VAR
-# is not present:-( TOP= && unset TOP is tribute to HP-UX /bin/sh,
-# which terminates unset with error if no variable was present:-(
-CLEARENV=	TOP= && unset TOP $${LIB+LIB} $${LIBS+LIBS}	\
-		$${INCLUDE+INCLUDE} $${INCLUDES+INCLUDES}	\
-		$${DIR+DIR} $${DIRS+DIRS} $${SRC+SRC}		\
-		$${LIBSRC+LIBSRC} $${LIBOBJ+LIBOBJ} $${ALL+ALL}	\
-		$${EXHEADER+EXHEADER} $${HEADER+HEADER}		\
-		$${GENERAL+GENERAL} $${CFLAGS+CFLAGS}		\
-		$${ASFLAGS+ASFLAGS} $${AFLAGS+AFLAGS}		\
-		$${LDCMD+LDCMD} $${LDFLAGS+LDFLAGS} $${SCRIPTS+SCRIPTS}	\
-		$${SHAREDCMD+SHAREDCMD} $${SHAREDFLAGS+SHAREDFLAGS}	\
-		$${SHARED_LIB+SHARED_LIB} $${LIBEXTRAS+LIBEXTRAS}
-
-BUILDENV=	PLATFORM='$(PLATFORM)' PROCESSOR='$(PROCESSOR)' \
-		CC='$(CC)' CFLAG='$(CFLAG)' 			\
-		AS='$(CC)' ASFLAG='$(CFLAG) -c'			\
-		AR='$(AR)' NM='$(NM)' RANLIB='$(RANLIB)'	\
-		CROSS_COMPILE='$(CROSS_COMPILE)'	\
-		PERL='$(PERL)' ENGDIRS='$(ENGDIRS)'		\
-		SDIRS='$(SDIRS)' LIBRPATH='$(INSTALLTOP)/$(LIBDIR)'	\
-		INSTALL_PREFIX='$(INSTALL_PREFIX)'		\
-		INSTALLTOP='$(INSTALLTOP)' OPENSSLDIR='$(OPENSSLDIR)'	\
-		LIBDIR='$(LIBDIR)'				\
-		MAKEDEPEND='$$$${TOP}/util/domd $$$${TOP} -MD $(MAKEDEPPROG)' \
-		DEPFLAG='-DOPENSSL_NO_DEPRECATED $(DEPFLAG)'	\
-		MAKEDEPPROG='$(MAKEDEPPROG)'			\
-		SHARED_LDFLAGS='$(SHARED_LDFLAGS)'		\
-		KRB5_INCLUDES='$(KRB5_INCLUDES)' LIBKRB5='$(LIBKRB5)'	\
-		ZLIB_INCLUDE='$(ZLIB_INCLUDE)' LIBZLIB='$(LIBZLIB)'	\
-		EXE_EXT='$(EXE_EXT)' SHARED_LIBS='$(SHARED_LIBS)'	\
-		SHLIB_EXT='$(SHLIB_EXT)' SHLIB_TARGET='$(SHLIB_TARGET)'	\
-		PEX_LIBS='$(PEX_LIBS)' EX_LIBS='$(EX_LIBS)'	\
-		CPUID_OBJ='$(CPUID_OBJ)'			\
-		BN_ASM='$(BN_ASM)' DES_ENC='$(DES_ENC)' 	\
-		AES_ENC='$(AES_ENC)' CMLL_ENC='$(CMLL_ENC)'	\
-		BF_ENC='$(BF_ENC)' CAST_ENC='$(CAST_ENC)'	\
-		RC4_ENC='$(RC4_ENC)' RC5_ENC='$(RC5_ENC)'	\
-		SHA1_ASM_OBJ='$(SHA1_ASM_OBJ)'			\
-		MD5_ASM_OBJ='$(MD5_ASM_OBJ)'			\
-		RMD160_ASM_OBJ='$(RMD160_ASM_OBJ)'		\
-		WP_ASM_OBJ='$(WP_ASM_OBJ)'			\
-		MODES_ASM_OBJ='$(MODES_ASM_OBJ)'		\
-		ENGINES_ASM_OBJ='$(ENGINES_ASM_OBJ)'		\
-		PERLASM_SCHEME='$(PERLASM_SCHEME)'		\
-		FIPSLIBDIR='${FIPSLIBDIR}'			\
-		FIPSDIR='${FIPSDIR}'				\
-		FIPSCANLIB="$${FIPSCANLIB:-$(FIPSCANLIB)}"	\
-		THIS=$${THIS:-$@} MAKEFILE=Makefile MAKEOVERRIDES=
-# MAKEOVERRIDES= effectively "equalizes" GNU-ish and SysV-ish make flavors,
-# which in turn eliminates ambiguities in variable treatment with -e.
-
-# BUILD_CMD is a generic macro to build a given target in a given
-# subdirectory.  The target must be given through the shell variable
-# `target' and the subdirectory to build in must be given through `dir'.
-# This macro shouldn't be used directly, use RECURSIVE_BUILD_CMD or
-# BUILD_ONE_CMD instead.
-#
-# BUILD_ONE_CMD is a macro to build a given target in a given
-# subdirectory if that subdirectory is part of $(DIRS).  It requires
-# exactly the same shell variables as BUILD_CMD.
-#
-# RECURSIVE_BUILD_CMD is a macro to build a given target in all
-# subdirectories defined in $(DIRS).  It requires that the target
-# is given through the shell variable `target'.
-BUILD_CMD=  if [ -d "$$dir" ]; then \
-	    (	cd $$dir && echo "making $$target in $$dir..." && \
-		$(CLEARENV) && $(MAKE) -e $(BUILDENV) TOP=.. DIR=$$dir $$target \
-	    ) || exit 1; \
-	    fi
-RECURSIVE_BUILD_CMD=for dir in $(DIRS); do $(BUILD_CMD); done
-BUILD_ONE_CMD=\
-	if expr " $(DIRS) " : ".* $$dir " >/dev/null 2>&1; then \
-		$(BUILD_CMD); \
-	fi
-
-reflect:
-	@[ -n "$(THIS)" ] && $(CLEARENV) && $(MAKE) $(THIS) -e $(BUILDENV)
-
-sub_all: build_all
-build_all: build_libs build_apps build_tests build_tools
-
-build_libs: build_crypto build_ssl build_engines
-
-build_crypto:
-	@dir=crypto; target=all; $(BUILD_ONE_CMD)
-build_ssl:
-	@dir=ssl; target=all; $(BUILD_ONE_CMD)
-build_engines:
-	@dir=engines; target=all; $(BUILD_ONE_CMD)
-build_apps:
-	@dir=apps; target=all; $(BUILD_ONE_CMD)
-build_tests:
-	@dir=test; target=all; $(BUILD_ONE_CMD)
-build_tools:
-	@dir=tools; target=all; $(BUILD_ONE_CMD)
-
-all_testapps: build_libs build_testapps
-build_testapps:
-	@dir=crypto; target=testapps; $(BUILD_ONE_CMD)
-
-fips_premain_dso$(EXE_EXT): libcrypto.a
-	[ -z "$(FIPSCANLIB)" ] || $(CC) $(CFLAG) -Iinclude \
-		-DFINGERPRINT_PREMAIN_DSO_LOAD -o $@  \
-		$(FIPSLIBDIR)fips_premain.c $(FIPSLIBDIR)fipscanister.o \
-		libcrypto.a $(EX_LIBS)
-
-libcrypto$(SHLIB_EXT): libcrypto.a fips_premain_dso$(EXE_EXT)
-	@if [ "$(SHLIB_TARGET)" != "" ]; then \
-		if [ "$(FIPSCANLIB)" = "libcrypto" ]; then \
-			FIPSLD_LIBCRYPTO=libcrypto.a ; \
-			FIPSLD_CC="$(CC)"; CC=$(FIPSDIR)/bin/fipsld; \
-			export CC FIPSLD_CC FIPSLD_LIBCRYPTO; \
-		fi; \
-		$(MAKE) -e SHLIBDIRS=crypto build-shared; \
-	else \
-		echo "There's no support for shared libraries on this platform" >&2; \
-		exit 1; \
-	fi
-
-libssl$(SHLIB_EXT): libcrypto$(SHLIB_EXT) libssl.a
-	@if [ "$(SHLIB_TARGET)" != "" ]; then \
-		$(MAKE) SHLIBDIRS=ssl SHLIBDEPS='-lcrypto' build-shared; \
-	else \
-		echo "There's no support for shared libraries on this platform" >&2; \
-		exit 1; \
-	fi
-
-clean-shared:
-	@set -e; for i in $(SHLIBDIRS); do \
-		if [ -n "$(SHARED_LIBS_LINK_EXTS)" ]; then \
-			tmp="$(SHARED_LIBS_LINK_EXTS)"; \
-			for j in $${tmp:-x}; do \
-				( set -x; rm -f lib$$i$$j ); \
-			done; \
-		fi; \
-		( set -x; rm -f lib$$i$(SHLIB_EXT) ); \
-		if [ "$(PLATFORM)" = "Cygwin" ]; then \
-			( set -x; rm -f cyg$$i$(SHLIB_EXT) lib$$i$(SHLIB_EXT).a ); \
-		fi; \
-	done
-
-link-shared:
-	@ set -e; for i in $(SHLIBDIRS); do \
-		$(MAKE) -f $(HERE)/Makefile.shared -e $(BUILDENV) \
-			LIBNAME=$$i LIBVERSION=$(SHLIB_MAJOR).$(SHLIB_MINOR) \
-			LIBCOMPATVERSIONS=";$(SHLIB_VERSION_HISTORY)" \
-			symlink.$(SHLIB_TARGET); \
-		libs="$$libs -l$$i"; \
-	done
-
-build-shared: do_$(SHLIB_TARGET) link-shared
-
-do_$(SHLIB_TARGET):
-	@ set -e; libs='-L. $(SHLIBDEPS)'; for i in $(SHLIBDIRS); do \
-		if [ "$$i" = "ssl" -a -n "$(LIBKRB5)" ]; then \
-			libs="$(LIBKRB5) $$libs"; \
-		fi; \
-		$(CLEARENV) && $(MAKE) -f Makefile.shared -e $(BUILDENV) \
-			LIBNAME=$$i LIBVERSION=$(SHLIB_MAJOR).$(SHLIB_MINOR) \
-			LIBCOMPATVERSIONS=";$(SHLIB_VERSION_HISTORY)" \
-			LIBDEPS="$$libs $(EX_LIBS)" \
-			link_a.$(SHLIB_TARGET); \
-		libs="-l$$i $$libs"; \
-	done
-
-libcrypto.pc: Makefile
-	@ ( echo 'prefix=$(INSTALLTOP)'; \
-	    echo 'exec_prefix=$${prefix}'; \
-	    echo 'libdir=$${exec_prefix}/$(LIBDIR)'; \
-	    echo 'includedir=$${prefix}/include'; \
-	    echo ''; \
-	    echo 'Name: OpenSSL-libcrypto'; \
-	    echo 'Description: OpenSSL cryptography library'; \
-	    echo 'Version: '$(VERSION); \
-	    echo 'Requires: '; \
-	    echo 'Libs: -L$${libdir} -lcrypto'; \
-	    echo 'Libs.private: $(EX_LIBS)'; \
-	    echo 'Cflags: -I$${includedir} $(KRB5_INCLUDES)' ) > libcrypto.pc
-
-libssl.pc: Makefile
-	@ ( echo 'prefix=$(INSTALLTOP)'; \
-	    echo 'exec_prefix=$${prefix}'; \
-	    echo 'libdir=$${exec_prefix}/$(LIBDIR)'; \
-	    echo 'includedir=$${prefix}/include'; \
-	    echo ''; \
-	    echo 'Name: OpenSSL'; \
-	    echo 'Description: Secure Sockets Layer and cryptography libraries'; \
-	    echo 'Version: '$(VERSION); \
-	    echo 'Requires: '; \
-	    echo 'Libs: -L$${libdir} -lssl -lcrypto'; \
-	    echo 'Libs.private: $(EX_LIBS)'; \
-	    echo 'Cflags: -I$${includedir} $(KRB5_INCLUDES)' ) > libssl.pc
-
-openssl.pc: Makefile
-	@ ( echo 'prefix=$(INSTALLTOP)'; \
-	    echo 'exec_prefix=$${prefix}'; \
-	    echo 'libdir=$${exec_prefix}/$(LIBDIR)'; \
-	    echo 'includedir=$${prefix}/include'; \
-	    echo ''; \
-	    echo 'Name: OpenSSL'; \
-	    echo 'Description: Secure Sockets Layer and cryptography libraries and tools'; \
-	    echo 'Version: '$(VERSION); \
-	    echo 'Requires: '; \
-	    echo 'Libs: -L$${libdir} -lssl -lcrypto'; \
-	    echo 'Libs.private: $(EX_LIBS)'; \
-	    echo 'Cflags: -I$${includedir} $(KRB5_INCLUDES)' ) > openssl.pc
-
-Makefile: Makefile.org Configure config
-	@echo "Makefile is older than Makefile.org, Configure or config."
-	@echo "Reconfigure the source tree (via './config' or 'perl Configure'), please."
-	@false
-
-libclean:
-	rm -f *.map *.so *.so.* *.dylib *.dll engines/*.so engines/*.dll engines/*.dylib *.a engines/*.a */lib */*/lib
-
-clean:	libclean
-	rm -f shlib/*.o *.o core a.out fluff rehash.time testlog make.log cctest cctest.c
-	@set -e; target=clean; $(RECURSIVE_BUILD_CMD)
-	rm -f $(LIBS)
-	rm -f openssl.pc libssl.pc libcrypto.pc
-	rm -f speed.* .pure
-	rm -f $(TARFILE)
-	@set -e; for i in $(ONEDIRS) ;\
-	do \
-	rm -fr $$i/*; \
-	done
-
-makefile.one: files
-	$(PERL) util/mk1mf.pl >makefile.one; \
-	sh util/do_ms.sh
-
-files:
-	$(PERL) $(TOP)/util/files.pl Makefile > $(TOP)/MINFO
-	@set -e; target=files; $(RECURSIVE_BUILD_CMD)
-
-links:
-	@$(PERL) $(TOP)/util/mkdir-p.pl include/openssl
-	@$(PERL) $(TOP)/util/mklink.pl include/openssl $(EXHEADER)
-	@set -e; target=links; $(RECURSIVE_BUILD_CMD)
-
-gentests:
-	@(cd test && echo "generating dummy tests (if needed)..." && \
-	$(CLEARENV) && $(MAKE) -e $(BUILDENV) TESTS='$(TESTS)' OPENSSL_DEBUG_MEMORY=on generate );
-
-dclean:
-	rm -rf *.bak include/openssl certs/.0
-	@set -e; target=dclean; $(RECURSIVE_BUILD_CMD)
-
-rehash: rehash.time
-rehash.time: certs apps
-	@if [ -z "$(CROSS_COMPILE)" ]; then \
-		(OPENSSL="`pwd`/util/opensslwrap.sh"; \
-		[ -x "apps/openssl.exe" ] && OPENSSL="apps/openssl.exe" || :; \
-		OPENSSL_DEBUG_MEMORY=on; \
-		export OPENSSL OPENSSL_DEBUG_MEMORY; \
-		$(PERL) tools/c_rehash certs) && \
-		touch rehash.time; \
-	else :; fi
-
-test:   tests
-
-tests: rehash
-	@(cd test && echo "testing..." && \
-	$(CLEARENV) && $(MAKE) -e $(BUILDENV) TOP=.. TESTS='$(TESTS)' OPENSSL_DEBUG_MEMORY=on OPENSSL_CONF=../apps/openssl.cnf tests );
-	OPENSSL_CONF=apps/openssl.cnf util/opensslwrap.sh version -a
-
-report:
-	@$(PERL) util/selftest.pl
-
-depend:
-	@set -e; target=depend; $(RECURSIVE_BUILD_CMD)
-
-lint:
-	@set -e; target=lint; $(RECURSIVE_BUILD_CMD)
-
-tags:
-	rm -f TAGS
-	find . -name '[^.]*.[ch]' | xargs etags -a
-
-errors:
-	$(PERL) util/ck_errf.pl -strict */*.c */*/*.c
-	$(PERL) util/mkerr.pl -recurse -write
-	(cd engines; $(MAKE) PERL=$(PERL) errors)
-
-stacks:
-	$(PERL) util/mkstack.pl -write
-
-util/libeay.num::
-	$(PERL) util/mkdef.pl crypto update
-
-util/ssleay.num::
-	$(PERL) util/mkdef.pl ssl update
-
-crypto/objects/obj_dat.h: crypto/objects/obj_dat.pl crypto/objects/obj_mac.h
-	$(PERL) crypto/objects/obj_dat.pl crypto/objects/obj_mac.h crypto/objects/obj_dat.h
-crypto/objects/obj_mac.h: crypto/objects/objects.pl crypto/objects/objects.txt crypto/objects/obj_mac.num
-	$(PERL) crypto/objects/objects.pl crypto/objects/objects.txt crypto/objects/obj_mac.num crypto/objects/obj_mac.h
-crypto/objects/obj_xref.h: crypto/objects/objxref.pl crypto/objects/obj_xref.txt crypto/objects/obj_mac.num
-	$(PERL) crypto/objects/objxref.pl crypto/objects/obj_mac.num crypto/objects/obj_xref.txt >crypto/objects/obj_xref.h
-
-apps/openssl-vms.cnf: apps/openssl.cnf
-	$(PERL) VMS/VMSify-conf.pl < apps/openssl.cnf > apps/openssl-vms.cnf
-
-crypto/bn/bn_prime.h: crypto/bn/bn_prime.pl
-	$(PERL) crypto/bn/bn_prime.pl >crypto/bn/bn_prime.h
-
-
-TABLE: Configure
-	(echo 'Output of `Configure TABLE'"':"; \
-	$(PERL) Configure TABLE) > TABLE
-
-update: errors stacks util/libeay.num util/ssleay.num crypto/objects/obj_dat.h crypto/objects/obj_xref.h apps/openssl-vms.cnf crypto/bn/bn_prime.h TABLE depend
-
-# Build distribution tar-file. As the list of files returned by "find" is
-# pretty long, on several platforms a "too many arguments" error or similar
-# would occur. Therefore the list of files is temporarily stored into a file
-# and read directly, requiring GNU-Tar. Call "make TAR=gtar dist" if the normal
-# tar does not support the --files-from option.
-tar:
-	find . -type d -print | xargs chmod 755
-	find . -type f -print | xargs chmod a+r
-	find . -type f -perm -0100 -print | xargs chmod a+x
-	find * \! -path CVS/\* \! -path \*/CVS/\* \! -name CVS \! -name .cvsignore \! -name STATUS \! -name TABLE | sort > ../$(TARFILE).list; \
-	$(TAR) $(TARFLAGS) --files-from ../$(TARFILE).list -cvf - | \
-	tardy --user_number=0  --user_name=openssl \
-	      --group_number=0 --group_name=openssl \
-	      --prefix=openssl-$(VERSION) - |\
-	gzip --best >../$(TARFILE).gz; \
-	rm -f ../$(TARFILE).list; \
-	ls -l ../$(TARFILE).gz
-
-tar-snap:
-	@$(TAR) $(TARFLAGS) -cvf - \
-		`find * \! -path CVS/\* \! -path \*/CVS/\* \! -name CVS \! -name .cvsignore \! -name STATUS \! -name TABLE \! -name '*.o' \! -name '*.a' \! -name '*.so' \! -name '*.so.*'  \! -name 'openssl' \! -name '*test' \! -name '.#*' \! -name '*~' | sort` |\
-	tardy --user_number=0  --user_name=openssl \
-	      --group_number=0 --group_name=openssl \
-	      --prefix=openssl-$(VERSION) - > ../$(TARFILE);\
-	ls -l ../$(TARFILE)
-
-dist:   
-	$(PERL) Configure dist
-	@$(MAKE) dist_pem_h
-	@$(MAKE) SDIRS='$(SDIRS)' clean
-	@$(MAKE) TAR='$(TAR)' TARFLAGS='$(TARFLAGS)' tar
-
-dist_pem_h:
-	(cd crypto/pem; $(MAKE) -e $(BUILDENV) pem.h; $(MAKE) clean)
-
-install: all install_docs install_sw
-
-install_sw:
-	@$(PERL) $(TOP)/util/mkdir-p.pl $(INSTALL_PREFIX)$(INSTALLTOP)/bin \
-		$(INSTALL_PREFIX)$(INSTALLTOP)/$(LIBDIR) \
-		$(INSTALL_PREFIX)$(INSTALLTOP)/$(LIBDIR)/engines \
-		$(INSTALL_PREFIX)$(INSTALLTOP)/$(LIBDIR)/pkgconfig \
-		$(INSTALL_PREFIX)$(INSTALLTOP)/include/openssl \
-		$(INSTALL_PREFIX)$(OPENSSLDIR)/misc \
-		$(INSTALL_PREFIX)$(OPENSSLDIR)/certs \
-		$(INSTALL_PREFIX)$(OPENSSLDIR)/private
-	@set -e; headerlist="$(EXHEADER)"; for i in $$headerlist;\
-	do \
-	(cp $$i $(INSTALL_PREFIX)$(INSTALLTOP)/include/openssl/$$i; \
-	chmod 644 $(INSTALL_PREFIX)$(INSTALLTOP)/include/openssl/$$i ); \
-	done;
-	@set -e; target=install; $(RECURSIVE_BUILD_CMD)
-	@set -e; liblist="$(LIBS)"; for i in $$liblist ;\
-	do \
-		if [ -f "$$i" ]; then \
-		(       echo installing $$i; \
-			cp $$i $(INSTALL_PREFIX)$(INSTALLTOP)/$(LIBDIR)/$$i.new; \
-			$(RANLIB) $(INSTALL_PREFIX)$(INSTALLTOP)/$(LIBDIR)/$$i.new; \
-			chmod 644 $(INSTALL_PREFIX)$(INSTALLTOP)/$(LIBDIR)/$$i.new; \
-			mv -f $(INSTALL_PREFIX)$(INSTALLTOP)/$(LIBDIR)/$$i.new $(INSTALL_PREFIX)$(INSTALLTOP)/$(LIBDIR)/$$i ); \
-		fi; \
-	done;
-	@set -e; if [ -n "$(SHARED_LIBS)" ]; then \
-		tmp="$(SHARED_LIBS)"; \
-		for i in $${tmp:-x}; \
-		do \
-			if [ -f "$$i" -o -f "$$i.a" ]; then \
-			(       echo installing $$i; \
-				if [ "$(PLATFORM)" != "Cygwin" ]; then \
-					cp $$i $(INSTALL_PREFIX)$(INSTALLTOP)/$(LIBDIR)/$$i.new; \
-					chmod 555 $(INSTALL_PREFIX)$(INSTALLTOP)/$(LIBDIR)/$$i.new; \
-					mv -f $(INSTALL_PREFIX)$(INSTALLTOP)/$(LIBDIR)/$$i.new $(INSTALL_PREFIX)$(INSTALLTOP)/$(LIBDIR)/$$i; \
-				else \
-					c=`echo $$i | sed 's/^lib\(.*\)\.dll\.a/cyg\1-$(SHLIB_VERSION_NUMBER).dll/'`; \
-					cp $$c $(INSTALL_PREFIX)$(INSTALLTOP)/bin/$$c.new; \
-					chmod 755 $(INSTALL_PREFIX)$(INSTALLTOP)/bin/$$c.new; \
-					mv -f $(INSTALL_PREFIX)$(INSTALLTOP)/bin/$$c.new $(INSTALL_PREFIX)$(INSTALLTOP)/bin/$$c; \
-					cp $$i $(INSTALL_PREFIX)$(INSTALLTOP)/$(LIBDIR)/$$i.new; \
-					chmod 644 $(INSTALL_PREFIX)$(INSTALLTOP)/$(LIBDIR)/$$i.new; \
-					mv -f $(INSTALL_PREFIX)$(INSTALLTOP)/$(LIBDIR)/$$i.new $(INSTALL_PREFIX)$(INSTALLTOP)/$(LIBDIR)/$$i; \
-				fi ); \
-				if expr $(PLATFORM) : 'mingw' > /dev/null; then \
-				(	case $$i in \
-						*crypto*) i=libeay32.dll;; \
-						*ssl*)    i=ssleay32.dll;; \
-					esac; \
-					echo installing $$i; \
-	 				cp $$i $(INSTALL_PREFIX)$(INSTALLTOP)/bin/$$i.new; \
-	 				chmod 755 $(INSTALL_PREFIX)$(INSTALLTOP)/bin/$$i.new; \
-	 				mv -f $(INSTALL_PREFIX)$(INSTALLTOP)/bin/$$i.new $(INSTALL_PREFIX)$(INSTALLTOP)/bin/$$i ); \
-				fi; \
-			fi; \
-		done; \
-		(	here="`pwd`"; \
-			cd $(INSTALL_PREFIX)$(INSTALLTOP)/$(LIBDIR); \
-			$(MAKE) -f $$here/Makefile HERE="$$here" link-shared ); \
-		if [ "$(INSTALLTOP)" != "/usr" ]; then \
-			echo 'OpenSSL shared libraries have been installed in:'; \
-			echo '  $(INSTALLTOP)'; \
-			echo ''; \
-			sed -e '1,/^$$/d' doc/openssl-shared.txt; \
-		fi; \
-	fi
-	cp libcrypto.pc $(INSTALL_PREFIX)$(INSTALLTOP)/$(LIBDIR)/pkgconfig
-	chmod 644 $(INSTALL_PREFIX)$(INSTALLTOP)/$(LIBDIR)/pkgconfig/libcrypto.pc
-	cp libssl.pc $(INSTALL_PREFIX)$(INSTALLTOP)/$(LIBDIR)/pkgconfig
-	chmod 644 $(INSTALL_PREFIX)$(INSTALLTOP)/$(LIBDIR)/pkgconfig/libssl.pc
-	cp openssl.pc $(INSTALL_PREFIX)$(INSTALLTOP)/$(LIBDIR)/pkgconfig
-	chmod 644 $(INSTALL_PREFIX)$(INSTALLTOP)/$(LIBDIR)/pkgconfig/openssl.pc
-
-install_html_docs:
-	here="`pwd`"; \
-	for subdir in apps crypto ssl; do \
-		mkdir -p $(INSTALL_PREFIX)$(HTMLDIR)/$$subdir; \
-		for i in doc/$$subdir/*.pod; do \
-			fn=`basename $$i .pod`; \
-			echo "installing html/$$fn.$(HTMLSUFFIX)"; \
-			cat $$i \
-			| sed -r 's/L<([^)]*)(\([0-9]\))?\|([^)]*)(\([0-9]\))?>/L<\1|\3>/g' \
-			| pod2html --podroot=doc --htmlroot=.. --podpath=apps:crypto:ssl \
-			| sed -r 's/<!DOCTYPE.*//g' \
-			> $(INSTALL_PREFIX)$(HTMLDIR)/$$subdir/$$fn.$(HTMLSUFFIX); \
-			$(PERL) util/extract-names.pl < $$i | \
-				grep -v $$filecase "^$$fn\$$" | \
-				(cd $(INSTALL_PREFIX)$(HTMLDIR)/$$subdir; \
-				 while read n; do \
-					PLATFORM=$(PLATFORM) $$here/util/point.sh $$fn.$(HTMLSUFFIX) "$$n".$(HTMLSUFFIX); \
-				 done); \
-		done; \
-	done
-
-install_docs:
-	@$(PERL) $(TOP)/util/mkdir-p.pl \
-		$(INSTALL_PREFIX)$(MANDIR)/man1 \
-		$(INSTALL_PREFIX)$(MANDIR)/man3 \
-		$(INSTALL_PREFIX)$(MANDIR)/man5 \
-		$(INSTALL_PREFIX)$(MANDIR)/man7
-	@pod2man="`cd ./util; ./pod2mantest $(PERL)`"; \
-	here="`pwd`"; \
-	filecase=; \
-	if [ "$(PLATFORM)" = "DJGPP" -o "$(PLATFORM)" = "Cygwin" -o "$(PLATFORM)" = "mingw" ]; then \
-		filecase=-i; \
-	fi; \
-	set -e; for i in doc/apps/*.pod; do \
-		fn=`basename $$i .pod`; \
-		sec=`$(PERL) util/extract-section.pl 1 < $$i`; \
-		echo "installing man$$sec/$$fn.$${sec}$(MANSUFFIX)"; \
-		(cd `$(PERL) util/dirname.pl $$i`; \
-		sh -c "$$pod2man \
-			--section=$$sec --center=OpenSSL \
-			--release=$(VERSION) `basename $$i`") \
-			>  $(INSTALL_PREFIX)$(MANDIR)/man$$sec/$$fn.$${sec}$(MANSUFFIX); \
-		$(PERL) util/extract-names.pl < $$i | \
-			(grep -v $$filecase "^$$fn\$$"; true) | \
-			(grep -v "[	]"; true) | \
-			(cd $(INSTALL_PREFIX)$(MANDIR)/man$$sec/; \
-			 while read n; do \
-				PLATFORM=$(PLATFORM) $$here/util/point.sh $$fn.$${sec}$(MANSUFFIX) "$$n".$${sec}$(MANSUFFIX); \
-			 done); \
-	done; \
-	set -e; for i in doc/crypto/*.pod doc/ssl/*.pod; do \
-		fn=`basename $$i .pod`; \
-		sec=`$(PERL) util/extract-section.pl 3 < $$i`; \
-		echo "installing man$$sec/$$fn.$${sec}$(MANSUFFIX)"; \
-		(cd `$(PERL) util/dirname.pl $$i`; \
-		sh -c "$$pod2man \
-			--section=$$sec --center=OpenSSL \
-			--release=$(VERSION) `basename $$i`") \
-			>  $(INSTALL_PREFIX)$(MANDIR)/man$$sec/$$fn.$${sec}$(MANSUFFIX); \
-		$(PERL) util/extract-names.pl < $$i | \
-			(grep -v $$filecase "^$$fn\$$"; true) | \
-			(grep -v "[	]"; true) | \
-			(cd $(INSTALL_PREFIX)$(MANDIR)/man$$sec/; \
-			 while read n; do \
-				PLATFORM=$(PLATFORM) $$here/util/point.sh $$fn.$${sec}$(MANSUFFIX) "$$n".$${sec}$(MANSUFFIX); \
-			 done); \
-	done
-
-# DO NOT DELETE THIS LINE -- make depend depends on it.
diff --git a/src/third_party/openssl/openssl/apps/CA.pl b/src/third_party/openssl/openssl/apps/CA.pl
deleted file mode 100644
index a3965ec..0000000
--- a/src/third_party/openssl/openssl/apps/CA.pl
+++ /dev/null
@@ -1,189 +0,0 @@
-#!/usr/bin/perl
-#
-# CA - wrapper around ca to make it easier to use ... basically ca requires
-#      some setup stuff to be done before you can use it and this makes
-#      things easier between now and when Eric is convinced to fix it :-)
-#
-# CA -newca ... will setup the right stuff
-# CA -newreq[-nodes] ... will generate a certificate request 
-# CA -sign ... will sign the generated request and output 
-#
-# At the end of that grab newreq.pem and newcert.pem (one has the key 
-# and the other the certificate) and cat them together and that is what
-# you want/need ... I'll make even this a little cleaner later.
-#
-#
-# 12-Jan-96 tjh    Added more things ... including CA -signcert which
-#                  converts a certificate to a request and then signs it.
-# 10-Jan-96 eay    Fixed a few more bugs and added the SSLEAY_CONFIG
-#		   environment variable so this can be driven from
-#		   a script.
-# 25-Jul-96 eay    Cleaned up filenames some more.
-# 11-Jun-96 eay    Fixed a few filename missmatches.
-# 03-May-96 eay    Modified to use 'ssleay cmd' instead of 'cmd'.
-# 18-Apr-96 tjh    Original hacking
-#
-# Tim Hudson
-# tjh@cryptsoft.com
-#
-
-# 27-Apr-98 snh    Translation into perl, fix existing CA bug.
-#
-#
-# Steve Henson
-# shenson@bigfoot.com
-
-# default openssl.cnf file has setup as per the following
-# demoCA ... where everything is stored
-
-my $openssl;
-if(defined $ENV{OPENSSL}) {
-	$openssl = $ENV{OPENSSL};
-} else {
-	$openssl = "openssl";
-	$ENV{OPENSSL} = $openssl;
-}
-
-$SSLEAY_CONFIG=$ENV{"SSLEAY_CONFIG"};
-$DAYS="-days 365";	# 1 year
-$CADAYS="-days 1095";	# 3 years
-$REQ="$openssl req $SSLEAY_CONFIG";
-$CA="$openssl ca $SSLEAY_CONFIG";
-$VERIFY="$openssl verify";
-$X509="$openssl x509";
-$PKCS12="$openssl pkcs12";
-
-$CATOP="./demoCA";
-$CAKEY="cakey.pem";
-$CAREQ="careq.pem";
-$CACERT="cacert.pem";
-
-$DIRMODE = 0777;
-
-$RET = 0;
-
-foreach (@ARGV) {
-	if ( /^(-\?|-h|-help)$/ ) {
-	    print STDERR "usage: CA -newcert|-newreq|-newreq-nodes|-newca|-sign|-verify\n";
-	    exit 0;
-	} elsif (/^-newcert$/) {
-	    # create a certificate
-	    system ("$REQ -new -x509 -keyout newkey.pem -out newcert.pem $DAYS");
-	    $RET=$?;
-	    print "Certificate is in newcert.pem, private key is in newkey.pem\n"
-	} elsif (/^-newreq$/) {
-	    # create a certificate request
-	    system ("$REQ -new -keyout newkey.pem -out newreq.pem $DAYS");
-	    $RET=$?;
-	    print "Request is in newreq.pem, private key is in newkey.pem\n";
-	} elsif (/^-newreq-nodes$/) {
-	    # create a certificate request
-	    system ("$REQ -new -nodes -keyout newkey.pem -out newreq.pem $DAYS");
-	    $RET=$?;
-	    print "Request is in newreq.pem, private key is in newkey.pem\n";
-	} elsif (/^-newca$/) {
-		# if explicitly asked for or it doesn't exist then setup the
-		# directory structure that Eric likes to manage things 
-	    $NEW="1";
-	    if ( "$NEW" || ! -f "${CATOP}/serial" ) {
-		# create the directory hierarchy
-		mkdir $CATOP, $DIRMODE;
-		mkdir "${CATOP}/certs", $DIRMODE;
-		mkdir "${CATOP}/crl", $DIRMODE ;
-		mkdir "${CATOP}/newcerts", $DIRMODE;
-		mkdir "${CATOP}/private", $DIRMODE;
-		open OUT, ">${CATOP}/index.txt";
-		close OUT;
-		open OUT, ">${CATOP}/crlnumber";
-		print OUT "01\n";
-		close OUT;
-	    }
-	    if ( ! -f "${CATOP}/private/$CAKEY" ) {
-		print "CA certificate filename (or enter to create)\n";
-		$FILE = <STDIN>;
-
-		chop $FILE;
-
-		# ask user for existing CA certificate
-		if ($FILE) {
-		    cp_pem($FILE,"${CATOP}/private/$CAKEY", "PRIVATE");
-		    cp_pem($FILE,"${CATOP}/$CACERT", "CERTIFICATE");
-		    $RET=$?;
-		} else {
-		    print "Making CA certificate ...\n";
-		    system ("$REQ -new -keyout " .
-			"${CATOP}/private/$CAKEY -out ${CATOP}/$CAREQ");
-		    system ("$CA -create_serial " .
-			"-out ${CATOP}/$CACERT $CADAYS -batch " . 
-			"-keyfile ${CATOP}/private/$CAKEY -selfsign " .
-			"-extensions v3_ca " .
-			"-infiles ${CATOP}/$CAREQ ");
-		    $RET=$?;
-		}
-	    }
-	} elsif (/^-pkcs12$/) {
-	    my $cname = $ARGV[1];
-	    $cname = "My Certificate" unless defined $cname;
-	    system ("$PKCS12 -in newcert.pem -inkey newkey.pem " .
-			"-certfile ${CATOP}/$CACERT -out newcert.p12 " .
-			"-export -name \"$cname\"");
-	    $RET=$?;
-	    print "PKCS #12 file is in newcert.p12\n";
-	    exit $RET;
-	} elsif (/^-xsign$/) {
-	    system ("$CA -policy policy_anything -infiles newreq.pem");
-	    $RET=$?;
-	} elsif (/^(-sign|-signreq)$/) {
-	    system ("$CA -policy policy_anything -out newcert.pem " .
-							"-infiles newreq.pem");
-	    $RET=$?;
-	    print "Signed certificate is in newcert.pem\n";
-	} elsif (/^(-signCA)$/) {
-	    system ("$CA -policy policy_anything -out newcert.pem " .
-					"-extensions v3_ca -infiles newreq.pem");
-	    $RET=$?;
-	    print "Signed CA certificate is in newcert.pem\n";
-	} elsif (/^-signcert$/) {
-	    system ("$X509 -x509toreq -in newreq.pem -signkey newreq.pem " .
-								"-out tmp.pem");
-	    system ("$CA -policy policy_anything -out newcert.pem " .
-							"-infiles tmp.pem");
-	    $RET = $?;
-	    print "Signed certificate is in newcert.pem\n";
-	} elsif (/^-verify$/) {
-	    if (shift) {
-		foreach $j (@ARGV) {
-		    system ("$VERIFY -CAfile $CATOP/$CACERT $j");
-		    $RET=$? if ($? != 0);
-		}
-		exit $RET;
-	    } else {
-		    system ("$VERIFY -CAfile $CATOP/$CACERT newcert.pem");
-		    $RET=$?;
-	    	    exit 0;
-	    }
-	} else {
-	    print STDERR "Unknown arg $_\n";
-	    print STDERR "usage: CA -newcert|-newreq|-newreq-nodes|-newca|-sign|-verify\n";
-	    exit 1;
-	}
-}
-
-exit $RET;
-
-sub cp_pem {
-my ($infile, $outfile, $bound) = @_;
-open IN, $infile;
-open OUT, ">$outfile";
-my $flag = 0;
-while (<IN>) {
-	$flag = 1 if (/^-----BEGIN.*$bound/) ;
-	print OUT $_ if ($flag);
-	if (/^-----END.*$bound/) {
-		close IN;
-		close OUT;
-		return;
-	}
-}
-}
-
diff --git a/src/third_party/openssl/openssl/apps/md4.c b/src/third_party/openssl/openssl/apps/md4.c
deleted file mode 120000
index 7f457b2..0000000
--- a/src/third_party/openssl/openssl/apps/md4.c
+++ /dev/null
@@ -1 +0,0 @@
-../crypto/md4/md4.c
\ No newline at end of file
diff --git a/src/third_party/openssl/openssl/crypto/opensslconf.h b/src/third_party/openssl/openssl/crypto/opensslconf.h
deleted file mode 100644
index f17eaa9..0000000
--- a/src/third_party/openssl/openssl/crypto/opensslconf.h
+++ /dev/null
@@ -1,262 +0,0 @@
-/* opensslconf.h */
-/* WARNING: Generated automatically from opensslconf.h.in by Configure. */
-
-/* OpenSSL was configured with the following options: */
-#ifndef OPENSSL_DOING_MAKEDEPEND
-
-
-#ifndef OPENSSL_NO_CAST
-# define OPENSSL_NO_CAST
-#endif
-#ifndef OPENSSL_NO_EC_NISTP_64_GCC_128
-# define OPENSSL_NO_EC_NISTP_64_GCC_128
-#endif
-#ifndef OPENSSL_NO_GMP
-# define OPENSSL_NO_GMP
-#endif
-#ifndef OPENSSL_NO_IDEA
-# define OPENSSL_NO_IDEA
-#endif
-#ifndef OPENSSL_NO_JPAKE
-# define OPENSSL_NO_JPAKE
-#endif
-#ifndef OPENSSL_NO_KRB5
-# define OPENSSL_NO_KRB5
-#endif
-#ifndef OPENSSL_NO_MD2
-# define OPENSSL_NO_MD2
-#endif
-#ifndef OPENSSL_NO_RC5
-# define OPENSSL_NO_RC5
-#endif
-#ifndef OPENSSL_NO_RFC3779
-# define OPENSSL_NO_RFC3779
-#endif
-#ifndef OPENSSL_NO_SCTP
-# define OPENSSL_NO_SCTP
-#endif
-#ifndef OPENSSL_NO_SEED
-# define OPENSSL_NO_SEED
-#endif
-#ifndef OPENSSL_NO_SHA0
-# define OPENSSL_NO_SHA0
-#endif
-#ifndef OPENSSL_NO_STORE
-# define OPENSSL_NO_STORE
-#endif
-#ifndef OPENSSL_NO_WHRLPOOL
-# define OPENSSL_NO_WHRLPOOL
-#endif
-
-#endif /* OPENSSL_DOING_MAKEDEPEND */
-
-#ifndef OPENSSL_THREADS
-# define OPENSSL_THREADS
-#endif
-#ifndef OPENSSL_NO_DYNAMIC_ENGINE
-# define OPENSSL_NO_DYNAMIC_ENGINE
-#endif
-
-/* The OPENSSL_NO_* macros are also defined as NO_* if the application
-   asks for it.  This is a transient feature that is provided for those
-   who haven't had the time to do the appropriate changes in their
-   applications.  */
-#ifdef OPENSSL_ALGORITHM_DEFINES
-# if defined(OPENSSL_NO_CAST) && !defined(NO_CAST)
-#  define NO_CAST
-# endif
-# if defined(OPENSSL_NO_EC_NISTP_64_GCC_128) && !defined(NO_EC_NISTP_64_GCC_128)
-#  define NO_EC_NISTP_64_GCC_128
-# endif
-# if defined(OPENSSL_NO_GMP) && !defined(NO_GMP)
-#  define NO_GMP
-# endif
-# if defined(OPENSSL_NO_IDEA) && !defined(NO_IDEA)
-#  define NO_IDEA
-# endif
-# if defined(OPENSSL_NO_JPAKE) && !defined(NO_JPAKE)
-#  define NO_JPAKE
-# endif
-# if defined(OPENSSL_NO_KRB5) && !defined(NO_KRB5)
-#  define NO_KRB5
-# endif
-# if defined(OPENSSL_NO_MD2) && !defined(NO_MD2)
-#  define NO_MD2
-# endif
-# if defined(OPENSSL_NO_RC5) && !defined(NO_RC5)
-#  define NO_RC5
-# endif
-# if defined(OPENSSL_NO_RFC3779) && !defined(NO_RFC3779)
-#  define NO_RFC3779
-# endif
-# if defined(OPENSSL_NO_SCTP) && !defined(NO_SCTP)
-#  define NO_SCTP
-# endif
-# if defined(OPENSSL_NO_SEED) && !defined(NO_SEED)
-#  define NO_SEED
-# endif
-# if defined(OPENSSL_NO_SHA0) && !defined(NO_SHA0)
-#  define NO_SHA0
-# endif
-# if defined(OPENSSL_NO_STORE) && !defined(NO_STORE)
-#  define NO_STORE
-# endif
-# if defined(OPENSSL_NO_WHRLPOOL) && !defined(NO_WHRLPOOL)
-#  define NO_WHRLPOOL
-# endif
-#endif
-
-/* crypto/opensslconf.h.in */
-
-/* Generate 80386 code? */
-#undef I386_ONLY
-
-#if !(defined(VMS) || defined(__VMS)) /* VMS uses logical names instead */
-#if defined(HEADER_CRYPTLIB_H) && !defined(OPENSSLDIR)
-#define ENGINESDIR "/usr/local/ssl/lib/engines"
-#define OPENSSLDIR "/usr/local/ssl"
-#endif
-#endif
-
-#undef OPENSSL_UNISTD
-#define OPENSSL_UNISTD <unistd.h>
-
-#undef OPENSSL_EXPORT_VAR_AS_FUNCTION
-
-#if defined(HEADER_IDEA_H) && !defined(IDEA_INT)
-#define IDEA_INT unsigned int
-#endif
-
-#if defined(HEADER_MD2_H) && !defined(MD2_INT)
-#define MD2_INT unsigned int
-#endif
-
-#if defined(HEADER_RC2_H) && !defined(RC2_INT)
-/* I need to put in a mod for the alpha - eay */
-#define RC2_INT unsigned int
-#endif
-
-#if defined(HEADER_RC4_H)
-#if !defined(RC4_INT)
-/* using int types make the structure larger but make the code faster
- * on most boxes I have tested - up to %20 faster. */
-/*
- * I don't know what does "most" mean, but declaring "int" is a must on:
- * - Intel P6 because partial register stalls are very expensive;
- * - elder Alpha because it lacks byte load/store instructions;
- */
-#define RC4_INT unsigned char
-#endif
-#if !defined(RC4_CHUNK)
-/*
- * This enables code handling data aligned at natural CPU word
- * boundary. See crypto/rc4/rc4_enc.c for further details.
- */
-#define RC4_CHUNK unsigned long
-#endif
-#endif
-
-#if (defined(HEADER_NEW_DES_H) || defined(HEADER_DES_H)) && !defined(DES_LONG)
-/* If this is set to 'unsigned int' on a DEC Alpha, this gives about a
- * %20 speed up (longs are 8 bytes, int's are 4). */
-#ifndef DES_LONG
-#define DES_LONG unsigned int
-#endif
-#endif
-
-#if defined(HEADER_BN_H) && !defined(CONFIG_HEADER_BN_H)
-#define CONFIG_HEADER_BN_H
-#define BN_LLONG
-
-/* Should we define BN_DIV2W here? */
-
-/* Only one for the following should be defined */
-#undef SIXTY_FOUR_BIT_LONG
-#undef SIXTY_FOUR_BIT
-#define THIRTY_TWO_BIT
-#endif
-
-#if defined(HEADER_RC4_LOCL_H) && !defined(CONFIG_HEADER_RC4_LOCL_H)
-#define CONFIG_HEADER_RC4_LOCL_H
-/* if this is defined data[i] is used instead of *data, this is a %20
- * speedup on x86 */
-#undef RC4_INDEX
-#endif
-
-#if defined(HEADER_BF_LOCL_H) && !defined(CONFIG_HEADER_BF_LOCL_H)
-#define CONFIG_HEADER_BF_LOCL_H
-#define BF_PTR
-#endif /* HEADER_BF_LOCL_H */
-
-#if defined(HEADER_DES_LOCL_H) && !defined(CONFIG_HEADER_DES_LOCL_H)
-#define CONFIG_HEADER_DES_LOCL_H
-#ifndef DES_DEFAULT_OPTIONS
-/* the following is tweaked from a config script, that is why it is a
- * protected undef/define */
-#ifndef DES_PTR
-#undef DES_PTR
-#endif
-
-/* This helps C compiler generate the correct code for multiple functional
- * units.  It reduces register dependancies at the expense of 2 more
- * registers */
-#ifndef DES_RISC1
-#undef DES_RISC1
-#endif
-
-#ifndef DES_RISC2
-#undef DES_RISC2
-#endif
-
-#if defined(DES_RISC1) && defined(DES_RISC2)
-YOU SHOULD NOT HAVE BOTH DES_RISC1 AND DES_RISC2 DEFINED!!!!!
-#endif
-
-/* Unroll the inner loop, this sometimes helps, sometimes hinders.
- * Very mucy CPU dependant */
-#ifndef DES_UNROLL
-#define DES_UNROLL
-#endif
-
-/* These default values were supplied by
- * Peter Gutman <pgut001@cs.auckland.ac.nz>
- * They are only used if nothing else has been defined */
-#if !defined(DES_PTR) && !defined(DES_RISC1) && !defined(DES_RISC2) && !defined(DES_UNROLL)
-/* Special defines which change the way the code is built depending on the
-   CPU and OS.  For SGI machines you can use _MIPS_SZLONG (32 or 64) to find
-   even newer MIPS CPU's, but at the moment one size fits all for
-   optimization options.  Older Sparc's work better with only UNROLL, but
-   there's no way to tell at compile time what it is you're running on */
- 
-#if defined( sun )		/* Newer Sparc's */
-#  define DES_PTR
-#  define DES_RISC1
-#  define DES_UNROLL
-#elif defined( __ultrix )	/* Older MIPS */
-#  define DES_PTR
-#  define DES_RISC2
-#  define DES_UNROLL
-#elif defined( __osf1__ )	/* Alpha */
-#  define DES_PTR
-#  define DES_RISC2
-#elif defined ( _AIX )		/* RS6000 */
-  /* Unknown */
-#elif defined( __hpux )		/* HP-PA */
-  /* Unknown */
-#elif defined( __aux )		/* 68K */
-  /* Unknown */
-#elif defined( __dgux )		/* 88K (but P6 in latest boxes) */
-#  define DES_UNROLL
-#elif defined( __sgi )		/* Newer MIPS */
-#  define DES_PTR
-#  define DES_RISC2
-#  define DES_UNROLL
-#elif defined(i386) || defined(__i386__)	/* x86 boxes, should be gcc */
-#  define DES_PTR
-#  define DES_RISC1
-#  define DES_UNROLL
-#endif /* Systems-specific speed defines */
-#endif
-
-#endif /* DES_DEFAULT_OPTIONS */
-#endif /* HEADER_DES_LOCL_H */
diff --git a/src/third_party/openssl/openssl/include/openssl/aes.h b/src/third_party/openssl/openssl/include/openssl/aes.h
deleted file mode 100644
index f646d41..0000000
--- a/src/third_party/openssl/openssl/include/openssl/aes.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/aes/aes.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/asn1.h b/src/third_party/openssl/openssl/include/openssl/asn1.h
deleted file mode 100644
index 5432ed8..0000000
--- a/src/third_party/openssl/openssl/include/openssl/asn1.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/asn1/asn1.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/asn1_mac.h b/src/third_party/openssl/openssl/include/openssl/asn1_mac.h
deleted file mode 100644
index 214787c..0000000
--- a/src/third_party/openssl/openssl/include/openssl/asn1_mac.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/asn1/asn1_mac.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/asn1t.h b/src/third_party/openssl/openssl/include/openssl/asn1t.h
deleted file mode 100644
index 4de87a9..0000000
--- a/src/third_party/openssl/openssl/include/openssl/asn1t.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/asn1/asn1t.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/bio.h b/src/third_party/openssl/openssl/include/openssl/bio.h
deleted file mode 100644
index 34f8a2d..0000000
--- a/src/third_party/openssl/openssl/include/openssl/bio.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/bio/bio.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/blowfish.h b/src/third_party/openssl/openssl/include/openssl/blowfish.h
deleted file mode 100644
index 8d515fe..0000000
--- a/src/third_party/openssl/openssl/include/openssl/blowfish.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/bf/blowfish.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/bn.h b/src/third_party/openssl/openssl/include/openssl/bn.h
deleted file mode 100644
index f47d65a..0000000
--- a/src/third_party/openssl/openssl/include/openssl/bn.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/bn/bn.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/buffer.h b/src/third_party/openssl/openssl/include/openssl/buffer.h
deleted file mode 100644
index 1d2c2a2..0000000
--- a/src/third_party/openssl/openssl/include/openssl/buffer.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/buffer/buffer.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/camellia.h b/src/third_party/openssl/openssl/include/openssl/camellia.h
deleted file mode 100644
index 5a0a141..0000000
--- a/src/third_party/openssl/openssl/include/openssl/camellia.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/camellia/camellia.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/cast.h b/src/third_party/openssl/openssl/include/openssl/cast.h
deleted file mode 100644
index 12cf92c..0000000
--- a/src/third_party/openssl/openssl/include/openssl/cast.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/cast/cast.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/cmac.h b/src/third_party/openssl/openssl/include/openssl/cmac.h
deleted file mode 100644
index a7579ae..0000000
--- a/src/third_party/openssl/openssl/include/openssl/cmac.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/cmac/cmac.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/cms.h b/src/third_party/openssl/openssl/include/openssl/cms.h
deleted file mode 100644
index 8687c7f..0000000
--- a/src/third_party/openssl/openssl/include/openssl/cms.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/cms/cms.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/comp.h b/src/third_party/openssl/openssl/include/openssl/comp.h
deleted file mode 100644
index d14e36c..0000000
--- a/src/third_party/openssl/openssl/include/openssl/comp.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/comp/comp.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/conf.h b/src/third_party/openssl/openssl/include/openssl/conf.h
deleted file mode 100644
index 3882c82..0000000
--- a/src/third_party/openssl/openssl/include/openssl/conf.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/conf/conf.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/conf_api.h b/src/third_party/openssl/openssl/include/openssl/conf_api.h
deleted file mode 100644
index 0393357..0000000
--- a/src/third_party/openssl/openssl/include/openssl/conf_api.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/conf/conf_api.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/crypto.h b/src/third_party/openssl/openssl/include/openssl/crypto.h
deleted file mode 100644
index 7e3d91e..0000000
--- a/src/third_party/openssl/openssl/include/openssl/crypto.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/crypto.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/des.h b/src/third_party/openssl/openssl/include/openssl/des.h
deleted file mode 100644
index 1d6631e..0000000
--- a/src/third_party/openssl/openssl/include/openssl/des.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/des/des.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/des_old.h b/src/third_party/openssl/openssl/include/openssl/des_old.h
deleted file mode 100644
index e582873..0000000
--- a/src/third_party/openssl/openssl/include/openssl/des_old.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/des/des_old.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/dh.h b/src/third_party/openssl/openssl/include/openssl/dh.h
deleted file mode 100644
index f70a767..0000000
--- a/src/third_party/openssl/openssl/include/openssl/dh.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/dh/dh.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/dsa.h b/src/third_party/openssl/openssl/include/openssl/dsa.h
deleted file mode 100644
index 0365acf..0000000
--- a/src/third_party/openssl/openssl/include/openssl/dsa.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/dsa/dsa.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/dso.h b/src/third_party/openssl/openssl/include/openssl/dso.h
deleted file mode 100644
index f3c8de2..0000000
--- a/src/third_party/openssl/openssl/include/openssl/dso.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/dso/dso.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/dtls1.h b/src/third_party/openssl/openssl/include/openssl/dtls1.h
deleted file mode 100644
index ac8ab57..0000000
--- a/src/third_party/openssl/openssl/include/openssl/dtls1.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../ssl/dtls1.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/e_os2.h b/src/third_party/openssl/openssl/include/openssl/e_os2.h
deleted file mode 100644
index ab3f1ee..0000000
--- a/src/third_party/openssl/openssl/include/openssl/e_os2.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../e_os2.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/ebcdic.h b/src/third_party/openssl/openssl/include/openssl/ebcdic.h
deleted file mode 100644
index 6dedc70..0000000
--- a/src/third_party/openssl/openssl/include/openssl/ebcdic.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/ebcdic.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/ec.h b/src/third_party/openssl/openssl/include/openssl/ec.h
deleted file mode 100644
index 7d20614..0000000
--- a/src/third_party/openssl/openssl/include/openssl/ec.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/ec/ec.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/ecdh.h b/src/third_party/openssl/openssl/include/openssl/ecdh.h
deleted file mode 100644
index ad6e3dc..0000000
--- a/src/third_party/openssl/openssl/include/openssl/ecdh.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/ecdh/ecdh.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/ecdsa.h b/src/third_party/openssl/openssl/include/openssl/ecdsa.h
deleted file mode 100644
index da45123..0000000
--- a/src/third_party/openssl/openssl/include/openssl/ecdsa.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/ecdsa/ecdsa.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/engine.h b/src/third_party/openssl/openssl/include/openssl/engine.h
deleted file mode 100644
index 2dceaac..0000000
--- a/src/third_party/openssl/openssl/include/openssl/engine.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/engine/engine.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/err.h b/src/third_party/openssl/openssl/include/openssl/err.h
deleted file mode 100644
index caf89a9..0000000
--- a/src/third_party/openssl/openssl/include/openssl/err.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/err/err.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/evp.h b/src/third_party/openssl/openssl/include/openssl/evp.h
deleted file mode 100644
index dd7bcda..0000000
--- a/src/third_party/openssl/openssl/include/openssl/evp.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/evp/evp.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/hmac.h b/src/third_party/openssl/openssl/include/openssl/hmac.h
deleted file mode 100644
index 202128b..0000000
--- a/src/third_party/openssl/openssl/include/openssl/hmac.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/hmac/hmac.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/idea.h b/src/third_party/openssl/openssl/include/openssl/idea.h
deleted file mode 100644
index bdf697d..0000000
--- a/src/third_party/openssl/openssl/include/openssl/idea.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/idea/idea.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/krb5_asn.h b/src/third_party/openssl/openssl/include/openssl/krb5_asn.h
deleted file mode 100644
index 0d3feea..0000000
--- a/src/third_party/openssl/openssl/include/openssl/krb5_asn.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/krb5/krb5_asn.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/kssl.h b/src/third_party/openssl/openssl/include/openssl/kssl.h
deleted file mode 100644
index 719634a..0000000
--- a/src/third_party/openssl/openssl/include/openssl/kssl.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../ssl/kssl.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/lhash.h b/src/third_party/openssl/openssl/include/openssl/lhash.h
deleted file mode 100644
index 2d3db87..0000000
--- a/src/third_party/openssl/openssl/include/openssl/lhash.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/lhash/lhash.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/md2.h b/src/third_party/openssl/openssl/include/openssl/md2.h
deleted file mode 100644
index 4c3398c..0000000
--- a/src/third_party/openssl/openssl/include/openssl/md2.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/md2/md2.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/md4.h b/src/third_party/openssl/openssl/include/openssl/md4.h
deleted file mode 100644
index 611806e..0000000
--- a/src/third_party/openssl/openssl/include/openssl/md4.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/md4/md4.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/md5.h b/src/third_party/openssl/openssl/include/openssl/md5.h
deleted file mode 100644
index aa8cd0b..0000000
--- a/src/third_party/openssl/openssl/include/openssl/md5.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/md5/md5.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/mdc2.h b/src/third_party/openssl/openssl/include/openssl/mdc2.h
deleted file mode 100644
index ac284a1..0000000
--- a/src/third_party/openssl/openssl/include/openssl/mdc2.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/mdc2/mdc2.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/modes.h b/src/third_party/openssl/openssl/include/openssl/modes.h
deleted file mode 100644
index f57fcfe..0000000
--- a/src/third_party/openssl/openssl/include/openssl/modes.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/modes/modes.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/obj_mac.h b/src/third_party/openssl/openssl/include/openssl/obj_mac.h
deleted file mode 100644
index 3890fa9..0000000
--- a/src/third_party/openssl/openssl/include/openssl/obj_mac.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/objects/obj_mac.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/objects.h b/src/third_party/openssl/openssl/include/openssl/objects.h
deleted file mode 100644
index 5365a04..0000000
--- a/src/third_party/openssl/openssl/include/openssl/objects.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/objects/objects.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/ocsp.h b/src/third_party/openssl/openssl/include/openssl/ocsp.h
deleted file mode 100644
index 50e2885..0000000
--- a/src/third_party/openssl/openssl/include/openssl/ocsp.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/ocsp/ocsp.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/opensslv.h b/src/third_party/openssl/openssl/include/openssl/opensslv.h
deleted file mode 100644
index c39a0c3..0000000
--- a/src/third_party/openssl/openssl/include/openssl/opensslv.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/opensslv.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/ossl_typ.h b/src/third_party/openssl/openssl/include/openssl/ossl_typ.h
deleted file mode 100644
index ddd7e58..0000000
--- a/src/third_party/openssl/openssl/include/openssl/ossl_typ.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/ossl_typ.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/pem.h b/src/third_party/openssl/openssl/include/openssl/pem.h
deleted file mode 100644
index 5bcc5c5..0000000
--- a/src/third_party/openssl/openssl/include/openssl/pem.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/pem/pem.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/pem2.h b/src/third_party/openssl/openssl/include/openssl/pem2.h
deleted file mode 100644
index bcd3acf..0000000
--- a/src/third_party/openssl/openssl/include/openssl/pem2.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/pem/pem2.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/pkcs12.h b/src/third_party/openssl/openssl/include/openssl/pkcs12.h
deleted file mode 100644
index 0b5fbbf..0000000
--- a/src/third_party/openssl/openssl/include/openssl/pkcs12.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/pkcs12/pkcs12.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/pkcs7.h b/src/third_party/openssl/openssl/include/openssl/pkcs7.h
deleted file mode 100644
index 2e19d7c..0000000
--- a/src/third_party/openssl/openssl/include/openssl/pkcs7.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/pkcs7/pkcs7.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/pqueue.h b/src/third_party/openssl/openssl/include/openssl/pqueue.h
deleted file mode 100644
index 9681ff5..0000000
--- a/src/third_party/openssl/openssl/include/openssl/pqueue.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/pqueue/pqueue.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/rand.h b/src/third_party/openssl/openssl/include/openssl/rand.h
deleted file mode 100644
index 9d1521b..0000000
--- a/src/third_party/openssl/openssl/include/openssl/rand.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/rand/rand.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/rc2.h b/src/third_party/openssl/openssl/include/openssl/rc2.h
deleted file mode 100644
index f2f2bd1..0000000
--- a/src/third_party/openssl/openssl/include/openssl/rc2.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/rc2/rc2.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/rc4.h b/src/third_party/openssl/openssl/include/openssl/rc4.h
deleted file mode 100644
index 306de2f..0000000
--- a/src/third_party/openssl/openssl/include/openssl/rc4.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/rc4/rc4.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/ripemd.h b/src/third_party/openssl/openssl/include/openssl/ripemd.h
deleted file mode 100644
index 11351fc..0000000
--- a/src/third_party/openssl/openssl/include/openssl/ripemd.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/ripemd/ripemd.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/rsa.h b/src/third_party/openssl/openssl/include/openssl/rsa.h
deleted file mode 100644
index 975e5d3..0000000
--- a/src/third_party/openssl/openssl/include/openssl/rsa.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/rsa/rsa.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/safestack.h b/src/third_party/openssl/openssl/include/openssl/safestack.h
deleted file mode 100644
index 8a282b8..0000000
--- a/src/third_party/openssl/openssl/include/openssl/safestack.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/stack/safestack.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/seed.h b/src/third_party/openssl/openssl/include/openssl/seed.h
deleted file mode 100644
index bbbf596..0000000
--- a/src/third_party/openssl/openssl/include/openssl/seed.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/seed/seed.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/sha.h b/src/third_party/openssl/openssl/include/openssl/sha.h
deleted file mode 100644
index ab9d94c..0000000
--- a/src/third_party/openssl/openssl/include/openssl/sha.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/sha/sha.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/srp.h b/src/third_party/openssl/openssl/include/openssl/srp.h
deleted file mode 100644
index 8217476..0000000
--- a/src/third_party/openssl/openssl/include/openssl/srp.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/srp/srp.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/srtp.h b/src/third_party/openssl/openssl/include/openssl/srtp.h
deleted file mode 100644
index e185494..0000000
--- a/src/third_party/openssl/openssl/include/openssl/srtp.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../ssl/srtp.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/ssl.h b/src/third_party/openssl/openssl/include/openssl/ssl.h
deleted file mode 100644
index 0b0589c..0000000
--- a/src/third_party/openssl/openssl/include/openssl/ssl.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../ssl/ssl.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/ssl2.h b/src/third_party/openssl/openssl/include/openssl/ssl2.h
deleted file mode 100644
index 11b2205..0000000
--- a/src/third_party/openssl/openssl/include/openssl/ssl2.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../ssl/ssl2.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/ssl23.h b/src/third_party/openssl/openssl/include/openssl/ssl23.h
deleted file mode 100644
index fe4dae6..0000000
--- a/src/third_party/openssl/openssl/include/openssl/ssl23.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../ssl/ssl23.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/ssl3.h b/src/third_party/openssl/openssl/include/openssl/ssl3.h
deleted file mode 100644
index 0fb66a6..0000000
--- a/src/third_party/openssl/openssl/include/openssl/ssl3.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../ssl/ssl3.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/stack.h b/src/third_party/openssl/openssl/include/openssl/stack.h
deleted file mode 100644
index 295968c..0000000
--- a/src/third_party/openssl/openssl/include/openssl/stack.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/stack/stack.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/store.h b/src/third_party/openssl/openssl/include/openssl/store.h
deleted file mode 100644
index 84dc39f..0000000
--- a/src/third_party/openssl/openssl/include/openssl/store.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/store/store.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/symhacks.h b/src/third_party/openssl/openssl/include/openssl/symhacks.h
deleted file mode 100644
index f946f4f..0000000
--- a/src/third_party/openssl/openssl/include/openssl/symhacks.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/symhacks.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/tls1.h b/src/third_party/openssl/openssl/include/openssl/tls1.h
deleted file mode 100644
index c43a70f..0000000
--- a/src/third_party/openssl/openssl/include/openssl/tls1.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../ssl/tls1.h"
diff --git a/src/third_party/openssl/openssl/include/openssl/ts.h b/src/third_party/openssl/openssl/include/openssl/ts.h
deleted file mode 100644
index fe8a2cb..0000000
--- a/src/third_party/openssl/openssl/include/openssl/ts.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../crypto/ts/ts.h"
diff --git