Merge inbound to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Mon, 14 Apr 2014 15:42:17 -0400
changeset 196877 5b6e82e7bbbff6bb98bbc68745f1d8ad8352f3f5
parent 196860 05a67671514daa4c7d033b766ee976d4f112a683 (current diff)
parent 196876 cbe169081f1cce6e8cad4c9d0ec18013042bab24 (diff)
child 196900 23528b4efd67c53b46ceaac02c377bb6443487ce
child 196949 1adbd1657ba17bfcfd47232851ccf352100bf70e
child 196959 986251916a35376704f572f8504140a67dce4f89
push id3624
push userasasaki@mozilla.com
push dateMon, 09 Jun 2014 21:49:01 +0000
treeherdermozilla-beta@b1a5da15899a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone31.0a1
first release with
nightly linux32
5b6e82e7bbbf / 31.0a1 / 20140415030203 / files
nightly linux64
5b6e82e7bbbf / 31.0a1 / 20140415030203 / files
nightly mac
5b6e82e7bbbf / 31.0a1 / 20140415030203 / files
nightly win32
5b6e82e7bbbf / 31.0a1 / 20140415030203 / files
nightly win64
5b6e82e7bbbf / 31.0a1 / 20140415030203 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c.
testing/marionette/client/marionette/client.py
--- a/content/base/test/test_bug682592.html
+++ b/content/base/test/test_bug682592.html
@@ -34,16 +34,20 @@ https://bugzilla.mozilla.org/show_bug.cg
    So, instead of many diferent reftests, this mochitest implements a
    reftest-like. It creates reference text fragments in reference iframe, test
    text fragments in test iframe, and compare the documents. Then, it reloads
    test iframe. Reference iframe does not need to be reloaded between tests.
    It's ok (and maybe, desired) to keep bidi always enabled in that document. 
 */
 
 SimpleTest.waitForExplicitFinish();
+if (navigator.platform.startsWith("Linux arm")) {
+  SimpleTest.expectAssertions(0, 2);
+}
+
 var refFrame = document.getElementById("iframe-ref")
 var testFrame = document.getElementById("iframe-test");
 
 refFrame.addEventListener("load", function() {
   testFrame.addEventListener("load", function() {
     try {
       tests.next();
       ok(compareSnapshots(snapshotWindow(testFrame.contentWindow), 
--- a/dom/asmjscache/AsmJSCache.h
+++ b/dom/asmjscache/AsmJSCache.h
@@ -152,19 +152,19 @@ DeallocEntryChild(PAsmJSCacheEntryChild*
 } // namespace asmjscache
 } // namespace dom
 } // namespace mozilla
 
 namespace IPC {
 
 template <>
 struct ParamTraits<mozilla::dom::asmjscache::OpenMode> :
-  public EnumSerializer<mozilla::dom::asmjscache::OpenMode,
-                        mozilla::dom::asmjscache::eOpenForRead,
-                        mozilla::dom::asmjscache::NUM_OPEN_MODES>
+  public ContiguousEnumSerializer<mozilla::dom::asmjscache::OpenMode,
+                                  mozilla::dom::asmjscache::eOpenForRead,
+                                  mozilla::dom::asmjscache::NUM_OPEN_MODES>
 { };
 
 template <>
 struct ParamTraits<mozilla::dom::asmjscache::Metadata>
 {
   typedef mozilla::dom::asmjscache::Metadata paramType;
   static void Write(Message* aMsg, const paramType& aParam);
   static bool Read(const Message* aMsg, void** aIter, paramType* aResult);
--- a/dom/bluetooth/ipc/BluetoothMessageUtils.h
+++ b/dom/bluetooth/ipc/BluetoothMessageUtils.h
@@ -9,16 +9,17 @@
 
 #include "mozilla/dom/bluetooth/BluetoothCommon.h"
 #include "ipc/IPCMessageUtils.h"
 
 namespace IPC {
 
 template <>
 struct ParamTraits<mozilla::dom::bluetooth::BluetoothObjectType>
-  : public EnumSerializer<mozilla::dom::bluetooth::BluetoothObjectType,
-                          mozilla::dom::bluetooth::TYPE_MANAGER,
-                          mozilla::dom::bluetooth::TYPE_INVALID>
+  : public ContiguousEnumSerializer<
+             mozilla::dom::bluetooth::BluetoothObjectType,
+             mozilla::dom::bluetooth::TYPE_MANAGER,
+             mozilla::dom::bluetooth::TYPE_INVALID>
 { };
 
 } // namespace IPC
 
 #endif // mozilla_dom_bluetooth_ipc_bluetoothchild_h__
--- a/dom/indexedDB/ipc/SerializationHelpers.h
+++ b/dom/indexedDB/ipc/SerializationHelpers.h
@@ -12,19 +12,20 @@
 #include "mozilla/dom/indexedDB/KeyPath.h"
 #include "mozilla/dom/indexedDB/IDBCursor.h"
 #include "mozilla/dom/indexedDB/IDBTransaction.h"
 
 namespace IPC {
 
 template <>
 struct ParamTraits<mozilla::dom::quota::PersistenceType> :
-  public EnumSerializer<mozilla::dom::quota::PersistenceType,
-                        mozilla::dom::quota::PERSISTENCE_TYPE_PERSISTENT,
-                        mozilla::dom::quota::PERSISTENCE_TYPE_INVALID>
+  public ContiguousEnumSerializer<
+           mozilla::dom::quota::PersistenceType,
+           mozilla::dom::quota::PERSISTENCE_TYPE_PERSISTENT,
+           mozilla::dom::quota::PERSISTENCE_TYPE_INVALID>
 { };
 
 template <>
 struct ParamTraits<mozilla::dom::indexedDB::Key>
 {
   typedef mozilla::dom::indexedDB::Key paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
@@ -40,19 +41,20 @@ struct ParamTraits<mozilla::dom::indexed
   static void Log(const paramType& aParam, std::wstring* aLog)
   {
     LogParam(aParam.mBuffer, aLog);
   }
 };
 
 template <>
 struct ParamTraits<mozilla::dom::indexedDB::KeyPath::KeyPathType> :
-  public EnumSerializer<mozilla::dom::indexedDB::KeyPath::KeyPathType,
-                        mozilla::dom::indexedDB::KeyPath::NONEXISTENT,
-                        mozilla::dom::indexedDB::KeyPath::ENDGUARD>
+  public ContiguousEnumSerializer<
+           mozilla::dom::indexedDB::KeyPath::KeyPathType,
+           mozilla::dom::indexedDB::KeyPath::NONEXISTENT,
+           mozilla::dom::indexedDB::KeyPath::ENDGUARD>
 { };
 
 template <>
 struct ParamTraits<mozilla::dom::indexedDB::KeyPath>
 {
   typedef mozilla::dom::indexedDB::KeyPath paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
@@ -70,26 +72,28 @@ struct ParamTraits<mozilla::dom::indexed
   static void Log(const paramType& aParam, std::wstring* aLog)
   {
     LogParam(aParam.mStrings, aLog);
   }
 };
 
 template <>
 struct ParamTraits<mozilla::dom::indexedDB::IDBCursor::Direction> :
-  public EnumSerializer<mozilla::dom::indexedDB::IDBCursor::Direction,
-                        mozilla::dom::indexedDB::IDBCursor::NEXT,
-                        mozilla::dom::indexedDB::IDBCursor::DIRECTION_INVALID>
+  public ContiguousEnumSerializer<
+           mozilla::dom::indexedDB::IDBCursor::Direction,
+           mozilla::dom::indexedDB::IDBCursor::NEXT,
+           mozilla::dom::indexedDB::IDBCursor::DIRECTION_INVALID>
 { };
 
 template <>
 struct ParamTraits<mozilla::dom::indexedDB::IDBTransaction::Mode> :
-  public EnumSerializer<mozilla::dom::indexedDB::IDBTransaction::Mode,
-                        mozilla::dom::indexedDB::IDBTransaction::READ_ONLY,
-                        mozilla::dom::indexedDB::IDBTransaction::MODE_INVALID>
+  public ContiguousEnumSerializer<
+           mozilla::dom::indexedDB::IDBTransaction::Mode,
+           mozilla::dom::indexedDB::IDBTransaction::READ_ONLY,
+           mozilla::dom::indexedDB::IDBTransaction::MODE_INVALID>
 { };
 
 template <>
 struct ParamTraits<mozilla::dom::indexedDB::IndexInfo>
 {
   typedef mozilla::dom::indexedDB::IndexInfo paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
--- a/dom/ipc/TabMessageUtils.h
+++ b/dom/ipc/TabMessageUtils.h
@@ -85,17 +85,17 @@ struct ParamTraits<mozilla::dom::AudioCh
 
   static void Log(const paramType& aParam, std::wstring* aLog)
   {
   }
 };
 
 template <>
 struct ParamTraits<mozilla::dom::AudioChannelState>
-  : public EnumSerializer<mozilla::dom::AudioChannelState,
-                          mozilla::dom::AUDIO_CHANNEL_STATE_NORMAL,
-                          mozilla::dom::AUDIO_CHANNEL_STATE_LAST>
+  : public ContiguousEnumSerializer<mozilla::dom::AudioChannelState,
+                                    mozilla::dom::AUDIO_CHANNEL_STATE_NORMAL,
+                                    mozilla::dom::AUDIO_CHANNEL_STATE_LAST>
 { };
 
 }
 
 
 #endif
--- a/dom/mobilemessage/src/Types.h
+++ b/dom/mobilemessage/src/Types.h
@@ -82,66 +82,72 @@ enum MessageType {
 
 namespace IPC {
 
 /**
  * Delivery state serializer.
  */
 template <>
 struct ParamTraits<mozilla::dom::mobilemessage::DeliveryState>
-  : public EnumSerializer<mozilla::dom::mobilemessage::DeliveryState,
-                          mozilla::dom::mobilemessage::eDeliveryState_Sent,
-                          mozilla::dom::mobilemessage::eDeliveryState_EndGuard>
+  : public ContiguousEnumSerializer<
+             mozilla::dom::mobilemessage::DeliveryState,
+             mozilla::dom::mobilemessage::eDeliveryState_Sent,
+             mozilla::dom::mobilemessage::eDeliveryState_EndGuard>
 {};
 
 /**
  * Delivery status serializer.
  */
 template <>
 struct ParamTraits<mozilla::dom::mobilemessage::DeliveryStatus>
-  : public EnumSerializer<mozilla::dom::mobilemessage::DeliveryStatus,
-                          mozilla::dom::mobilemessage::eDeliveryStatus_NotApplicable,
-                          mozilla::dom::mobilemessage::eDeliveryStatus_EndGuard>
+  : public ContiguousEnumSerializer<
+             mozilla::dom::mobilemessage::DeliveryStatus,
+             mozilla::dom::mobilemessage::eDeliveryStatus_NotApplicable,
+             mozilla::dom::mobilemessage::eDeliveryStatus_EndGuard>
 {};
 
 /**
  * Read status serializer.
  */
 template <>
 struct ParamTraits<mozilla::dom::mobilemessage::ReadStatus>
-  : public EnumSerializer<mozilla::dom::mobilemessage::ReadStatus,
-                          mozilla::dom::mobilemessage::eReadStatus_NotApplicable,
-                          mozilla::dom::mobilemessage::eReadStatus_EndGuard>
+  : public ContiguousEnumSerializer<
+             mozilla::dom::mobilemessage::ReadStatus,
+             mozilla::dom::mobilemessage::eReadStatus_NotApplicable,
+             mozilla::dom::mobilemessage::eReadStatus_EndGuard>
 {};
 
 /**
  * Read state serializer.
  */
 template <>
 struct ParamTraits<mozilla::dom::mobilemessage::ReadState>
-  : public EnumSerializer<mozilla::dom::mobilemessage::ReadState,
-                          mozilla::dom::mobilemessage::eReadState_Unknown,
-                          mozilla::dom::mobilemessage::eReadState_EndGuard>
+  : public ContiguousEnumSerializer<
+             mozilla::dom::mobilemessage::ReadState,
+             mozilla::dom::mobilemessage::eReadState_Unknown,
+             mozilla::dom::mobilemessage::eReadState_EndGuard>
 {};
 
 /**
  * Message class serializer.
  */
 template <>
 struct ParamTraits<mozilla::dom::mobilemessage::MessageClass>
-  : public EnumSerializer<mozilla::dom::mobilemessage::MessageClass,
-                          mozilla::dom::mobilemessage::eMessageClass_Normal,
-                          mozilla::dom::mobilemessage::eMessageClass_EndGuard>
+  : public ContiguousEnumSerializer<
+             mozilla::dom::mobilemessage::MessageClass,
+             mozilla::dom::mobilemessage::eMessageClass_Normal,
+             mozilla::dom::mobilemessage::eMessageClass_EndGuard>
 {};
 
 /**
  * MessageType class serializer.
  */
 template <>
 struct ParamTraits<mozilla::dom::mobilemessage::MessageType>
-  : public EnumSerializer<mozilla::dom::mobilemessage::MessageType,
-                          mozilla::dom::mobilemessage::eMessageType_SMS,
-                          mozilla::dom::mobilemessage::eMessageType_EndGuard>
+  : public ContiguousEnumSerializer<
+             mozilla::dom::mobilemessage::MessageType,
+             mozilla::dom::mobilemessage::eMessageType_SMS,
+             mozilla::dom::mobilemessage::eMessageType_EndGuard>
 {};
 
 } // namespace IPC
 
 #endif // mozilla_dom_mobilemessage_Types_h
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -239,82 +239,92 @@ struct ParamTraits<gfx3DMatrix>
             Rd(_31) && Rd(_32) && Rd(_33) && Rd(_34) &&
             Rd(_41) && Rd(_42) && Rd(_43) && Rd(_44));
 #undef Rd
   }
 };
 
 template <>
 struct ParamTraits<gfxContentType>
-  : public TypedEnumSerializer<gfxContentType,
-                               gfxContentType::COLOR,
-                               gfxContentType::SENTINEL>
+  : public ContiguousTypedEnumSerializer<
+             gfxContentType,
+             gfxContentType::COLOR,
+             gfxContentType::SENTINEL>
 {};
 
 template <>
 struct ParamTraits<gfxSurfaceType>
-  : public TypedEnumSerializer<gfxSurfaceType,
-                               gfxSurfaceType::Image,
-                               gfxSurfaceType::Max>
+  : public ContiguousTypedEnumSerializer<
+             gfxSurfaceType,
+             gfxSurfaceType::Image,
+             gfxSurfaceType::Max>
 {};
 
 template <>
 struct ParamTraits<mozilla::GraphicsFilterType>
-  : public EnumSerializer<mozilla::GraphicsFilterType,
-                          GraphicsFilter::FILTER_FAST,
-                          GraphicsFilter::FILTER_SENTINEL>
+  : public ContiguousEnumSerializer<
+             mozilla::GraphicsFilterType,
+             GraphicsFilter::FILTER_FAST,
+             GraphicsFilter::FILTER_SENTINEL>
 {};
 
 template <>
 struct ParamTraits<mozilla::layers::LayersBackend>
-  : public TypedEnumSerializer<mozilla::layers::LayersBackend,
-                               mozilla::layers::LayersBackend::LAYERS_NONE,
-                               mozilla::layers::LayersBackend::LAYERS_LAST>
+  : public ContiguousTypedEnumSerializer<
+             mozilla::layers::LayersBackend,
+             mozilla::layers::LayersBackend::LAYERS_NONE,
+             mozilla::layers::LayersBackend::LAYERS_LAST>
 {};
 
 template <>
 struct ParamTraits<mozilla::layers::ScaleMode>
-  : public TypedEnumSerializer<mozilla::layers::ScaleMode,
-                               mozilla::layers::ScaleMode::SCALE_NONE,
-                               mozilla::layers::ScaleMode::SENTINEL>
+  : public ContiguousTypedEnumSerializer<
+             mozilla::layers::ScaleMode,
+             mozilla::layers::ScaleMode::SCALE_NONE,
+             mozilla::layers::ScaleMode::SENTINEL>
 {};
 
 template <>
 struct ParamTraits<gfxImageFormat>
-  : public TypedEnumSerializer<gfxImageFormat,
-                               gfxImageFormat::ARGB32,
-                               gfxImageFormat::Unknown>
+  : public ContiguousTypedEnumSerializer<
+             gfxImageFormat,
+             gfxImageFormat::ARGB32,
+             gfxImageFormat::Unknown>
 {};
 
 template <>
 struct ParamTraits<mozilla::gfx::AttributeName>
-  : public EnumSerializer<mozilla::gfx::AttributeName,
-                          mozilla::gfx::eBlendBlendmode,
-                          mozilla::gfx::eLastAttributeName>
+  : public ContiguousEnumSerializer<
+             mozilla::gfx::AttributeName,
+             mozilla::gfx::eBlendBlendmode,
+             mozilla::gfx::eLastAttributeName>
 {};
 
 template <>
 struct ParamTraits<mozilla::gfx::AttributeType>
-  : public TypedEnumSerializer<mozilla::gfx::AttributeType,
-                               mozilla::gfx::AttributeType::eBool,
-                               mozilla::gfx::AttributeType::Max>
+  : public ContiguousTypedEnumSerializer<
+             mozilla::gfx::AttributeType,
+             mozilla::gfx::AttributeType::eBool,
+             mozilla::gfx::AttributeType::Max>
 {};
 
 template <>
 struct ParamTraits<mozilla::gfx::PrimitiveType>
-  : public TypedEnumSerializer<mozilla::gfx::PrimitiveType,
-                               mozilla::gfx::PrimitiveType::Empty,
-                               mozilla::gfx::PrimitiveType::Max>
+  : public ContiguousTypedEnumSerializer<
+             mozilla::gfx::PrimitiveType,
+             mozilla::gfx::PrimitiveType::Empty,
+             mozilla::gfx::PrimitiveType::Max>
 {};
 
 template <>
 struct ParamTraits<mozilla::gfx::ColorSpace>
-  : public TypedEnumSerializer<mozilla::gfx::ColorSpace,
-                               mozilla::gfx::ColorSpace::SRGB,
-                               mozilla::gfx::ColorSpace::Max>
+  : public ContiguousTypedEnumSerializer<
+             mozilla::gfx::ColorSpace,
+             mozilla::gfx::ColorSpace::SRGB,
+             mozilla::gfx::ColorSpace::Max>
 {};
 
 /*
 template <>
 struct ParamTraits<mozilla::PixelFormat>
   : public EnumSerializer<mozilla::PixelFormat,
                           gfxImageFormat::ARGB32,
                           gfxImageFormat::Unknown>
@@ -793,26 +803,28 @@ struct ParamTraits<mozilla::layers::Text
     return ReadParam(aMsg, aIter, &aResult->mCompositableType) &&
            ReadParam(aMsg, aIter, &aResult->mDeprecatedTextureHostFlags) &&
            ReadParam(aMsg, aIter, &aResult->mTextureFlags);
   }
 };
 
 template <>
 struct ParamTraits<mozilla::layers::CompositableType>
-  : public EnumSerializer<mozilla::layers::CompositableType,
-                          mozilla::layers::BUFFER_UNKNOWN,
-                          mozilla::layers::BUFFER_COUNT>
+  : public ContiguousEnumSerializer<
+             mozilla::layers::CompositableType,
+             mozilla::layers::BUFFER_UNKNOWN,
+             mozilla::layers::BUFFER_COUNT>
 {};
 
 template <>
 struct ParamTraits<mozilla::gfx::SurfaceFormat>
-  : public TypedEnumSerializer<mozilla::gfx::SurfaceFormat,
-                               mozilla::gfx::SurfaceFormat::B8G8R8A8,
-                               mozilla::gfx::SurfaceFormat::UNKNOWN>
+  : public ContiguousTypedEnumSerializer<
+             mozilla::gfx::SurfaceFormat,
+             mozilla::gfx::SurfaceFormat::B8G8R8A8,
+             mozilla::gfx::SurfaceFormat::UNKNOWN>
 {};
 
 template <>
 struct ParamTraits<mozilla::layers::ScrollableLayerGuid>
 {
   typedef mozilla::layers::ScrollableLayerGuid paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
--- a/gfx/layers/ipc/ShadowLayerUtils.h
+++ b/gfx/layers/ipc/ShadowLayerUtils.h
@@ -81,16 +81,17 @@ struct ParamTraits<mozilla::layers::Magi
   typedef mozilla::layers::MagicGrallocBufferHandle paramType;
   static void Write(Message*, const paramType&) {}
   static bool Read(const Message*, void**, paramType*) { return false; }
 };
 #endif  // !defined(MOZ_HAVE_XSURFACEDESCRIPTORGRALLOC)
 
 template <>
 struct ParamTraits<mozilla::ScreenRotation>
-  : public EnumSerializer<mozilla::ScreenRotation,
-                          mozilla::ROTATION_0,
-                          mozilla::ROTATION_COUNT>
+  : public ContiguousEnumSerializer<
+             mozilla::ScreenRotation,
+             mozilla::ROTATION_0,
+             mozilla::ROTATION_COUNT>
 {};
 
 } // namespace IPC
 
 #endif // IPC_ShadowLayerUtils_h
--- a/hal/HalSensor.h
+++ b/hal/HalSensor.h
@@ -53,23 +53,25 @@ typedef Observer<SensorAccuracy> ISensor
 #include "ipc/IPCMessageUtils.h"
 
 namespace IPC {
   /**
    * Serializer for SensorType
    */
   template <>
   struct ParamTraits<mozilla::hal::SensorType>:
-    public EnumSerializer<mozilla::hal::SensorType,
-                          mozilla::hal::SENSOR_UNKNOWN,
-                          mozilla::hal::NUM_SENSOR_TYPE> {
+    public ContiguousEnumSerializer<
+             mozilla::hal::SensorType,
+             mozilla::hal::SENSOR_UNKNOWN,
+             mozilla::hal::NUM_SENSOR_TYPE> {
   };
 
   template <>
   struct ParamTraits<mozilla::hal::SensorAccuracyType>:
-    public EnumSerializer<mozilla::hal::SensorAccuracyType,
-                          mozilla::hal::SENSOR_ACCURACY_UNKNOWN,
-                          mozilla::hal::NUM_SENSOR_ACCURACY_TYPE> {
+    public ContiguousEnumSerializer<
+             mozilla::hal::SensorAccuracyType,
+             mozilla::hal::SENSOR_ACCURACY_UNKNOWN,
+             mozilla::hal::NUM_SENSOR_ACCURACY_TYPE> {
 
   };
 } // namespace IPC
 
 #endif /* __HAL_SENSOR_H_ */
--- a/hal/HalTypes.h
+++ b/hal/HalTypes.h
@@ -207,122 +207,135 @@ typedef Observer<FMRadioOperationInforma
 
 namespace IPC {
 
 /**
  * Light type serializer.
  */
 template <>
 struct ParamTraits<mozilla::hal::LightType>
-  : public EnumSerializer<mozilla::hal::LightType,
-                          mozilla::hal::eHalLightID_Backlight,
-                          mozilla::hal::eHalLightID_Count>
+  : public ContiguousEnumSerializer<
+             mozilla::hal::LightType,
+             mozilla::hal::eHalLightID_Backlight,
+             mozilla::hal::eHalLightID_Count>
 {};
 
 /**
  * Light mode serializer.
  */
 template <>
 struct ParamTraits<mozilla::hal::LightMode>
-  : public EnumSerializer<mozilla::hal::LightMode,
-                          mozilla::hal::eHalLightMode_User,
-                          mozilla::hal::eHalLightMode_Count>
+  : public ContiguousEnumSerializer<
+             mozilla::hal::LightMode,
+             mozilla::hal::eHalLightMode_User,
+             mozilla::hal::eHalLightMode_Count>
 {};
 
 /**
  * Flash mode serializer.
  */
 template <>
 struct ParamTraits<mozilla::hal::FlashMode>
-  : public EnumSerializer<mozilla::hal::FlashMode,
-                          mozilla::hal::eHalLightFlash_None,
-                          mozilla::hal::eHalLightFlash_Count>
+  : public ContiguousEnumSerializer<
+             mozilla::hal::FlashMode,
+             mozilla::hal::eHalLightFlash_None,
+             mozilla::hal::eHalLightFlash_Count>
 {};
 
 /**
  * Serializer for ShutdownMode.
  */
 template <>
 struct ParamTraits<mozilla::hal::ShutdownMode>
-  : public EnumSerializer<mozilla::hal::ShutdownMode,
-                          mozilla::hal::eHalShutdownMode_Unknown,
-                          mozilla::hal::eHalShutdownMode_Count>
+  : public ContiguousEnumSerializer<
+             mozilla::hal::ShutdownMode,
+             mozilla::hal::eHalShutdownMode_Unknown,
+             mozilla::hal::eHalShutdownMode_Count>
 {};
 
 /**
  * WakeLockControl serializer.
  */
 template <>
 struct ParamTraits<mozilla::hal::WakeLockControl>
-  : public EnumSerializer<mozilla::hal::WakeLockControl,
-                          mozilla::hal::WAKE_LOCK_REMOVE_ONE,
-                          mozilla::hal::NUM_WAKE_LOCK>
+  : public ContiguousEnumSerializer<
+             mozilla::hal::WakeLockControl,
+             mozilla::hal::WAKE_LOCK_REMOVE_ONE,
+             mozilla::hal::NUM_WAKE_LOCK>
 {};
 
 /**
  * Serializer for SwitchState
  */
 template <>
 struct ParamTraits<mozilla::hal::SwitchState>:
-  public EnumSerializer<mozilla::hal::SwitchState,
-                        mozilla::hal::SWITCH_STATE_UNKNOWN,
-                        mozilla::hal::NUM_SWITCH_STATE> {
+  public ContiguousEnumSerializer<
+           mozilla::hal::SwitchState,
+           mozilla::hal::SWITCH_STATE_UNKNOWN,
+           mozilla::hal::NUM_SWITCH_STATE> {
 };
 
 /**
  * Serializer for SwitchDevice
  */
 template <>
 struct ParamTraits<mozilla::hal::SwitchDevice>:
-  public EnumSerializer<mozilla::hal::SwitchDevice,
-                        mozilla::hal::SWITCH_DEVICE_UNKNOWN,
-                        mozilla::hal::NUM_SWITCH_DEVICE> {
+  public ContiguousEnumSerializer<
+           mozilla::hal::SwitchDevice,
+           mozilla::hal::SWITCH_DEVICE_UNKNOWN,
+           mozilla::hal::NUM_SWITCH_DEVICE> {
 };
 
 template <>
 struct ParamTraits<mozilla::hal::ProcessPriority>:
-  public EnumSerializer<mozilla::hal::ProcessPriority,
-                        mozilla::hal::PROCESS_PRIORITY_UNKNOWN,
-                        mozilla::hal::NUM_PROCESS_PRIORITY> {
+  public ContiguousEnumSerializer<
+           mozilla::hal::ProcessPriority,
+           mozilla::hal::PROCESS_PRIORITY_UNKNOWN,
+           mozilla::hal::NUM_PROCESS_PRIORITY> {
 };
 
 /**
  * Serializer for FMRadioOperation
  */
 template <>
 struct ParamTraits<mozilla::hal::FMRadioOperation>:
-  public EnumSerializer<mozilla::hal::FMRadioOperation,
-                        mozilla::hal::FM_RADIO_OPERATION_UNKNOWN,
-                        mozilla::hal::NUM_FM_RADIO_OPERATION>
+  public ContiguousEnumSerializer<
+           mozilla::hal::FMRadioOperation,
+           mozilla::hal::FM_RADIO_OPERATION_UNKNOWN,
+           mozilla::hal::NUM_FM_RADIO_OPERATION>
 {};
 
 /**
  * Serializer for FMRadioOperationStatus
  */
 template <>
 struct ParamTraits<mozilla::hal::FMRadioOperationStatus>:
-  public EnumSerializer<mozilla::hal::FMRadioOperationStatus,
-                        mozilla::hal::FM_RADIO_OPERATION_STATUS_UNKNOWN,
-                        mozilla::hal::NUM_FM_RADIO_OPERATION_STATUS>
+  public ContiguousEnumSerializer<
+           mozilla::hal::FMRadioOperationStatus,
+           mozilla::hal::FM_RADIO_OPERATION_STATUS_UNKNOWN,
+           mozilla::hal::NUM_FM_RADIO_OPERATION_STATUS>
 {};
 
 /**
  * Serializer for FMRadioSeekDirection
  */
 template <>
 struct ParamTraits<mozilla::hal::FMRadioSeekDirection>:
-  public EnumSerializer<mozilla::hal::FMRadioSeekDirection,
-                        mozilla::hal::FM_RADIO_SEEK_DIRECTION_UNKNOWN,
-                        mozilla::hal::NUM_FM_RADIO_SEEK_DIRECTION>
+  public ContiguousEnumSerializer<
+           mozilla::hal::FMRadioSeekDirection,
+           mozilla::hal::FM_RADIO_SEEK_DIRECTION_UNKNOWN,
+           mozilla::hal::NUM_FM_RADIO_SEEK_DIRECTION>
 {};
 
 /**
  * Serializer for FMRadioCountry
  **/
 template <>
 struct ParamTraits<mozilla::hal::FMRadioCountry>:
-  public EnumSerializer<mozilla::hal::FMRadioCountry,
-                        mozilla::hal::FM_RADIO_COUNTRY_UNKNOWN,
-                        mozilla::hal::NUM_FM_RADIO_COUNTRY>
+  public ContiguousEnumSerializer<
+           mozilla::hal::FMRadioCountry,
+           mozilla::hal::FM_RADIO_COUNTRY_UNKNOWN,
+           mozilla::hal::NUM_FM_RADIO_COUNTRY>
 {};
+
 } // namespace IPC
 
 #endif // mozilla_hal_Types_h
--- a/ipc/glue/IPCMessageUtils.h
+++ b/ipc/glue/IPCMessageUtils.h
@@ -11,16 +11,17 @@
 #include "chrome/common/ipc_message_utils.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/TimeStamp.h"
 #ifdef XP_WIN
 #include "mozilla/TimeStamp_windows.h"
 #endif
 #include "mozilla/TypedEnum.h"
+#include "mozilla/IntegerTypeTraits.h"
 
 #include <stdint.h>
 
 #include "nsID.h"
 #include "nsMemory.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "js/StructuredClone.h"
@@ -86,102 +87,121 @@ struct SerializedStructuredCloneBuffer
 
 } // namespace mozilla
 
 namespace IPC {
 
 /**
  * Generic enum serializer.
  *
+ * Consider using the specializations below, such as ContiguousEnumSerializer.
+ *
  * This is a generic serializer for any enum type used in IPDL.
  * Programmers can define ParamTraits<E> for enum type E by deriving
- * EnumSerializer<E, smallestLegal, highGuard>.
+ * EnumSerializer<E, MyEnumValidator> where MyEnumValidator is a struct
+ * that has to define a static IsLegalValue function returning whether
+ * a given value is a legal value of the enum type at hand.
  *
- * The serializer would check value againts a range specified by
- * smallestLegal and highGuard.  Only values from smallestLegal to
- * highGuard are valid, include smallestLegal but highGuard.
+ * \sa https://developer.mozilla.org/en/IPDL/Type_Serialization
+ */
+template <typename E, typename EnumValidator>
+struct EnumSerializer {
+  typedef E paramType;
+  typedef typename mozilla::UnsignedStdintTypeForSize<sizeof(paramType)>::Type
+          uintParamType;
+
+  static void Write(Message* aMsg, const paramType& aValue) {
+    MOZ_ASSERT(EnumValidator::IsLegalValue(aValue));
+    WriteParam(aMsg, uintParamType(aValue));
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult) {
+    uintParamType value;
+    if(!ReadParam(aMsg, aIter, &value) ||
+       !EnumValidator::IsLegalValue(paramType(value))) {
+      return false;
+    }
+    *aResult = paramType(value);
+    return true;
+  }
+};
+
+template <typename E,
+          E MinLegal,
+          E HighBound>
+struct ContiguousEnumValidator
+{
+  static bool IsLegalValue(E e)
+  {
+    return MinLegal <= e && e < HighBound;
+  }
+};
+
+template <typename E,
+          MOZ_TEMPLATE_ENUM_CLASS_ENUM_TYPE(E) MinLegal,
+          MOZ_TEMPLATE_ENUM_CLASS_ENUM_TYPE(E) HighBound>
+class ContiguousTypedEnumValidator
+{
+  // Silence overzealous -Wtype-limits bug in GCC fixed in GCC 4.8:
+  // "comparison of unsigned expression >= 0 is always true"
+  // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11856
+  template <typename T>
+  static bool IsLessThanOrEqual(T a, T b) { return a <= b; }
+
+public:
+  static bool IsLegalValue(E e)
+  {
+    typedef MOZ_TEMPLATE_ENUM_CLASS_ENUM_TYPE(E) ActualEnumType;
+    return IsLessThanOrEqual(MinLegal, ActualEnumType(e)) &&
+           ActualEnumType(e) < HighBound;
+  }
+};
+
+/**
+ * Specialization of EnumSerializer for enums with contiguous enum values.
+ *
+ * Provide two values: MinLegal, HighBound. An enum value x will be
+ * considered legal if MinLegal <= x < HighBound.
  *
  * For example, following is definition of serializer for enum type FOO.
  * \code
  * enum FOO { FOO_FIRST, FOO_SECOND, FOO_LAST, NUM_FOO };
  *
  * template <>
  * struct ParamTraits<FOO>:
- *     public EnumSerializer<FOO, FOO_FIRST, NUM_FOO> {};
+ *     public ContiguousEnumSerializer<FOO, FOO_FIRST, NUM_FOO> {};
  * \endcode
  * FOO_FIRST, FOO_SECOND, and FOO_LAST are valid value.
- *
- * \sa https://developer.mozilla.org/en/IPDL/Type_Serialization
  */
-template <typename E, E smallestLegal, E highBound>
-struct EnumSerializer {
-  typedef E paramType;
-
-  static bool IsLegalValue(const paramType &aValue) {
-    return smallestLegal <= aValue && aValue < highBound;
-  }
-
-  static void Write(Message* aMsg, const paramType& aValue) {
-    MOZ_ASSERT(IsLegalValue(aValue));
-    WriteParam(aMsg, (int32_t)aValue);
-  }
-
-  static bool Read(const Message* aMsg, void** aIter, paramType* aResult) {
-    int32_t value;
-    if(!ReadParam(aMsg, aIter, &value) ||
-       !IsLegalValue(paramType(value))) {
-      return false;
-    }
-    *aResult = paramType(value);
-    return true;
-  }
-};
+template <typename E,
+          E MinLegal,
+          E HighBound>
+struct ContiguousEnumSerializer
+  : EnumSerializer<E,
+                   ContiguousEnumValidator<E, MinLegal, HighBound>>
+{};
 
 /**
- * Variant of EnumSerializer for MFBT's typed enums
- * defined by MOZ_BEGIN_ENUM_CLASS in mfbt/TypedEnum.h
- *
- * This is only needed on non-C++11 compilers such as B2G's GCC 4.4,
- * where MOZ_BEGIN_ENUM_CLASS is implemented using a nested enum, T::Enum,
- * in a wrapper class T. In this case, the "typed enum" type T cannot be
- * used as an integer template parameter type. MOZ_TEMPLATE_ENUM_CLASS_ENUM_TYPE(T)
- * is how we get at the integer enum type.
+ * Similar to ContiguousEnumSerializer, but for MFBT typed enums
+ * as constructed by MOZ_BEGIN_ENUM_CLASS. This can go away when
+ * we drop MOZ_BEGIN_ENUM_CLASS and use C++11 enum classes directly.
  */
 template <typename E,
-          MOZ_TEMPLATE_ENUM_CLASS_ENUM_TYPE(E) smallestLegal,
-          MOZ_TEMPLATE_ENUM_CLASS_ENUM_TYPE(E) highBound>
-struct TypedEnumSerializer {
-  typedef E paramType;
-  typedef MOZ_TEMPLATE_ENUM_CLASS_ENUM_TYPE(E) intParamType;
-
-  static bool IsLegalValue(const paramType &aValue) {
-    return smallestLegal <= intParamType(aValue) && intParamType(aValue) < highBound;
-  }
-
-  static void Write(Message* aMsg, const paramType& aValue) {
-    MOZ_ASSERT(IsLegalValue(aValue));
-    WriteParam(aMsg, int32_t(intParamType(aValue)));
-  }
-
-  static bool Read(const Message* aMsg, void** aIter, paramType* aResult) {
-    int32_t value;
-    if(!ReadParam(aMsg, aIter, &value) ||
-       !IsLegalValue(intParamType(value))) {
-      return false;
-    }
-    *aResult = intParamType(value);
-    return true;
-  }
-};
+          MOZ_TEMPLATE_ENUM_CLASS_ENUM_TYPE(E) MinLegal,
+          MOZ_TEMPLATE_ENUM_CLASS_ENUM_TYPE(E) HighBound>
+struct ContiguousTypedEnumSerializer
+  : EnumSerializer<E,
+                   ContiguousTypedEnumValidator<E, MinLegal, HighBound>>
+{};
 
 template <>
 struct ParamTraits<base::ChildPrivileges>
-  : public EnumSerializer<base::ChildPrivileges,
-                          base::PRIVILEGES_DEFAULT,
-                          base::PRIVILEGES_LAST>
+  : public ContiguousEnumSerializer<base::ChildPrivileges,
+                                    base::PRIVILEGES_DEFAULT,
+                                    base::PRIVILEGES_LAST>
 { };
 
 template<>
 struct ParamTraits<int8_t>
 {
   typedef int8_t paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
@@ -472,19 +492,19 @@ struct ParamTraits<float>
   static void Log(const paramType& aParam, std::wstring* aLog)
   {
     aLog->append(StringPrintf(L"%g", aParam));
   }
 };
 
 template <>
 struct ParamTraits<nsCSSProperty>
-  : public EnumSerializer<nsCSSProperty,
-                          eCSSProperty_UNKNOWN,
-                          eCSSProperty_COUNT>
+  : public ContiguousEnumSerializer<nsCSSProperty,
+                                    eCSSProperty_UNKNOWN,
+                                    eCSSProperty_COUNT>
 {};
 
 template<>
 struct ParamTraits<mozilla::void_t>
 {
   typedef mozilla::void_t paramType;
   static void Write(Message* aMsg, const paramType& aParam) { }
   static bool
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug991457.js
@@ -0,0 +1,64 @@
+function test() {
+    this.init();
+    for (var i=0; i<10; i++) {
+        delete this.blocks[10][9];
+        this.collapse_blocks();
+    }
+    this.look_for_holes();
+}
+test.prototype.init = function() {
+    this.blocks = new Array(20);
+    for (var x=0; x<this.blocks.length; x++) {
+        this.blocks[x] = new Array(10);
+        for (var y=0; y<this.blocks[x].length; y++) {
+            this.blocks[x][y] = {};
+        }
+    }
+}
+test.prototype.move_block = function(x,y,x1,y1) {
+    this.blocks[x][y] = this.blocks[x1][y1];
+    if (this.blocks[x][y])
+        delete this.blocks[x1][y1];
+}
+test.prototype.collapse_blocks = function() {
+    var didSomething=0;
+    do {
+        didSomething=0;
+        for (var x=0; x<this.blocks.length; x++)
+            for (var y=1; y<this.blocks[x].length; y++) {
+                if (!this.blocks[x][y] && this.blocks[x][y-1]) {
+                    this.move_block(x,y,x,y-1);
+                    didSomething=1;
+                }
+            }
+    } while (didSomething);
+
+    do {
+        didSomething = 0;
+        for (var x=0; x<this.blocks.length-1; x++) {
+            if (!this.blocks[x][9] && this.blocks[x+1][9]) {
+                for (var y=0; y<this.blocks[x].length; y++)
+                    this.move_block(x,y,x+1,y);
+                didSomething = 1;
+            }
+	}
+    } while (didSomething);
+}
+test.prototype.look_for_holes = function() {
+    var was_empty = false;
+    var n_empty = 0;
+    for (var x=0; x<this.blocks.length; x++) {
+        var empty = true;
+        for (var y=0; y<this.blocks[x].length; y++) {
+            if (this.blocks[x][y]) {
+                empty = false;
+		n_empty++;
+	    }
+	}
+	if (was_empty)
+	    assertEq(empty, true);
+        was_empty = empty;
+    }
+    assertEq(n_empty, 190);
+}
+new test();
--- a/js/src/jit-test/tests/self-hosting/define-value-property.js
+++ b/js/src/jit-test/tests/self-hosting/define-value-property.js
@@ -1,5 +1,8 @@
 // These tests just mustn't trigger asserts.
+if (!this.hasOwnProperty('Intl'))
+    quit();
+
 Object.prototype.get = 5;
 new Intl.Collator().resolvedOptions();
 
 Intl.DateTimeFormat.supportedLocalesOf('en');
--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -598,26 +598,16 @@ class VarType
     AsmJSCoercion toCoercion() const {
         switch(which_) {
           case Int:     return AsmJS_ToInt32;
           case Double:  return AsmJS_ToNumber;
           case Float:   return AsmJS_FRound;
         }
         MOZ_ASSUME_UNREACHABLE("VarType can only be Int, Double or Float");
     }
-    static VarType FromMIRType(MIRType type) {
-        JS_ASSERT(type == MIRType_Int32 || type == MIRType_Double || type == MIRType_Float32);
-        switch(type) {
-          case MIRType_Int32:   return Int;
-          case MIRType_Float32: return Float;
-          case MIRType_Double:  return Double;
-          default:;
-        }
-        MOZ_ASSUME_UNREACHABLE("FromMIRType MIR type not handled");
-    }
     static VarType FromCheckedType(Type type) {
         JS_ASSERT(type.isInt() || type.isMaybeDouble() || type.isFloatish());
         if (type.isMaybeDouble())
             return Double;
         else if (type.isFloatish())
             return Float;
         else
             return Int;
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -1821,30 +1821,31 @@ bool
 CodeGenerator::visitPostWriteBarrierO(LPostWriteBarrierO *lir)
 {
 #ifdef JSGC_GENERATIONAL
     OutOfLineCallPostWriteBarrier *ool = new(alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
     if (!addOutOfLineCode(ool))
         return false;
 
     const Nursery &nursery = GetIonContext()->runtime->gcNursery();
+    Register temp = ToRegister(lir->temp());
 
     if (lir->object()->isConstant()) {
         JS_ASSERT(!nursery.isInside(&lir->object()->toConstant()->toObject()));
     } else {
-        Label tenured;
         Register objreg = ToRegister(lir->object());
-        masm.branchPtr(Assembler::Below, objreg, ImmWord(nursery.start()), &tenured);
-        masm.branchPtr(Assembler::Below, objreg, ImmWord(nursery.heapEnd()), ool->rejoin());
-        masm.bind(&tenured);
+        masm.movePtr(ImmWord(-ptrdiff_t(nursery.start())), temp);
+        masm.addPtr(objreg, temp);
+        masm.branchPtr(Assembler::Below, temp, Imm32(Nursery::NurserySize), ool->rejoin());
     }
 
     Register valuereg = ToRegister(lir->value());
-    masm.branchPtr(Assembler::Below, valuereg, ImmWord(nursery.start()), ool->rejoin());
-    masm.branchPtr(Assembler::Below, valuereg, ImmWord(nursery.heapEnd()), ool->entry());
+    masm.movePtr(ImmWord(-ptrdiff_t(nursery.start())), temp);
+    masm.addPtr(valuereg, temp);
+    masm.branchPtr(Assembler::Below, temp, Imm32(Nursery::NurserySize), ool->entry());
 
     masm.bind(ool->rejoin());
 #endif
     return true;
 }
 
 bool
 CodeGenerator::visitPostWriteBarrierV(LPostWriteBarrierV *lir)
@@ -1857,26 +1858,29 @@ CodeGenerator::visitPostWriteBarrierV(LP
     ValueOperand value = ToValue(lir, LPostWriteBarrierV::Input);
     masm.branchTestObject(Assembler::NotEqual, value, ool->rejoin());
 
     const Nursery &nursery = GetIonContext()->runtime->gcNursery();
 
     if (lir->object()->isConstant()) {
         JS_ASSERT(!nursery.isInside(&lir->object()->toConstant()->toObject()));
     } else {
-        Label tenured;
+        Register temp = ToRegister(lir->temp());
         Register objreg = ToRegister(lir->object());
-        masm.branchPtr(Assembler::Below, objreg, ImmWord(nursery.start()), &tenured);
-        masm.branchPtr(Assembler::Below, objreg, ImmWord(nursery.heapEnd()), ool->rejoin());
-        masm.bind(&tenured);
-    }
-
-    Register valuereg = masm.extractObject(value, ToTempUnboxRegister(lir->temp()));
-    masm.branchPtr(Assembler::Below, valuereg, ImmWord(nursery.start()), ool->rejoin());
-    masm.branchPtr(Assembler::Below, valuereg, ImmWord(nursery.heapEnd()), ool->entry());
+        masm.movePtr(ImmWord(-ptrdiff_t(nursery.start())), temp);
+        masm.addPtr(objreg, temp);
+        masm.branchPtr(Assembler::Below, temp, Imm32(Nursery::NurserySize), ool->rejoin());
+    }
+
+    // This section is a little different because we mustn't trash the temp
+    // register before we use its contents.
+    Register temp = ToRegister(lir->temp());
+    masm.unboxObject(value, temp);
+    masm.addPtr(ImmWord(-ptrdiff_t(nursery.start())), temp);
+    masm.branchPtr(Assembler::Below, temp, Imm32(Nursery::NurserySize), ool->entry());
 
     masm.bind(ool->rejoin());
 #endif
     return true;
 }
 
 bool
 CodeGenerator::visitCallNative(LCallNative *call)
@@ -6495,17 +6499,17 @@ CodeGenerator::link(JSContext *cx, types
         Assembler::patchDataWithValueCheck(CodeLocationLabel(code, patchableTraceLoggers_[i]),
                                            ImmPtr(logger),
                                            ImmPtr(nullptr));
     }
     uint32_t scriptId = TraceLogCreateTextId(logger, script);
     for (uint32_t i = 0; i < patchableTLScripts_.length(); i++) {
         patchableTLScripts_[i].fixup(&masm);
         Assembler::patchDataWithValueCheck(CodeLocationLabel(code, patchableTLScripts_[i]),
-                                           ImmPtr((void *)scriptId),
+                                           ImmPtr((void *) uintptr_t(scriptId)),
                                            ImmPtr((void *)0));
     }
 #endif
 
     switch (executionMode) {
       case SequentialExecution:
         // The correct state for prebarriers is unknown until the end of compilation,
         // since a GC can occur during code generation. All barriers are emitted
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -5492,35 +5492,40 @@ class LMonitorTypes : public LInstructio
         return mir_->toMonitorTypes();
     }
     const LDefinition *temp() {
         return getTemp(0);
     }
 };
 
 // Generational write barrier used when writing an object to another object.
-class LPostWriteBarrierO : public LInstructionHelper<0, 2, 0>
+class LPostWriteBarrierO : public LInstructionHelper<0, 2, 1>
 {
   public:
     LIR_HEADER(PostWriteBarrierO)
 
-    LPostWriteBarrierO(const LAllocation &obj, const LAllocation &value) {
+    LPostWriteBarrierO(const LAllocation &obj, const LAllocation &value,
+                       const LDefinition &temp) {
         setOperand(0, obj);
         setOperand(1, value);
+        setTemp(0, temp);
     }
 
     const MPostWriteBarrier *mir() const {
         return mir_->toPostWriteBarrier();
     }
     const LAllocation *object() {
         return getOperand(0);
     }
     const LAllocation *value() {
         return getOperand(1);
     }
+    const LDefinition *temp() {
+        return getTemp(0);
+    }
 };
 
 // Generational write barrier used when writing a value to another object.
 class LPostWriteBarrierV : public LInstructionHelper<0, 1 + BOX_PIECES, 1>
 {
   public:
     LIR_HEADER(PostWriteBarrierV)
 
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2326,23 +2326,25 @@ LIRGenerator::visitMonitorTypes(MMonitor
 }
 
 bool
 LIRGenerator::visitPostWriteBarrier(MPostWriteBarrier *ins)
 {
 #ifdef JSGC_GENERATIONAL
     switch (ins->value()->type()) {
       case MIRType_Object: {
-        LPostWriteBarrierO *lir = new(alloc()) LPostWriteBarrierO(useRegisterOrConstant(ins->object()),
-                                                         useRegister(ins->value()));
+        LPostWriteBarrierO *lir =
+            new(alloc()) LPostWriteBarrierO(useRegisterOrConstant(ins->object()),
+                                            useRegister(ins->value()),
+                                            temp());
         return add(lir, ins) && assignSafepoint(lir, ins);
       }
       case MIRType_Value: {
         LPostWriteBarrierV *lir =
-            new(alloc()) LPostWriteBarrierV(useRegisterOrConstant(ins->object()), tempToUnbox());
+            new(alloc()) LPostWriteBarrierV(useRegisterOrConstant(ins->object()), temp());
         if (!useBox(lir, LPostWriteBarrierV::Input, ins->value()))
             return false;
         return add(lir, ins) && assignSafepoint(lir, ins);
       }
       default:
         // Currently, only objects can be in the nursery. Other instruction
         // types cannot hold nursery pointers.
         return true;
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -5841,16 +5841,22 @@ class MLoadElement
     bool needsHoleCheck_;
     bool loadDoubles_;
 
     MLoadElement(MDefinition *elements, MDefinition *index, bool needsHoleCheck, bool loadDoubles)
       : MBinaryInstruction(elements, index),
         needsHoleCheck_(needsHoleCheck),
         loadDoubles_(loadDoubles)
     {
+        if (needsHoleCheck) {
+            // Uses may be optimized away based on this instruction's result
+            // type. This means it's invalid to DCE this instruction, as we
+            // have to invalidate when we read a hole.
+            setGuard();
+        }
         setResultType(MIRType_Value);
         setMovable();
         JS_ASSERT(elements->type() == MIRType_Elements);
         JS_ASSERT(index->type() == MIRType_Int32);
     }
 
   public:
     INSTRUCTION_HEADER(LoadElement)
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -2515,16 +2515,22 @@ MacroAssemblerARMCompat::cmpPtr(const Re
 
 void
 MacroAssemblerARMCompat::cmpPtr(const Register &lhs, const ImmGCPtr &rhs)
 {
     ma_cmp(lhs, rhs);
 }
 
 void
+MacroAssemblerARMCompat::cmpPtr(const Register &lhs, const Imm32 &rhs)
+{
+    ma_cmp(lhs, rhs);
+}
+
+void
 MacroAssemblerARMCompat::cmpPtr(const Address &lhs, const Register &rhs)
 {
     loadPtr(lhs, ScratchRegister);
     cmpPtr(ScratchRegister, rhs);
 }
 
 void
 MacroAssemblerARMCompat::cmpPtr(const Address &lhs, const ImmWord &rhs)
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -984,16 +984,19 @@ class MacroAssemblerARMCompat : public M
     }
     void branchPtr(Condition cond, Register lhs, ImmPtr imm, Label *label) {
         branchPtr(cond, lhs, ImmWord(uintptr_t(imm.value)), label);
     }
     void branchPtr(Condition cond, Register lhs, AsmJSImmPtr imm, Label *label) {
         movePtr(imm, ScratchRegister);
         branchPtr(cond, lhs, ScratchRegister, label);
     }
+    void branchPtr(Condition cond, Register lhs, Imm32 imm, Label *label) {
+        branch32(cond, lhs, imm, label);
+    }
     void decBranchPtr(Condition cond, const Register &lhs, Imm32 imm, Label *label) {
         subPtr(imm, lhs);
         branch32(cond, lhs, Imm32(0), label);
     }
     void moveValue(const Value &val, Register type, Register data);
 
     CodeOffsetJump jumpWithPatch(RepatchLabel *label, Condition cond = Always);
     template <typename T>
@@ -1369,16 +1372,17 @@ class MacroAssemblerARMCompat : public M
     void cmp32(const Register &lhs, const Register &rhs);
     void cmp32(const Operand &lhs, const Imm32 &rhs);
     void cmp32(const Operand &lhs, const Register &rhs);
 
     void cmpPtr(const Register &lhs, const ImmWord &rhs);
     void cmpPtr(const Register &lhs, const ImmPtr &rhs);
     void cmpPtr(const Register &lhs, const Register &rhs);
     void cmpPtr(const Register &lhs, const ImmGCPtr &rhs);
+    void cmpPtr(const Register &lhs, const Imm32 &rhs);
     void cmpPtr(const Address &lhs, const Register &rhs);
     void cmpPtr(const Address &lhs, const ImmWord &rhs);
     void cmpPtr(const Address &lhs, const ImmPtr &rhs);
 
     void subPtr(Imm32 imm, const Register dest);
     void subPtr(const Address &addr, const Register dest);
     void subPtr(const Register &src, const Register &dest);
     void subPtr(const Register &src, const Address &dest);
--- a/js/src/jit/mips/MacroAssembler-mips.h
+++ b/js/src/jit/mips/MacroAssembler-mips.h
@@ -668,16 +668,19 @@ class MacroAssemblerMIPSCompat : public 
     }
     void branchPtr(Condition cond, Register lhs, ImmPtr imm, Label *label) {
         branchPtr(cond, lhs, ImmWord(uintptr_t(imm.value)), label);
     }
     void branchPtr(Condition cond, Register lhs, AsmJSImmPtr imm, Label *label) {
         movePtr(imm, ScratchRegister);
         branchPtr(cond, lhs, ScratchRegister, label);
     }
+    void branchPtr(Condition cond, Register lhs, Imm32 imm, Label *label) {
+        ma_b(lhs, imm, label, cond);
+    }
     void decBranchPtr(Condition cond, const Register &lhs, Imm32 imm, Label *label) {
         subPtr(imm, lhs);
         branch32(cond, lhs, Imm32(0), label);
     }
 
 protected:
     uint32_t getType(const Value &val);
     void moveData(const Value &val, Register data);
--- a/js/src/jit/x64/MacroAssembler-x64.h
+++ b/js/src/jit/x64/MacroAssembler-x64.h
@@ -452,16 +452,19 @@ class MacroAssemblerX64 : public MacroAs
     void cmpPtr(const Register &lhs, const ImmPtr rhs) {
         cmpPtr(lhs, ImmWord(uintptr_t(rhs.value)));
     }
     void cmpPtr(const Register &lhs, const ImmGCPtr rhs) {
         JS_ASSERT(lhs != ScratchReg);
         movq(rhs, ScratchReg);
         cmpq(lhs, ScratchReg);
     }
+    void cmpPtr(const Register &lhs, const Imm32 rhs) {
+        cmpq(lhs, rhs);
+    }
     void cmpPtr(const Operand &lhs, const ImmGCPtr rhs) {
         movq(rhs, ScratchReg);
         cmpq(lhs, ScratchReg);
     }
     void cmpPtr(const Operand &lhs, const ImmWord rhs) {
         if ((intptr_t)rhs.value <= INT32_MAX && (intptr_t)rhs.value >= INT32_MIN) {
             cmpq(lhs, Imm32((int32_t)rhs.value));
         } else {
--- a/js/src/jit/x86/MacroAssembler-x86.h
+++ b/js/src/jit/x86/MacroAssembler-x86.h
@@ -486,16 +486,19 @@ class MacroAssemblerX86 : public MacroAs
         cmpl(lhs, Imm32(rhs.value));
     }
     void cmpPtr(Register lhs, const ImmPtr imm) {
         cmpPtr(lhs, ImmWord(uintptr_t(imm.value)));
     }
     void cmpPtr(Register lhs, const ImmGCPtr rhs) {
         cmpl(lhs, rhs);
     }
+    void cmpPtr(Register lhs, const Imm32 rhs) {
+        cmpl(lhs, rhs);
+    }
     void cmpPtr(const Operand &lhs, const ImmWord rhs) {
         cmpl(lhs, rhs);
     }
     void cmpPtr(const Operand &lhs, const ImmPtr imm) {
         cmpPtr(lhs, ImmWord(uintptr_t(imm.value)));
     }
     void cmpPtr(const Operand &lhs, const ImmGCPtr rhs) {
         cmpl(lhs, rhs);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1918,22 +1918,25 @@ JS_SetGCParameter(JSRuntime *rt, JSGCPar
       case JSGC_HIGH_FREQUENCY_LOW_LIMIT:
         rt->gcHighFrequencyLowLimitBytes = value * 1024 * 1024;
         break;
       case JSGC_HIGH_FREQUENCY_HIGH_LIMIT:
         rt->gcHighFrequencyHighLimitBytes = value * 1024 * 1024;
         break;
       case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX:
         rt->gcHighFrequencyHeapGrowthMax = value / 100.0;
+        MOZ_ASSERT(rt->gcHighFrequencyHeapGrowthMax / 0.85 > 1.0);
         break;
       case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN:
         rt->gcHighFrequencyHeapGrowthMin = value / 100.0;
+        MOZ_ASSERT(rt->gcHighFrequencyHeapGrowthMin / 0.85 > 1.0);
         break;
       case JSGC_LOW_FREQUENCY_HEAP_GROWTH:
         rt->gcLowFrequencyHeapGrowth = value / 100.0;
+        MOZ_ASSERT(rt->gcLowFrequencyHeapGrowth / 0.9 > 1.0);
         break;
       case JSGC_DYNAMIC_HEAP_GROWTH:
         rt->gcDynamicHeapGrowth = value;
         break;
       case JSGC_DYNAMIC_MARK_SLICE:
         rt->gcDynamicMarkSlice = value;
         break;
       case JSGC_ALLOCATION_THRESHOLD:
@@ -2036,20 +2039,18 @@ JS_SetGCParametersBasedOnAvailableMemory
     };
 
     const JSGCConfig *config = minimal;
     if (availMem > 512) {
         static const JSGCConfig nominal[NumGCConfigs] = {
             {JSGC_MAX_MALLOC_BYTES, 6 * 1024 * 1024},
             {JSGC_SLICE_TIME_BUDGET, 30},
             {JSGC_HIGH_FREQUENCY_TIME_LIMIT, 1000},
-            // This are the current default settings but this is likely inverted as
-            // explained for the computation of Next_GC in Bug 863398 comment 21.
-            {JSGC_HIGH_FREQUENCY_HIGH_LIMIT, 100},
-            {JSGC_HIGH_FREQUENCY_LOW_LIMIT, 500},
+            {JSGC_HIGH_FREQUENCY_HIGH_LIMIT, 500},
+            {JSGC_HIGH_FREQUENCY_LOW_LIMIT, 100},
             {JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX, 300},
             {JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN, 150},
             {JSGC_LOW_FREQUENCY_HEAP_GROWTH, 150},
             {JSGC_HIGH_FREQUENCY_TIME_LIMIT, 1500},
             {JSGC_HIGH_FREQUENCY_TIME_LIMIT, 1500},
             {JSGC_HIGH_FREQUENCY_TIME_LIMIT, 1500},
             {JSGC_ALLOCATION_THRESHOLD, 30},
             {JSGC_DECOMMIT_THRESHOLD, 32},
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -420,17 +420,17 @@ js::intrinsic_UnsafePutElements(JSContex
         JS_ASSERT(args[arri].toObject().isNative() || IsTypedObjectArray(args[arri].toObject()));
         JS_ASSERT(args[idxi].isInt32());
 
         RootedObject arrobj(cx, &args[arri].toObject());
         uint32_t idx = args[idxi].toInt32();
 
         if (arrobj->is<TypedArrayObject>() || arrobj->is<TypedObject>()) {
             JS_ASSERT(!arrobj->is<TypedArrayObject>() || idx < arrobj->as<TypedArrayObject>().length());
-            JS_ASSERT(!arrobj->is<TypedObject>() || idx < arrobj->as<TypedObject>().length());
+            JS_ASSERT(!arrobj->is<TypedObject>() || idx < uint32_t(arrobj->as<TypedObject>().length()));
             RootedValue tmp(cx, args[elemi]);
             // XXX: Always non-strict.
             if (!JSObject::setElement(cx, arrobj, arrobj, idx, &tmp, false))
                 return false;
         } else {
             JS_ASSERT(idx < arrobj->getDenseInitializedLength());
             arrobj->setDenseElementWithType(cx, idx, args[elemi]);
         }
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -1392,16 +1392,29 @@ XPCJSRuntime::InterruptCallback(JSContex
     //
     // This has gone on long enough! Time to take action. ;-)
     //
 
     // Get the DOM window associated with the running script. If the script is
     // running in a non-DOM scope, we have to just let it keep running.
     RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
     nsRefPtr<nsGlobalWindow> win = WindowOrNull(global);
+    if (!win && IsSandbox(global)) {
+        // If this is a sandbox associated with a DOMWindow via a
+        // sandboxPrototype, use that DOMWindow. This supports GreaseMonkey
+        // and JetPack content scripts.
+        JS::Rooted<JSObject*> proto(cx);
+        if (!JS_GetPrototype(cx, global, &proto))
+            return false;
+        if (proto && IsSandboxPrototypeProxy(proto) &&
+            (proto = js::CheckedUnwrap(proto, /* stopAtOuter = */ false)))
+        {
+            win = WindowGlobalOrNull(proto);
+        }
+    }
     if (!win)
         return true;
 
     // Show the prompt to the user, and kill if requested.
     nsGlobalWindow::SlowScriptResponse response = win->ShowSlowScriptDialog();
     if (response == nsGlobalWindow::KillSlowScript)
         return false;
 
--- a/layout/base/crashtests/crashtests.list
+++ b/layout/base/crashtests/crashtests.list
@@ -39,17 +39,17 @@ load 263359-1.html
 load 265027-1.html
 load 265736-1.html
 load 265736-2.html
 load 265899-1.html
 load 265973-1.html
 asserts(6-12) load 265986-1.html # Bug 512405
 load 265999-1.html
 load 266222-1.html
-asserts(2-7) load 266360-1.html # bug 576358
+asserts(1-7) load 266360-1.html # bug 576358
 load 266445-1.html
 load 268157-1.html
 load 269566-1.html
 load 272647-1.html
 load 275746-1.html
 load 276053-1.html
 load 280708-1.html
 load 280708-2.html
--- a/layout/ipc/RenderFrameUtils.h
+++ b/layout/ipc/RenderFrameUtils.h
@@ -30,16 +30,17 @@ enum ScrollingBehavior {
 
 } // namespace layout
 } // namespace mozilla
 
 namespace IPC {
 
 template <>
 struct ParamTraits<mozilla::layout::ScrollingBehavior>
-  : public EnumSerializer<mozilla::layout::ScrollingBehavior,
-                          mozilla::layout::DEFAULT_SCROLLING,
-                          mozilla::layout::SCROLLING_BEHAVIOR_SENTINEL>
+  : public ContiguousEnumSerializer<
+             mozilla::layout::ScrollingBehavior,
+             mozilla::layout::DEFAULT_SCROLLING,
+             mozilla::layout::SCROLLING_BEHAVIOR_SENTINEL>
 {};
 
 } // namespace IPC
 
 #endif // mozilla_layer_RenderFrameUtils_h
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -407,20 +407,20 @@ pref("app.geo.reportdata", 0);
 
 // Disable the JS engine's gc on memory pressure, since we do one in the mobile
 // browser (bug 669346).
 pref("javascript.options.gc_on_memory_pressure", false);
 
 #ifdef MOZ_PKG_SPECIAL
 // low memory devices
 pref("javascript.options.mem.gc_high_frequency_heap_growth_max", 120);
-pref("javascript.options.mem.gc_high_frequency_heap_growth_min", 101);
+pref("javascript.options.mem.gc_high_frequency_heap_growth_min", 120);
 pref("javascript.options.mem.gc_high_frequency_high_limit_mb", 40);
 pref("javascript.options.mem.gc_high_frequency_low_limit_mb", 10);
-pref("javascript.options.mem.gc_low_frequency_heap_growth", 105);
+pref("javascript.options.mem.gc_low_frequency_heap_growth", 120);
 pref("javascript.options.mem.high_water_mark", 16);
 pref("javascript.options.mem.gc_allocation_threshold_mb", 3);
 pref("javascript.options.mem.gc_decommit_threshold_mb", 1);
 #else
 pref("javascript.options.mem.high_water_mark", 32);
 #endif
 
 pref("dom.max_chrome_script_run_time", 0); // disable slow script dialog for chrome
--- a/testing/config/marionette_requirements.txt
+++ b/testing/config/marionette_requirements.txt
@@ -1,2 +1,3 @@
 -r mozbase_requirements.txt
+../marionette/transport
 ../marionette
--- a/testing/marionette/client/marionette/b2g_update_test.py
+++ b/testing/marionette/client/marionette/b2g_update_test.py
@@ -7,41 +7,41 @@ import imp
 import os
 import re
 import subprocess
 import time
 import types
 import weakref
 
 from b2ginstance import B2GInstance
-from client import MarionetteClient
 from errors import InvalidResponseException
 from marionette import Marionette
 from marionette_test import MarionetteTestCase
+from marionette_transport import MarionetteTransport
 from runtests import MarionetteTestRunner, cli
 
-class B2GUpdateMarionetteClient(MarionetteClient):
+class B2GUpdateMarionetteClient(MarionetteTransport):
     RETRY_TIMEOUT   = 5
     CONNECT_TIMEOUT = 30
     SEND_TIMEOUT    = 60 * 5
     MAX_RETRIES     = 24
 
     def __init__(self, addr, port, runner):
-        MarionetteClient.__init__(self, addr, port)
+        super(B2GUpdateMarionetteClient, self).__init__(addr, port)
         self.runner = runner
 
     def connect(self):
         """ When using ADB port forwarding, the ADB server will actually accept
             connections even though there isn't a listening socket open on the
             device. Here we add a retry loop since we have to restart the debug
             server / b2g process for update tests
         """
         for i in range(self.MAX_RETRIES):
             try:
-                MarionetteClient.connect(self, timeout=self.CONNECT_TIMEOUT)
+                MarionetteTransport.connect(self, timeout=self.CONNECT_TIMEOUT)
                 break
             except:
                 if i == self.MAX_RETRIES - 1:
                     raise
 
                 time.sleep(self.RETRY_TIMEOUT)
                 self.runner.port_forward()
 
deleted file mode 100644
--- a/testing/marionette/client/marionette/client.py
+++ /dev/null
@@ -1,108 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-import errno
-import json
-import socket
-
-from errors import InvalidResponseException, ErrorCodes
-
-
-class MarionetteClient(object):
-    """ The Marionette socket client.  This speaks the same protocol
-        as the remote debugger inside Gecko, in which messages are
-        always preceded by the message length and a colon, e.g.,
-
-        20:{'command': 'test'}
-    """
-
-    max_packet_length = 4096
-
-    def __init__(self, addr, port):
-        self.addr = addr
-        self.port = port
-        self.sock = None
-        self.traits = None
-        self.applicationType = None
-        self.actor = 'root'
-
-    def _recv_n_bytes(self, n):
-        """ Convenience method for receiving exactly n bytes from
-            self.sock (assuming it's open and connected).
-        """
-        data = ''
-        while len(data) < n:
-            chunk = self.sock.recv(n - len(data))
-            if chunk == '':
-                break
-            data += chunk
-        return data
-
-    def receive(self):
-        """ Receive the next complete response from the server, and return
-            it as a dict.  Each response from the server is prepended by
-            len(message) + ':'.
-        """
-        assert(self.sock)
-        response = self.sock.recv(10)
-        sep = response.find(':')
-        length = response[0:sep]
-        if length != '':
-            response = response[sep + 1:]
-            response += self._recv_n_bytes(int(length) + 1 + len(length) - 10)
-            return json.loads(response)
-        else:
-            raise InvalidResponseException("Could not communicate with Marionette server. "
-                                           "Is the Gecko process still running?",
-                                           status=ErrorCodes.INVALID_RESPONSE)
-
-    def connect(self, timeout=360.0):
-        """ Connect to the server and process the hello message we expect
-            to receive in response.
-        """
-        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-        self.sock.settimeout(timeout)
-        try:
-            self.sock.connect((self.addr, self.port))
-        except:
-            # Unset self.sock so that the next attempt to send will cause
-            # another connection attempt.
-            self.sock = None
-            raise
-        hello = self.receive()
-        self.traits = hello.get('traits')
-        self.applicationType = hello.get('applicationType')
-
-        # get the marionette actor id
-        response = self.send({'to': 'root', 'name': 'getMarionetteID'})
-        self.actor = response['id']
-
-    def send(self, msg):
-        """ Send a message on the socket, prepending it with len(msg) + ':'.
-        """
-        if not self.sock:
-            self.connect()
-        if 'to' not in msg:
-            msg['to'] = self.actor
-        data = json.dumps(msg)
-        data = '%s:%s' % (len(data), data)
-
-        for packet in [data[i:i + self.max_packet_length] for i in
-                       range(0, len(data), self.max_packet_length)]:
-            try: 
-                self.sock.send(packet)
-            except IOError as e:
-                if e.errno == errno.EPIPE:
-                    raise IOError("%s: Connection to Marionette server is lost. Check gecko.log (desktop firefox) or logcat (b2g) for errors." % str(e))
-                else:
-                    raise e
-
-        response = self.receive()
-        return response
-
-    def close(self):
-        """ Close the socket.
-        """
-        self.sock.close()
-        self.sock = None
--- a/testing/marionette/client/marionette/emulator.py
+++ b/testing/marionette/client/marionette/emulator.py
@@ -288,17 +288,17 @@ waitFor(
             """)
         except ScriptTimeoutException:
             print 'timed out'
             # We silently ignore the timeout if it occurs, since
             # isSystemMessageListenerReady() isn't available on
             # older emulators.  45s *should* be enough of a delay
             # to allow telephony API's to work.
             pass
-        except InvalidResponseException:
+        except (InvalidResponseException, IOError):
             self.check_for_minidumps()
             raise
         print '...done'
 
     def connect(self):
         self.adb = B2GInstance.check_adb(self.homedir, emulator=True)
         self.start_adb()
 
--- a/testing/marionette/client/marionette/marionette.py
+++ b/testing/marionette/client/marionette/marionette.py
@@ -6,22 +6,22 @@ import ConfigParser
 import datetime
 import os
 import socket
 import sys
 import time
 import traceback
 
 from application_cache import ApplicationCache
-from client import MarionetteClient
 from decorators import do_crash_check
 from emulator import Emulator
 from emulator_screen import EmulatorScreen
 from errors import *
 from keys import Keys
+from marionette_transport import MarionetteTransport
 
 import geckoinstance
 
 class HTMLElement(object):
     """
     Represents a DOM Element.
     """
 
@@ -506,28 +506,28 @@ class Marionette(object):
 
         if connectToRunningEmulator:
             self.emulator = Emulator(homedir=homedir,
                                      logcat_dir=self.logcat_dir)
             self.emulator.connect()
             self.port = self.emulator.setup_port_forwarding(self.port)
             assert(self.emulator.wait_for_port()), "Timed out waiting for port!"
 
-        self.client = MarionetteClient(self.host, self.port)
+        self.client = MarionetteTransport(self.host, self.port)
 
         if emulator:
             self.emulator.setup(self,
                                 gecko_path=gecko_path,
                                 busybox=busybox)
 
     def cleanup(self):
         if self.session:
             try:
                 self.delete_session()
-            except (MarionetteException, socket.error):
+            except (MarionetteException, socket.error, IOError):
                 # These exceptions get thrown if the Marionette server
                 # hit an exception/died or the connection died. We can
                 # do no further server-side cleanup in this case.
                 pass
             self.session = None
         if self.emulator:
             self.emulator.close()
         if self.instance:
--- a/testing/marionette/client/requirements.txt
+++ b/testing/marionette/client/requirements.txt
@@ -1,8 +1,9 @@
+./transport
 manifestdestiny
 mozhttpd >= 0.5
 mozinfo >= 0.7
 mozprocess >= 0.9
 mozrunner >= 5.15
 mozdevice >= 0.22
 moznetwork >= 0.21
 mozcrash >= 0.5
--- a/testing/marionette/client/setup.py
+++ b/testing/marionette/client/setup.py
@@ -1,24 +1,42 @@
 import os
 from setuptools import setup, find_packages
+import sys
 
 version = '0.7.6'
 
 # get documentation from the README
 try:
     here = os.path.dirname(os.path.abspath(__file__))
     description = file(os.path.join(here, 'README.md')).read()
 except (OSError, IOError):
     description = ''
 
 # dependencies
 with open('requirements.txt') as f:
     deps = f.read().splitlines()
 
+# Requirements.txt contains a pointer to the local copy of marionette_transport;
+# if we're installing using setup.py, handle this locally or replace with a valid
+# pypi package reference.
+deps = [x for x in deps if 'transport' not in x]
+transport_dir = os.path.join(os.path.dirname(__file__), os.path.pardir, 'transport')
+method = [x for x in sys.argv if x in ('develop', 'install')]
+if os.path.exists(transport_dir) and method:
+    cmd = [sys.executable, 'setup.py', method[0]]
+    import subprocess
+    try:
+        subprocess.check_call(cmd, cwd=transport_dir)
+    except subprocess.CalledProcessError:
+        print "Error running setup.py in %s" % directory
+        raise
+else:
+    deps += ['marionette-transport == 0.1']
+
 setup(name='marionette_client',
       version=version,
       description="Marionette test automation client",
       long_description=description,
       classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
       keywords='mozilla',
       author='Jonathan Griffin',
       author_email='jgriffin@mozilla.com',
new file mode 100644
--- /dev/null
+++ b/testing/marionette/transport/README.md
@@ -0,0 +1,14 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+# Marionette Transport Layer
+
+[Marionette](https://developer.mozilla.org/en/Marionette) is a 
+Mozilla project to enable remote automation in Gecko-based projects,
+including desktop Firefox, mobile Firefox, and Firefox OS.   It's inspired
+by [Selenium Webdriver](http://www.seleniumhq.org/projects/webdriver/).
+
+This package defines the transport layer used by a Marionette client to
+communicate with the Marionette server embedded in Gecko.  It has no entry
+points; rather it's designed to be used by Marionette client implementations.
new file mode 100644
--- /dev/null
+++ b/testing/marionette/transport/marionette_transport/__init__.py
@@ -0,0 +1,1 @@
+from transport import MarionetteTransport
new file mode 100644
--- /dev/null
+++ b/testing/marionette/transport/marionette_transport/transport.py
@@ -0,0 +1,105 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import errno
+import json
+import socket
+
+
+class MarionetteTransport(object):
+    """ The Marionette socket client.  This speaks the same protocol
+        as the remote debugger inside Gecko, in which messages are
+        always preceded by the message length and a colon, e.g.,
+
+        20:{'command': 'test'}
+    """
+
+    max_packet_length = 4096
+    connection_lost_msg = "Connection to Marionette server is lost. Check gecko.log (desktop firefox) or logcat (b2g) for errors."
+
+    def __init__(self, addr, port):
+        self.addr = addr
+        self.port = port
+        self.sock = None
+        self.traits = None
+        self.applicationType = None
+        self.actor = 'root'
+
+    def _recv_n_bytes(self, n):
+        """ Convenience method for receiving exactly n bytes from
+            self.sock (assuming it's open and connected).
+        """
+        data = ''
+        while len(data) < n:
+            chunk = self.sock.recv(n - len(data))
+            if chunk == '':
+                break
+            data += chunk
+        return data
+
+    def receive(self):
+        """ Receive the next complete response from the server, and return
+            it as a dict.  Each response from the server is prepended by
+            len(message) + ':'.
+        """
+        assert(self.sock)
+        response = self.sock.recv(10)
+        sep = response.find(':')
+        length = response[0:sep]
+        if length != '':
+            response = response[sep + 1:]
+            response += self._recv_n_bytes(int(length) + 1 + len(length) - 10)
+            return json.loads(response)
+        else:
+            raise IOError(self.connection_lost_msg)
+
+    def connect(self, timeout=360.0):
+        """ Connect to the server and process the hello message we expect
+            to receive in response.
+        """
+        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self.sock.settimeout(timeout)
+        try:
+            self.sock.connect((self.addr, self.port))
+        except:
+            # Unset self.sock so that the next attempt to send will cause
+            # another connection attempt.
+            self.sock = None
+            raise
+        hello = self.receive()
+        self.traits = hello.get('traits')
+        self.applicationType = hello.get('applicationType')
+
+        # get the marionette actor id
+        response = self.send({'to': 'root', 'name': 'getMarionetteID'})
+        self.actor = response['id']
+
+    def send(self, msg):
+        """ Send a message on the socket, prepending it with len(msg) + ':'.
+        """
+        if not self.sock:
+            self.connect()
+        if 'to' not in msg:
+            msg['to'] = self.actor
+        data = json.dumps(msg)
+        data = '%s:%s' % (len(data), data)
+
+        for packet in [data[i:i + self.max_packet_length] for i in
+                       range(0, len(data), self.max_packet_length)]:
+            try: 
+                self.sock.send(packet)
+            except IOError as e:
+                if e.errno == errno.EPIPE:
+                    raise IOError("%s: %s" % (str(e)), self.connection_lost_msg)
+                else:
+                    raise e
+
+        response = self.receive()
+        return response
+
+    def close(self):
+        """ Close the socket.
+        """
+        self.sock.close()
+        self.sock = None
new file mode 100644
--- /dev/null
+++ b/testing/marionette/transport/setup.py
@@ -0,0 +1,34 @@
+import os
+from setuptools import setup, find_packages
+
+version = '0.1'
+
+# get documentation from the README
+try:
+    here = os.path.dirname(os.path.abspath(__file__))
+    description = file(os.path.join(here, 'README.md')).read()
+except (OSError, IOError):
+    description = ''
+
+# dependencies
+deps = []
+
+setup(name='marionette-transport',
+      version=version,
+      description="Transport layer for Marionette client",
+      long_description=description,
+      classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
+      keywords='mozilla',
+      author='Mozilla Automation and Tools Team',
+      author_email='tools@lists.mozilla.org',
+      url='https://developer.mozilla.org/en-US/docs/Marionette',
+      license='MPL',
+      packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
+      package_data={},
+      include_package_data=False,
+      zip_safe=False,
+      entry_points="""
+      """,
+      install_requires=deps,
+      )
+
--- a/testing/testsuite-targets.mk
+++ b/testing/testsuite-targets.mk
@@ -524,17 +524,19 @@ stage-steeplechase: make-stage-dir
 	$(NSINSTALL) -D $(PKG_STAGE)/steeplechase/
 	cp -RL $(DEPTH)/_tests/steeplechase $(PKG_STAGE)/steeplechase/tests
 	cp -RL $(DIST)/xpi-stage/specialpowers $(PKG_STAGE)/steeplechase
 	cp -RL $(topsrcdir)/testing/profiles/prefs_general.js $(PKG_STAGE)/steeplechase
 
 MARIONETTE_DIR=$(PKG_STAGE)/marionette
 stage-marionette: make-stage-dir
 	$(NSINSTALL) -D $(MARIONETTE_DIR)/tests
-	@(cd $(topsrcdir)/testing/marionette/client && tar --exclude marionette/tests $(TAR_CREATE_FLAGS) - *) | (cd $(MARIONETTE_DIR) && tar -xf -)
+	$(NSINSTALL) -D $(MARIONETTE_DIR)/transport
+	@(cd $(topsrcdir)/testing/marionette/client && tar --exclude marionette/tests $(TAR_CREATE_FLAGS) - *) | (cd $(MARIONETTE_DIR)/ && tar -xf -)
+	@(cd $(topsrcdir)/testing/marionette/transport && tar $(TAR_CREATE_FLAGS) - *) | (cd $(MARIONETTE_DIR)/transport && tar -xf -)
 	$(PYTHON) $(topsrcdir)/testing/marionette/client/marionette/tests/print-manifest-dirs.py \
           $(topsrcdir) \
           $(topsrcdir)/testing/marionette/client/marionette/tests/unit-tests.ini \
           | (cd $(topsrcdir) && xargs tar $(TAR_CREATE_FLAGS) -) \
           | (cd $(MARIONETTE_DIR)/tests && tar -xf -)
 
 stage-mozbase: make-stage-dir
 	$(MAKE) -C $(DEPTH)/testing/mozbase stage-package
--- a/toolkit/mozapps/update/updater/moz.build
+++ b/toolkit/mozapps/update/updater/moz.build
@@ -71,10 +71,10 @@ DELAYLOAD_DLLS += [
     'wsock32.dll',
 ]
 
 if CONFIG['_MSC_VER']:
     WIN32_EXE_LDFLAGS += ['-ENTRY:wmainCRTStartup']
 
 if CONFIG['MOZ_UPDATE_CHANNEL'] in ('beta', 'release', 'esr'):
     DEFINES['MAR_SIGNING_RELEASE_BETA'] = '1'
-elif CONFIG['MOZ_UPDATE_CHANNEL'] in ('nightly', 'aurora', 'nightly-elm', 'nightly-profiling', 'nightly-oak'):
+elif CONFIG['MOZ_UPDATE_CHANNEL'] in ('nightly', 'aurora', 'nightly-elm', 'nightly-profiling', 'nightly-oak', 'nightly-ux'):
     DEFINES['MAR_SIGNING_AURORA_NIGHTLY'] = '1'
new file mode 100644
--- /dev/null
+++ b/tools/profiler/AutoObjectMapper.cpp
@@ -0,0 +1,207 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <sys/mman.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/NullPtr.h"
+
+#include "PlatformMacros.h"
+#include "AutoObjectMapper.h"
+
+#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
+# include <dlfcn.h>
+# include "mozilla/Types.h"
+  // FIXME move these out of mozglue/linker/ElfLoader.h into their
+  // own header, so as to avoid conflicts arising from two definitions
+  // of Array
+  extern "C" {
+    MFBT_API size_t
+    __dl_get_mappable_length(void *handle);
+    MFBT_API void *
+    __dl_mmap(void *handle, void *addr, size_t length, off_t offset);
+    MFBT_API void
+    __dl_munmap(void *handle, void *addr, size_t length);
+  }
+  // The following are for get_installation_lib_dir()
+# include "nsString.h"
+# include "nsDirectoryServiceUtils.h"
+# include "nsDirectoryServiceDefs.h"
+#endif
+
+
+// A helper function for creating failure error messages in
+// AutoObjectMapper*::Map.
+static void
+failedToMessage(void(*aLog)(const char*),
+                const char* aHowFailed, std::string aFileName)
+{
+  char buf[300];
+  snprintf(buf, sizeof(buf), "AutoObjectMapper::Map: Failed to %s \'%s\'",
+           aHowFailed, aFileName.c_str());
+  buf[sizeof(buf)-1] = 0;
+  aLog(buf);
+}
+
+
+AutoObjectMapperPOSIX::AutoObjectMapperPOSIX(void(*aLog)(const char*))
+  : mImage(nullptr)
+  , mSize(0)
+  , mLog(aLog)
+  , mIsMapped(false)
+{}
+
+AutoObjectMapperPOSIX::~AutoObjectMapperPOSIX() {
+  if (!mIsMapped) {
+    // There's nothing to do.
+    MOZ_ASSERT(!mImage);
+    MOZ_ASSERT(mSize == 0);
+    return;
+  }
+  MOZ_ASSERT(mSize > 0);
+  // The following assertion doesn't necessarily have to be true,
+  // but we assume (reasonably enough) that no mmap facility would
+  // be crazy enough to map anything at page zero.
+  MOZ_ASSERT(mImage);
+  munmap(mImage, mSize);
+}
+
+bool AutoObjectMapperPOSIX::Map(/*OUT*/void** start, /*OUT*/size_t* length,
+                                std::string fileName)
+{
+  MOZ_ASSERT(!mIsMapped);
+
+  int fd = open(fileName.c_str(), O_RDONLY);
+  if (fd == -1) {
+    failedToMessage(mLog, "open", fileName);
+    return false;
+  }
+
+  struct stat st;
+  int    err = fstat(fd, &st);
+  size_t sz  = (err == 0) ? st.st_size : 0;
+  if (err != 0 || sz == 0) {
+    failedToMessage(mLog, "fstat", fileName);
+    close(fd);
+    return false;
+  }
+
+  void* image = mmap(nullptr, sz, PROT_READ, MAP_SHARED, fd, 0);
+  if (image == MAP_FAILED) {
+    failedToMessage(mLog, "mmap", fileName);
+    close(fd);
+    return false;
+  }
+
+  close(fd);
+  mIsMapped = true;
+  mImage = *start  = image;
+  mSize  = *length = sz;
+  return true;
+}
+
+
+#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
+// A helper function for AutoObjectMapperFaultyLib::Map.  Finds out
+// where the installation's lib directory is, since we'll have to look
+// in there to get hold of libmozglue.so.  Returned C string is heap
+// allocated and the caller must deallocate it.
+static char*
+get_installation_lib_dir()
+{
+  nsCOMPtr<nsIProperties>
+    directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
+  if (!directoryService) {
+    return nullptr;
+  }
+  nsCOMPtr<nsIFile> greDir;
+  nsresult rv = directoryService->Get(NS_GRE_DIR, NS_GET_IID(nsIFile),
+                                      getter_AddRefs(greDir));
+  if (NS_FAILED(rv)) return nullptr;
+  nsCString path;
+  rv = greDir->GetNativePath(path);
+  if (NS_FAILED(rv)) {
+    return nullptr;
+  }
+  return strdup(path.get());
+}
+
+AutoObjectMapperFaultyLib::AutoObjectMapperFaultyLib(void(*aLog)(const char*))
+  : AutoObjectMapperPOSIX(aLog)
+  , mHdl(nullptr)
+{}
+
+AutoObjectMapperFaultyLib::~AutoObjectMapperFaultyLib() {
+  if (mHdl) {
+    // We've got an object mapped by faulty.lib.  Unmap it via faulty.lib.
+    MOZ_ASSERT(mSize > 0);
+    // Assert on the basis that no valid mapping would start at page zero.
+    MOZ_ASSERT(mImage);
+    __dl_munmap(mHdl, mImage, mSize);
+    dlclose(mHdl);
+    // Stop assertions in ~AutoObjectMapperPOSIX from failing.
+    mImage = nullptr;
+    mSize  = 0;
+  }
+  // At this point the parent class destructor, ~AutoObjectMapperPOSIX,
+  // gets called.  If that has something mapped in the normal way, it
+  // will unmap it in the normal way.  Unfortunately there's no
+  // obvious way to enforce the requirement that the object is mapped
+  // either by faulty.lib or by the parent class, but not by both.
+}
+
+bool AutoObjectMapperFaultyLib::Map(/*OUT*/void** start, /*OUT*/size_t* length,
+                                    std::string fileName)
+{
+  MOZ_ASSERT(!mHdl);
+
+  if (fileName == "libmozglue.so") {
+
+    // Do (2) in the comment above.
+    char* libdir = get_installation_lib_dir();
+    if (libdir) {
+      fileName = std::string(libdir) + "/lib/" + fileName;
+      free(libdir);
+    }
+    // Hand the problem off to the standard mapper.
+    return AutoObjectMapperPOSIX::Map(start, length, fileName);
+
+  } else {
+
+    // Do cases (1) and (3) in the comment above.  We have to
+    // grapple with faulty.lib directly.
+    void* hdl = dlopen(fileName.c_str(), RTLD_GLOBAL | RTLD_LAZY);
+    if (!hdl) {
+      failedToMessage(mLog, "get handle for ELF file", fileName);
+      return false;
+    }
+
+    size_t sz = __dl_get_mappable_length(hdl);
+    if (sz == 0) {
+      dlclose(hdl);
+      failedToMessage(mLog, "get size for ELF file", fileName);
+      return false;
+    }
+
+    void* image = __dl_mmap(hdl, nullptr, sz, 0);
+    if (image == MAP_FAILED) {
+      dlclose(hdl);
+      failedToMessage(mLog, "mmap ELF file", fileName);
+      return false;
+    }
+
+    mHdl   = hdl;
+    mImage = *start  = image;
+    mSize  = *length = sz;
+    return true;
+  }
+}
+
+#endif // defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
new file mode 100644
--- /dev/null
+++ b/tools/profiler/AutoObjectMapper.h
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef AutoObjectMapper_h
+#define AutoObjectMapper_h
+
+#include <string>
+
+#include "mozilla/Attributes.h"
+#include "PlatformMacros.h"
+
+// A (nearly-) RAII class that maps an object in and then unmaps it on
+// destruction.  This base class version uses the "normal" POSIX
+// functions: open, fstat, close, mmap, munmap.
+
+class MOZ_STACK_CLASS AutoObjectMapperPOSIX {
+public:
+  // The constructor does not attempt to map the file, because that
+  // might fail.  Instead, once the object has been constructed,
+  // call Map() to attempt the mapping.  There is no corresponding
+  // Unmap() since the unmapping is done in the destructor.  Failure
+  // messages are sent to |aLog|.
+  AutoObjectMapperPOSIX(void(*aLog)(const char*));
+
+  // Unmap the file on destruction of this object.
+  ~AutoObjectMapperPOSIX();
+
+  // Map |fileName| into the address space and return the mapping
+  // extents.  If the file is zero sized this will fail.  The file is
+  // mapped read-only and private.  Returns true iff the mapping
+  // succeeded, in which case *start and *length hold its extent.
+  // Once a call to Map succeeds, all subsequent calls to it will
+  // fail.
+  bool Map(/*OUT*/void** start, /*OUT*/size_t* length, std::string fileName);
+
+protected:
+  // If we are currently holding a mapped object, these record the
+  // mapped address range.
+  void*  mImage;
+  size_t mSize;
+
+  // A logging sink, for complaining about mapping failures.
+  void (*mLog)(const char*);
+
+private:
+  // Are we currently holding a mapped object?  This is private to
+  // the base class.  Derived classes need to have their own way to
+  // track whether they are holding a mapped object.
+  bool mIsMapped;
+
+  // Disable copying and assignment.
+  AutoObjectMapperPOSIX(const AutoObjectMapperPOSIX&);
+  AutoObjectMapperPOSIX& operator=(const AutoObjectMapperPOSIX&);
+  // Disable heap allocation of this class.
+  void* operator new(size_t);
+  void* operator new[](size_t);
+  void  operator delete(void*);
+  void  operator delete[](void*);
+};
+
+
+#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
+// This is a variant of AutoObjectMapperPOSIX suitable for use in
+// conjunction with faulty.lib on Android.  How it behaves depends on
+// the name of the file to be mapped.  There are three possible cases:
+//
+// (1) /foo/bar/xyzzy/blah.apk!/libwurble.so
+//     We hand it as-is to faulty.lib and let it fish the relevant
+//     bits out of the APK.
+//
+// (2) libmozglue.so
+//     This is part of the Fennec installation, but is not in the
+//     APK.  Instead we have to figure out the installation path
+//     and look for it there.  Because of faulty.lib limitations,
+//     we have to use regular open/mmap instead of faulty.lib.
+//
+// (3) libanythingelse.so
+//     faulty.lib assumes this is a system library, and prepends
+//     "/system/lib/" to the path.  So as in (1), we can give it
+//     as-is to faulty.lib.
+//
+// Hence (1) and (3) require special-casing here.  Case (2) simply
+// hands the problem to the parent class.
+
+class MOZ_STACK_CLASS AutoObjectMapperFaultyLib : public AutoObjectMapperPOSIX {
+public:
+  AutoObjectMapperFaultyLib(void(*aLog)(const char*));
+
+  ~AutoObjectMapperFaultyLib();
+
+  bool Map(/*OUT*/void** start, /*OUT*/size_t* length, std::string fileName);
+
+private:
+  // faulty.lib requires us to maintain an abstract handle that can be
+  // used later to unmap the area.  If this is non-NULL, it is assumed
+  // that unmapping is to be done by faulty.lib.  Otherwise it goes
+  // via the normal mechanism.
+  void* mHdl;
+
+  // Disable copying and assignment.
+  AutoObjectMapperFaultyLib(const AutoObjectMapperFaultyLib&);
+  AutoObjectMapperFaultyLib& operator=(const AutoObjectMapperFaultyLib&);
+  // Disable heap allocation of this class.
+  void* operator new(size_t);
+  void* operator new[](size_t);
+  void  operator delete(void*);
+  void  operator delete[](void*);
+};
+
+#endif // defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
+
+#endif // AutoObjectMapper_h
new file mode 100644
--- /dev/null
+++ b/tools/profiler/LulCommon.cpp
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+
+// Copyright (c) 2011, 2013 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+
+// This file is derived from the following files in
+// toolkit/crashreporter/google-breakpad:
+//   src/common/module.cc
+//   src/common/unique_string.cc
+
+// There's no internal-only interface for LulCommon.  Hence include
+// the external interface directly.
+#include "LulCommonExt.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <string>
+#include <map>
+
+
+namespace lul {
+
+using std::string;
+
+////////////////////////////////////////////////////////////////
+// Module
+//
+Module::Module(const string &name, const string &os,
+               const string &architecture, const string &id) :
+    name_(name),
+    os_(os),
+    architecture_(architecture),
+    id_(id) { }
+
+Module::~Module() {
+}
+
+
+////////////////////////////////////////////////////////////////
+// UniqueString
+//
+class UniqueString {
+ public:
+  UniqueString(string str) { str_ = strdup(str.c_str()); }
+  ~UniqueString() { free(reinterpret_cast<void*>(const_cast<char*>(str_))); }
+  const char* str_;
+};
+
+class UniqueStringUniverse {
+public:
+  UniqueStringUniverse() {};
+  const UniqueString* FindOrCopy(string str) {
+    std::map<string, UniqueString*>::iterator it = map_.find(str);
+    if (it == map_.end()) {
+      UniqueString* ustr = new UniqueString(str);
+      map_[str] = ustr;
+      return ustr;
+    } else {
+      return it->second;
+    }
+  }
+private:
+  std::map<string, UniqueString*> map_;
+};
+
+
+const UniqueString* ToUniqueString(string str) {
+  // For the initialisation of sUniverse to be threadsafe,
+  // this relies on C++11's semantics.
+  static UniqueStringUniverse sUniverse;
+  return sUniverse.FindOrCopy(str);
+}
+
+const char* const FromUniqueString(const UniqueString* ustr)
+{
+  return ustr->str_;
+}
+
+}  // namespace lul
new file mode 100644
--- /dev/null
+++ b/tools/profiler/LulCommonExt.h
@@ -0,0 +1,582 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+
+// Copyright (c) 2006, 2010, 2012, 2013 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// module.h: Define google_breakpad::Module. A Module holds debugging
+// information, and can write that information out as a Breakpad
+// symbol file.
+
+
+//  (C) Copyright Greg Colvin and Beman Dawes 1998, 1999.
+//  Copyright (c) 2001, 2002 Peter Dimov
+//
+//  Permission to copy, use, modify, sell and distribute this software
+//  is granted provided this copyright notice appears in all copies.
+//  This software is provided "as is" without express or implied
+//  warranty, and with no claim as to its suitability for any purpose.
+//
+//  See http://www.boost.org/libs/smart_ptr/scoped_ptr.htm for documentation.
+//
+
+
+// This file is derived from the following files in
+// toolkit/crashreporter/google-breakpad:
+//   src/common/unique_string.h
+//   src/common/scoped_ptr.h
+//   src/common/module.h
+
+// External interface for the "Common" component of LUL.
+
+#ifndef LulCommonExt_h
+#define LulCommonExt_h
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#include <string>
+#include <map>
+#include <vector>
+#include <cstddef>            // for std::ptrdiff_t
+
+#include "mozilla/Assertions.h"
+#include "mozilla/NullPtr.h"
+
+namespace lul {
+
+////////////////////////////////////////////////////////////////
+// UniqueString
+//
+
+// Abstract type
+class UniqueString;
+
+// Unique-ify a string.  |ToUniqueString| can never return nullptr.
+const UniqueString* ToUniqueString(std::string);
+
+// Get the contained C string (debugging only)
+const char* const FromUniqueString(const UniqueString*);
+
+// Some handy pre-uniqified strings.  Z is an escape character:
+//   ZS        '$'
+//   ZD        '.'
+//   Zeq       '='
+//   Zplus     '+'
+//   Zstar     '*'
+//   Zslash    '/'
+//   Zpercent  '%'
+//   Zat       '@'
+//   Zcaret    '^'
+
+// Note that ustr__empty and (UniqueString*)nullptr are considered
+// to be different.
+//
+// Unfortunately these have to be written as functions so as to
+// make them safe to use in static initialisers.
+
+// ""
+inline static const UniqueString* ustr__empty() {
+  static const UniqueString* us = nullptr;
+  if (!us) us = ToUniqueString("");
+  return us;
+}
+
+// ".cfa"
+inline static const UniqueString* ustr__ZDcfa() {
+  static const UniqueString* us = nullptr;
+  if (!us) us = ToUniqueString(".cfa");
+  return us;
+}
+
+// ".ra"
+inline static const UniqueString* ustr__ZDra() {
+  static const UniqueString* us = nullptr;
+  if (!us) us = ToUniqueString(".ra");
+  return us;
+}
+
+
+////////////////////////////////////////////////////////////////
+// GUID
+//
+
+typedef struct {
+  uint32_t data1;
+  uint16_t data2;
+  uint16_t data3;
+  uint8_t  data4[8];
+} MDGUID;  // GUID
+
+typedef MDGUID GUID;
+
+
+////////////////////////////////////////////////////////////////
+// scoped_ptr
+//
+
+//  scoped_ptr mimics a built-in pointer except that it guarantees deletion
+//  of the object pointed to, either on destruction of the scoped_ptr or via
+//  an explicit reset(). scoped_ptr is a simple solution for simple needs;
+//  use shared_ptr or std::auto_ptr if your needs are more complex.
+
+//  *** NOTE ***
+//  If your scoped_ptr is a class member of class FOO pointing to a
+//  forward declared type BAR (as shown below), then you MUST use a non-inlined
+//  version of the destructor.  The destructor of a scoped_ptr (called from
+//  FOO's destructor) must have a complete definition of BAR in order to
+//  destroy it.  Example:
+//
+//  -- foo.h --
+//  class BAR;
+//
+//  class FOO {
+//   public:
+//    FOO();
+//    ~FOO();  // Required for sources that instantiate class FOO to compile!
+//
+//   private:
+//    scoped_ptr<BAR> bar_;
+//  };
+//
+//  -- foo.cc --
+//  #include "foo.h"
+//  FOO::~FOO() {} // Empty, but must be non-inlined to FOO's class definition.
+
+//  scoped_ptr_malloc added by Google
+//  When one of these goes out of scope, instead of doing a delete or
+//  delete[], it calls free().  scoped_ptr_malloc<char> is likely to see
+//  much more use than any other specializations.
+
+//  release() added by Google
+//  Use this to conditionally transfer ownership of a heap-allocated object
+//  to the caller, usually on method success.
+
+template <typename T>
+class scoped_ptr {
+ private:
+
+  T* ptr;
+
+  scoped_ptr(scoped_ptr const &);
+  scoped_ptr & operator=(scoped_ptr const &);
+
+ public:
+
+  typedef T element_type;
+
+  explicit scoped_ptr(T* p = 0): ptr(p) {}
+
+  ~scoped_ptr() {
+    typedef char type_must_be_complete[sizeof(T)];
+    delete ptr;
+  }
+
+  void reset(T* p = 0) {
+    typedef char type_must_be_complete[sizeof(T)];
+
+    if (ptr != p) {
+      delete ptr;
+      ptr = p;
+    }
+  }
+
+  T& operator*() const {
+    MOZ_ASSERT(ptr != 0);
+    return *ptr;
+  }
+
+  T* operator->() const  {
+    MOZ_ASSERT(ptr != 0);
+    return ptr;
+  }
+
+  bool operator==(T* p) const {
+    return ptr == p;
+  }
+
+  bool operator!=(T* p) const {
+    return ptr != p;
+  }
+
+  T* get() const  {
+    return ptr;
+  }
+
+  void swap(scoped_ptr & b) {
+    T* tmp = b.ptr;
+    b.ptr = ptr;
+    ptr = tmp;
+  }
+
+  T* release() {
+    T* tmp = ptr;
+    ptr = 0;
+    return tmp;
+  }
+
+ private:
+
+  // no reason to use these: each scoped_ptr should have its own object
+  template <typename U> bool operator==(scoped_ptr<U> const& p) const;
+  template <typename U> bool operator!=(scoped_ptr<U> const& p) const;
+};
+
+template<typename T> inline
+void swap(scoped_ptr<T>& a, scoped_ptr<T>& b) {
+  a.swap(b);
+}
+
+template<typename T> inline
+bool operator==(T* p, const scoped_ptr<T>& b) {
+  return p == b.get();
+}
+
+template<typename T> inline
+bool operator!=(T* p, const scoped_ptr<T>& b) {
+  return p != b.get();
+}
+
+//  scoped_array extends scoped_ptr to arrays. Deletion of the array pointed to
+//  is guaranteed, either on destruction of the scoped_array or via an explicit
+//  reset(). Use shared_array or std::vector if your needs are more complex.
+
+template<typename T>
+class scoped_array {
+ private:
+
+  T* ptr;
+
+  scoped_array(scoped_array const &);
+  scoped_array & operator=(scoped_array const &);
+
+ public:
+
+  typedef T element_type;
+
+  explicit scoped_array(T* p = 0) : ptr(p) {}
+
+  ~scoped_array() {
+    typedef char type_must_be_complete[sizeof(T)];
+    delete[] ptr;
+  }
+
+  void reset(T* p = 0) {
+    typedef char type_must_be_complete[sizeof(T)];
+
+    if (ptr != p) {
+      delete [] ptr;
+      ptr = p;
+    }
+  }
+
+  T& operator[](std::ptrdiff_t i) const {
+    MOZ_ASSERT(ptr != 0);
+    MOZ_ASSERT(i >= 0);
+    return ptr[i];
+  }
+
+  bool operator==(T* p) const {
+    return ptr == p;
+  }
+
+  bool operator!=(T* p) const {
+    return ptr != p;
+  }
+
+  T* get() const {
+    return ptr;
+  }
+
+  void swap(scoped_array & b) {
+    T* tmp = b.ptr;
+    b.ptr = ptr;
+    ptr = tmp;
+  }
+
+  T* release() {
+    T* tmp = ptr;
+    ptr = 0;
+    return tmp;
+  }
+
+ private:
+
+  // no reason to use these: each scoped_array should have its own object
+  template <typename U> bool operator==(scoped_array<U> const& p) const;
+  template <typename U> bool operator!=(scoped_array<U> const& p) const;
+};
+
+template<class T> inline
+void swap(scoped_array<T>& a, scoped_array<T>& b) {
+  a.swap(b);
+}
+
+template<typename T> inline
+bool operator==(T* p, const scoped_array<T>& b) {
+  return p == b.get();
+}
+
+template<typename T> inline
+bool operator!=(T* p, const scoped_array<T>& b) {
+  return p != b.get();
+}
+
+
+// This class wraps the c library function free() in a class that can be
+// passed as a template argument to scoped_ptr_malloc below.
+class ScopedPtrMallocFree {
+ public:
+  inline void operator()(void* x) const {
+    free(x);
+  }
+};
+
+// scoped_ptr_malloc<> is similar to scoped_ptr<>, but it accepts a
+// second template argument, the functor used to free the object.
+
+template<typename T, typename FreeProc = ScopedPtrMallocFree>
+class scoped_ptr_malloc {
+ private:
+
+  T* ptr;
+
+  scoped_ptr_malloc(scoped_ptr_malloc const &);
+  scoped_ptr_malloc & operator=(scoped_ptr_malloc const &);
+
+ public:
+
+  typedef T element_type;
+
+  explicit scoped_ptr_malloc(T* p = 0): ptr(p) {}
+
+  ~scoped_ptr_malloc() {
+    typedef char type_must_be_complete[sizeof(T)];
+    free_((void*) ptr);
+  }
+
+  void reset(T* p = 0) {
+    typedef char type_must_be_complete[sizeof(T)];
+
+    if (ptr != p) {
+      free_((void*) ptr);
+      ptr = p;
+    }
+  }
+
+  T& operator*() const {
+    MOZ_ASSERT(ptr != 0);
+    return *ptr;
+  }
+
+  T* operator->() const {
+    MOZ_ASSERT(ptr != 0);
+    return ptr;
+  }
+
+  bool operator==(T* p) const {
+    return ptr == p;
+  }
+
+  bool operator!=(T* p) const {
+    return ptr != p;
+  }
+
+  T* get() const {
+    return ptr;
+  }
+
+  void swap(scoped_ptr_malloc & b) {
+    T* tmp = b.ptr;
+    b.ptr = ptr;
+    ptr = tmp;
+  }
+
+  T* release() {
+    T* tmp = ptr;
+    ptr = 0;
+    return tmp;
+  }
+
+ private:
+
+  // no reason to use these: each scoped_ptr_malloc should have its own object
+  template <typename U, typename GP>
+  bool operator==(scoped_ptr_malloc<U, GP> const& p) const;
+  template <typename U, typename GP>
+  bool operator!=(scoped_ptr_malloc<U, GP> const& p) const;
+
+  static FreeProc const free_;
+};
+
+template<typename T, typename FP>
+FP const scoped_ptr_malloc<T,FP>::free_ = FP();
+
+template<typename T, typename FP> inline
+void swap(scoped_ptr_malloc<T,FP>& a, scoped_ptr_malloc<T,FP>& b) {
+  a.swap(b);
+}
+
+template<typename T, typename FP> inline
+bool operator==(T* p, const scoped_ptr_malloc<T,FP>& b) {
+  return p == b.get();
+}
+
+template<typename T, typename FP> inline
+bool operator!=(T* p, const scoped_ptr_malloc<T,FP>& b) {
+  return p != b.get();
+}
+
+
+////////////////////////////////////////////////////////////////
+// Module
+//
+
+// A Module represents the contents of a module, and supports methods
+// for adding information produced by parsing STABS or DWARF data
+// --- possibly both from the same file --- and then writing out the
+// unified contents as a Breakpad-format symbol file.
+class Module {
+public:
+  // The type of addresses and sizes in a symbol table.
+  typedef uint64_t Address;
+
+  // Representation of an expression.  This can either be a postfix
+  // expression, in which case it is stored as a string, or a simple
+  // expression of the form (identifier + imm) or *(identifier + imm).
+  // It can also be invalid (denoting "no value").
+  enum ExprHow {
+    kExprInvalid = 1,
+    kExprPostfix,
+    kExprSimple,
+    kExprSimpleMem
+  };
+
+  struct Expr {
+    // Construct a simple-form expression
+    Expr(const UniqueString* ident, long offset, bool deref) {
+      if (ident == ustr__empty()) {
+        Expr();
+      } else {
+        postfix_ = "";
+        ident_ = ident;
+        offset_ = offset;
+        how_ = deref ? kExprSimpleMem : kExprSimple;
+      }
+    }
+
+    // Construct an invalid expression
+    Expr() {
+      postfix_ = "";
+      ident_ = nullptr;
+      offset_ = 0;
+      how_ = kExprInvalid;
+    }
+
+    // Return the postfix expression string, either directly,
+    // if this is a postfix expression, or by synthesising it
+    // for a simple expression.
+    std::string getExprPostfix() const {
+      switch (how_) {
+        case kExprPostfix:
+          return postfix_;
+        case kExprSimple:
+        case kExprSimpleMem: {
+          char buf[40];
+          sprintf(buf, " %ld %c%s", labs(offset_), offset_ < 0 ? '-' : '+',
+                                    how_ == kExprSimple ? "" : " ^");
+          return std::string(FromUniqueString(ident_)) + std::string(buf);
+        }
+        case kExprInvalid:
+        default:
+          MOZ_ASSERT(0 && "getExprPostfix: invalid Module::Expr type");
+          return "Expr::genExprPostfix: kExprInvalid";
+      }
+    }
+
+    // The identifier that gives the starting value for simple expressions.
+    const UniqueString* ident_;
+    // The offset to add for simple expressions.
+    long        offset_;
+    // The Postfix expression string to evaluate for non-simple expressions.
+    std::string postfix_;
+    // The operation expressed by this expression.
+    ExprHow     how_;
+  };
+
+  // A map from register names to expressions that recover
+  // their values. This can represent a complete set of rules to
+  // follow at some address, or a set of changes to be applied to an
+  // extant set of rules.
+  // NOTE! there are two completely different types called RuleMap.  This
+  // is one of them.
+  typedef std::map<const UniqueString*, Expr> RuleMap;
+
+  // A map from addresses to RuleMaps, representing changes that take
+  // effect at given addresses.
+  typedef std::map<Address, RuleMap> RuleChangeMap;
+
+  // A range of 'STACK CFI' stack walking information. An instance of
+  // this structure corresponds to a 'STACK CFI INIT' record and the
+  // subsequent 'STACK CFI' records that fall within its range.
+  struct StackFrameEntry {
+    // The starting address and number of bytes of machine code this
+    // entry covers.
+    Address address, size;
+
+    // The initial register recovery rules, in force at the starting
+    // address.
+    RuleMap initial_rules;
+
+    // A map from addresses to rule changes. To find the rules in
+    // force at a given address, start with initial_rules, and then
+    // apply the changes given in this map for all addresses up to and
+    // including the address you're interested in.
+    RuleChangeMap rule_changes;
+  };
+
+  // Create a new module with the given name, operating system,
+  // architecture, and ID string.
+  Module(const std::string &name, const std::string &os,
+         const std::string &architecture, const std::string &id);
+  ~Module();
+
+private:
+
+  // Module header entries.
+  std::string name_, os_, architecture_, id_;
+};
+
+
+}  // namespace lul
+
+#endif // LulCommonExt_h
new file mode 100644
--- /dev/null
+++ b/tools/profiler/LulDwarf.cpp
@@ -0,0 +1,2010 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+
+// Copyright (c) 2010 Google Inc. All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// CFI reader author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// Implementation of dwarf2reader::LineInfo, dwarf2reader::CompilationUnit,
+// and dwarf2reader::CallFrameInfo. See dwarf2reader.h for details.
+
+// This file is derived from the following files in
+// toolkit/crashreporter/google-breakpad:
+//   src/common/dwarf/bytereader.cc
+//   src/common/dwarf/dwarf2reader.cc
+//   src/common/dwarf_cfi_to_module.cc
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <map>
+#include <stack>
+#include <string>
+
+#include "mozilla/Assertions.h"
+
+#include "LulCommonExt.h"
+#include "LulDwarfInt.h"
+
+// Set this to 1 for verbose logging
+#define DEBUG_DWARF 0
+
+
+namespace lul {
+
+using std::string;
+
+ByteReader::ByteReader(enum Endianness endian)
+    :offset_reader_(NULL), address_reader_(NULL), endian_(endian),
+     address_size_(0), offset_size_(0),
+     have_section_base_(), have_text_base_(), have_data_base_(),
+     have_function_base_() { }
+
+ByteReader::~ByteReader() { }
+
+void ByteReader::SetOffsetSize(uint8 size) {
+  offset_size_ = size;
+  MOZ_ASSERT(size == 4 || size == 8);
+  if (size == 4) {
+    this->offset_reader_ = &ByteReader::ReadFourBytes;
+  } else {
+    this->offset_reader_ = &ByteReader::ReadEightBytes;
+  }
+}
+
+void ByteReader::SetAddressSize(uint8 size) {
+  address_size_ = size;
+  MOZ_ASSERT(size == 4 || size == 8);
+  if (size == 4) {
+    this->address_reader_ = &ByteReader::ReadFourBytes;
+  } else {
+    this->address_reader_ = &ByteReader::ReadEightBytes;
+  }
+}
+
+uint64 ByteReader::ReadInitialLength(const char* start, size_t* len) {
+  const uint64 initial_length = ReadFourBytes(start);
+  start += 4;
+
+  // In DWARF2/3, if the initial length is all 1 bits, then the offset
+  // size is 8 and we need to read the next 8 bytes for the real length.
+  if (initial_length == 0xffffffff) {
+    SetOffsetSize(8);
+    *len = 12;
+    return ReadOffset(start);
+  } else {
+    SetOffsetSize(4);
+    *len = 4;
+  }
+  return initial_length;
+}
+
+bool ByteReader::ValidEncoding(DwarfPointerEncoding encoding) const {
+  if (encoding == DW_EH_PE_omit) return true;
+  if (encoding == DW_EH_PE_aligned) return true;
+  if ((encoding & 0x7) > DW_EH_PE_udata8)
+    return false;
+  if ((encoding & 0x70) > DW_EH_PE_funcrel)
+    return false;
+  return true;
+}
+
+bool ByteReader::UsableEncoding(DwarfPointerEncoding encoding) const {
+  switch (encoding & 0x70) {
+    case DW_EH_PE_absptr:  return true;
+    case DW_EH_PE_pcrel:   return have_section_base_;
+    case DW_EH_PE_textrel: return have_text_base_;
+    case DW_EH_PE_datarel: return have_data_base_;
+    case DW_EH_PE_funcrel: return have_function_base_;
+    default:               return false;
+  }
+}
+
+uint64 ByteReader::ReadEncodedPointer(const char *buffer,
+                                      DwarfPointerEncoding encoding,
+                                      size_t *len) const {
+  // UsableEncoding doesn't approve of DW_EH_PE_omit, so we shouldn't
+  // see it here.
+  MOZ_ASSERT(encoding != DW_EH_PE_omit);
+
+  // The Linux Standards Base 4.0 does not make this clear, but the
+  // GNU tools (gcc/unwind-pe.h; readelf/dwarf.c; gdb/dwarf2-frame.c)
+  // agree that aligned pointers are always absolute, machine-sized,
+  // machine-signed pointers.
+  if (encoding == DW_EH_PE_aligned) {
+    MOZ_ASSERT(have_section_base_);
+
+    // We don't need to align BUFFER in *our* address space. Rather, we
+    // need to find the next position in our buffer that would be aligned
+    // when the .eh_frame section the buffer contains is loaded into the
+    // program's memory. So align assuming that buffer_base_ gets loaded at
+    // address section_base_, where section_base_ itself may or may not be
+    // aligned.
+
+    // First, find the offset to START from the closest prior aligned
+    // address.
+    uint64 skew = section_base_ & (AddressSize() - 1);
+    // Now find the offset from that aligned address to buffer.
+    uint64 offset = skew + (buffer - buffer_base_);
+    // Round up to the next boundary.
+    uint64 aligned = (offset + AddressSize() - 1) & -AddressSize();
+    // Convert back to a pointer.
+    const char *aligned_buffer = buffer_base_ + (aligned - skew);
+    // Finally, store the length and actually fetch the pointer.
+    *len = aligned_buffer - buffer + AddressSize();
+    return ReadAddress(aligned_buffer);
+  }
+
+  // Extract the value first, ignoring whether it's a pointer or an
+  // offset relative to some base.
+  uint64 offset;
+  switch (encoding & 0x0f) {
+    case DW_EH_PE_absptr:
+      // DW_EH_PE_absptr is weird, as it is used as a meaningful value for
+      // both the high and low nybble of encoding bytes. When it appears in
+      // the high nybble, it means that the pointer is absolute, not an
+      // offset from some base address. When it appears in the low nybble,
+      // as here, it means that the pointer is stored as a normal
+      // machine-sized and machine-signed address. A low nybble of
+      // DW_EH_PE_absptr does not imply that the pointer is absolute; it is
+      // correct for us to treat the value as an offset from a base address
+      // if the upper nybble is not DW_EH_PE_absptr.
+      offset = ReadAddress(buffer);
+      *len = AddressSize();
+      break;
+
+    case DW_EH_PE_uleb128:
+      offset = ReadUnsignedLEB128(buffer, len);
+      break;
+
+    case DW_EH_PE_udata2:
+      offset = ReadTwoBytes(buffer);
+      *len = 2;
+      break;
+
+    case DW_EH_PE_udata4:
+      offset = ReadFourBytes(buffer);
+      *len = 4;
+      break;
+
+    case DW_EH_PE_udata8:
+      offset = ReadEightBytes(buffer);
+      *len = 8;
+      break;
+
+    case DW_EH_PE_sleb128:
+      offset = ReadSignedLEB128(buffer, len);
+      break;
+
+    case DW_EH_PE_sdata2:
+      offset = ReadTwoBytes(buffer);
+      // Sign-extend from 16 bits.
+      offset = (offset ^ 0x8000) - 0x8000;
+      *len = 2;
+      break;
+
+    case DW_EH_PE_sdata4:
+      offset = ReadFourBytes(buffer);
+      // Sign-extend from 32 bits.
+      offset = (offset ^ 0x80000000ULL) - 0x80000000ULL;
+      *len = 4;
+      break;
+
+    case DW_EH_PE_sdata8:
+      // No need to sign-extend; this is the full width of our type.
+      offset = ReadEightBytes(buffer);
+      *len = 8;
+      break;
+
+    default:
+      abort();
+  }
+
+  // Find the appropriate base address.
+  uint64 base;
+  switch (encoding & 0x70) {
+    case DW_EH_PE_absptr:
+      base = 0;
+      break;
+
+    case DW_EH_PE_pcrel:
+      MOZ_ASSERT(have_section_base_);
+      base = section_base_ + (buffer - buffer_base_);
+      break;
+
+    case DW_EH_PE_textrel:
+      MOZ_ASSERT(have_text_base_);
+      base = text_base_;
+      break;
+
+    case DW_EH_PE_datarel:
+      MOZ_ASSERT(have_data_base_);
+      base = data_base_;
+      break;
+
+    case DW_EH_PE_funcrel:
+      MOZ_ASSERT(have_function_base_);
+      base = function_base_;
+      break;
+
+    default:
+      abort();
+  }
+
+  uint64 pointer = base + offset;
+
+  // Remove inappropriate upper bits.
+  if (AddressSize() == 4)
+    pointer = pointer & 0xffffffff;
+  else
+    MOZ_ASSERT(AddressSize() == sizeof(uint64));
+
+  return pointer;
+}
+
+
+// A DWARF rule for recovering the address or value of a register, or
+// computing the canonical frame address. There is one subclass of this for
+// each '*Rule' member function in CallFrameInfo::Handler.
+//
+// It's annoying that we have to handle Rules using pointers (because
+// the concrete instances can have an arbitrary size). They're small,
+// so it would be much nicer if we could just handle them by value
+// instead of fretting about ownership and destruction.
+//
+// It seems like all these could simply be instances of std::tr1::bind,
+// except that we need instances to be EqualityComparable, too.
+//
+// This could logically be nested within State, but then the qualified names
+// get horrendous.
+class CallFrameInfo::Rule {
+ public:
+  virtual ~Rule() { }
+
+  // Tell HANDLER that, at ADDRESS in the program, REGISTER can be
+  // recovered using this rule. If REGISTER is kCFARegister, then this rule
+  // describes how to compute the canonical frame address. Return what the
+  // HANDLER member function returned.
+  virtual bool Handle(Handler *handler,
+                      uint64 address, int register) const = 0;
+
+  // Equality on rules. We use these to decide which rules we need
+  // to report after a DW_CFA_restore_state instruction.
+  virtual bool operator==(const Rule &rhs) const = 0;
+
+  bool operator!=(const Rule &rhs) const { return ! (*this == rhs); }
+
+  // Return a pointer to a copy of this rule.
+  virtual Rule *Copy() const = 0;
+
+  // If this is a base+offset rule, change its base register to REG.
+  // Otherwise, do nothing. (Ugly, but required for DW_CFA_def_cfa_register.)
+  virtual void SetBaseRegister(unsigned reg) { }
+
+  // If this is a base+offset rule, change its offset to OFFSET. Otherwise,
+  // do nothing. (Ugly, but required for DW_CFA_def_cfa_offset.)
+  virtual void SetOffset(long long offset) { }
+
+  // A RTTI workaround, to make it possible to implement equality
+  // comparisons on classes derived from this one.
+  enum CFIRTag {
+    CFIR_UNDEFINED_RULE,
+    CFIR_SAME_VALUE_RULE,
+    CFIR_OFFSET_RULE,
+    CFIR_VAL_OFFSET_RULE,
+    CFIR_REGISTER_RULE,
+    CFIR_EXPRESSION_RULE,
+    CFIR_VAL_EXPRESSION_RULE
+  };
+
+  // Produce the tag that identifies the child class of this object.
+  virtual CFIRTag getTag() const = 0;
+};
+
+// Rule: the value the register had in the caller cannot be recovered.
+class CallFrameInfo::UndefinedRule: public CallFrameInfo::Rule {
+ public:
+  UndefinedRule() { }
+  ~UndefinedRule() { }
+  CFIRTag getTag() const { return CFIR_UNDEFINED_RULE; }
+  bool Handle(Handler *handler, uint64 address, int reg) const {
+    return handler->UndefinedRule(address, reg);
+  }
+  bool operator==(const Rule &rhs) const {
+    if (rhs.getTag() != CFIR_UNDEFINED_RULE) return false;
+    return true;
+  }
+  Rule *Copy() const { return new UndefinedRule(*this); }
+};
+
+// Rule: the register's value is the same as that it had in the caller.
+class CallFrameInfo::SameValueRule: public CallFrameInfo::Rule {
+ public:
+  SameValueRule() { }
+  ~SameValueRule() { }
+  CFIRTag getTag() const { return CFIR_SAME_VALUE_RULE; }
+  bool Handle(Handler *handler, uint64 address, int reg) const {
+    return handler->SameValueRule(address, reg);
+  }
+  bool operator==(const Rule &rhs) const {
+    if (rhs.getTag() != CFIR_SAME_VALUE_RULE) return false;
+    return true;
+  }
+  Rule *Copy() const { return new SameValueRule(*this); }
+};
+
+// Rule: the register is saved at OFFSET from BASE_REGISTER.  BASE_REGISTER
+// may be CallFrameInfo::Handler::kCFARegister.
+class CallFrameInfo::OffsetRule: public CallFrameInfo::Rule {
+ public:
+  OffsetRule(int base_register, long offset)
+      : base_register_(base_register), offset_(offset) { }
+  ~OffsetRule() { }
+  CFIRTag getTag() const { return CFIR_OFFSET_RULE; }
+  bool Handle(Handler *handler, uint64 address, int reg) const {
+    return handler->OffsetRule(address, reg, base_register_, offset_);
+  }
+  bool operator==(const Rule &rhs) const {
+    if (rhs.getTag() != CFIR_OFFSET_RULE) return false;
+    const OffsetRule *our_rhs = static_cast<const OffsetRule *>(&rhs);
+    return (base_register_ == our_rhs->base_register_ &&
+            offset_ == our_rhs->offset_);
+  }
+  Rule *Copy() const { return new OffsetRule(*this); }
+  // We don't actually need SetBaseRegister or SetOffset here, since they
+  // are only ever applied to CFA rules, for DW_CFA_def_cfa_offset, and it
+  // doesn't make sense to use OffsetRule for computing the CFA: it
+  // computes the address at which a register is saved, not a value.
+ private:
+  int base_register_;
+  long offset_;
+};
+
+// Rule: the value the register had in the caller is the value of
+// BASE_REGISTER plus offset. BASE_REGISTER may be
+// CallFrameInfo::Handler::kCFARegister.
+class CallFrameInfo::ValOffsetRule: public CallFrameInfo::Rule {
+ public:
+  ValOffsetRule(int base_register, long offset)
+      : base_register_(base_register), offset_(offset) { }
+  ~ValOffsetRule() { }
+  CFIRTag getTag() const { return CFIR_VAL_OFFSET_RULE; }
+  bool Handle(Handler *handler, uint64 address, int reg) const {
+    return handler->ValOffsetRule(address, reg, base_register_, offset_);
+  }
+  bool operator==(const Rule &rhs) const {
+    if (rhs.getTag() != CFIR_VAL_OFFSET_RULE) return false;
+    const ValOffsetRule *our_rhs = static_cast<const ValOffsetRule *>(&rhs);
+    return (base_register_ == our_rhs->base_register_ &&
+            offset_ == our_rhs->offset_);
+  }
+  Rule *Copy() const { return new ValOffsetRule(*this); }
+  void SetBaseRegister(unsigned reg) { base_register_ = reg; }
+  void SetOffset(long long offset) { offset_ = offset; }
+ private:
+  int base_register_;
+  long offset_;
+};
+
+// Rule: the register has been saved in another register REGISTER_NUMBER_.
+class CallFrameInfo::RegisterRule: public CallFrameInfo::Rule {
+ public:
+  explicit RegisterRule(int register_number)
+      : register_number_(register_number) { }
+  ~RegisterRule() { }
+  CFIRTag getTag() const { return CFIR_REGISTER_RULE; }
+  bool Handle(Handler *handler, uint64 address, int reg) const {
+    return handler->RegisterRule(address, reg, register_number_);
+  }
+  bool operator==(const Rule &rhs) const {
+    if (rhs.getTag() != CFIR_REGISTER_RULE) return false;
+    const RegisterRule *our_rhs = static_cast<const RegisterRule *>(&rhs);
+    return (register_number_ == our_rhs->register_number_);
+  }
+  Rule *Copy() const { return new RegisterRule(*this); }
+ private:
+  int register_number_;
+};
+
+// Rule: EXPRESSION evaluates to the address at which the register is saved.
+class CallFrameInfo::ExpressionRule: public CallFrameInfo::Rule {
+ public:
+  explicit ExpressionRule(const string &expression)
+      : expression_(expression) { }
+  ~ExpressionRule() { }
+  CFIRTag getTag() const { return CFIR_EXPRESSION_RULE; }
+  bool Handle(Handler *handler, uint64 address, int reg) const {
+    return handler->ExpressionRule(address, reg, expression_);
+  }
+  bool operator==(const Rule &rhs) const {
+    if (rhs.getTag() != CFIR_EXPRESSION_RULE) return false;
+    const ExpressionRule *our_rhs = static_cast<const ExpressionRule *>(&rhs);
+    return (expression_ == our_rhs->expression_);
+  }
+  Rule *Copy() const { return new ExpressionRule(*this); }
+ private:
+  string expression_;
+};
+
+// Rule: EXPRESSION evaluates to the previous value of the register.
+class CallFrameInfo::ValExpressionRule: public CallFrameInfo::Rule {
+ public:
+  explicit ValExpressionRule(const string &expression)
+      : expression_(expression) { }
+  ~ValExpressionRule() { }
+  CFIRTag getTag() const { return CFIR_VAL_EXPRESSION_RULE; }
+  bool Handle(Handler *handler, uint64 address, int reg) const {
+    return handler->ValExpressionRule(address, reg, expression_);
+  }
+  bool operator==(const Rule &rhs) const {
+    if (rhs.getTag() != CFIR_VAL_EXPRESSION_RULE) return false;
+    const ValExpressionRule *our_rhs =
+        static_cast<const ValExpressionRule *>(&rhs);
+    return (expression_ == our_rhs->expression_);
+  }
+  Rule *Copy() const { return new ValExpressionRule(*this); }
+ private:
+  string expression_;
+};
+
+// A map from register numbers to rules.
+class CallFrameInfo::RuleMap {
+ public:
+  RuleMap() : cfa_rule_(NULL) { }
+  RuleMap(const RuleMap &rhs) : cfa_rule_(NULL) { *this = rhs; }
+  ~RuleMap() { Clear(); }
+
+  RuleMap &operator=(const RuleMap &rhs);
+
+  // Set the rule for computing the CFA to RULE. Take ownership of RULE.
+  void SetCFARule(Rule *rule) { delete cfa_rule_; cfa_rule_ = rule; }
+
+  // Return the current CFA rule. Unlike RegisterRule, this RuleMap retains
+  // ownership of the rule. We use this for DW_CFA_def_cfa_offset and
+  // DW_CFA_def_cfa_register, and for detecting references to the CFA before
+  // a rule for it has been established.
+  Rule *CFARule() const { return cfa_rule_; }
+
+  // Return the rule for REG, or NULL if there is none. The caller takes
+  // ownership of the result.
+  Rule *RegisterRule(int reg) const;
+
+  // Set the rule for computing REG to RULE. Take ownership of RULE.
+  void SetRegisterRule(int reg, Rule *rule);
+
+  // Make all the appropriate calls to HANDLER as if we were changing from
+  // this RuleMap to NEW_RULES at ADDRESS. We use this to implement
+  // DW_CFA_restore_state, where lots of rules can change simultaneously.
+  // Return true if all handlers returned true; otherwise, return false.
+  bool HandleTransitionTo(Handler *handler, uint64 address,
+                          const RuleMap &new_rules) const;
+
+ private:
+  // A map from register numbers to Rules.
+  typedef std::map<int, Rule *> RuleByNumber;
+
+  // Remove all register rules and clear cfa_rule_.
+  void Clear();
+
+  // The rule for computing the canonical frame address. This RuleMap owns
+  // this rule.
+  Rule *cfa_rule_;
+
+  // A map from register numbers to postfix expressions to recover
+  // their values. This RuleMap owns the Rules the map refers to.
+  RuleByNumber registers_;
+};
+
+CallFrameInfo::RuleMap &CallFrameInfo::RuleMap::operator=(const RuleMap &rhs) {
+  Clear();
+  // Since each map owns the rules it refers to, assignment must copy them.
+  if (rhs.cfa_rule_) cfa_rule_ = rhs.cfa_rule_->Copy();
+  for (RuleByNumber::const_iterator it = rhs.registers_.begin();
+       it != rhs.registers_.end(); it++)
+    registers_[it->first] = it->second->Copy();
+  return *this;
+}
+
+CallFrameInfo::Rule *CallFrameInfo::RuleMap::RegisterRule(int reg) const {
+  MOZ_ASSERT(reg != Handler::kCFARegister);
+  RuleByNumber::const_iterator it = registers_.find(reg);
+  if (it != registers_.end())
+    return it->second->Copy();
+  else
+    return NULL;
+}
+
+void CallFrameInfo::RuleMap::SetRegisterRule(int reg, Rule *rule) {
+  MOZ_ASSERT(reg != Handler::kCFARegister);
+  MOZ_ASSERT(rule);
+  Rule **slot = &registers_[reg];
+  delete *slot;
+  *slot = rule;
+}
+
+bool CallFrameInfo::RuleMap::HandleTransitionTo(
+    Handler *handler,
+    uint64 address,
+    const RuleMap &new_rules) const {
+  // Transition from cfa_rule_ to new_rules.cfa_rule_.
+  if (cfa_rule_ && new_rules.cfa_rule_) {
+    if (*cfa_rule_ != *new_rules.cfa_rule_ &&
+        !new_rules.cfa_rule_->Handle(handler, address, Handler::kCFARegister))
+      return false;
+  } else if (cfa_rule_) {
+    // this RuleMap has a CFA rule but new_rules doesn't.
+    // CallFrameInfo::Handler has no way to handle this --- and shouldn't;
+    // it's garbage input. The instruction interpreter should have
+    // detected this and warned, so take no action here.
+  } else if (new_rules.cfa_rule_) {
+    // This shouldn't be possible: NEW_RULES is some prior state, and
+    // there's no way to remove entries.
+    MOZ_ASSERT(0);
+  } else {
+    // Both CFA rules are empty.  No action needed.
+  }
+
+  // Traverse the two maps in order by register number, and report
+  // whatever differences we find.
+  RuleByNumber::const_iterator old_it = registers_.begin();
+  RuleByNumber::const_iterator new_it = new_rules.registers_.begin();
+  while (old_it != registers_.end() && new_it != new_rules.registers_.end()) {
+    if (old_it->first < new_it->first) {
+      // This RuleMap has an entry for old_it->first, but NEW_RULES
+      // doesn't.
+      //
+      // This isn't really the right thing to do, but since CFI generally
+      // only mentions callee-saves registers, and GCC's convention for
+      // callee-saves registers is that they are unchanged, it's a good
+      // approximation.
+      if (!handler->SameValueRule(address, old_it->first))
+        return false;
+      old_it++;
+    } else if (old_it->first > new_it->first) {
+      // NEW_RULES has entry for new_it->first, but this RuleMap
+      // doesn't. This shouldn't be possible: NEW_RULES is some prior
+      // state, and there's no way to remove entries.
+      MOZ_ASSERT(0);
+    } else {
+      // Both maps have an entry for this register. Report the new
+      // rule if it is different.
+      if (*old_it->second != *new_it->second &&
+          !new_it->second->Handle(handler, address, new_it->first))
+        return false;
+      new_it++, old_it++;
+    }
+  }
+  // Finish off entries from this RuleMap with no counterparts in new_rules.
+  while (old_it != registers_.end()) {
+    if (!handler->SameValueRule(address, old_it->first))
+      return false;
+    old_it++;
+  }
+  // Since we only make transitions from a rule set to some previously
+  // saved rule set, and we can only add rules to the map, NEW_RULES
+  // must have fewer rules than *this.
+  MOZ_ASSERT(new_it == new_rules.registers_.end());
+
+  return true;
+}
+
+// Remove all register rules and clear cfa_rule_.
+void CallFrameInfo::RuleMap::Clear() {
+  delete cfa_rule_;
+  cfa_rule_ = NULL;
+  for (RuleByNumber::iterator it = registers_.begin();
+       it != registers_.end(); it++)
+    delete it->second;
+  registers_.clear();
+}
+
+// The state of the call frame information interpreter as it processes
+// instructions from a CIE and FDE.
+class CallFrameInfo::State {
+ public:
+  // Create a call frame information interpreter state with the given
+  // reporter, reader, handler, and initial call frame info address.
+  State(ByteReader *reader, Handler *handler, Reporter *reporter,
+        uint64 address)
+      : reader_(reader), handler_(handler), reporter_(reporter),
+        address_(address), entry_(NULL), cursor_(NULL),
+        saved_rules_(NULL) { }
+
+  ~State() {
+    if (saved_rules_)
+      delete saved_rules_;
+  }
+
+  // Interpret instructions from CIE, save the resulting rule set for
+  // DW_CFA_restore instructions, and return true. On error, report
+  // the problem to reporter_ and return false.
+  bool InterpretCIE(const CIE &cie);
+
+  // Interpret instructions from FDE, and return true. On error,
+  // report the problem to reporter_ and return false.
+  bool InterpretFDE(const FDE &fde);
+
+ private:
+  // The operands of a CFI instruction, for ParseOperands.
+  struct Operands {
+    unsigned register_number;  // A register number.
+    uint64 offset;             // An offset or address.
+    long signed_offset;        // A signed offset.
+    string expression;         // A DWARF expression.
+  };
+
+  // Parse CFI instruction operands from STATE's instruction stream as
+  // described by FORMAT. On success, populate OPERANDS with the
+  // results, and return true. On failure, report the problem and
+  // return false.
+  //
+  // Each character of FORMAT should be one of the following:
+  //
+  //   'r'  unsigned LEB128 register number (OPERANDS->register_number)
+  //   'o'  unsigned LEB128 offset          (OPERANDS->offset)
+  //   's'  signed LEB128 offset            (OPERANDS->signed_offset)
+  //   'a'  machine-size address            (OPERANDS->offset)
+  //        (If the CIE has a 'z' augmentation string, 'a' uses the
+  //        encoding specified by the 'R' argument.)
+  //   '1'  a one-byte offset               (OPERANDS->offset)
+  //   '2'  a two-byte offset               (OPERANDS->offset)
+  //   '4'  a four-byte offset              (OPERANDS->offset)
+  //   '8'  an eight-byte offset            (OPERANDS->offset)
+  //   'e'  a DW_FORM_block holding a       (OPERANDS->expression)
+  //        DWARF expression
+  bool ParseOperands(const char *format, Operands *operands);
+
+  // Interpret one CFI instruction from STATE's instruction stream, update
+  // STATE, report any rule changes to handler_, and return true. On
+  // failure, report the problem and return false.
+  bool DoInstruction();
+
+  // The following Do* member functions are subroutines of DoInstruction,
+  // factoring out the actual work of operations that have several
+  // different encodings.
+
+  // Set the CFA rule to be the value of BASE_REGISTER plus OFFSET, and
+  // return true. On failure, report and return false. (Used for
+  // DW_CFA_def_cfa and DW_CFA_def_cfa_sf.)
+  bool DoDefCFA(unsigned base_register, long offset);
+
+  // Change the offset of the CFA rule to OFFSET, and return true. On
+  // failure, report and return false. (Subroutine for
+  // DW_CFA_def_cfa_offset and DW_CFA_def_cfa_offset_sf.)
+  bool DoDefCFAOffset(long offset);
+
+  // Specify that REG can be recovered using RULE, and return true. On
+  // failure, report and return false.
+  bool DoRule(unsigned reg, Rule *rule);
+
+  // Specify that REG can be found at OFFSET from the CFA, and return true.
+  // On failure, report and return false. (Subroutine for DW_CFA_offset,
+  // DW_CFA_offset_extended, and DW_CFA_offset_extended_sf.)
+  bool DoOffset(unsigned reg, long offset);
+
+  // Specify that the caller's value for REG is the CFA plus OFFSET,
+  // and return true. On failure, report and return false. (Subroutine
+  // for DW_CFA_val_offset and DW_CFA_val_offset_sf.)
+  bool DoValOffset(unsigned reg, long offset);
+
+  // Restore REG to the rule established in the CIE, and return true. On
+  // failure, report and return false. (Subroutine for DW_CFA_restore and
+  // DW_CFA_restore_extended.)
+  bool DoRestore(unsigned reg);
+
+  // Return the section offset of the instruction at cursor. For use
+  // in error messages.
+  uint64 CursorOffset() { return entry_->offset + (cursor_ - entry_->start); }
+
+  // Report that entry_ is incomplete, and return false. For brevity.
+  bool ReportIncomplete() {
+    reporter_->Incomplete(entry_->offset, entry_->kind);
+    return false;
+  }
+
+  // For reading multi-byte values with the appropriate endianness.
+  ByteReader *reader_;
+
+  // The handler to which we should report the data we find.
+  Handler *handler_;
+
+  // For reporting problems in the info we're parsing.
+  Reporter *reporter_;
+
+  // The code address to which the next instruction in the stream applies.
+  uint64 address_;
+
+  // The entry whose instructions we are currently processing. This is
+  // first a CIE, and then an FDE.
+  const Entry *entry_;
+
+  // The next instruction to process.
+  const char *cursor_;
+
+  // The current set of rules.
+  RuleMap rules_;
+
+  // The set of rules established by the CIE, used by DW_CFA_restore
+  // and DW_CFA_restore_extended. We set this after interpreting the
+  // CIE's instructions.
+  RuleMap cie_rules_;
+
+  // A stack of saved states, for DW_CFA_remember_state and
+  // DW_CFA_restore_state.
+  std::stack<RuleMap>* saved_rules_;
+};
+
+bool CallFrameInfo::State::InterpretCIE(const CIE &cie) {
+  entry_ = &cie;
+  cursor_ = entry_->instructions;
+  while (cursor_ < entry_->end)
+    if (!DoInstruction())
+      return false;
+  // Note the rules established by the CIE, for use by DW_CFA_restore
+  // and DW_CFA_restore_extended.
+  cie_rules_ = rules_;
+  return true;
+}
+
+bool CallFrameInfo::State::InterpretFDE(const FDE &fde) {
+  entry_ = &fde;
+  cursor_ = entry_->instructions;
+  while (cursor_ < entry_->end)
+    if (!DoInstruction())
+      return false;
+  return true;
+}
+
+bool CallFrameInfo::State::ParseOperands(const char *format,
+                                         Operands *operands) {
+  size_t len;
+  const char *operand;
+
+  for (operand = format; *operand; operand++) {
+    size_t bytes_left = entry_->end - cursor_;
+    switch (*operand) {
+      case 'r':
+        operands->register_number = reader_->ReadUnsignedLEB128(cursor_, &len);
+        if (len > bytes_left) return ReportIncomplete();
+        cursor_ += len;
+        break;
+
+      case 'o':
+        operands->offset = reader_->ReadUnsignedLEB128(cursor_, &len);
+        if (len > bytes_left) return ReportIncomplete();
+        cursor_ += len;
+        break;
+
+      case 's':
+        operands->signed_offset = reader_->ReadSignedLEB128(cursor_, &len);
+        if (len > bytes_left) return ReportIncomplete();
+        cursor_ += len;
+        break;
+
+      case 'a':
+        operands->offset =
+          reader_->ReadEncodedPointer(cursor_, entry_->cie->pointer_encoding,
+                                      &len);
+        if (len > bytes_left) return ReportIncomplete();
+        cursor_ += len;
+        break;
+
+      case '1':
+        if (1 > bytes_left) return ReportIncomplete();
+        operands->offset = static_cast<unsigned char>(*cursor_++);
+        break;
+
+      case '2':
+        if (2 > bytes_left) return ReportIncomplete();
+        operands->offset = reader_->ReadTwoBytes(cursor_);
+        cursor_ += 2;
+        break;
+
+      case '4':
+        if (4 > bytes_left) return ReportIncomplete();
+        operands->offset = reader_->ReadFourBytes(cursor_);
+        cursor_ += 4;
+        break;
+
+      case '8':
+        if (8 > bytes_left) return ReportIncomplete();
+        operands->offset = reader_->ReadEightBytes(cursor_);
+        cursor_ += 8;
+        break;
+
+      case 'e': {
+        size_t expression_length = reader_->ReadUnsignedLEB128(cursor_, &len);
+        if (len > bytes_left || expression_length > bytes_left - len)
+          return ReportIncomplete();
+        cursor_ += len;
+        operands->expression = string(cursor_, expression_length);
+        cursor_ += expression_length;
+        break;
+      }
+
+      default:
+        MOZ_ASSERT(0);
+    }
+  }
+
+  return true;
+}
+
+bool CallFrameInfo::State::DoInstruction() {
+  CIE *cie = entry_->cie;
+  Operands ops;
+
+  // Our entry's kind should have been set by now.
+  MOZ_ASSERT(entry_->kind != kUnknown);
+
+  // We shouldn't have been invoked unless there were more
+  // instructions to parse.
+  MOZ_ASSERT(cursor_ < entry_->end);
+
+  unsigned opcode = *cursor_++;
+  if ((opcode & 0xc0) != 0) {
+    switch (opcode & 0xc0) {
+      // Advance the address.
+      case DW_CFA_advance_loc: {
+        size_t code_offset = opcode & 0x3f;
+        address_ += code_offset * cie->code_alignment_factor;
+        break;
+      }
+
+      // Find a register at an offset from the CFA.
+      case DW_CFA_offset:
+        if (!ParseOperands("o", &ops) ||
+            !DoOffset(opcode & 0x3f, ops.offset * cie->data_alignment_factor))
+          return false;
+        break;
+
+      // Restore the rule established for a register by the CIE.
+      case DW_CFA_restore:
+        if (!DoRestore(opcode & 0x3f)) return false;
+        break;
+
+      // The 'if' above should have excluded this possibility.
+      default:
+        MOZ_ASSERT(0);
+    }
+
+    // Return here, so the big switch below won't be indented.
+    return true;
+  }
+
+  switch (opcode) {
+    // Set the address.
+    case DW_CFA_set_loc:
+      if (!ParseOperands("a", &ops)) return false;
+      address_ = ops.offset;
+      break;
+
+    // Advance the address.
+    case DW_CFA_advance_loc1:
+      if (!ParseOperands("1", &ops)) return false;
+      address_ += ops.offset * cie->code_alignment_factor;
+      break;
+
+    // Advance the address.
+    case DW_CFA_advance_loc2:
+      if (!ParseOperands("2", &ops)) return false;
+      address_ += ops.offset * cie->code_alignment_factor;
+      break;
+
+    // Advance the address.
+    case DW_CFA_advance_loc4:
+      if (!ParseOperands("4", &ops)) return false;
+      address_ += ops.offset * cie->code_alignment_factor;
+      break;
+
+    // Advance the address.
+    case DW_CFA_MIPS_advance_loc8:
+      if (!ParseOperands("8", &ops)) return false;
+      address_ += ops.offset * cie->code_alignment_factor;
+      break;
+
+    // Compute the CFA by adding an offset to a register.
+    case DW_CFA_def_cfa:
+      if (!ParseOperands("ro", &ops) ||
+          !DoDefCFA(ops.register_number, ops.offset))
+        return false;
+      break;
+
+    // Compute the CFA by adding an offset to a register.
+    case DW_CFA_def_cfa_sf:
+      if (!ParseOperands("rs", &ops) ||
+          !DoDefCFA(ops.register_number,
+                    ops.signed_offset * cie->data_alignment_factor))
+        return false;
+      break;
+
+    // Change the base register used to compute the CFA.
+    case DW_CFA_def_cfa_register: {
+      Rule *cfa_rule = rules_.CFARule();
+      if (!cfa_rule) {
+        reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset());
+        return false;
+      }
+      if (!ParseOperands("r", &ops)) return false;
+      cfa_rule->SetBaseRegister(ops.register_number);
+      if (!cfa_rule->Handle(handler_, address_, Handler::kCFARegister))
+        return false;
+      break;
+    }
+
+    // Change the offset used to compute the CFA.
+    case DW_CFA_def_cfa_offset:
+      if (!ParseOperands("o", &ops) ||
+          !DoDefCFAOffset(ops.offset))
+        return false;
+      break;
+
+    // Change the offset used to compute the CFA.
+    case DW_CFA_def_cfa_offset_sf:
+      if (!ParseOperands("s", &ops) ||
+          !DoDefCFAOffset(ops.signed_offset * cie->data_alignment_factor))
+        return false;
+      break;
+
+    // Specify an expression whose value is the CFA.
+    case DW_CFA_def_cfa_expression: {
+      if (!ParseOperands("e", &ops))
+        return false;
+      Rule *rule = new ValExpressionRule(ops.expression);
+      rules_.SetCFARule(rule);
+      if (!rule->Handle(handler_, address_, Handler::kCFARegister))
+        return false;
+      break;
+    }
+
+    // The register's value cannot be recovered.
+    case DW_CFA_undefined: {
+      if (!ParseOperands("r", &ops) ||
+          !DoRule(ops.register_number, new UndefinedRule()))
+        return false;
+      break;
+    }
+
+    // The register's value is unchanged from its value in the caller.
+    case DW_CFA_same_value: {
+      if (!ParseOperands("r", &ops) ||
+          !DoRule(ops.register_number, new SameValueRule()))
+        return false;
+      break;
+    }
+
+    // Find a register at an offset from the CFA.
+    case DW_CFA_offset_extended:
+      if (!ParseOperands("ro", &ops) ||
+          !DoOffset(ops.register_number,
+                    ops.offset * cie->data_alignment_factor))
+        return false;
+      break;
+
+    // The register is saved at an offset from the CFA.
+    case DW_CFA_offset_extended_sf:
+      if (!ParseOperands("rs", &ops) ||
+          !DoOffset(ops.register_number,
+                    ops.signed_offset * cie->data_alignment_factor))
+        return false;
+      break;
+
+    // The register is saved at an offset from the CFA.
+    case DW_CFA_GNU_negative_offset_extended:
+      if (!ParseOperands("ro", &ops) ||
+          !DoOffset(ops.register_number,
+                    -ops.offset * cie->data_alignment_factor))
+        return false;
+      break;
+
+    // The register's value is the sum of the CFA plus an offset.
+    case DW_CFA_val_offset:
+      if (!ParseOperands("ro", &ops) ||
+          !DoValOffset(ops.register_number,
+                       ops.offset * cie->data_alignment_factor))
+        return false;
+      break;
+
+    // The register's value is the sum of the CFA plus an offset.
+    case DW_CFA_val_offset_sf:
+      if (!ParseOperands("rs", &ops) ||
+          !DoValOffset(ops.register_number,
+                       ops.signed_offset * cie->data_alignment_factor))
+        return false;
+      break;
+
+    // The register has been saved in another register.
+    case DW_CFA_register: {
+      if (!ParseOperands("ro", &ops) ||
+          !DoRule(ops.register_number, new RegisterRule(ops.offset)))
+        return false;
+      break;
+    }
+
+    // An expression yields the address at which the register is saved.
+    case DW_CFA_expression: {
+      if (!ParseOperands("re", &ops) ||
+          !DoRule(ops.register_number, new ExpressionRule(ops.expression)))
+        return false;
+      break;
+    }
+
+    // An expression yields the caller's value for the register.
+    case DW_CFA_val_expression: {
+      if (!ParseOperands("re", &ops) ||
+          !DoRule(ops.register_number, new ValExpressionRule(ops.expression)))
+        return false;
+      break;
+    }
+
+    // Restore the rule established for a register by the CIE.
+    case DW_CFA_restore_extended:
+      if (!ParseOperands("r", &ops) ||
+          !DoRestore( ops.register_number))
+        return false;
+      break;
+
+    // Save the current set of rules on a stack.
+    case DW_CFA_remember_state:
+      if (!saved_rules_) {
+        saved_rules_ = new std::stack<RuleMap>();
+      }
+      saved_rules_->push(rules_);
+      break;
+
+    // Pop the current set of rules off the stack.
+    case DW_CFA_restore_state: {
+      if (!saved_rules_ || saved_rules_->empty()) {
+        reporter_->EmptyStateStack(entry_->offset, entry_->kind,
+                                   CursorOffset());
+        return false;
+      }
+      const RuleMap &new_rules = saved_rules_->top();
+      if (rules_.CFARule() && !new_rules.CFARule()) {
+        reporter_->ClearingCFARule(entry_->offset, entry_->kind,
+                                   CursorOffset());
+        return false;
+      }
+      rules_.HandleTransitionTo(handler_, address_, new_rules);
+      rules_ = new_rules;
+      saved_rules_->pop();
+      break;
+    }
+
+    // No operation.  (Padding instruction.)
+    case DW_CFA_nop:
+      break;
+
+    // A SPARC register window save: Registers 8 through 15 (%o0-%o7)
+    // are saved in registers 24 through 31 (%i0-%i7), and registers
+    // 16 through 31 (%l0-%l7 and %i0-%i7) are saved at CFA offsets
+    // (0-15 * the register size). The register numbers must be
+    // hard-coded. A GNU extension, and not a pretty one.
+    case DW_CFA_GNU_window_save: {
+      // Save %o0-%o7 in %i0-%i7.
+      for (int i = 8; i < 16; i++)
+        if (!DoRule(i, new RegisterRule(i + 16)))
+          return false;
+      // Save %l0-%l7 and %i0-%i7 at the CFA.
+      for (int i = 16; i < 32; i++)
+        // Assume that the byte reader's address size is the same as
+        // the architecture's register size. !@#%*^ hilarious.
+        if (!DoRule(i, new OffsetRule(Handler::kCFARegister,
+                                      (i - 16) * reader_->AddressSize())))
+          return false;
+      break;
+    }
+
+    // I'm not sure what this is. GDB doesn't use it for unwinding.
+    case DW_CFA_GNU_args_size:
+      if (!ParseOperands("o", &ops)) return false;
+      break;
+
+    // An opcode we don't recognize.
+    default: {
+      reporter_->BadInstruction(entry_->offset, entry_->kind, CursorOffset());
+      return false;
+    }
+  }
+
+  return true;
+}
+
+bool CallFrameInfo::State::DoDefCFA(unsigned base_register, long offset) {
+  Rule *rule = new ValOffsetRule(base_register, offset);
+  rules_.SetCFARule(rule);
+  return rule->Handle(handler_, address_, Handler::kCFARegister);
+}
+
+bool CallFrameInfo::State::DoDefCFAOffset(long offset) {
+  Rule *cfa_rule = rules_.CFARule();
+  if (!cfa_rule) {
+    reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset());
+    return false;
+  }
+  cfa_rule->SetOffset(offset);
+  return cfa_rule->Handle(handler_, address_, Handler::kCFARegister);
+}
+
+bool CallFrameInfo::State::DoRule(unsigned reg, Rule *rule) {
+  rules_.SetRegisterRule(reg, rule);
+  return rule->Handle(handler_, address_, reg);
+}
+
+bool CallFrameInfo::State::DoOffset(unsigned reg, long offset) {
+  if (!rules_.CFARule()) {
+    reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset());
+    return false;
+  }
+  return DoRule(reg,
+                new OffsetRule(Handler::kCFARegister, offset));
+}
+
+bool CallFrameInfo::State::DoValOffset(unsigned reg, long offset) {
+  if (!rules_.CFARule()) {
+    reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset());
+    return false;
+  }
+  return DoRule(reg,
+                new ValOffsetRule(Handler::kCFARegister, offset));
+}
+
+bool CallFrameInfo::State::DoRestore(unsigned reg) {
+  // DW_CFA_restore and DW_CFA_restore_extended don't make sense in a CIE.
+  if (entry_->kind == kCIE) {
+    reporter_->RestoreInCIE(entry_->offset, CursorOffset());
+    return false;
+  }
+  Rule *rule = cie_rules_.RegisterRule(reg);
+  if (!rule) {
+    // This isn't really the right thing to do, but since CFI generally
+    // only mentions callee-saves registers, and GCC's convention for
+    // callee-saves registers is that they are unchanged, it's a good
+    // approximation.
+    rule = new SameValueRule();
+  }
+  return DoRule(reg, rule);
+}
+
+bool CallFrameInfo::ReadEntryPrologue(const char *cursor, Entry *entry) {
+  const char *buffer_end = buffer_ + buffer_length_;
+
+  // Initialize enough of ENTRY for use in error reporting.
+  entry->offset = cursor - buffer_;
+  entry->start = cursor;
+  entry->kind = kUnknown;
+  entry->end = NULL;
+
+  // Read the initial length. This sets reader_'s offset size.
+  size_t length_size;
+  uint64 length = reader_->ReadInitialLength(cursor, &length_size);
+  if (length_size > size_t(buffer_end - cursor))
+    return ReportIncomplete(entry);
+  cursor += length_size;
+
+  // In a .eh_frame section, a length of zero marks the end of the series
+  // of entries.
+  if (length == 0 && eh_frame_) {
+    entry->kind = kTerminator;
+    entry->end = cursor;
+    return true;
+  }
+
+  // Validate the length.
+  if (length > size_t(buffer_end - cursor))
+    return ReportIncomplete(entry);
+
+  // The length is the number of bytes after the initial length field;
+  // we have that position handy at this point, so compute the end
+  // now. (If we're parsing 64-bit-offset DWARF on a 32-bit machine,
+  // and the length didn't fit in a size_t, we would have rejected it
+  // above.)
+  entry->end = cursor + length;
+
+  // Parse the next field: either the offset of a CIE or a CIE id.
+  size_t offset_size = reader_->OffsetSize();
+  if (offset_size > size_t(entry->end - cursor)) return ReportIncomplete(entry);
+  entry->id = reader_->ReadOffset(cursor);
+
+  // Don't advance cursor past id field yet; in .eh_frame data we need
+  // the id's position to compute the section offset of an FDE's CIE.
+
+  // Now we can decide what kind of entry this is.
+  if (eh_frame_) {
+    // In .eh_frame data, an ID of zero marks the entry as a CIE, and
+    // anything else is an offset from the id field of the FDE to the start
+    // of the CIE.
+    if (entry->id == 0) {
+      entry->kind = kCIE;
+    } else {
+      entry->kind = kFDE;
+      // Turn the offset from the id into an offset from the buffer's start.
+      entry->id = (cursor - buffer_) - entry->id;
+    }
+  } else {
+    // In DWARF CFI data, an ID of ~0 (of the appropriate width, given the
+    // offset size for the entry) marks the entry as a CIE, and anything
+    // else is the offset of the CIE from the beginning of the section.
+    if (offset_size == 4)
+      entry->kind = (entry->id == 0xffffffff) ? kCIE : kFDE;
+    else {
+      MOZ_ASSERT(offset_size == 8);
+      entry->kind = (entry->id == 0xffffffffffffffffULL) ? kCIE : kFDE;
+    }
+  }
+
+  // Now advance cursor past the id.
+   cursor += offset_size;
+
+  // The fields specific to this kind of entry start here.
+  entry->fields = cursor;
+
+  entry->cie = NULL;
+
+  return true;
+}
+
+bool CallFrameInfo::ReadCIEFields(CIE *cie) {
+  const char *cursor = cie->fields;
+  size_t len;
+
+  MOZ_ASSERT(cie->kind == kCIE);
+
+  // Prepare for early exit.
+  cie->version = 0;
+  cie->augmentation.clear();
+  cie->code_alignment_factor = 0;
+  cie->data_alignment_factor = 0;
+  cie->return_address_register = 0;
+  cie->has_z_augmentation = false;
+  cie->pointer_encoding = DW_EH_PE_absptr;
+  cie->instructions = 0;
+
+  // Parse the version number.
+  if (cie->end - cursor < 1)
+    return ReportIncomplete(cie);
+  cie->version = reader_->ReadOneByte(cursor);
+  cursor++;
+
+  // If we don't recognize the version, we can't parse any more fields of the
+  // CIE. For DWARF CFI, we handle versions 1 through 3 (there was never a
+  // version 2 of CFI data). For .eh_frame, we handle versions 1 and 3 as well;
+  // the difference between those versions seems to be the same as for
+  // .debug_frame.
+  if (cie->version < 1 || cie->version > 3) {
+    reporter_->UnrecognizedVersion(cie->offset, cie->version);
+    return false;
+  }
+
+  const char *augmentation_start = cursor;
+  const void *augmentation_end =
+      memchr(augmentation_start, '\0', cie->end - augmentation_start);
+  if (! augmentation_end) return ReportIncomplete(cie);
+  cursor = static_cast<const char *>(augmentation_end);
+  cie->augmentation = string(augmentation_start,
+                                  cursor - augmentation_start);
+  // Skip the terminating '\0'.
+  cursor++;
+
+  // Is this CFI augmented?
+  if (!cie->augmentation.empty()) {
+    // Is it an augmentation we recognize?
+    if (cie->augmentation[0] == DW_Z_augmentation_start) {
+      // Linux C++ ABI 'z' augmentation, used for exception handling data.
+      cie->has_z_augmentation = true;
+    } else {
+      // Not an augmentation we recognize. Augmentations can have arbitrary
+      // effects on the form of rest of the content, so we have to give up.
+      reporter_->UnrecognizedAugmentation(cie->offset, cie->augmentation);
+      return false;
+    }
+  }
+
+  // Parse the code alignment factor.
+  cie->code_alignment_factor = reader_->ReadUnsignedLEB128(cursor, &len);
+  if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie);
+  cursor += len;
+
+  // Parse the data alignment factor.
+  cie->data_alignment_factor = reader_->ReadSignedLEB128(cursor, &len);
+  if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie);
+  cursor += len;
+
+  // Parse the return address register. This is a ubyte in version 1, and
+  // a ULEB128 in version 3.
+  if (cie->version == 1) {
+    if (cursor >= cie->end) return ReportIncomplete(cie);
+    cie->return_address_register = uint8(*cursor++);
+  } else {
+    cie->return_address_register = reader_->ReadUnsignedLEB128(cursor, &len);
+    if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie);
+    cursor += len;
+  }
+
+  // If we have a 'z' augmentation string, find the augmentation data and
+  // use the augmentation string to parse it.
+  if (cie->has_z_augmentation) {
+    uint64_t data_size = reader_->ReadUnsignedLEB128(cursor, &len);
+    if (size_t(cie->end - cursor) < len + data_size)
+      return ReportIncomplete(cie);
+    cursor += len;
+    const char *data = cursor;
+    cursor += data_size;
+    const char *data_end = cursor;
+
+    cie->has_z_lsda = false;
+    cie->has_z_personality = false;
+    cie->has_z_signal_frame = false;
+
+    // Walk the augmentation string, and extract values from the
+    // augmentation data as the string directs.
+    for (size_t i = 1; i < cie->augmentation.size(); i++) {
+      switch (cie->augmentation[i]) {
+        case DW_Z_has_LSDA:
+          // The CIE's augmentation data holds the language-specific data
+          // area pointer's encoding, and the FDE's augmentation data holds
+          // the pointer itself.
+          cie->has_z_lsda = true;
+          // Fetch the LSDA encoding from the augmentation data.
+          if (data >= data_end) return ReportIncomplete(cie);
+          cie->lsda_encoding = DwarfPointerEncoding(*data++);
+          if (!reader_->ValidEncoding(cie->lsda_encoding)) {
+            reporter_->InvalidPointerEncoding(cie->offset, cie->lsda_encoding);
+            return false;
+          }
+          // Don't check if the encoding is usable here --- we haven't
+          // read the FDE's fields yet, so we're not prepared for
+          // DW_EH_PE_funcrel, although that's a fine encoding for the
+          // LSDA to use, since it appears in the FDE.
+          break;
+
+        case DW_Z_has_personality_routine:
+          // The CIE's augmentation data holds the personality routine
+          // pointer's encoding, followed by the pointer itself.
+          cie->has_z_personality = true;
+          // Fetch the personality routine pointer's encoding from the
+          // augmentation data.
+          if (data >= data_end) return ReportIncomplete(cie);
+          cie->personality_encoding = DwarfPointerEncoding(*data++);
+          if (!reader_->ValidEncoding(cie->personality_encoding)) {
+            reporter_->InvalidPointerEncoding(cie->offset,
+                                              cie->personality_encoding);
+            return false;
+          }
+          if (!reader_->UsableEncoding(cie->personality_encoding)) {
+            reporter_->UnusablePointerEncoding(cie->offset,
+                                               cie->personality_encoding);
+            return false;
+          }
+          // Fetch the personality routine's pointer itself from the data.
+          cie->personality_address =
+            reader_->ReadEncodedPointer(data, cie->personality_encoding,
+                                        &len);
+          if (len > size_t(data_end - data))
+            return ReportIncomplete(cie);
+          data += len;
+          break;
+
+        case DW_Z_has_FDE_address_encoding:
+          // The CIE's augmentation data holds the pointer encoding to use
+          // for addresses in the FDE.
+          if (data >= data_end) return ReportIncomplete(cie);
+          cie->pointer_encoding = DwarfPointerEncoding(*data++);
+          if (!reader_->ValidEncoding(cie->pointer_encoding)) {
+            reporter_->InvalidPointerEncoding(cie->offset,
+                                              cie->pointer_encoding);
+            return false;
+          }
+          if (!reader_->UsableEncoding(cie->pointer_encoding)) {
+            reporter_->UnusablePointerEncoding(cie->offset,
+                                               cie->pointer_encoding);
+            return false;
+          }
+          break;
+
+        case DW_Z_is_signal_trampoline:
+          // Frames using this CIE are signal delivery frames.
+          cie->has_z_signal_frame = true;
+          break;
+
+        default:
+          // An augmentation we don't recognize.
+          reporter_->UnrecognizedAugmentation(cie->offset, cie->augmentation);
+          return false;
+      }
+    }
+  }
+
+  // The CIE's instructions start here.
+  cie->instructions = cursor;
+
+  return true;
+}
+
+bool CallFrameInfo::ReadFDEFields(FDE *fde) {
+  const char *cursor = fde->fields;
+  size_t size;
+
+  fde->address = reader_->ReadEncodedPointer(cursor, fde->cie->pointer_encoding,
+                                             &size);
+  if (size > size_t(fde->end - cursor))
+    return ReportIncomplete(fde);
+  cursor += size;
+  reader_->SetFunctionBase(fde->address);
+
+  // For the length, we strip off the upper nybble of the encoding used for
+  // the starting address.
+  DwarfPointerEncoding length_encoding =
+    DwarfPointerEncoding(fde->cie->pointer_encoding & 0x0f);
+  fde->size = reader_->ReadEncodedPointer(cursor, length_encoding, &size);
+  if (size > size_t(fde->end - cursor))
+    return ReportIncomplete(fde);
+  cursor += size;
+
+  // If the CIE has a 'z' augmentation string, then augmentation data
+  // appears here.
+  if (fde->cie->has_z_augmentation) {
+    uint64_t data_size = reader_->ReadUnsignedLEB128(cursor, &size);
+    if (size_t(fde->end - cursor) < size + data_size)
+      return ReportIncomplete(fde);
+    cursor += size;
+
+    // In the abstract, we should walk the augmentation string, and extract
+    // items from the FDE's augmentation data as we encounter augmentation
+    // string characters that specify their presence: the ordering of items
+    // in the augmentation string determines the arrangement of values in
+    // the augmentation data.
+    //
+    // In practice, there's only ever one value in FDE augmentation data
+    // that we support --- the LSDA pointer --- and we have to bail if we
+    // see any unrecognized augmentation string characters. So if there is
+    // anything here at all, we know what it is, and where it starts.
+    if (fde->cie->has_z_lsda) {
+      // Check whether the LSDA's pointer encoding is usable now: only once
+      // we've parsed the FDE's starting address do we call reader_->
+      // SetFunctionBase, so that the DW_EH_PE_funcrel encoding becomes
+      // usable.
+      if (!reader_->UsableEncoding(fde->cie->lsda_encoding)) {
+        reporter_->UnusablePointerEncoding(fde->cie->offset,
+                                           fde->cie->lsda_encoding);
+        return false;
+      }
+
+      fde->lsda_address =
+        reader_->ReadEncodedPointer(cursor, fde->cie->lsda_encoding, &size);
+      if (size > data_size)
+        return ReportIncomplete(fde);
+      // Ideally, we would also complain here if there were unconsumed
+      // augmentation data.
+    }
+
+    cursor += data_size;
+  }
+
+  // The FDE's instructions start after those.
+  fde->instructions = cursor;
+
+  return true;
+}
+
+bool CallFrameInfo::Start() {
+  const char *buffer_end = buffer_ + buffer_length_;
+  const char *cursor;
+  bool all_ok = true;
+  const char *entry_end;
+  bool ok;
+
+  // Traverse all the entries in buffer_, skipping CIEs and offering
+  // FDEs to the handler.
+  for (cursor = buffer_; cursor < buffer_end;
+       cursor = entry_end, all_ok = all_ok && ok) {
+    FDE fde;
+
+    // Make it easy to skip this entry with 'continue': assume that
+    // things are not okay until we've checked all the data, and
+    // prepare the address of the next entry.
+    ok = false;
+
+    // Read the entry's prologue.
+    if (!ReadEntryPrologue(cursor, &fde)) {
+      if (!fde.end) {
+        // If we couldn't even figure out this entry's extent, then we
+        // must stop processing entries altogether.
+        all_ok = false;
+        break;
+      }
+      entry_end = fde.end;
+      continue;
+    }
+
+    // The next iteration picks up after this entry.
+    entry_end = fde.end;
+
+    // Did we see an .eh_frame terminating mark?
+    if (fde.kind == kTerminator) {
+      // If there appears to be more data left in the section after the
+      // terminating mark, warn the user. But this is just a warning;
+      // we leave all_ok true.
+      if (fde.end < buffer_end) reporter_->EarlyEHTerminator(fde.offset);
+      break;
+    }
+
+    // In this loop, we skip CIEs. We only parse them fully when we
+    // parse an FDE that refers to them. This limits our memory
+    // consumption (beyond the buffer itself) to that needed to
+    // process the largest single entry.
+    if (fde.kind != kFDE) {
+      ok = true;
+      continue;
+    }
+
+    // Validate the CIE pointer.
+    if (fde.id > buffer_length_) {
+      reporter_->CIEPointerOutOfRange(fde.offset, fde.id);
+      continue;
+    }
+
+    CIE cie;
+
+    // Parse this FDE's CIE header.
+    if (!ReadEntryPrologue(buffer_ + fde.id, &cie))
+      continue;
+    // This had better be an actual CIE.
+    if (cie.kind != kCIE) {
+      reporter_->BadCIEId(fde.offset, fde.id);
+      continue;
+    }
+    if (!ReadCIEFields(&cie))
+      continue;
+
+    // We now have the values that govern both the CIE and the FDE.
+    cie.cie = &cie;
+    fde.cie = &cie;
+
+    // Parse the FDE's header.
+    if (!ReadFDEFields(&fde))
+      continue;
+
+    // Call Entry to ask the consumer if they're interested.
+    if (!handler_->Entry(fde.offset, fde.address, fde.size,
+                         cie.version, cie.augmentation,
+                         cie.return_address_register)) {
+      // The handler isn't interested in this entry. That's not an error.
+      ok = true;
+      continue;
+    }
+
+    if (cie.has_z_augmentation) {
+      // Report the personality routine address, if we have one.
+      if (cie.has_z_personality) {
+        if (!handler_
+            ->PersonalityRoutine(cie.personality_address,
+                                 IsIndirectEncoding(cie.personality_encoding)))
+          continue;
+      }
+
+      // Report the language-specific data area address, if we have one.
+      if (cie.has_z_lsda) {
+        if (!handler_
+            ->LanguageSpecificDataArea(fde.lsda_address,
+                                       IsIndirectEncoding(cie.lsda_encoding)))
+          continue;
+      }
+
+      // If this is a signal-handling frame, report that.
+      if (cie.has_z_signal_frame) {
+        if (!handler_->SignalHandler())
+          continue;
+      }
+    }
+
+    // Interpret the CIE's instructions, and then the FDE's instructions.
+    State state(reader_, handler_, reporter_, fde.address);
+    ok = state.InterpretCIE(cie) && state.InterpretFDE(fde);
+
+    // Tell the ByteReader that the function start address from the
+    // FDE header is no longer valid.
+    reader_->ClearFunctionBase();
+
+    // Report the end of the entry.
+    handler_->End();
+  }
+
+  return all_ok;
+}
+
+const char *CallFrameInfo::KindName(EntryKind kind) {
+  if (kind == CallFrameInfo::kUnknown)
+    return "entry";
+  else if (kind == CallFrameInfo::kCIE)
+    return "common information entry";
+  else if (kind == CallFrameInfo::kFDE)
+    return "frame description entry";
+  else {
+    MOZ_ASSERT (kind == CallFrameInfo::kTerminator);
+    return ".eh_frame sequence terminator";
+  }
+}
+
+bool CallFrameInfo::ReportIncomplete(Entry *entry) {
+  reporter_->Incomplete(entry->offset, entry->kind);
+  return false;
+}
+
+void CallFrameInfo::Reporter::Incomplete(uint64 offset,
+                                         CallFrameInfo::EntryKind kind) {
+  char buf[300];
+  snprintf(buf, sizeof(buf),
+           "%s: CFI %s at offset 0x%llx in '%s': entry ends early\n",
+           filename_.c_str(), CallFrameInfo::KindName(kind), offset,
+           section_.c_str());
+  log_(buf);
+}
+
+void CallFrameInfo::Reporter::EarlyEHTerminator(uint64 offset) {
+  char buf[300];
+  snprintf(buf, sizeof(buf),
+           "%s: CFI at offset 0x%llx in '%s': saw end-of-data marker"
+           " before end of section contents\n",
+           filename_.c_str(), offset, section_.c_str());
+  log_(buf);
+}
+
+void CallFrameInfo::Reporter::CIEPointerOutOfRange(uint64 offset,
+                                                   uint64 cie_offset) {
+  char buf[300];
+  snprintf(buf, sizeof(buf),
+           "%s: CFI frame description entry at offset 0x%llx in '%s':"
+           " CIE pointer is out of range: 0x%llx\n",
+           filename_.c_str(), offset, section_.c_str(), cie_offset);
+  log_(buf);
+}
+
+void CallFrameInfo::Reporter::BadCIEId(uint64 offset, uint64 cie_offset) {
+  char buf[300];
+  snprintf(buf, sizeof(buf),
+           "%s: CFI frame description entry at offset 0x%llx in '%s':"
+           " CIE pointer does not point to a CIE: 0x%llx\n",
+           filename_.c_str(), offset, section_.c_str(), cie_offset);
+  log_(buf);
+}
+
+void CallFrameInfo::Reporter::UnrecognizedVersion(uint64 offset, int version) {
+  char buf[300];
+  snprintf(buf, sizeof(buf),
+           "%s: CFI frame description entry at offset 0x%llx in '%s':"
+           " CIE specifies unrecognized version: %d\n",
+           filename_.c_str(), offset, section_.c_str(), version);
+  log_(buf);
+}
+
+void CallFrameInfo::Reporter::UnrecognizedAugmentation(uint64 offset,
+                                                       const string &aug) {
+  char buf[300];
+  snprintf(buf, sizeof(buf),
+           "%s: CFI frame description entry at offset 0x%llx in '%s':"
+           " CIE specifies unrecognized augmentation: '%s'\n",
+           filename_.c_str(), offset, section_.c_str(), aug.c_str());
+  log_(buf);
+}
+
+void CallFrameInfo::Reporter::InvalidPointerEncoding(uint64 offset,
+                                                     uint8 encoding) {
+  char buf[300];
+  snprintf(buf, sizeof(buf),
+           "%s: CFI common information entry at offset 0x%llx in '%s':"
+           " 'z' augmentation specifies invalid pointer encoding: 0x%02x\n",
+           filename_.c_str(), offset, section_.c_str(), encoding);
+  log_(buf);
+}
+
+void CallFrameInfo::Reporter::UnusablePointerEncoding(uint64 offset,
+                                                      uint8 encoding) {
+  char buf[300];
+  snprintf(buf, sizeof(buf),
+           "%s: CFI common information entry at offset 0x%llx in '%s':"
+           " 'z' augmentation specifies a pointer encoding for which"
+           " we have no base address: 0x%02x\n",
+           filename_.c_str(), offset, section_.c_str(), encoding);
+  log_(buf);
+}
+
+void CallFrameInfo::Reporter::RestoreInCIE(uint64 offset, uint64 insn_offset) {
+  char buf[300];
+  snprintf(buf, sizeof(buf),
+           "%s: CFI common information entry at offset 0x%llx in '%s':"
+           " the DW_CFA_restore instruction at offset 0x%llx"
+           " cannot be used in a common information entry\n",
+           filename_.c_str(), offset, section_.c_str(), insn_offset);
+  log_(buf);
+}
+
+void CallFrameInfo::Reporter::BadInstruction(uint64 offset,
+                                             CallFrameInfo::EntryKind kind,
+                                             uint64 insn_offset) {
+  char buf[300];
+  snprintf(buf, sizeof(buf),
+           "%s: CFI %s at offset 0x%llx in section '%s':"
+           " the instruction at offset 0x%llx is unrecognized\n",
+           filename_.c_str(), CallFrameInfo::KindName(kind),
+           offset, section_.c_str(), insn_offset);
+  log_(buf);
+}
+
+void CallFrameInfo::Reporter::NoCFARule(uint64 offset,
+                                        CallFrameInfo::EntryKind kind,
+                                        uint64 insn_offset) {
+  char buf[300];
+  snprintf(buf, sizeof(buf),
+           "%s: CFI %s at offset 0x%llx in section '%s':"
+           " the instruction at offset 0x%llx assumes that a CFA rule has"
+           " been set, but none has been set\n",
+           filename_.c_str(), CallFrameInfo::KindName(kind), offset,
+           section_.c_str(), insn_offset);
+  log_(buf);
+}
+
+void CallFrameInfo::Reporter::EmptyStateStack(uint64 offset,
+                                              CallFrameInfo::EntryKind kind,
+                                              uint64 insn_offset) {
+  char buf[300];
+  snprintf(buf, sizeof(buf),
+           "%s: CFI %s at offset 0x%llx in section '%s':"
+           " the DW_CFA_restore_state instruction at offset 0x%llx"
+           " should pop a saved state from the stack, but the stack is empty\n",
+           filename_.c_str(), CallFrameInfo::KindName(kind), offset,
+           section_.c_str(), insn_offset);
+  log_(buf);
+}
+
+void CallFrameInfo::Reporter::ClearingCFARule(uint64 offset,
+                                              CallFrameInfo::EntryKind kind,
+                                              uint64 insn_offset) {
+  char buf[300];
+  snprintf(buf, sizeof(buf),
+           "%s: CFI %s at offset 0x%llx in section '%s':"
+           " the DW_CFA_restore_state instruction at offset 0x%llx"
+           " would clear the CFA rule in effect\n",
+           filename_.c_str(), CallFrameInfo::KindName(kind), offset,
+           section_.c_str(), insn_offset);
+  log_(buf);
+}
+
+
+const unsigned int DwarfCFIToModule::RegisterNames::I386() {
+  /*
+   8 "$eax", "$ecx", "$edx", "$ebx", "$esp", "$ebp", "$esi", "$edi",
+   3 "$eip", "$eflags", "$unused1",
+   8 "$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7",
+   2 "$unused2", "$unused3",
+   8 "$xmm0", "$xmm1", "$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7",
+   8 "$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7",
+   3 "$fcw", "$fsw", "$mxcsr",
+   8 "$es", "$cs", "$ss", "$ds", "$fs", "$gs", "$unused4", "$unused5",
+   2 "$tr", "$ldtr"
+  */
+  return 8 + 3 + 8 + 2 + 8 + 8 + 3 + 8 + 2;
+}
+
+const unsigned int DwarfCFIToModule::RegisterNames::X86_64() {
+  /*
+   8 "$rax", "$rdx", "$rcx", "$rbx", "$rsi", "$rdi", "$rbp", "$rsp",
+   8 "$r8",  "$r9",  "$r10", "$r11", "$r12", "$r13", "$r14", "$r15",
+   1 "$rip",
+   8 "$xmm0","$xmm1","$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7",
+   8 "$xmm8","$xmm9","$xmm10","$xmm11","$xmm12","$xmm13","$xmm14","$xmm15",
+   8 "$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7",
+   8 "$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7",
+   1 "$rflags",
+   8 "$es", "$cs", "$ss", "$ds", "$fs", "$gs", "$unused1", "$unused2",
+   4 "$fs.base", "$gs.base", "$unused3", "$unused4",
+   2 "$tr", "$ldtr",
+   3 "$mxcsr", "$fcw", "$fsw"
+  */
+  return 8 + 8 + 1 + 8 + 8 + 8 + 8 + 1 + 8 + 4 + 2 + 3;
+}
+
+// Per ARM IHI 0040A, section 3.1
+const unsigned int DwarfCFIToModule::RegisterNames::ARM() {
+  /*
+   8 "r0",  "r1",  "r2",  "r3",  "r4",  "r5",  "r6",  "r7",
+   8 "r8",  "r9",  "r10", "r11", "r12", "sp",  "lr",  "pc",
+   8 "f0",  "f1",  "f2",  "f3",  "f4",  "f5",  "f6",  "f7",
+   8 "fps", "cpsr", "",   "",    "",    "",    "",    "",
+   8 "",    "",    "",    "",    "",    "",    "",    "",
+   8 "",    "",    "",    "",    "",    "",    "",    "",
+   8 "",    "",    "",    "",    "",    "",    "",    "",
+   8 "",    "",    "",    "",    "",    "",    "",    "",
+   8 "s0",  "s1",  "s2",  "s3",  "s4",  "s5",  "s6",  "s7",
+   8 "s8",  "s9",  "s10", "s11", "s12", "s13", "s14", "s15",
+   8 "s16", "s17", "s18", "s19", "s20", "s21", "s22", "s23",
+   8 "s24", "s25", "s26", "s27", "s28", "s29", "s30", "s31",
+   8 "f0",  "f1",  "f2",  "f3",  "f4",  "f5",  "f6",  "f7"
+  */
+  return 13 * 8;
+}
+
+bool DwarfCFIToModule::Entry(size_t offset, uint64 address, uint64 length,
+                             uint8 version, const string &augmentation,
+                             unsigned return_address) {
+  if (DEBUG_DWARF)
+    printf("LUL.DW DwarfCFIToModule::Entry 0x%llx,+%lld\n", address, length);
+
+  summ_->Entry(address, length);
+
+  // If dwarf2reader::CallFrameInfo can handle this version and
+  // augmentation, then we should be okay with that, so there's no
+  // need to check them here.
+
+  // Get ready to collect entries.
+  return_address_ = return_address;
+
+  // Breakpad STACK CFI records must provide a .ra rule, but DWARF CFI
+  // may not establish any rule for .ra if the return address column
+  // is an ordinary register, and that register holds the return
+  // address on entry to the function. So establish an initial .ra
+  // rule citing the return address register.
+  if (return_address_ < num_dw_regs_) {
+    summ_->Rule(address, return_address_, return_address, 0, false);
+  }
+
+  return true;
+}
+
+const UniqueString* DwarfCFIToModule::RegisterName(int i) {
+  if (i < 0) {
+    MOZ_ASSERT(i == kCFARegister);
+    return ustr__ZDcfa();
+  }
+  unsigned reg = i;
+  if (reg == return_address_)
+    return ustr__ZDra();
+
+  char buf[30];
+  sprintf(buf, "dwarf_reg_%u", reg);
+  return ToUniqueString(buf);
+}
+
+bool DwarfCFIToModule::UndefinedRule(uint64 address, int reg) {
+  reporter_->UndefinedNotSupported(entry_offset_, RegisterName(reg));
+  // Treat this as a non-fatal error.
+  return true;
+}
+
+bool DwarfCFIToModule::SameValueRule(uint64 address, int reg) {
+  if (DEBUG_DWARF)
+    printf("LUL.DW  0x%llx: old r%d = Same\n", address, reg);
+  // reg + 0
+  summ_->Rule(address, reg, reg, 0, false);
+  return true;
+}
+
+bool DwarfCFIToModule::OffsetRule(uint64 address, int reg,
+                                  int base_register, long offset) {
+  if (DEBUG_DWARF)
+    printf("LUL.DW  0x%llx: old r%d = *(r%d + %ld)\n",
+           address, reg, base_register, offset);
+  // *(base_register + offset)
+  summ_->Rule(address, reg, base_register, offset, true);
+  return true;
+}
+
+bool DwarfCFIToModule::ValOffsetRule(uint64 address, int reg,
+                                     int base_register, long offset) {
+  if (DEBUG_DWARF)
+    printf("LUL.DW  0x%llx: old r%d = r%d + %ld\n",
+           address, reg, base_register, offset);
+  // base_register + offset
+  summ_->Rule(address, reg, base_register, offset, false);
+  return true;
+}
+
+bool DwarfCFIToModule::RegisterRule(uint64 address, int reg,
+                                    int base_register) {
+  if (DEBUG_DWARF)
+    printf("LUL.DW  0x%llx: old r%d = r%d\n", address, reg, base_register);
+  // base_register + 0
+  summ_->Rule(address, reg, base_register, 0, false);
+  return true;
+}
+
+bool DwarfCFIToModule::ExpressionRule(uint64 address, int reg,
+                                      const string &expression) {
+  reporter_->ExpressionsNotSupported(entry_offset_, RegisterName(reg));
+  // Treat this as a non-fatal error.
+  return true;
+}
+
+bool DwarfCFIToModule::ValExpressionRule(uint64 address, int reg,
+                                         const string &expression) {
+  reporter_->ExpressionsNotSupported(entry_offset_, RegisterName(reg));
+  // Treat this as a non-fatal error.
+  return true;
+}
+
+bool DwarfCFIToModule::End() {
+  //module_->AddStackFrameEntry(entry_);
+  if (DEBUG_DWARF)
+    printf("LUL.DW DwarfCFIToModule::End()\n");
+  summ_->End();
+  return true;
+}
+
+void DwarfCFIToModule::Reporter::UndefinedNotSupported(
+    size_t offset,
+    const UniqueString* reg) {
+  char buf[300];
+  snprintf(buf, sizeof(buf),
+           "DwarfCFIToModule::Reporter::UndefinedNotSupported()\n");
+  log_(buf);
+  //BPLOG(INFO) << file_ << ", section '" << section_
+  //  << "': the call frame entry at offset 0x"
+  //  << std::setbase(16) << offset << std::setbase(10)
+  //  << " sets the rule for register '" << FromUniqueString(reg)
+  //  << "' to 'undefined', but the Breakpad symbol file format cannot "
+  //  << " express this";
+}
+
+// FIXME: move this somewhere sensible
+static bool is_power_of_2(uint64_t n)
+{
+  int i, nSetBits = 0;
+  for (i = 0; i < 8*(int)sizeof(n); i++) {
+    if ((n & ((uint64_t)1) << i) != 0)
+      nSetBits++;
+  }
+  return nSetBits <= 1;
+}
+
+void DwarfCFIToModule::Reporter::ExpressionsNotSupported(
+    size_t offset,
+    const UniqueString* reg) {
+  static uint64_t n_complaints = 0; // This isn't threadsafe
+  n_complaints++;
+  if (!is_power_of_2(n_complaints))
+    return;
+  char buf[300];
+  snprintf(buf, sizeof(buf),
+           "DwarfCFIToModule::Reporter::"
+           "ExpressionsNotSupported(shown %llu times)\n",
+           (unsigned long long int)n_complaints);
+  log_(buf);
+  //BPLOG(INFO) << file_ << ", section '" << section_
+  //  << "': the call frame entry at offset 0x"
+  //  << std::setbase(16) << offset << std::setbase(10)
+  //  << " uses a DWARF expression to describe how to recover register '"
+  //  << FromUniqueString(reg) << "', but this translator cannot yet "
+  //  << "translate DWARF expressions to Breakpad postfix expressions (shown "
+  //  << n_complaints << " times)";
+}
+
+} // namespace lul
new file mode 100644
--- /dev/null
+++ b/tools/profiler/LulDwarfExt.h
@@ -0,0 +1,1272 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+
+// Copyright 2006, 2010 Google Inc. All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// This file is derived from the following files in
+// toolkit/crashreporter/google-breakpad:
+//   src/common/dwarf/types.h
+//   src/common/dwarf/dwarf2enums.h
+//   src/common/dwarf/bytereader.h
+//   src/common/dwarf_cfi_to_module.h
+//   src/common/dwarf/dwarf2reader.h
+
+#ifndef LulDwarfExt_h
+#define LulDwarfExt_h
+
+#include <stdint.h>
+
+#include "mozilla/Assertions.h"
+
+#include "LulDwarfSummariser.h"
+
+typedef signed char         int8;
+typedef short               int16;
+typedef int                 int32;
+typedef long long           int64;
+
+typedef unsigned char      uint8;
+typedef unsigned short     uint16;
+typedef unsigned int       uint32;
+typedef unsigned long long uint64;
+
+#ifdef __PTRDIFF_TYPE__
+typedef          __PTRDIFF_TYPE__ intptr;
+typedef unsigned __PTRDIFF_TYPE__ uintptr;
+#else
+#error "Can't find pointer-sized integral types."
+#endif
+
+
+namespace lul {
+
+// Exception handling frame description pointer formats, as described
+// by the Linux Standard Base Core Specification 4.0, section 11.5,
+// DWARF Extensions.
+enum DwarfPointerEncoding
+  {
+    DW_EH_PE_absptr	= 0x00,
+    DW_EH_PE_omit	= 0xff,
+    DW_EH_PE_uleb128    = 0x01,
+    DW_EH_PE_udata2	= 0x02,
+    DW_EH_PE_udata4	= 0x03,
+    DW_EH_PE_udata8	= 0x04,
+    DW_EH_PE_sleb128    = 0x09,
+    DW_EH_PE_sdata2	= 0x0A,
+    DW_EH_PE_sdata4	= 0x0B,
+    DW_EH_PE_sdata8	= 0x0C,
+    DW_EH_PE_pcrel	= 0x10,
+    DW_EH_PE_textrel	= 0x20,
+    DW_EH_PE_datarel	= 0x30,
+    DW_EH_PE_funcrel	= 0x40,
+    DW_EH_PE_aligned	= 0x50,
+
+    // The GNU toolchain sources define this enum value as well,
+    // simply to help classify the lower nybble values into signed and
+    // unsigned groups.
+    DW_EH_PE_signed	= 0x08,
+
+    // This is not documented in LSB 4.0, but it is used in both the
+    // Linux and OS X toolchains. It can be added to any other
+    // encoding (except DW_EH_PE_aligned), and indicates that the
+    // encoded value represents the address at which the true address
+    // is stored, not the true address itself.
+    DW_EH_PE_indirect	= 0x80
+  };
+
+
+// We can't use the obvious name of LITTLE_ENDIAN and BIG_ENDIAN
+// because it conflicts with a macro
+enum Endianness {
+  ENDIANNESS_BIG,
+  ENDIANNESS_LITTLE
+};
+
+// A ByteReader knows how to read single- and multi-byte values of
+// various endiannesses, sizes, and encodings, as used in DWARF
+// debugging information and Linux C++ exception handling data.
+class ByteReader {
+ public:
+  // Construct a ByteReader capable of reading one-, two-, four-, and
+  // eight-byte values according to ENDIANNESS, absolute machine-sized
+  // addresses, DWARF-style "initial length" values, signed and
+  // unsigned LEB128 numbers, and Linux C++ exception handling data's
+  // encoded pointers.
+  explicit ByteReader(enum Endianness endianness);
+  virtual ~ByteReader();
+
+  // Read a single byte from BUFFER and return it as an unsigned 8 bit
+  // number.
+  uint8 ReadOneByte(const char* buffer) const;
+
+  // Read two bytes from BUFFER and return them as an unsigned 16 bit
+  // number, using this ByteReader's endianness.
+  uint16 ReadTwoBytes(const char* buffer) const;
+
+  // Read four bytes from BUFFER and return them as an unsigned 32 bit
+  // number, using this ByteReader's endianness. This function returns
+  // a uint64 so that it is compatible with ReadAddress and
+  // ReadOffset. The number it returns will never be outside the range
+  // of an unsigned 32 bit integer.
+  uint64 ReadFourBytes(const char* buffer) const;
+
+  // Read eight bytes from BUFFER and return them as an unsigned 64
+  // bit number, using this ByteReader's endianness.
+  uint64 ReadEightBytes(const char* buffer) const;
+
+  // Read an unsigned LEB128 (Little Endian Base 128) number from
+  // BUFFER and return it as an unsigned 64 bit integer. Set LEN to
+  // the number of bytes read.
+  //
+  // The unsigned LEB128 representation of an integer N is a variable
+  // number of bytes:
+  //
+  // - If N is between 0 and 0x7f, then its unsigned LEB128
+  //   representation is a single byte whose value is N.
+  //
+  // - Otherwise, its unsigned LEB128 representation is (N & 0x7f) |
+  //   0x80, followed by the unsigned LEB128 representation of N /
+  //   128, rounded towards negative infinity.
+  //
+  // In other words, we break VALUE into groups of seven bits, put
+  // them in little-endian order, and then write them as eight-bit
+  // bytes with the high bit on all but the last.
+  uint64 ReadUnsignedLEB128(const char* buffer, size_t* len) const;
+
+  // Read a signed LEB128 number from BUFFER and return it as an
+  // signed 64 bit integer. Set LEN to the number of bytes read.
+  //
+  // The signed LEB128 representation of an integer N is a variable
+  // number of bytes:
+  //
+  // - If N is between -0x40 and 0x3f, then its signed LEB128
+  //   representation is a single byte whose value is N in two's
+  //   complement.
+  //
+  // - Otherwise, its signed LEB128 representation is (N & 0x7f) |
+  //   0x80, followed by the signed LEB128 representation of N / 128,
+  //   rounded towards negative infinity.
+  //
+  // In other words, we break VALUE into groups of seven bits, put
+  // them in little-endian order, and then write them as eight-bit
+  // bytes with the high bit on all but the last.
+  int64 ReadSignedLEB128(const char* buffer, size_t* len) const;
+
+  // Indicate that addresses on this architecture are SIZE bytes long. SIZE
+  // must be either 4 or 8. (DWARF allows addresses to be any number of
+  // bytes in length from 1 to 255, but we only support 32- and 64-bit
+  // addresses at the moment.) You must call this before using the
+  // ReadAddress member function.
+  //
+  // For data in a .debug_info section, or something that .debug_info
+  // refers to like line number or macro data, the compilation unit
+  // header's address_size field indicates the address size to use. Call
+  // frame information doesn't indicate its address size (a shortcoming of
+  // the spec); you must supply the appropriate size based on the
+  // architecture of the target machine.
+  void SetAddressSize(uint8 size);
+
+  // Return the current address size, in bytes. This is either 4,
+  // indicating 32-bit addresses, or 8, indicating 64-bit addresses.
+  uint8 AddressSize() const { return address_size_; }
+
+  // Read an address from BUFFER and return it as an unsigned 64 bit
+  // integer, respecting this ByteReader's endianness and address size. You
+  // must call SetAddressSize before calling this function.
+  uint64 ReadAddress(const char* buffer) const;
+
+  // DWARF actually defines two slightly different formats: 32-bit DWARF
+  // and 64-bit DWARF. This is *not* related to the size of registers or
+  // addresses on the target machine; it refers only to the size of section
+  // offsets and data lengths appearing in the DWARF data. One only needs
+  // 64-bit DWARF when the debugging data itself is larger than 4GiB.
+  // 32-bit DWARF can handle x86_64 or PPC64 code just fine, unless the
+  // debugging data itself is very large.
+  //
+  // DWARF information identifies itself as 32-bit or 64-bit DWARF: each
+  // compilation unit and call frame information entry begins with an
+  // "initial length" field, which, in addition to giving the length of the
+  // data, also indicates the size of section offsets and lengths appearing
+  // in that data. The ReadInitialLength member function, below, reads an
+  // initial length and sets the ByteReader's offset size as a side effect.
+  // Thus, in the normal process of reading DWARF data, the appropriate
+  // offset size is set automatically. So, you should only need to call
+  // SetOffsetSize if you are using the same ByteReader to jump from the
+  // midst of one block of DWARF data into another.
+
+  // Read a DWARF "initial length" field from START, and return it as
+  // an unsigned 64 bit integer, respecting this ByteReader's
+  // endianness. Set *LEN to the length of the initial length in
+  // bytes, either four or twelve. As a side effect, set this
+  // ByteReader's offset size to either 4 (if we see a 32-bit DWARF
+  // initial length) or 8 (if we see a 64-bit DWARF initial length).
+  //
+  // A DWARF initial length is either:
+  //
+  // - a byte count stored as an unsigned 32-bit value less than
+  //   0xffffff00, indicating that the data whose length is being
+  //   measured uses the 32-bit DWARF format, or
+  //
+  // - The 32-bit value 0xffffffff, followed by a 64-bit byte count,
+  //   indicating that the data whose length is being measured uses
+  //   the 64-bit DWARF format.
+  uint64 ReadInitialLength(const char* start, size_t* len);
+
+  // Read an offset from BUFFER and return it as an unsigned 64 bit
+  // integer, respecting the ByteReader's endianness. In 32-bit DWARF, the
+  // offset is 4 bytes long; in 64-bit DWARF, the offset is eight bytes
+  // long. You must call ReadInitialLength or SetOffsetSize before calling
+  // this function; see the comments above for details.
+  uint64 ReadOffset(const char* buffer) const;
+
+  // Return the current offset size, in bytes.
+  // A return value of 4 indicates that we are reading 32-bit DWARF.
+  // A return value of 8 indicates that we are reading 64-bit DWARF.
+  uint8 OffsetSize() const { return offset_size_; }
+
+  // Indicate that section offsets and lengths are SIZE bytes long. SIZE
+  // must be either 4 (meaning 32-bit DWARF) or 8 (meaning 64-bit DWARF).
+  // Usually, you should not call this function yourself; instead, let a
+  // call to ReadInitialLength establish the data's offset size
+  // automatically.
+  void SetOffsetSize(uint8 size);
+
+  // The Linux C++ ABI uses a variant of DWARF call frame information
+  // for exception handling. This data is included in the program's
+  // address space as the ".eh_frame" section, and intepreted at
+  // runtime to walk the stack, find exception handlers, and run
+  // cleanup code. The format is mostly the same as DWARF CFI, with
+  // some adjustments made to provide the additional
+  // exception-handling data, and to make the data easier to work with
+  // in memory --- for example, to allow it to be placed in read-only
+  // memory even when describing position-independent code.
+  //
+  // In particular, exception handling data can select a number of
+  // different encodings for pointers that appear in the data, as
+  // described by the DwarfPointerEncoding enum. There are actually
+  // four axes(!) to the encoding:
+  //
+  // - The pointer size: pointers can be 2, 4, or 8 bytes long, or use
+  //   the DWARF LEB128 encoding.
+  //
+  // - The pointer's signedness: pointers can be signed or unsigned.
+  //
+  // - The pointer's base address: the data stored in the exception
+  //   handling data can be the actual address (that is, an absolute
+  //   pointer), or relative to one of a number of different base
+  //   addreses --- including that of the encoded pointer itself, for
+  //   a form of "pc-relative" addressing.
+  //
+  // - The pointer may be indirect: it may be the address where the
+  //   true pointer is stored. (This is used to refer to things via
+  //   global offset table entries, program linkage table entries, or
+  //   other tricks used in position-independent code.)
+  //
+  // There are also two options that fall outside that matrix
+  // altogether: the pointer may be omitted, or it may have padding to
+  // align it on an appropriate address boundary. (That last option
+  // may seem like it should be just another axis, but it is not.)
+
+  // Indicate that the exception handling data is loaded starting at
+  // SECTION_BASE, and that the start of its buffer in our own memory
+  // is BUFFER_BASE. This allows us to find the address that a given
+  // byte in our buffer would have when loaded into the program the
+  // data describes. We need this to resolve DW_EH_PE_pcrel pointers.
+  void SetCFIDataBase(uint64 section_base, const char *buffer_base);
+
+  // Indicate that the base address of the program's ".text" section
+  // is TEXT_BASE. We need this to resolve DW_EH_PE_textrel pointers.
+  void SetTextBase(uint64 text_base);
+
+  // Indicate that the base address for DW_EH_PE_datarel pointers is
+  // DATA_BASE. The proper value depends on the ABI; it is usually the
+  // address of the global offset table, held in a designated register in
+  // position-independent code. You will need to look at the startup code
+  // for the target system to be sure. I tried; my eyes bled.
+  void SetDataBase(uint64 data_base);
+
+  // Indicate that the base address for the FDE we are processing is
+  // FUNCTION_BASE. This is the start address of DW_EH_PE_funcrel
+  // pointers. (This encoding does not seem to be used by the GNU
+  // toolchain.)
+  void SetFunctionBase(uint64 function_base);
+
+  // Indicate that we are no longer processing any FDE, so any use of
+  // a DW_EH_PE_funcrel encoding is an error.
+  void ClearFunctionBase();
+
+  // Return true if ENCODING is a valid pointer encoding.
+  bool ValidEncoding(DwarfPointerEncoding encoding) const;
+
+  // Return true if we have all the information we need to read a
+  // pointer that uses ENCODING. This checks that the appropriate
+  // SetFooBase function for ENCODING has been called.
+  bool UsableEncoding(DwarfPointerEncoding encoding) const;
+
+  // Read an encoded pointer from BUFFER using ENCODING; return the
+  // absolute address it represents, and set *LEN to the pointer's
+  // length in bytes, including any padding for aligned pointers.
+  //
+  // This function calls 'abort' if ENCODING is invalid or refers to a
+  // base address this reader hasn't been given, so you should check
+  // with ValidEncoding and UsableEncoding first if you would rather
+  // die in a more helpful way.
+  uint64 ReadEncodedPointer(const char *buffer, DwarfPointerEncoding encoding,
+                            size_t *len) const;
+
+ private:
+
+  // Function pointer type for our address and offset readers.
+  typedef uint64 (ByteReader::*AddressReader)(const char*) const;
+
+  // Read an offset from BUFFER and return it as an unsigned 64 bit
+  // integer.  DWARF2/3 define offsets as either 4 or 8 bytes,
+  // generally depending on the amount of DWARF2/3 info present.
+  // This function pointer gets set by SetOffsetSize.
+  AddressReader offset_reader_;
+
+  // Read an address from BUFFER and return it as an unsigned 64 bit
+  // integer.  DWARF2/3 allow addresses to be any size from 0-255
+  // bytes currently.  Internally we support 4 and 8 byte addresses,
+  // and will CHECK on anything else.
+  // This function pointer gets set by SetAddressSize.
+  AddressReader address_reader_;
+
+  Endianness endian_;
+  uint8 address_size_;
+  uint8 offset_size_;
+
+  // Base addresses for Linux C++ exception handling data's encoded pointers.
+  bool have_section_base_, have_text_base_, have_data_base_;
+  bool have_function_base_;
+  uint64 section_base_;
+  uint64 text_base_, data_base_, function_base_;
+  const char *buffer_base_;
+};
+
+
+inline uint8 ByteReader::ReadOneByte(const char* buffer) const {
+  return buffer[0];
+}
+
+inline uint16 ByteReader::ReadTwoBytes(const char* signed_buffer) const {
+  const unsigned char *buffer
+    = reinterpret_cast<const unsigned char *>(signed_buffer);
+  const uint16 buffer0 = buffer[0];
+  const uint16 buffer1 = buffer[1];
+  if (endian_ == ENDIANNESS_LITTLE) {
+    return buffer0 | buffer1 << 8;
+  } else {
+    return buffer1 | buffer0 << 8;
+  }
+}
+
+inline uint64 ByteReader::ReadFourBytes(const char* signed_buffer) const {
+  const unsigned char *buffer
+    = reinterpret_cast<const unsigned char *>(signed_buffer);
+  const uint32 buffer0 = buffer[0];
+  const uint32 buffer1 = buffer[1];
+  const uint32 buffer2 = buffer[2];
+  const uint32 buffer3 = buffer[3];
+  if (endian_ == ENDIANNESS_LITTLE) {
+    return buffer0 | buffer1 << 8 | buffer2 << 16 | buffer3 << 24;
+  } else {
+    return buffer3 | buffer2 << 8 | buffer1 << 16 | buffer0 << 24;
+  }
+}
+
+inline uint64 ByteReader::ReadEightBytes(const char* signed_buffer) const {
+  const unsigned char *buffer
+    = reinterpret_cast<const unsigned char *>(signed_buffer);
+  const uint64 buffer0 = buffer[0];
+  const uint64 buffer1 = buffer[1];
+  const uint64 buffer2 = buffer[2];
+  const uint64 buffer3 = buffer[3];
+  const uint64 buffer4 = buffer[4];
+  const uint64 buffer5 = buffer[5];
+  const uint64 buffer6 = buffer[6];
+  const uint64 buffer7 = buffer[7];
+  if (endian_ == ENDIANNESS_LITTLE) {
+    return buffer0 | buffer1 << 8 | buffer2 << 16 | buffer3 << 24 |
+      buffer4 << 32 | buffer5 << 40 | buffer6 << 48 | buffer7 << 56;
+  } else {
+    return buffer7 | buffer6 << 8 | buffer5 << 16 | buffer4 << 24 |
+      buffer3 << 32 | buffer2 << 40 | buffer1 << 48 | buffer0 << 56;
+  }
+}
+
+// Read an unsigned LEB128 number.  Each byte contains 7 bits of
+// information, plus one bit saying whether the number continues or
+// not.
+
+inline uint64 ByteReader::ReadUnsignedLEB128(const char* buffer,
+                                             size_t* len) const {
+  uint64 result = 0;
+  size_t num_read = 0;
+  unsigned int shift = 0;
+  unsigned char byte;
+
+  do {
+    byte = *buffer++;
+    num_read++;
+
+    result |= (static_cast<uint64>(byte & 0x7f)) << shift;
+
+    shift += 7;
+
+  } while (byte & 0x80);
+
+  *len = num_read;
+
+  return result;
+}
+
+// Read a signed LEB128 number.  These are like regular LEB128
+// numbers, except the last byte may have a sign bit set.
+
+inline int64 ByteReader::ReadSignedLEB128(const char* buffer,
+                                          size_t* len) const {
+  int64 result = 0;
+  unsigned int shift = 0;
+  size_t num_read = 0;
+  unsigned char byte;
+
+  do {
+      byte = *buffer++;
+      num_read++;
+      result |= (static_cast<uint64>(byte & 0x7f) << shift);
+      shift += 7;
+  } while (byte & 0x80);
+
+  if ((shift < 8 * sizeof (result)) && (byte & 0x40))
+    result |= -((static_cast<int64>(1)) << shift);
+  *len = num_read;
+  return result;
+}
+
+inline uint64 ByteReader::ReadOffset(const char* buffer) const {
+  MOZ_ASSERT(this->offset_reader_);
+  return (this->*offset_reader_)(buffer);
+}
+
+inline uint64 ByteReader::ReadAddress(const char* buffer) const {
+  MOZ_ASSERT(this->address_reader_);
+  return (this->*address_reader_)(buffer);
+}
+
+inline void ByteReader::SetCFIDataBase(uint64 section_base,
+                                       const char *buffer_base) {
+  section_base_ = section_base;
+  buffer_base_ = buffer_base;
+  have_section_base_ = true;
+}
+
+inline void ByteReader::SetTextBase(uint64 text_base) {
+  text_base_ = text_base;
+  have_text_base_ = true;
+}
+
+inline void ByteReader::SetDataBase(uint64 data_base) {
+  data_base_ = data_base;
+  have_data_base_ = true;
+}
+
+inline void ByteReader::SetFunctionBase(uint64 function_base) {
+  function_base_ = function_base;
+  have_function_base_ = true;
+}
+
+inline void ByteReader::ClearFunctionBase() {
+  have_function_base_ = false;
+}
+
+
+// (derived from)
+// dwarf_cfi_to_module.h: Define the DwarfCFIToModule class, which
+// accepts parsed DWARF call frame info and adds it to a Summariser object.
+
+// This class is a reader for DWARF's Call Frame Information.  CFI
+// describes how to unwind stack frames --- even for functions that do
+// not follow fixed conventions for saving registers, whose frame size
+// varies as they execute, etc.
+//
+// CFI describes, at each machine instruction, how to compute the
+// stack frame's base address, how to find the return address, and
+// where to find the saved values of the caller's registers (if the
+// callee has stashed them somewhere to free up the registers for its
+// own use).
+//
+// For example, suppose we have a function whose machine code looks
+// like this (imagine an assembly language that looks like C, for a
+// machine with 32-bit registers, and a stack that grows towards lower
+// addresses):
+//
+// func:                                ; entry point; return address at sp
+// func+0:      sp = sp - 16            ; allocate space for stack frame
+// func+1:      sp[12] = r0             ; save r0 at sp+12
+// ...                                  ; other code, not frame-related
+// func+10:     sp -= 4; *sp = x        ; push some x on the stack
+// ...                                  ; other code, not frame-related
+// func+20:     r0 = sp[16]             ; restore saved r0
+// func+21:     sp += 20                ; pop whole stack frame
+// func+22:     pc = *sp; sp += 4       ; pop return address and jump to it
+//
+// DWARF CFI is (a very compressed representation of) a table with a
+// row for each machine instruction address and a column for each
+// register showing how to restore it, if possible.
+//
+// A special column named "CFA", for "Canonical Frame Address", tells how
+// to compute the base address of the frame; registers' entries may
+// refer to the CFA in describing where the registers are saved.
+//
+// Another special column, named "RA", represents the return address.
+//
+// For example, here is a complete (uncompressed) table describing the
+// function above:
+//
+//     insn      cfa    r0      r1 ...  ra
+//     =======================================
+//     func+0:   sp                     cfa[0]
+//     func+1:   sp+16                  cfa[0]
+//     func+2:   sp+16  cfa[-4]         cfa[0]
+//     func+11:  sp+20  cfa[-4]         cfa[0]
+//     func+21:  sp+20                  cfa[0]
+//     func+22:  sp                     cfa[0]
+//
+// Some things to note here:
+//
+// - Each row describes the state of affairs *before* executing the
+//   instruction at the given address.  Thus, the row for func+0
+//   describes the state before we allocate the stack frame.  In the
+//   next row, the formula for computing the CFA has changed,
+//   reflecting that allocation.
+//
+// - The other entries are written in terms of the CFA; this allows
+//   them to remain unchanged as the stack pointer gets bumped around.
+//   For example, the rule for recovering the return address (the "ra"
+//   column) remains unchanged throughout the function, even as the
+//   stack pointer takes on three different offsets from the return
+//   address.
+//
+// - Although we haven't shown it, most calling conventions designate
+//   "callee-saves" and "caller-saves" registers. The callee must
+//   preserve the values of callee-saves registers; if it uses them,
+//   it must save their original values somewhere, and restore them
+//   before it returns. In contrast, the callee is free to trash
+//   caller-saves registers; if the callee uses these, it will
+//   probably not bother to save them anywhere, and the CFI will
+//   probably mark their values as "unrecoverable".
+//
+//   (However, since the caller cannot assume the callee was going to
+//   save them, caller-saves registers are probably dead in the caller
+//   anyway, so compilers usually don't generate CFA for caller-saves
+//   registers.)
+//
+// - Exactly where the CFA points is a matter of convention that
+//   depends on the architecture and ABI in use. In the example, the
+//   CFA is the value the stack pointer had upon entry to the
+//   function, pointing at the saved return address. But on the x86,
+//   the call frame information generated by GCC follows the
+//   convention that the CFA is the address *after* the saved return
+//   address.
+//
+//   But by definition, the CFA remains constant throughout the
+//   lifetime of the frame. This makes it a useful value for other
+//   columns to refer to. It is also gives debuggers a useful handle
+//   for identifying a frame.
+//
+// If you look at the table above, you'll notice that a given entry is
+// often the same as the one immediately above it: most instructions
+// change only one or two aspects of the stack frame, if they affect
+// it at all. The DWARF format takes advantage of this fact, and
+// reduces the size of the data by mentioning only the addresses and
+// columns at which changes take place. So for the above, DWARF CFI
+// data would only actually mention the following:
+//
+//     insn      cfa    r0      r1 ...  ra
+//     =======================================
+//     func+0:   sp                     cfa[0]
+//     func+1:   sp+16
+//     func+2:          cfa[-4]
+//     func+11:  sp+20
+//     func+21:         r0
+//     func+22:  sp
+//
+// In fact, this is the way the parser reports CFI to the consumer: as
+// a series of statements of the form, "At address X, column Y changed
+// to Z," and related conventions for describing the initial state.
+//
+// Naturally, it would be impractical to have to scan the entire
+// program's CFI, noting changes as we go, just to recover the
+// unwinding rules in effect at one particular instruction. To avoid
+// this, CFI data is grouped into "entries", each of which covers a
+// specified range of addresses and begins with a complete statement
+// of the rules for all recoverable registers at that starting
+// address. Each entry typically covers a single function.
+//
+// Thus, to compute the contents of a given row of the table --- that
+// is, rules for recovering the CFA, RA, and registers at a given
+// instruction --- the consumer should find the entry that covers that
+// instruction's address, start with the initial state supplied at the
+// beginning of the entry, and work forward until it has processed all
+// the changes up to and including those for the present instruction.
+//
+// There are seven kinds of rules that can appear in an entry of the
+// table:
+//
+// - "undefined": The given register is not preserved by the callee;
+//   its value cannot be recovered.
+//
+// - "same value": This register has the same value it did in the callee.
+//
+// - offset(N): The register is saved at offset N from the CFA.
+//
+// - val_offset(N): The value the register had in the caller is the
+//   CFA plus offset N. (This is usually only useful for describing
+//   the stack pointer.)
+//
+// - register(R): The register's value was saved in another register R.
+//
+// - expression(E): Evaluating the DWARF expression E using the
+//   current frame's registers' values yields the address at which the
+//   register was saved.
+//
+// - val_expression(E): Evaluating the DWARF expression E using the
+//   current frame's registers' values yields the value the register
+//   had in the caller.
+
+class CallFrameInfo {
+ public:
+  // The different kinds of entries one finds in CFI. Used internally,
+  // and for error reporting.
+  enum EntryKind { kUnknown, kCIE, kFDE, kTerminator };
+
+  // The handler class to which the parser hands the parsed call frame
+  // information.  Defined below.
+  class Handler;
+
+  // A reporter class, which CallFrameInfo uses to report errors
+  // encountered while parsing call frame information.  Defined below.
+  class Reporter;
+
+  // Create a DWARF CFI parser. BUFFER points to the contents of the
+  // .debug_frame section to parse; BUFFER_LENGTH is its length in bytes.
+  // REPORTER is an error reporter the parser should use to report
+  // problems. READER is a ByteReader instance that has the endianness and
+  // address size set properly. Report the data we find to HANDLER.
+  //
+  // This class can also parse Linux C++ exception handling data, as found
+  // in '.eh_frame' sections. This data is a variant of DWARF CFI that is
+  // placed in loadable segments so that it is present in the program's
+  // address space, and is interpreted by the C++ runtime to search the
+  // call stack for a handler interested in the exception being thrown,
+  // actually pop the frames, and find cleanup code to run.
+  //
+  // There are two differences between the call frame information described
+  // in the DWARF standard and the exception handling data Linux places in
+  // the .eh_frame section:
+  //
+  // - Exception handling data uses uses a different format for call frame
+  //   information entry headers. The distinguished CIE id, the way FDEs
+  //   refer to their CIEs, and the way the end of the series of entries is
+  //   determined are all slightly different.
+  //
+  //   If the constructor's EH_FRAME argument is true, then the
+  //   CallFrameInfo parses the entry headers as Linux C++ exception
+  //   handling data. If EH_FRAME is false or omitted, the CallFrameInfo
+  //   parses standard DWARF call frame information.
+  //
+  // - Linux C++ exception handling data uses CIE augmentation strings
+  //   beginning with 'z' to specify the presence of additional data after
+  //   the CIE and FDE headers and special encodings used for addresses in
+  //   frame description entries.
+  //
+  //   CallFrameInfo can handle 'z' augmentations in either DWARF CFI or
+  //   exception handling data if you have supplied READER with the base
+  //   addresses needed to interpret the pointer encodings that 'z'
+  //   augmentations can specify. See the ByteReader interface for details
+  //   about the base addresses. See the CallFrameInfo::Handler interface
+  //   for details about the additional information one might find in
+  //   'z'-augmented data.
+  //
+  // Thus:
+  //
+  // - If you are parsing standard DWARF CFI, as found in a .debug_frame
+  //   section, you should pass false for the EH_FRAME argument, or omit
+  //   it, and you need not worry about providing READER with the
+  //   additional base addresses.
+  //
+  // - If you want to parse Linux C++ exception handling data from a
+  //   .eh_frame section, you should pass EH_FRAME as true, and call
+  //   READER's Set*Base member functions before calling our Start method.
+  //
+  // - If you want to parse DWARF CFI that uses the 'z' augmentations
+  //   (although I don't think any toolchain ever emits such data), you
+  //   could pass false for EH_FRAME, but call READER's Set*Base members.
+  //
+  // The extensions the Linux C++ ABI makes to DWARF for exception
+  // handling are described here, rather poorly:
+  // http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html
+  // http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
+  //
+  // The mechanics of C++ exception handling, personality routines,
+  // and language-specific data areas are described here, rather nicely:
+  // http://www.codesourcery.com/public/cxx-abi/abi-eh.html
+
+  CallFrameInfo(const char *buffer, size_t buffer_length,
+                ByteReader *reader, Handler *handler, Reporter *reporter,
+                bool eh_frame = false)
+      : buffer_(buffer), buffer_length_(buffer_length),
+        reader_(reader), handler_(handler), reporter_(reporter),
+        eh_frame_(eh_frame) { }
+
+  ~CallFrameInfo() { }
+
+  // Parse the entries in BUFFER, reporting what we find to HANDLER.
+  // Return true if we reach the end of the section successfully, or
+  // false if we encounter an error.
+  bool Start();
+
+  // Return the textual name of KIND. For error reporting.
+  static const char *KindName(EntryKind kind);
+
+ private:
+
+  struct CIE;
+
+  // A CFI entry, either an FDE or a CIE.
+  struct Entry {
+    // The starting offset of the entry in the section, for error
+    // reporting.
+    size_t offset;
+
+    // The start of this entry in the buffer.
+    const char *start;
+
+    // Which kind of entry this is.
+    //
+    // We want to be able to use this for error reporting even while we're
+    // in the midst of parsing. Error reporting code may assume that kind,
+    // offset, and start fields are valid, although kind may be kUnknown.
+    EntryKind kind;
+
+    // The end of this entry's common prologue (initial length and id), and
+    // the start of this entry's kind-specific fields.
+    const char *fields;
+
+    // The start of this entry's instructions.
+    const char *instructions;
+
+    // The address past the entry's last byte in the buffer. (Note that
+    // since offset points to the entry's initial length field, and the
+    // length field is the number of bytes after that field, this is not
+    // simply buffer_ + offset + length.)
+    const char *end;
+
+    // For both DWARF CFI and .eh_frame sections, this is the CIE id in a
+    // CIE, and the offset of the associated CIE in an FDE.
+    uint64 id;
+
+    // The CIE that applies to this entry, if we've parsed it. If this is a
+    // CIE, then this field points to this structure.
+    CIE *cie;
+  };
+
+  // A common information entry (CIE).
+  struct CIE: public Entry {
+    uint8 version;                      // CFI data version number
+    std::string augmentation;           // vendor format extension markers
+    uint64 code_alignment_factor;       // scale for code address adjustments
+    int data_alignment_factor;          // scale for stack pointer adjustments
+    unsigned return_address_register;   // which register holds the return addr
+
+    // True if this CIE includes Linux C++ ABI 'z' augmentation data.
+    bool has_z_augmentation;
+
+    // Parsed 'z' augmentation data. These are meaningful only if
+    // has_z_augmentation is true.
+    bool has_z_lsda;                    // The 'z' augmentation included 'L'.
+    bool has_z_personality;             // The 'z' augmentation included 'P'.
+    bool has_z_signal_frame;            // The 'z' augmentation included 'S'.
+
+    // If has_z_lsda is true, this is the encoding to be used for language-
+    // specific data area pointers in FDEs.
+    DwarfPointerEncoding lsda_encoding;
+
+    // If has_z_personality is true, this is the encoding used for the
+    // personality routine pointer in the augmentation data.
+    DwarfPointerEncoding personality_encoding;
+
+    // If has_z_personality is true, this is the address of the personality
+    // routine --- or, if personality_encoding & DW_EH_PE_indirect, the
+    // address where the personality routine's address is stored.
+    uint64 personality_address;
+
+    // This is the encoding used for addresses in the FDE header and
+    // in DW_CFA_set_loc instructions. This is always valid, whether
+    // or not we saw a 'z' augmentation string; its default value is
+    // DW_EH_PE_absptr, which is what normal DWARF CFI uses.
+    DwarfPointerEncoding pointer_encoding;
+  };
+
+  // A frame description entry (FDE).
+  struct FDE: public Entry {
+    uint64 address;                     // start address of described code
+    uint64 size;                        // size of described code, in bytes
+
+    // If cie->has_z_lsda is true, then this is the language-specific data
+    // area's address --- or its address's address, if cie->lsda_encoding
+    // has the DW_EH_PE_indirect bit set.
+    uint64 lsda_address;
+  };
+
+  // Internal use.
+  class Rule;
+  class UndefinedRule;
+  class SameValueRule;
+  class OffsetRule;
+  class ValOffsetRule;
+  class RegisterRule;
+  class ExpressionRule;
+  class ValExpressionRule;
+  class RuleMap;
+  class State;
+
+  // Parse the initial length and id of a CFI entry, either a CIE, an FDE,
+  // or a .eh_frame end-of-data mark. CURSOR points to the beginning of the
+  // data to parse. On success, populate ENTRY as appropriate, and return
+  // true. On failure, report the problem, and return false. Even if we
+  // return false, set ENTRY->end to the first byte after the entry if we
+  // were able to figure that out, or NULL if we weren't.
+  bool ReadEntryPrologue(const char *cursor, Entry *entry);
+
+  // Parse the fields of a CIE after the entry prologue, including any 'z'
+  // augmentation data. Assume that the 'Entry' fields of CIE are
+  // populated; use CIE->fields and CIE->end as the start and limit for
+  // parsing. On success, populate the rest of *CIE, and return true; on
+  // failure, report the problem and return false.
+  bool ReadCIEFields(CIE *cie);
+
+  // Parse the fields of an FDE after the entry prologue, including any 'z'
+  // augmentation data. Assume that the 'Entry' fields of *FDE are
+  // initialized; use FDE->fields and FDE->end as the start and limit for
+  // parsing. Assume that FDE->cie is fully initialized. On success,
+  // populate the rest of *FDE, and return true; on failure, report the
+  // problem and return false.
+  bool ReadFDEFields(FDE *fde);
+
+  // Report that ENTRY is incomplete, and return false. This is just a
+  // trivial wrapper for invoking reporter_->Incomplete; it provides a
+  // little brevity.
+  bool ReportIncomplete(Entry *entry);
+
+  // Return true if ENCODING has the DW_EH_PE_indirect bit set.
+  static bool IsIndirectEncoding(DwarfPointerEncoding encoding) {
+    return encoding & DW_EH_PE_indirect;
+  }
+
+  // The contents of the DWARF .debug_info section we're parsing.
+  const char *buffer_;
+  size_t buffer_length_;
+
+  // For reading multi-byte values with the appropriate endianness.
+  ByteReader *reader_;
+
+  // The handler to which we should report the data we find.
+  Handler *handler_;
+
+  // For reporting problems in the info we're parsing.
+  Reporter *reporter_;
+
+  // True if we are processing .eh_frame-format data.
+  bool eh_frame_;
+};
+
+
+// The handler class for CallFrameInfo.  The a CFI parser calls the
+// member functions of a handler object to report the data it finds.
+class CallFrameInfo::Handler {
+ public:
+  // The pseudo-register number for the canonical frame address.
+  enum { kCFARegister = DW_REG_CFA };
+
+  Handler() { }
+  virtual ~Handler() { }
+
+  // The parser has found CFI for the machine code at ADDRESS,
+  // extending for LENGTH bytes. OFFSET is the offset of the frame
+  // description entry in the section, for use in error messages.
+  // VERSION is the version number of the CFI format. AUGMENTATION is
+  // a string describing any producer-specific extensions present in
+  // the data. RETURN_ADDRESS is the number of the register that holds
+  // the address to which the function should return.
+  //
+  // Entry should return true to process this CFI, or false to skip to
+  // the next entry.
+  //
+  // The parser invokes Entry for each Frame Description Entry (FDE)
+  // it finds.  The parser doesn't report Common Information Entries
+  // to the handler explicitly; instead, if the handler elects to
+  // process a given FDE, the parser reiterates the appropriate CIE's
+  // contents at the beginning of the FDE's rules.
+  virtual bool Entry(size_t offset, uint64 address, uint64 length,
+                     uint8 version, const std::string &augmentation,
+                     unsigned return_address) = 0;
+
+  // When the Entry function returns true, the parser calls these
+  // handler functions repeatedly to describe the rules for recovering
+  // registers at each instruction in the given range of machine code.
+  // Immediately after a call to Entry, the handler should assume that
+  // the rule for each callee-saves register is "unchanged" --- that
+  // is, that the register still has the value it had in the caller.
+  //
+  // If a *Rule function returns true, we continue processing this entry's
+  // instructions. If a *Rule function returns false, we stop evaluating
+  // instructions, and skip to the next entry. Either way, we call End
+  // before going on to the next entry.
+  //
+  // In all of these functions, if the REG parameter is kCFARegister, then
+  // the rule describes how to find the canonical frame address.
+  // kCFARegister may be passed as a BASE_REGISTER argument, meaning that
+  // the canonical frame address should be used as the base address for the
+  // computation. All other REG values will be positive.
+
+  // At ADDRESS, register REG's value is not recoverable.
+  virtual bool UndefinedRule(uint64 address, int reg) = 0;
+
+  // At ADDRESS, register REG's value is the same as that it had in
+  // the caller.
+  virtual bool SameValueRule(uint64 address, int reg) = 0;
+
+  // At ADDRESS, register REG has been saved at offset OFFSET from
+  // BASE_REGISTER.
+  virtual bool OffsetRule(uint64 address, int reg,
+                          int base_register, long offset) = 0;
+
+  // At ADDRESS, the caller's value of register REG is the current
+  // value of BASE_REGISTER plus OFFSET. (This rule doesn't provide an
+  // address at which the register's value is saved.)
+  virtual bool ValOffsetRule(uint64 address, int reg,
+                             int base_register, long offset) = 0;
+
+  // At ADDRESS, register REG has been saved in BASE_REGISTER. This differs
+  // from ValOffsetRule(ADDRESS, REG, BASE_REGISTER, 0), in that
+  // BASE_REGISTER is the "home" for REG's saved value: if you want to
+  // assign to a variable whose home is REG in the calling frame, you
+  // should put the value in BASE_REGISTER.
+  virtual bool RegisterRule(uint64 address, int reg, int base_register) = 0;
+
+  // At ADDRESS, the DWARF expression EXPRESSION yields the address at
+  // which REG was saved.
+  virtual bool ExpressionRule(uint64 address, int reg,
+                              const std::string &expression) = 0;
+
+  // At ADDRESS, the DWARF expression EXPRESSION yields the caller's
+  // value for REG. (This rule doesn't provide an address at which the
+  // register's value is saved.)
+  virtual bool ValExpressionRule(uint64 address, int reg,
+                                 const std::string &expression) = 0;
+
+  // Indicate that the rules for the address range reported by the
+  // last call to Entry are complete.  End should return true if
+  // everything is okay, or false if an error has occurred and parsing
+  // should stop.
+  virtual bool End() = 0;
+
+  // Handler functions for Linux C++ exception handling data. These are
+  // only called if the data includes 'z' augmentation strings.
+
+  // The Linux C++ ABI uses an extension of the DWARF CFI format to
+  // walk the stack to propagate exceptions from the throw to the
+  // appropriate catch, and do the appropriate cleanups along the way.
+  // CFI entries used for exception handling have two additional data
+  // associated with them:
+  //
+  // - The "language-specific data area" describes which exception
+  //   types the function has 'catch' clauses for, and indicates how
+  //   to go about re-entering the function at the appropriate catch
+  //   clause. If the exception is not caught, it describes the
+  //   destructors that must run before the frame is popped.
+  //
+  // - The "personality routine" is responsible for interpreting the
+  //   language-specific data area's contents, and deciding whether
+  //   the exception should continue to propagate down the stack,
+  //   perhaps after doing some cleanup for this frame, or whether the
+  //   exception will be caught here.
+  //
+  // In principle, the language-specific data area is opaque to
+  // everybody but the personality routine. In practice, these values
+  // may be useful or interesting to readers with extra context, and
+  // we have to at least skip them anyway, so we might as well report
+  // them to the handler.
+
+  // This entry's exception handling personality routine's address is
+  // ADDRESS. If INDIRECT is true, then ADDRESS is the address at
+  // which the routine's address is stored. The default definition for
+  // this handler function simply returns true, allowing parsing of
+  // the entry to continue.
+  virtual bool PersonalityRoutine(uint64 address, bool indirect) {
+    return true;
+  }
+
+  // This entry's language-specific data area (LSDA) is located at
+  // ADDRESS. If INDIRECT is true, then ADDRESS is the address at
+  // which the area's address is stored. The default definition for
+  // this handler function simply returns true, allowing parsing of
+  // the entry to continue.
+  virtual bool LanguageSpecificDataArea(uint64 address, bool indirect) {
+    return true;
+  }
+
+  // This entry describes a signal trampoline --- this frame is the
+  // caller of a signal handler. The default definition for this
+  // handler function simply returns true, allowing parsing of the
+  // entry to continue.
+  //
+  // The best description of the rationale for and meaning of signal
+  // trampoline CFI entries seems to be in the GCC bug database:
+  // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=26208
+  virtual bool SignalHandler() { return true; }
+};
+
+
+// The CallFrameInfo class makes calls on an instance of this class to
+// report errors or warn about problems in the data it is parsing.
+// These messages are sent to the message sink |aLog| provided to the
+// constructor.
+class CallFrameInfo::Reporter {
+ public:
+  // Create an error reporter which attributes troubles to the section
+  // named SECTION in FILENAME.
+  //
+  // Normally SECTION would be .debug_frame, but the Mac puts CFI data
+  // in a Mach-O section named __debug_frame. If we support
+  // Linux-style exception handling data, we could be reading an
+  // .eh_frame section.
+  Reporter(void (*aLog)(const char*),
+           const std::string &filename,
+           const std::string &section = ".debug_frame")
+      : log_(aLog), filename_(filename), section_(section) { }
+  virtual ~Reporter() { }
+
+  // The CFI entry at OFFSET ends too early to be well-formed. KIND
+  // indicates what kind of entry it is; KIND can be kUnknown if we
+  // haven't parsed enough of the entry to tell yet.
+  virtual void Incomplete(uint64 offset, CallFrameInfo::EntryKind kind);
+
+  // The .eh_frame data has a four-byte zero at OFFSET where the next
+  // entry's length would be; this is a terminator. However, the buffer
+  // length as given to the CallFrameInfo constructor says there should be
+  // more data.
+  virtual void EarlyEHTerminator(uint64 offset);
+
+  // The FDE at OFFSET refers to the CIE at CIE_OFFSET, but the
+  // section is not that large.
+  virtual void CIEPointerOutOfRange(uint64 offset, uint64 cie_offset);
+
+  // The FDE at OFFSET refers to the CIE at CIE_OFFSET, but the entry
+  // there is not a CIE.
+  virtual void BadCIEId(uint64 offset, uint64 cie_offset);
+
+  // The FDE at OFFSET refers to a CIE with version number VERSION,
+  // which we don't recognize. We cannot parse DWARF CFI if it uses
+  // a version number we don't recognize.
+  virtual void UnrecognizedVersion(uint64 offset, int version);
+
+  // The FDE at OFFSET refers to a CIE with augmentation AUGMENTATION,
+  // which we don't recognize. We cannot parse DWARF CFI if it uses
+  // augmentations we don't recognize.
+  virtual void UnrecognizedAugmentation(uint64 offset,
+                                        const std::string &augmentation);
+
+  // The pointer encoding ENCODING, specified by the CIE at OFFSET, is not
+  // a valid encoding.
+  virtual void InvalidPointerEncoding(uint64 offset, uint8 encoding);
+
+  // The pointer encoding ENCODING, specified by the CIE at OFFSET, depends
+  // on a base address which has not been supplied.
+  virtual void UnusablePointerEncoding(uint64 offset, uint8 encoding);
+
+  // The CIE at OFFSET contains a DW_CFA_restore instruction at
+  // INSN_OFFSET, which may not appear in a CIE.
+  virtual void RestoreInCIE(uint64 offset, uint64 insn_offset);
+
+  // The entry at OFFSET, of kind KIND, has an unrecognized
+  // instruction at INSN_OFFSET.
+  virtual void BadInstruction(uint64 offset, CallFrameInfo::EntryKind kind,
+                              uint64 insn_offset);
+
+  // The instruction at INSN_OFFSET in the entry at OFFSET, of kind
+  // KIND, establishes a rule that cites the CFA, but we have not
+  // established a CFA rule yet.
+  virtual void NoCFARule(uint64 offset, CallFrameInfo::EntryKind kind,
+                         uint64 insn_offset);
+
+  // The instruction at INSN_OFFSET in the entry at OFFSET, of kind
+  // KIND, is a DW_CFA_restore_state instruction, but the stack of
+  // saved states is empty.
+  virtual void EmptyStateStack(uint64 offset, CallFrameInfo::EntryKind kind,
+                               uint64 insn_offset);
+
+  // The DW_CFA_remember_state instruction at INSN_OFFSET in the entry
+  // at OFFSET, of kind KIND, would restore a state that has no CFA
+  // rule, whereas the current state does have a CFA rule. This is
+  // bogus input, which the CallFrameInfo::Handler interface doesn't
+  // (and shouldn't) have any way to report.
+  virtual void ClearingCFARule(uint64 offset, CallFrameInfo::EntryKind kind,
+                               uint64 insn_offset);
+
+ private:
+  // A logging sink function, as supplied by LUL's user.
+  void (*log_)(const char*);
+
+ protected:
+  // The name of the file whose CFI we're reading.
+  std::string filename_;
+
+  // The name of the CFI section in that file.
+  std::string section_;
+};
+
+
+using lul::CallFrameInfo;
+using lul::Summariser;
+
+// A class that accepts parsed call frame information from the DWARF
+// CFI parser and populates a google_breakpad::Module object with the
+// contents.
+class DwarfCFIToModule: public CallFrameInfo::Handler {
+ public:
+
+  // DwarfCFIToModule uses an instance of this class to report errors
+  // detected while converting DWARF CFI to Breakpad STACK CFI records.
+  class Reporter {
+   public:
+    // Create a reporter that writes messages to the message sink
+    // |aLog|. FILE is the name of the file we're processing, and
+    // SECTION is the name of the section within that file that we're
+    // looking at (.debug_frame, .eh_frame, etc.).
+    Reporter(void (*aLog)(const char*),
+             const std::string &file, const std::string &section)
+      : log_(aLog), file_(file), section_(section) { }
+    virtual ~Reporter() { }
+
+    // The DWARF CFI entry at OFFSET says that REG is undefined, but the
+    // Breakpad symbol file format cannot express this.
+    virtual void UndefinedNotSupported(size_t offset,
+                                       const UniqueString* reg);
+
+    // The DWARF CFI entry at OFFSET says that REG uses a DWARF
+    // expression to find its value, but DwarfCFIToModule is not
+    // capable of translating DWARF expressions to Breakpad postfix
+    // expressions.
+    virtual void ExpressionsNotSupported(size_t offset,
+                                         const UniqueString* reg);
+
+  private:
+    // A logging sink function, as supplied by LUL's user.
+    void (*log_)(const char*);
+  protected:
+    std::string file_, section_;
+  };
+
+  // Register name tables. If TABLE is a vector returned by one of these
+  // functions, then TABLE[R] is the name of the register numbered R in
+  // DWARF call frame information.
+  class RegisterNames {
+   public:
+    // Intel's "x86" or IA-32.
+    static const unsigned int I386();
+
+    // AMD x86_64, AMD64, Intel EM64T, or Intel 64
+    static const unsigned int X86_64();
+
+    // ARM.
+    static const unsigned int ARM();
+  };
+
+  // Create a handler for the dwarf2reader::CallFrameInfo parser that
+  // records the stack unwinding information it receives in SUMM.
+  //
+  // Use REGISTER_NAMES[I] as the name of register number I; *this
+  // keeps a reference to the vector, so the vector should remain
+  // alive for as long as the DwarfCFIToModule does.
+  //
+  // Use REPORTER for reporting problems encountered in the conversion
+  // process.
+  DwarfCFIToModule(const unsigned int num_dw_regs,
+                   Reporter *reporter,
+                   /*OUT*/Summariser* summ)
+      : summ_(summ), num_dw_regs_(num_dw_regs), reporter_(reporter),
+        return_address_(-1) {
+  }
+  virtual ~DwarfCFIToModule() {}
+
+  virtual bool Entry(size_t offset, uint64 address, uint64 length,
+                     uint8 version, const std::string &augmentation,
+                     unsigned return_address);
+  virtual bool UndefinedRule(uint64 address, int reg);
+  virtual bool SameValueRule(uint64 address, int reg);
+  virtual bool OffsetRule(uint64 address, int reg,
+                          int base_register, long offset);
+  virtual bool ValOffsetRule(uint64 address, int reg,
+                             int base_register, long offset);
+  virtual bool RegisterRule(uint64 address, int reg, int base_register);
+  virtual bool ExpressionRule(uint64 address, int reg,
+                              const std::string &expression);
+  virtual bool ValExpressionRule(uint64 address, int reg,
+                                 const std::string &expression);
+  virtual bool End();
+
+ private:
+  // Return the name to use for register REG.
+  const UniqueString* RegisterName(int i);
+
+  // The Summariser to which we should give entries
+  Summariser* summ_;
+
+  // The number of Dwarf-defined register names for this architecture.
+  const unsigned int num_dw_regs_;
+
+  // The reporter to use to report problems.
+  Reporter *reporter_;
+
+  // The section offset of the current frame description entry, for
+  // use in error messages.
+  size_t entry_offset_;
+
+  // The return address column for that entry.
+  unsigned return_address_;
+};
+
+} // namespace lul
+
+#endif // LulDwarfExt_h
new file mode 100644
--- /dev/null
+++ b/tools/profiler/LulDwarfInt.h
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+
+// Copyright (c) 2008, 2010 Google Inc. All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// CFI reader author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// This file is derived from the following file in
+// toolkit/crashreporter/google-breakpad:
+//   src/common/dwarf/dwarf2enums.h
+
+#ifndef LulDwarfInt_h
+#define LulDwarfInt_h
+
+#include "LulCommonExt.h"
+#include "LulDwarfExt.h"
+
+namespace lul {
+
+// These enums do not follow the google3 style only because they are
+// known universally (specs, other implementations) by the names in
+// exactly this capitalization.
+// Tag names and codes.
+
+// Call Frame Info instructions.
+enum DwarfCFI
+  {
+    DW_CFA_advance_loc        = 0x40,
+    DW_CFA_offset             = 0x80,
+    DW_CFA_restore            = 0xc0,
+    DW_CFA_nop                = 0x00,
+    DW_CFA_set_loc            = 0x01,
+    DW_CFA_advance_loc1       = 0x02,
+    DW_CFA_advance_loc2       = 0x03,
+    DW_CFA_advance_loc4       = 0x04,
+    DW_CFA_offset_extended    = 0x05,
+    DW_CFA_restore_extended   = 0x06,
+    DW_CFA_undefined          = 0x07,
+    DW_CFA_same_value         = 0x08,
+    DW_CFA_register           = 0x09,
+    DW_CFA_remember_state     = 0x0a,
+    DW_CFA_restore_state      = 0x0b,
+    DW_CFA_def_cfa            = 0x0c,
+    DW_CFA_def_cfa_register   = 0x0d,
+    DW_CFA_def_cfa_offset     = 0x0e,
+    DW_CFA_def_cfa_expression = 0x0f,
+    DW_CFA_expression         = 0x10,
+    DW_CFA_offset_extended_sf = 0x11,
+    DW_CFA_def_cfa_sf         = 0x12,
+    DW_CFA_def_cfa_offset_sf  = 0x13,
+    DW_CFA_val_offset         = 0x14,
+    DW_CFA_val_offset_sf      = 0x15,
+    DW_CFA_val_expression     = 0x16,
+
+    // Opcodes in this range are reserved for user extensions.
+    DW_CFA_lo_user = 0x1c,
+    DW_CFA_hi_user = 0x3f,
+
+    // SGI/MIPS specific.
+    DW_CFA_MIPS_advance_loc8 = 0x1d,
+
+    // GNU extensions.
+    DW_CFA_GNU_window_save = 0x2d,
+    DW_CFA_GNU_args_size = 0x2e,
+    DW_CFA_GNU_negative_offset_extended = 0x2f
+  };
+
+// Exception handling 'z' augmentation letters.
+enum DwarfZAugmentationCodes {
+  // If the CFI augmentation string begins with 'z', then the CIE and FDE
+  // have an augmentation data area just before the instructions, whose
+  // contents are determined by the subsequent augmentation letters.
+  DW_Z_augmentation_start = 'z',
+
+  // If this letter is present in a 'z' augmentation string, the CIE
+  // augmentation data includes a pointer encoding, and the FDE
+  // augmentation data includes a language-specific data area pointer,
+  // represented using that encoding.
+  DW_Z_has_LSDA = 'L',
+
+  // If this letter is present in a 'z' augmentation string, the CIE
+  // augmentation data includes a pointer encoding, followed by a pointer
+  // to a personality routine, represented using that encoding.
+  DW_Z_has_personality_routine = 'P',
+
+  // If this letter is present in a 'z' augmentation string, the CIE
+  // augmentation data includes a pointer encoding describing how the FDE's
+  // initial location, address range, and DW_CFA_set_loc operands are
+  // encoded.
+  DW_Z_has_FDE_address_encoding = 'R',
+
+  // If this letter is present in a 'z' augmentation string, then code
+  // addresses covered by FDEs that cite this CIE are signal delivery
+  // trampolines. Return addresses of frames in trampolines should not be
+  // adjusted as described in section 6.4.4 of the DWARF 3 spec.
+  DW_Z_is_signal_trampoline = 'S'
+};
+
+} // namespace lul
+
+#endif // LulDwarfInt_h
new file mode 100644
--- /dev/null
+++ b/tools/profiler/LulDwarfSummariser.cpp
@@ -0,0 +1,242 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LulDwarfSummariser.h"
+
+#include "mozilla/Assertions.h"
+
+// Set this to 1 for verbose logging
+#define DEBUG_SUMMARISER 0
+
+namespace lul {
+
+Summariser::Summariser(SecMap* aSecMap, uintptr_t aTextBias,
+                       void(*aLog)(const char*))
+  : mSecMap(aSecMap)
+  , mTextBias(aTextBias)
+  , mLog(aLog)
+{
+  mCurrAddr = 0;
+  mMax1Addr = 0; // Gives an empty range.
+
+  // Initialise the running RuleSet to "haven't got a clue" status.
+  new (&mCurrRules) RuleSet();
+}
+
+void
+Summariser::Entry(uintptr_t aAddress, uintptr_t aLength)
+{
+  aAddress += mTextBias;
+  if (DEBUG_SUMMARISER) {
+    char buf[100];
+    snprintf(buf, sizeof(buf), "LUL Entry(%llx, %llu)\n",
+             (unsigned long long int)aAddress,
+             (unsigned long long int)aLength);
+    buf[sizeof(buf)-1] = 0;
+    mLog(buf);
+  }
+  // This throws away any previous summary, that is, assumes
+  // that the previous summary, if any, has been properly finished
+  // by a call to End().
+  mCurrAddr = aAddress;
+  mMax1Addr = aAddress + aLength;
+  new (&mCurrRules) RuleSet();
+}
+
+void
+Summariser::Rule(uintptr_t aAddress,
+                 int aNewReg, int aOldReg, intptr_t aOffset, bool aDeref)
+{
+  aAddress += mTextBias;
+  if (DEBUG_SUMMARISER) {
+    char buf[100];
+    snprintf(buf, sizeof(buf),
+             "LUL  0x%llx  old-r%d = %sr%d + %ld%s\n",
+             (unsigned long long int)aAddress, aNewReg,
+             aDeref ? "*(" : "", aOldReg, (long)aOffset, aDeref ? ")" : "");
+    buf[sizeof(buf)-1] = 0;
+    mLog(buf);
+  }
+  if (mCurrAddr < aAddress) {
+    // Flush the existing summary first.
+    mCurrRules.mAddr = mCurrAddr;
+    mCurrRules.mLen  = aAddress - mCurrAddr;
+    mSecMap->AddRuleSet(&mCurrRules);
+    if (DEBUG_SUMMARISER) {
+      mLog("LUL  "); mCurrRules.Print(mLog);
+      mLog("\n");
+    }
+    mCurrAddr = aAddress;
+  }
+
+  // FIXME: factor out common parts of the arch-dependent summarisers.
+
+#if defined(LUL_ARCH_arm)
+
+  // ----------------- arm ----------------- //
+
+  // Now, can we add the rule to our summary?  This depends on whether
+  // the registers and the overall expression are representable.  This
+  // is the heart of the summarisation process.
+  switch (aNewReg) {
+
+    case DW_REG_CFA:
+      // This is a rule that defines the CFA.  The only forms we
+      // choose to represent are: r7/11/12/13 + offset.  The offset
+      // must fit into 32 bits since 'uintptr_t' is 32 bit on ARM,
+      // hence there is no need to check it for overflow.
+      if (aDeref) {
+        goto cant_summarise;
+      }
+      switch (aOldReg) {
+        case DW_REG_ARM_R7:  case DW_REG_ARM_R11:
+        case DW_REG_ARM_R12: case DW_REG_ARM_R13:
+          break;
+        default:
+          goto cant_summarise;
+      }
+      mCurrRules.mCfaExpr = LExpr(LExpr::NODEREF, aOldReg, aOffset);
+      break;
+
+    case DW_REG_ARM_R7:  case DW_REG_ARM_R11: case DW_REG_ARM_R12:
+    case DW_REG_ARM_R13: case DW_REG_ARM_R14: case DW_REG_ARM_R15: {
+      // Check the aOldReg is valid.
+      switch (aOldReg) {
+        case DW_REG_CFA:
+        case DW_REG_ARM_R7:  case DW_REG_ARM_R11: case DW_REG_ARM_R12:
+        case DW_REG_ARM_R13: case DW_REG_ARM_R14: case DW_REG_ARM_R15:
+          break;
+        default:
+          goto cant_summarise;
+      }
+      // This is a new rule for one of r{7,11,12,13,14,15} and has a
+      // representable offset.  In particular the new value of r15 is
+      // going to be the return address.
+      LExpr expr = LExpr(aDeref ? LExpr::DEREF : LExpr::NODEREF,
+                         aOldReg, aOffset);
+      switch (aNewReg) {
+        case DW_REG_ARM_R7:  mCurrRules.mR7expr  = expr; break;
+        case DW_REG_ARM_R11: mCurrRules.mR11expr = expr; break;
+        case DW_REG_ARM_R12: mCurrRules.mR12expr = expr; break;
+        case DW_REG_ARM_R13: mCurrRules.mR13expr = expr; break;
+        case DW_REG_ARM_R14: mCurrRules.mR14expr = expr; break;
+        case DW_REG_ARM_R15: mCurrRules.mR15expr = expr; break;
+        default: MOZ_ASSERT(0);
+      }
+      break;
+    }
+
+    default:
+      goto cant_summarise;
+  }
+
+  // Mark callee-saved registers (r4 .. r11) as unchanged, if there is
+  // no other information about them.  FIXME: do this just once, at
+  // the point where the ruleset is committed.
+  if (mCurrRules.mR7expr.mHow == LExpr::UNKNOWN) {
+    mCurrRules.mR7expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R7, 0);
+  }
+  if (mCurrRules.mR11expr.mHow == LExpr::UNKNOWN) {
+    mCurrRules.mR11expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R11, 0);
+  }
+  if (mCurrRules.mR12expr.mHow == LExpr::UNKNOWN) {
+    mCurrRules.mR12expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R12, 0);
+  }
+
+  // The old r13 (SP) value before the call is always the same as the
+  // CFA.
+  mCurrRules.mR13expr = LExpr(LExpr::NODEREF, DW_REG_CFA, 0);
+
+  // If there's no information about R15 (the return address), say
+  // it's a copy of R14 (the link register).
+  if (mCurrRules.mR15expr.mHow == LExpr::UNKNOWN) {
+    mCurrRules.mR15expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R14, 0);
+  }
+
+#elif defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86)
+
+  // ---------------- x64/x86 ---------------- //
+
+  // Now, can we add the rule to our summary?  This depends on whether
+  // the registers and the overall expression are representable.  This
+  // is the heart of the summarisation process.  In the 64 bit case
+  // we need to check that aOffset will fit into an int32_t.  In the
+  // 32 bit case it is expected that the compiler will fold out the
+  // test since it always succeeds.
+  if (aNewReg == DW_REG_CFA) {
+    // This is a rule that defines the CFA.  The only forms we can
+    // represent are: = SP+offset or = FP+offset.
+    if (!aDeref && aOffset == (intptr_t)(int32_t)aOffset &&
+        (aOldReg == DW_REG_INTEL_XSP || aOldReg == DW_REG_INTEL_XBP)) {
+      mCurrRules.mCfaExpr = LExpr(LExpr::NODEREF, aOldReg, aOffset);
+    } else {
+      goto cant_summarise;
+    }
+  }
+  else
+  if ((aNewReg == DW_REG_INTEL_XSP ||
+       aNewReg == DW_REG_INTEL_XBP || aNewReg == DW_REG_INTEL_XIP) &&
+      (aOldReg == DW_REG_CFA ||
+       aOldReg == DW_REG_INTEL_XSP ||
+       aOldReg == DW_REG_INTEL_XBP || aOldReg == DW_REG_INTEL_XIP) &&
+      aOffset == (intptr_t)(int32_t)aOffset) {
+    // This is a new rule for SP, BP or the return address
+    // respectively, and has a representable offset.
+    LExpr expr = LExpr(aDeref ? LExpr::DEREF : LExpr::NODEREF,
+                       aOldReg, aOffset);
+    switch (aNewReg) {
+      case DW_REG_INTEL_XBP: mCurrRules.mXbpExpr = expr; break;
+      case DW_REG_INTEL_XSP: mCurrRules.mXspExpr = expr; break;
+      case DW_REG_INTEL_XIP: mCurrRules.mXipExpr = expr; break;
+      default: MOZ_CRASH("impossible value for aNewReg");
+    }
+  }
+  else {
+    goto cant_summarise;
+  }
+
+  // On Intel, it seems the old SP value before the call is always the
+  // same as the CFA.  Therefore, in the absence of any other way to
+  // recover the SP, specify that the CFA should be copied.
+  if (mCurrRules.mXspExpr.mHow == LExpr::UNKNOWN) {
+    mCurrRules.mXspExpr = LExpr(LExpr::NODEREF, DW_REG_CFA, 0);
+  }
+
+  // Also, gcc says "Undef" for BP when it is unchanged.
+  if (mCurrRules.mXbpExpr.mHow == LExpr::UNKNOWN) {
+    mCurrRules.mXbpExpr = LExpr(LExpr::NODEREF, DW_REG_INTEL_XBP, 0);
+  }
+
+#else
+
+# error "Unsupported arch"
+#endif
+
+  return;
+ cant_summarise:
+  if (0) {
+    mLog("LUL  can't summarise\n");
+  }
+}
+
+void
+Summariser::End()
+{
+  if (DEBUG_SUMMARISER) {
+    mLog("LUL End\n");
+  }
+  if (mCurrAddr < mMax1Addr) {
+    mCurrRules.mAddr = mCurrAddr;
+    mCurrRules.mLen  = mMax1Addr - mCurrAddr;
+    mSecMap->AddRuleSet(&mCurrRules);
+    if (DEBUG_SUMMARISER) {
+      mLog("LUL  "); mCurrRules.Print(mLog);
+      mLog("\n");
+    }
+  }
+}
+
+} // namespace lul
new file mode 100644
--- /dev/null
+++ b/tools/profiler/LulDwarfSummariser.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef LulDwarfSummariser_h
+#define LulDwarfSummariser_h
+
+#include "LulMainInt.h"
+
+namespace lul {
+
+class Summariser
+{
+public:
+  Summariser(SecMap* aSecMap, uintptr_t aTextBias, void(*aLog)(const char*));
+
+  void Entry(uintptr_t aAddress, uintptr_t aLength);
+  void End();
+  void Rule(uintptr_t aAddress,
+            int aNewReg, int aOldReg, intptr_t aOffset, bool aDeref);
+
+private:
+  // The SecMap in which we park the finished summaries (RuleSets).
+  SecMap* mSecMap;
+
+  // Running state for the current summary (RuleSet) under construction.
+  RuleSet mCurrRules;
+
+  // The start of the address range to which the RuleSet under
+  // construction applies.
+  uintptr_t mCurrAddr;
+
+  // The highest address, plus one, for which the RuleSet under
+  // construction could possibly apply.  If there are no further
+  // incoming events then mCurrRules will eventually be emitted
+  // as-is, for the range mCurrAddr.. mMax1Addr - 1, if that is
+  // nonempty.
+  uintptr_t mMax1Addr;
+
+  // The bias value (to add to the SVMAs, to get AVMAs) to be used
+  // when adding entries into mSecMap.
+  uintptr_t mTextBias;
+
+  // A logging sink, for debugging.
+  void (*mLog)(const char* aFmt);
+};
+
+} // namespace lul
+
+#endif // LulDwarfSummariser_h
new file mode 100644
--- /dev/null
+++ b/tools/profiler/LulElf.cpp
@@ -0,0 +1,1008 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+
+// Copyright (c) 2006, 2011, 2012 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Restructured in 2009 by: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// (derived from)
+// dump_symbols.cc: implement google_breakpad::WriteSymbolFile:
+// Find all the debugging info in a file and dump it as a Breakpad symbol file.
+//
+// dump_symbols.h: Read debugging information from an ELF file, and write
+// it out as a Breakpad symbol file.
+
+// This file is derived from the following files in
+// toolkit/crashreporter/google-breakpad:
+//   src/common/linux/dump_symbols.cc
+//   src/common/linux/elfutils.cc
+//   src/common/linux/file_id.cc
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "mozilla/Assertions.h"
+
+#include "LulPlatformMacros.h"
+#include "LulCommonExt.h"
+#include "LulDwarfExt.h"
+#if defined(LUL_PLAT_arm_android)
+# include "LulExidxExt.h"
+#endif
+#include "LulElfInt.h"
+#include "LulMainInt.h"
+
+
+#if defined(LUL_PLAT_arm_android) && !defined(SHT_ARM_EXIDX)
+// bionic and older glibsc don't define it
+# define SHT_ARM_EXIDX (SHT_LOPROC + 1)
+#endif
+
+
+// This namespace contains helper functions.
+namespace {
+
+using lul::DwarfCFIToModule;
+using lul::FindElfSectionByName;
+using lul::GetOffset;
+using lul::IsValidElf;
+using lul::Module;
+using lul::UniqueString;
+using lul::scoped_ptr;
+using lul::Summariser;
+using std::string;
+using std::vector;
+using std::set;
+
+//
+// FDWrapper
+//
+// Wrapper class to make sure opened file is closed.
+//
+class FDWrapper {
+ public:
+  explicit FDWrapper(int fd) :
+    fd_(fd) {}
+  ~FDWrapper() {
+    if (fd_ != -1)
+      close(fd_);
+  }
+  int get() {
+    return fd_;
+  }
+  int release() {
+    int fd = fd_;
+    fd_ = -1;
+    return fd;
+  }
+ private:
+  int fd_;
+};
+
+//
+// MmapWrapper
+//
+// Wrapper class to make sure mapped regions are unmapped.
+//
+class MmapWrapper {
+ public:
+  MmapWrapper() : is_set_(false) {}
+  ~MmapWrapper() {
+    if (is_set_ && base_ != NULL) {
+      MOZ_ASSERT(size_ > 0);
+      munmap(base_, size_);
+    }
+  }
+  void set(void *mapped_address, size_t mapped_size) {
+    is_set_ = true;
+    base_ = mapped_address;
+    size_ = mapped_size;
+  }
+  void release() {
+    MOZ_ASSERT(is_set_);
+    is_set_ = false;
+    base_ = NULL;
+    size_ = 0;
+  }
+
+ private:
+  bool is_set_;
+  void *base_;
+  size_t size_;
+};
+
+
+// Set NUM_DW_REGNAMES to be the number of Dwarf register names
+// appropriate to the machine architecture given in HEADER.  Return
+// true on success, or false if HEADER's machine architecture is not
+// supported.
+template<typename ElfClass>
+bool DwarfCFIRegisterNames(const typename ElfClass::Ehdr* elf_header,
+                           unsigned int* num_dw_regnames) {
+  switch (elf_header->e_machine) {
+    case EM_386:
+      *num_dw_regnames = DwarfCFIToModule::RegisterNames::I386();
+      return true;
+    case EM_ARM:
+      *num_dw_regnames = DwarfCFIToModule::RegisterNames::ARM();
+      return true;
+    case EM_X86_64:
+      *num_dw_regnames = DwarfCFIToModule::RegisterNames::X86_64();
+      return true;
+    default:
+      MOZ_ASSERT(0);
+      return false;
+  }
+}
+
+template<typename ElfClass>
+bool LoadDwarfCFI(const string& dwarf_filename,
+                  const typename ElfClass::Ehdr* elf_header,
+                  const char* section_name,
+                  const typename ElfClass::Shdr* section,
+                  const bool eh_frame,
+                  const typename ElfClass::Shdr* got_section,
+                  const typename ElfClass::Shdr* text_section,
+                  const bool big_endian,
+                  SecMap* smap,
+                  uintptr_t text_bias,
+                  void (*log)(const char*)) {
+  // Find the appropriate set of register names for this file's
+  // architecture.
+  unsigned int num_dw_regs = 0;
+  if (!DwarfCFIRegisterNames<ElfClass>(elf_header, &num_dw_regs)) {
+    fprintf(stderr, "%s: unrecognized ELF machine architecture '%d';"
+            " cannot convert DWARF call frame information\n",
+            dwarf_filename.c_str(), elf_header->e_machine);
+    return false;
+  }
+
+  const lul::Endianness endianness
+    = big_endian ? lul::ENDIANNESS_BIG : lul::ENDIANNESS_LITTLE;
+
+  // Find the call frame information and its size.
+  const char* cfi =
+      GetOffset<ElfClass, char>(elf_header, section->sh_offset);
+  size_t cfi_size = section->sh_size;
+
+  // Plug together the parser, handler, and their entourages.
+
+  // Here's a summariser, which will receive the output of the
+  // parser, create summaries, and add them to |smap|.
+  Summariser* summ = new Summariser(smap, text_bias, log);
+
+  DwarfCFIToModule::Reporter module_reporter(log, dwarf_filename, section_name);
+  DwarfCFIToModule handler(num_dw_regs, &module_reporter, summ);
+  lul::ByteReader byte_reader(endianness);
+
+  byte_reader.SetAddressSize(ElfClass::kAddrSize);
+
+  // Provide the base addresses for .eh_frame encoded pointers, if
+  // possible.
+  byte_reader.SetCFIDataBase(section->sh_addr, cfi);
+  if (got_section)
+    byte_reader.SetDataBase(got_section->sh_addr);
+  if (text_section)
+    byte_reader.SetTextBase(text_section->sh_addr);
+
+  lul::CallFrameInfo::Reporter dwarf_reporter(log, dwarf_filename,
+                                              section_name);
+  lul::CallFrameInfo parser(cfi, cfi_size,
+                            &byte_reader, &handler, &dwarf_reporter,
+                            eh_frame);
+  parser.Start();
+
+  delete summ;
+  return true;
+}
+
+#if defined(LUL_PLAT_arm_android)
+template<typename ElfClass>
+bool LoadARMexidx(const typename ElfClass::Ehdr* elf_header,
+                  const typename ElfClass::Shdr* exidx_section,
+                  const typename ElfClass::Shdr* extab_section,
+                  uint32_t loading_addr,
+                  uintptr_t text_bias,
+                  SecMap* smap,
+                  void (*log)(const char*)) {
+  // To do this properly we need to know:
+  // * the bounds of the .ARM.exidx section in the mapped image
+  // * the bounds of the .ARM.extab section in the mapped image
+  // * the vma of the last byte in the text section associated with the .exidx
+  // The first two are easy.  The third is a bit tricky.  If we can't
+  // figure out what it is, just pass in zero.
+  // Note that we are reading EXIDX directly out of the mapped in
+  // executable image.  Unlike with the CFI reader, there is no
+  // auxiliary, temporary mapping used to read the unwind data.
+  //
+  // An .exidx section is always required, but the .extab section
+  // can be optionally omitted, provided that .exidx does not refer
+  // to it.  If the .exidx is erroneous and does refer to .extab even
+  // though .extab is missing, the range checks done by GET_EX_U32 in
+  // ExceptionTableInfo::ExtabEntryExtract should prevent any invalid
+  // memory accesses, and cause the .extab to be rejected as invalid.
+  const char *exidx_img
+    = GetOffset<ElfClass, char>(elf_header, exidx_section->sh_offset);
+  size_t exidx_size = exidx_section->sh_size;
+  const char *extab_img
+    = extab_section
+        ? GetOffset<ElfClass, char>(elf_header, extab_section->sh_offset)
+        : nullptr;
+  size_t extab_size = extab_section ? extab_section->sh_size : 0;
+
+  // The sh_link field of the exidx section gives the section number
+  // for the associated text section.
+  uint32_t exidx_text_last_svma = 0;
+  int exidx_text_sno = exidx_section->sh_link;
+  typedef typename ElfClass::Shdr Shdr;
+  // |sections| points to the section header table
+  const Shdr* sections
+    = GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff);
+  const int num_sections = elf_header->e_shnum;
+  if (exidx_text_sno >= 0 && exidx_text_sno < num_sections) {
+    const Shdr* exidx_text_shdr = &sections[exidx_text_sno];
+    if (exidx_text_shdr->sh_size > 0) {
+      exidx_text_last_svma
+        = exidx_text_shdr->sh_addr + exidx_text_shdr->sh_size - 1;
+    }
+  }
+
+  lul::ARMExToModule handler(smap, log);
+  lul::ExceptionTableInfo
+    parser(exidx_img, exidx_size, extab_img, extab_size, exidx_text_last_svma,
+           &handler,
+           reinterpret_cast<const char*>(elf_header),
+           loading_addr, text_bias, log);
+  parser.Start();
+  return true;
+}
+#endif /* defined(LUL_PLAT_arm_android) */
+
+bool LoadELF(const string& obj_file, MmapWrapper* map_wrapper,
+             void** elf_header) {
+  int obj_fd = open(obj_file.c_str(), O_RDONLY);
+  if (obj_fd < 0) {
+    fprintf(stderr, "Failed to open ELF file '%s': %s\n",
+            obj_file.c_str(), strerror(errno));
+    return false;
+  }
+  FDWrapper obj_fd_wrapper(obj_fd);
+  struct stat st;
+  if (fstat(obj_fd, &st) != 0 && st.st_size <= 0) {
+    fprintf(stderr, "Unable to fstat ELF file '%s': %s\n",
+            obj_file.c_str(), strerror(errno));
+    return false;
+  }
+  // Mapping it read-only is good enough.  In any case, mapping it
+  // read-write confuses Valgrind's debuginfo acquire/discard
+  // heuristics, making it hard to profile the profiler.
+  void *obj_base = mmap(nullptr, st.st_size,
+                        PROT_READ, MAP_PRIVATE, obj_fd, 0);
+  if (obj_base == MAP_FAILED) {
+    fprintf(stderr, "Failed to mmap ELF file '%s': %s\n",
+            obj_file.c_str(), strerror(errno));
+    return false;
+  }
+  map_wrapper->set(obj_base, st.st_size);
+  *elf_header = obj_base;
+  if (!IsValidElf(*elf_header)) {
+    fprintf(stderr, "Not a valid ELF file: %s\n", obj_file.c_str());
+    return false;
+  }
+  return true;
+}
+
+// Get the endianness of ELF_HEADER. If it's invalid, return false.
+template<typename ElfClass>
+bool ElfEndianness(const typename ElfClass::Ehdr* elf_header,
+                   bool* big_endian) {
+  if (elf_header->e_ident[EI_DATA] == ELFDATA2LSB) {
+    *big_endian = false;
+    return true;
+  }
+  if (elf_header->e_ident[EI_DATA] == ELFDATA2MSB) {
+    *big_endian = true;
+    return true;
+  }
+
+  fprintf(stderr, "bad data encoding in ELF header: %d\n",
+          elf_header->e_ident[EI_DATA]);
+  return false;
+}
+
+//
+// LoadSymbolsInfo
+//
+// Holds the state between the two calls to LoadSymbols() in case it's necessary
+// to follow the .gnu_debuglink section and load debug information from a
+// different file.
+//
+template<typename ElfClass>
+class LoadSymbolsInfo {
+ public:
+  typedef typename ElfClass::Addr Addr;
+
+  explicit LoadSymbolsInfo(const vector<string>& dbg_dirs) :
+    debug_dirs_(dbg_dirs),
+    has_loading_addr_(false) {}
+
+  // Keeps track of which sections have been loaded so sections don't
+  // accidentally get loaded twice from two different files.
+  void LoadedSection(const string &section) {
+    if (loaded_sections_.count(section) == 0) {
+      loaded_sections_.insert(section);
+    } else {
+      fprintf(stderr, "Section %s has already been loaded.\n",
+              section.c_str());
+    }
+  }
+
+  string debuglink_file() const {
+    return debuglink_file_;
+  }
+
+ private:
+  const vector<string>& debug_dirs_; // Directories in which to
+                                     // search for the debug ELF file.
+
+  string debuglink_file_; // Full path to the debug ELF file.
+
+  bool has_loading_addr_; // Indicate if LOADING_ADDR_ is valid.
+
+  set<string> loaded_sections_; // Tracks the Loaded ELF sections
+                                // between calls to LoadSymbols().
+};
+
+// Find the preferred loading address of the binary.
+template<typename ElfClass>
+typename ElfClass::Addr GetLoadingAddress(
+    const typename ElfClass::Phdr* program_headers,
+    int nheader) {
+  typedef typename ElfClass::Phdr Phdr;
+
+  // For non-PIC executables (e_type == ET_EXEC), the load address is
+  // the start address of the first PT_LOAD segment.  (ELF requires
+  // the segments to be sorted by load address.)  For PIC executables
+  // and dynamic libraries (e_type == ET_DYN), this address will
+  // normally be zero.
+  for (int i = 0; i < nheader; ++i) {
+    const Phdr& header = program_headers[i];
+    if (header.p_type == PT_LOAD)
+      return header.p_vaddr;
+  }
+  return 0;
+}
+
+template<typename ElfClass>
+bool LoadSymbols(const string& obj_file,
+                 const bool big_endian,
+                 const typename ElfClass::Ehdr* elf_header,
+                 const bool read_gnu_debug_link,
+                 LoadSymbolsInfo<ElfClass>* info,
+                 SecMap* smap,
+                 void* rx_avma,
+                 void (*log)(const char*)) {
+  typedef typename ElfClass::Addr Addr;
+  typedef typename ElfClass::Phdr Phdr;
+  typedef typename ElfClass::Shdr Shdr;
+
+  char buf[500];
+  snprintf(buf, sizeof(buf), "LoadSymbols: BEGIN   %s\n", obj_file.c_str());
+  buf[sizeof(buf)-1] = 0;
+  log(buf);
+
+  // This is how the text bias is calculated.
+  // BEGIN CALCULATE BIAS
+  uintptr_t loading_addr = GetLoadingAddress<ElfClass>(
+      GetOffset<ElfClass, Phdr>(elf_header, elf_header->e_phoff),
+      elf_header->e_phnum);
+  uintptr_t text_bias = ((uintptr_t)rx_avma) - loading_addr;
+  snprintf(buf, sizeof(buf),
+           "LoadSymbols:   rx_avma=%llx, text_bias=%llx",
+           (unsigned long long int)(uintptr_t)rx_avma,
+           (unsigned long long int)text_bias);
+  buf[sizeof(buf)-1] = 0;
+  log(buf);
+  // END CALCULATE BIAS
+
+  const Shdr* sections =
+      GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff);
+  const Shdr* section_names = sections + elf_header->e_shstrndx;
+  const char* names =
+      GetOffset<ElfClass, char>(elf_header, section_names->sh_offset);
+  const char *names_end = names + section_names->sh_size;
+  bool found_usable_info = false;
+
+  // Dwarf Call Frame Information (CFI) is actually independent from
+  // the other DWARF debugging information, and can be used alone.
+  const Shdr* dwarf_cfi_section =
+      FindElfSectionByName<ElfClass>(".debug_frame", SHT_PROGBITS,
+                                     sections, names, names_end,
+                                     elf_header->e_shnum);
+  if (dwarf_cfi_section) {
+    // Ignore the return value of this function; even without call frame
+    // information, the other debugging information could be perfectly
+    // useful.
+    info->LoadedSection(".debug_frame");
+    bool result =
+        LoadDwarfCFI<ElfClass>(obj_file, elf_header, ".debug_frame",
+                               dwarf_cfi_section, false, 0, 0, big_endian,
+                               smap, text_bias, log);
+    found_usable_info = found_usable_info || result;
+    if (result)
+      log("LoadSymbols:   read CFI from .debug_frame");
+  }
+
+  // Linux C++ exception handling information can also provide
+  // unwinding data.
+  const Shdr* eh_frame_section =
+      FindElfSectionByName<ElfClass>(".eh_frame", SHT_PROGBITS,
+                                     sections, names, names_end,
+                                     elf_header->e_shnum);
+  if (eh_frame_section) {
+    // Pointers in .eh_frame data may be relative to the base addresses of
+    // certain sections. Provide those sections if present.
+    const Shdr* got_section =
+        FindElfSectionByName<ElfClass>(".got", SHT_PROGBITS,
+                                       sections, names, names_end,
+                                       elf_header->e_shnum);
+    const Shdr* text_section =
+        FindElfSectionByName<ElfClass>(".text", SHT_PROGBITS,
+                                       sections, names, names_end,
+                                       elf_header->e_shnum);
+    info->LoadedSection(".eh_frame");
+    // As above, ignore the return value of this function.
+    bool result =
+        LoadDwarfCFI<ElfClass>(obj_file, elf_header, ".eh_frame",
+                               eh_frame_section, true,
+                               got_section, text_section, big_endian,
+                               smap, text_bias, log);
+    found_usable_info = found_usable_info || result;
+    if (result)
+      log("LoadSymbols:   read CFI from .eh_frame");
+  }
+
+# if defined(LUL_PLAT_arm_android)
+  // ARM has special unwind tables that can be used.  .exidx is
+  // always required, and .extab is normally required, but may
+  // be omitted if it is empty.  See comments on LoadARMexidx()
+  // for more details.
+  const Shdr* arm_exidx_section =
+      FindElfSectionByName<ElfClass>(".ARM.exidx", SHT_ARM_EXIDX,
+                                     sections, names, names_end,
+                                     elf_header->e_shnum);
+  const Shdr* arm_extab_section =
+      FindElfSectionByName<ElfClass>(".ARM.extab", SHT_PROGBITS,
+                                     sections, names, names_end,
+                                     elf_header->e_shnum);
+  const Shdr* debug_info_section =
+      FindElfSectionByName<ElfClass>(".debug_info", SHT_PROGBITS,
+                                     sections, names, names_end,
+                                     elf_header->e_shnum);
+  // Only load information from this section if there isn't a .debug_info
+  // section.
+  if (!debug_info_section && arm_exidx_section) {
+    info->LoadedSection(".ARM.exidx");
+    if (arm_extab_section)
+      info->LoadedSection(".ARM.extab");
+    bool result = LoadARMexidx<ElfClass>(elf_header,
+                                         arm_exidx_section, arm_extab_section,
+                                         loading_addr, text_bias, smap, log);
+    found_usable_info = found_usable_info || result;
+    if (result)
+      log("LoadSymbols:   read EXIDX from .ARM.{exidx,extab}");
+  }
+# endif /* defined(LUL_PLAT_arm_android) */
+
+  snprintf(buf, sizeof(buf), "LoadSymbols: END     %s\n", obj_file.c_str());
+  buf[sizeof(buf)-1] = 0;
+  log(buf);
+
+  return found_usable_info;
+}
+
+// Return the breakpad symbol file identifier for the architecture of
+// ELF_HEADER.
+template<typename ElfClass>
+const char* ElfArchitecture(const typename ElfClass::Ehdr* elf_header) {
+  typedef typename ElfClass::Half Half;
+  Half arch = elf_header->e_machine;
+  switch (arch) {
+    case EM_386:        return "x86";
+    case EM_ARM:        return "arm";
+    case EM_MIPS:       return "mips";
+    case EM_PPC64:      return "ppc64";
+    case EM_PPC:        return "ppc";
+    case EM_S390:       return "s390";
+    case EM_SPARC:      return "sparc";
+    case EM_SPARCV9:    return "sparcv9";
+    case EM_X86_64:     return "x86_64";
+    default: return NULL;
+  }
+}
+
+// Format the Elf file identifier in IDENTIFIER as a UUID with the
+// dashes removed.
+string FormatIdentifier(unsigned char identifier[16]) {
+  char identifier_str[40];
+  lul::FileID::ConvertIdentifierToString(
+      identifier,
+      identifier_str,
+      sizeof(identifier_str));
+  string id_no_dash;
+  for (int i = 0; identifier_str[i] != '\0'; ++i)
+    if (identifier_str[i] != '-')
+      id_no_dash += identifier_str[i];
+  // Add an extra "0" by the end.  PDB files on Windows have an 'age'
+  // number appended to the end of the file identifier; this isn't
+  // really used or necessary on other platforms, but be consistent.
+  id_no_dash += '0';
+  return id_no_dash;
+}
+
+// Return the non-directory portion of FILENAME: the portion after the
+// last slash, or the whole filename if there are no slashes.
+string BaseFileName(const string &filename) {
+  // Lots of copies!  basename's behavior is less than ideal.
+  char *c_filename = strdup(filename.c_str());
+  string base = basename(c_filename);
+  free(c_filename);
+  return base;
+}
+
+template<typename ElfClass>
+bool ReadSymbolDataElfClass(const typename ElfClass::Ehdr* elf_header,
+                            const string& obj_filename,
+                            const vector<string>& debug_dirs,
+                            SecMap* smap, void* rx_avma,
+                            void (*log)(const char*)) {
+  typedef typename ElfClass::Ehdr Ehdr;
+  typedef typename ElfClass::Shdr Shdr;
+
+  unsigned char identifier[16];
+  if (!lul
+      ::FileID::ElfFileIdentifierFromMappedFile(elf_header, identifier)) {
+    fprintf(stderr, "%s: unable to generate file identifier\n",
+            obj_filename.c_str());
+    return false;
+  }
+
+  const char *architecture = ElfArchitecture<ElfClass>(elf_header);
+  if (!architecture) {
+    fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n",
+            obj_filename.c_str(), elf_header->e_machine);
+    return false;
+  }
+
+  // Figure out what endianness this file is.
+  bool big_endian;
+  if (!ElfEndianness<ElfClass>(elf_header, &big_endian))
+    return false;
+
+  string name = BaseFileName(obj_filename);
+  string os = "Linux";
+  string id = FormatIdentifier(identifier);
+
+  LoadSymbolsInfo<ElfClass> info(debug_dirs);
+  if (!LoadSymbols<ElfClass>(obj_filename, big_endian, elf_header,
+                             !debug_dirs.empty(), &info,
+                             smap, rx_avma, log)) {
+    const string debuglink_file = info.debuglink_file();
+    if (debuglink_file.empty())
+      return false;
+
+    // Load debuglink ELF file.
+    fprintf(stderr, "Found debugging info in %s\n", debuglink_file.c_str());
+    MmapWrapper debug_map_wrapper;
+    Ehdr* debug_elf_header = NULL;
+    if (!LoadELF(debuglink_file, &debug_map_wrapper,
+                 reinterpret_cast<void**>(&debug_elf_header)))
+      return false;
+    // Sanity checks to make sure everything matches up.
+    const char *debug_architecture =
+        ElfArchitecture<ElfClass>(debug_elf_header);
+    if (!debug_architecture) {
+      fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n",
+              debuglink_file.c_str(), debug_elf_header->e_machine);
+      return false;
+    }
+    if (strcmp(architecture, debug_architecture)) {
+      fprintf(stderr, "%s with ELF machine architecture %s does not match "
+              "%s with ELF architecture %s\n",
+              debuglink_file.c_str(), debug_architecture,
+              obj_filename.c_str(), architecture);
+      return false;
+    }
+
+    bool debug_big_endian;
+    if (!ElfEndianness<ElfClass>(debug_elf_header, &debug_big_endian))
+      return false;
+    if (debug_big_endian != big_endian) {
+      fprintf(stderr, "%s and %s does not match in endianness\n",
+              obj_filename.c_str(), debuglink_file.c_str());
+      return false;
+    }
+
+    if (!LoadSymbols<ElfClass>(debuglink_file, debug_big_endian,
+                               debug_elf_header, false, &info,
+                               smap, rx_avma, log)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+}  // namespace (anon)
+
+
+namespace lul {
+
+bool ReadSymbolDataInternal(const uint8_t* obj_file,
+                            const string& obj_filename,
+                            const vector<string>& debug_dirs,
+                            SecMap* smap, void* rx_avma,
+                            void (*log)(const char*)) {
+
+  if (!IsValidElf(obj_file)) {
+    fprintf(stderr, "Not a valid ELF file: %s\n", obj_filename.c_str());
+    return false;
+  }
+
+  int elfclass = ElfClass(obj_file);
+  if (elfclass == ELFCLASS32) {
+    return ReadSymbolDataElfClass<ElfClass32>(
+        reinterpret_cast<const Elf32_Ehdr*>(obj_file),
+        obj_filename, debug_dirs, smap, rx_avma, log);
+  }
+  if (elfclass == ELFCLASS64) {
+    return ReadSymbolDataElfClass<ElfClass64>(
+        reinterpret_cast<const Elf64_Ehdr*>(obj_file),
+        obj_filename, debug_dirs, smap, rx_avma, log);
+  }
+
+  return false;
+}
+
+bool ReadSymbolData(const string& obj_file,
+                    const vector<string>& debug_dirs,
+                    SecMap* smap, void* rx_avma,
+                    void (*log)(const char*)) {
+  MmapWrapper map_wrapper;
+  void* elf_header = NULL;
+  if (!LoadELF(obj_file, &map_wrapper, &elf_header))
+    return false;
+
+  return ReadSymbolDataInternal(reinterpret_cast<uint8_t*>(elf_header),
+                                obj_file, debug_dirs, smap, rx_avma, log);
+}
+
+
+namespace {
+
+template<typename ElfClass>
+void FindElfClassSection(const char *elf_base,
+                         const char *section_name,
+                         typename ElfClass::Word section_type,
+                         const void **section_start,
+                         int *section_size) {
+  typedef typename ElfClass::Ehdr Ehdr;
+  typedef typename ElfClass::Shdr Shdr;
+
+  MOZ_ASSERT(elf_base);
+  MOZ_ASSERT(section_start);
+  MOZ_ASSERT(section_size);
+
+  MOZ_ASSERT(strncmp(elf_base, ELFMAG, SELFMAG) == 0);
+
+  const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base);
+  MOZ_ASSERT(elf_header->e_ident[EI_CLASS] == ElfClass::kClass);
+
+  const Shdr* sections =
+    GetOffset<ElfClass,Shdr>(elf_header, elf_header->e_shoff);
+  const Shdr* section_names = sections + elf_header->e_shstrndx;
+  const char* names =
+    GetOffset<ElfClass,char>(elf_header, section_names->sh_offset);
+  const char *names_end = names + section_names->sh_size;
+
+  const Shdr* section =
+    FindElfSectionByName<ElfClass>(section_name, section_type,
+                                   sections, names, names_end,
+                                   elf_header->e_shnum);
+
+  if (section != NULL && section->sh_size > 0) {
+    *section_start = elf_base + section->sh_offset;
+    *section_size = section->sh_size;
+  }
+}
+
+template<typename ElfClass>
+void FindElfClassSegment(const char *elf_base,
+                         typename ElfClass::Word segment_type,
+                         const void **segment_start,
+                         int *segment_size) {
+  typedef typename ElfClass::Ehdr Ehdr;
+  typedef typename ElfClass::Phdr Phdr;
+
+  MOZ_ASSERT(elf_base);
+  MOZ_ASSERT(segment_start);
+  MOZ_ASSERT(segment_size);
+
+  MOZ_ASSERT(strncmp(elf_base, ELFMAG, SELFMAG) == 0);
+
+  const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base);
+  MOZ_ASSERT(elf_header->e_ident[EI_CLASS] == ElfClass::kClass);
+
+  const Phdr* phdrs =
+    GetOffset<ElfClass,Phdr>(elf_header, elf_header->e_phoff);
+
+  for (int i = 0; i < elf_header->e_phnum; ++i) {
+    if (phdrs[i].p_type == segment_type) {
+      *segment_start = elf_base + phdrs[i].p_offset;
+      *segment_size = phdrs[i].p_filesz;
+      return;
+    }
+  }
+}
+
+}  // namespace (anon)
+
+bool IsValidElf(const void* elf_base) {
+  return strncmp(reinterpret_cast<const char*>(elf_base),
+                 ELFMAG, SELFMAG) == 0;
+}
+
+int ElfClass(const void* elf_base) {
+  const ElfW(Ehdr)* elf_header =
+    reinterpret_cast<const ElfW(Ehdr)*>(elf_base);
+
+  return elf_header->e_ident[EI_CLASS];
+}
+
+bool FindElfSection(const void *elf_mapped_base,
+                    const char *section_name,
+                    uint32_t section_type,
+                    const void **section_start,
+                    int *section_size,
+                    int *elfclass) {
+  MOZ_ASSERT(elf_mapped_base);
+  MOZ_ASSERT(section_start);
+  MOZ_ASSERT(section_size);
+
+  *section_start = NULL;
+  *section_size = 0;
+
+  if (!IsValidElf(elf_mapped_base))
+    return false;
+
+  int cls = ElfClass(elf_mapped_base);
+  if (elfclass) {
+    *elfclass = cls;
+  }
+
+  const char* elf_base =
+    static_cast<const char*>(elf_mapped_base);
+
+  if (cls == ELFCLASS32) {
+    FindElfClassSection<ElfClass32>(elf_base, section_name, section_type,
+                                    section_start, section_size);
+    return *section_start != NULL;
+  } else if (cls == ELFCLASS64) {
+    FindElfClassSection<ElfClass64>(elf_base, section_name, section_type,
+                                    section_start, section_size);
+    return *section_start != NULL;
+  }
+
+  return false;
+}
+
+bool FindElfSegment(const void *elf_mapped_base,
+                    uint32_t segment_type,
+                    const void **segment_start,
+                    int *segment_size,
+                    int *elfclass) {
+  MOZ_ASSERT(elf_mapped_base);
+  MOZ_ASSERT(segment_start);
+  MOZ_ASSERT(segment_size);
+
+  *segment_start = NULL;
+  *segment_size = 0;
+
+  if (!IsValidElf(elf_mapped_base))
+    return false;
+
+  int cls = ElfClass(elf_mapped_base);
+  if (elfclass) {
+    *elfclass = cls;
+  }
+
+  const char* elf_base =
+    static_cast<const char*>(elf_mapped_base);
+
+  if (cls == ELFCLASS32) {
+    FindElfClassSegment<ElfClass32>(elf_base, segment_type,
+                                    segment_start, segment_size);
+    return *segment_start != NULL;
+  } else if (cls == ELFCLASS64) {
+    FindElfClassSegment<ElfClass64>(elf_base, segment_type,
+                                    segment_start, segment_size);
+    return *segment_start != NULL;
+  }
+
+  return false;
+}
+
+
+// (derived from)
+// file_id.cc: Return a unique identifier for a file
+//
+// See file_id.h for documentation
+//
+
+// ELF note name and desc are 32-bits word padded.
+#define NOTE_PADDING(a) ((a + 3) & ~3)
+
+// These functions are also used inside the crashed process, so be safe
+// and use the syscall/libc wrappers instead of direct syscalls or libc.
+
+template<typename ElfClass>
+static bool ElfClassBuildIDNoteIdentifier(const void *section, int length,
+                                          uint8_t identifier[kMDGUIDSize]) {
+  typedef typename ElfClass::Nhdr Nhdr;
+
+  const void* section_end = reinterpret_cast<const char*>(section) + length;
+  const Nhdr* note_header = reinterpret_cast<const Nhdr*>(section);
+  while (reinterpret_cast<const void *>(note_header) < section_end) {
+    if (note_header->n_type == NT_GNU_BUILD_ID)
+      break;
+    note_header = reinterpret_cast<const Nhdr*>(
+                  reinterpret_cast<const char*>(note_header) + sizeof(Nhdr) +
+                  NOTE_PADDING(note_header->n_namesz) +
+                  NOTE_PADDING(note_header->n_descsz));
+  }
+  if (reinterpret_cast<const void *>(note_header) >= section_end ||
+      note_header->n_descsz == 0) {
+    return false;
+  }
+
+  const char* build_id = reinterpret_cast<const char*>(note_header) +
+    sizeof(Nhdr) + NOTE_PADDING(note_header->n_namesz);
+  // Copy as many bits of the build ID as will fit
+  // into the GUID space.
+  memset(identifier, 0, kMDGUIDSize);
+  memcpy(identifier, build_id,
+         std::min(kMDGUIDSize, (size_t)note_header->n_descsz));
+
+  return true;
+}
+
+// Attempt to locate a .note.gnu.build-id section in an ELF binary
+// and copy as many bytes of it as will fit into |identifier|.
+static bool FindElfBuildIDNote(const void *elf_mapped_base,
+                               uint8_t identifier[kMDGUIDSize]) {
+  void* note_section;
+  int note_size, elfclass;
+  if ((!FindElfSegment(elf_mapped_base, PT_NOTE,
+                       (const void**)&note_section, &note_size, &elfclass) ||
+      note_size == 0)  &&
+      (!FindElfSection(elf_mapped_base, ".note.gnu.build-id", SHT_NOTE,
+                       (const void**)&note_section, &note_size, &elfclass) ||
+      note_size == 0)) {
+    return false;
+  }
+
+  if (elfclass == ELFCLASS32) {
+    return ElfClassBuildIDNoteIdentifier<ElfClass32>(note_section, note_size,
+                                                     identifier);
+  } else if (elfclass == ELFCLASS64) {
+    return ElfClassBuildIDNoteIdentifier<ElfClass64>(note_section, note_size,
+                                                     identifier);
+  }
+
+  return false;
+}
+
+// Attempt to locate the .text section of an ELF binary and generate
+// a simple hash by XORing the first page worth of bytes into |identifier|.
+static bool HashElfTextSection(const void *elf_mapped_base,
+                               uint8_t identifier[kMDGUIDSize]) {
+  void* text_section;
+  int text_size;
+  if (!FindElfSection(elf_mapped_base, ".text", SHT_PROGBITS,
+                      (const void**)&text_section, &text_size, NULL) ||
+      text_size == 0) {
+    return false;
+  }
+
+  memset(identifier, 0, kMDGUIDSize);
+  const uint8_t* ptr = reinterpret_cast<const uint8_t*>(text_section);
+  const uint8_t* ptr_end = ptr + std::min(text_size, 4096);
+  while (ptr < ptr_end) {
+    for (unsigned i = 0; i < kMDGUIDSize; i++)
+      identifier[i] ^= ptr[i];
+    ptr += kMDGUIDSize;
+  }
+  return true;
+}
+
+// static
+bool FileID::ElfFileIdentifierFromMappedFile(const void* base,
+                                             uint8_t identifier[kMDGUIDSize]) {
+  // Look for a build id note first.
+  if (FindElfBuildIDNote(base, identifier))
+    return true;
+
+  // Fall back on hashing the first page of the text section.
+  return HashElfTextSection(base, identifier);
+}
+
+// static
+void FileID::ConvertIdentifierToString(const uint8_t identifier[kMDGUIDSize],
+                                       char* buffer, int buffer_length) {
+  uint8_t identifier_swapped[kMDGUIDSize];
+
+  // Endian-ness swap to match dump processor expectation.
+  memcpy(identifier_swapped, identifier, kMDGUIDSize);
+  uint32_t* data1 = reinterpret_cast<uint32_t*>(identifier_swapped);
+  *data1 = htonl(*data1);
+  uint16_t* data2 = reinterpret_cast<uint16_t*>(identifier_swapped + 4);
+  *data2 = htons(*data2);
+  uint16_t* data3 = reinterpret_cast<uint16_t*>(identifier_swapped + 6);
+  *data3 = htons(*data3);
+
+  int buffer_idx = 0;
+  for (unsigned int idx = 0;
+       (buffer_idx < buffer_length) && (idx < kMDGUIDSize);
+       ++idx) {
+    int hi = (identifier_swapped[idx] >> 4) & 0x0F;
+    int lo = (identifier_swapped[idx]) & 0x0F;
+
+    if (idx == 4 || idx == 6 || idx == 8 || idx == 10)
+      buffer[buffer_idx++] = '-';
+
+    buffer[buffer_idx++] = (hi >= 10) ? 'A' + hi - 10 : '0' + hi;
+    buffer[buffer_idx++] = (lo >= 10) ? 'A' + lo - 10 : '0' + lo;
+  }
+
+  // NULL terminate
+  buffer[(buffer_idx < buffer_length) ? buffer_idx : buffer_idx - 1] = 0;
+}
+
+}  // namespace lul
new file mode 100644
--- /dev/null
+++ b/tools/profiler/LulElfExt.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+
+// Copyright (c) 2006, 2011, 2012 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file is derived from the following files in
+// toolkit/crashreporter/google-breakpad:
+//   src/common/linux/dump_symbols.h
+
+#ifndef LulElfExt_h
+#define LulElfExt_h
+
+// These two functions are the external interface to the
+// ELF/Dwarf/EXIDX reader.
+
+#include "LulMainInt.h"
+
+using lul::SecMap;
+
+namespace lul {
+
+// Find all the unwind information in OBJ_FILE, an ELF executable
+// or shared library, and add it to SMAP.
+bool ReadSymbolData(const std::string& obj_file,
+                    const std::vector<std::string>& debug_dirs,
+                    SecMap* smap, void* rx_avma,
+                    void (*log)(const char*));
+
+// The same as ReadSymbolData, except that OBJ_FILE is assumed to
+// point to a mapped-in image of OBJ_FILENAME.
+bool ReadSymbolDataInternal(const uint8_t* obj_file,
+                            const std::string& obj_filename,
+                            const std::vector<std::string>& debug_dirs,
+                            SecMap* smap, void* rx_avma,
+                            void (*log)(const char*));
+
+}  // namespace lul
+
+#endif // LulElfExt_h
new file mode 100644
--- /dev/null
+++ b/tools/profiler/LulElfInt.h
@@ -0,0 +1,234 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+
+// Copyright (c) 2006, 2012, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file is derived from the following files in
+// toolkit/crashreporter/google-breakpad:
+//   src/common/android/include/elf.h
+//   src/common/linux/elfutils.h
+//   src/common/linux/file_id.h
+//   src/common/linux/elfutils-inl.h
+
+#ifndef LulElfInt_h
+#define LulElfInt_h
+
+// This header defines functions etc internal to the ELF reader.  It
+// should not be included outside of LulElf.cpp.
+
+#include <elf.h>
+#include <stdlib.h>
+
+#include "mozilla/Assertions.h"
+
+#include "LulPlatformMacros.h"
+
+
+// (derived from)
+// elfutils.h: Utilities for dealing with ELF files.
+//
+
+#if defined(LUL_OS_android)
+
+// From toolkit/crashreporter/google-breakpad/src/common/android/include/elf.h
+// The Android headers don't always define this constant.
+#ifndef EM_X86_64
+#define EM_X86_64  62
+#endif
+
+#ifndef EM_PPC64
+#define EM_PPC64   21
+#endif
+
+#ifndef EM_S390
+#define EM_S390    22
+#endif
+
+#ifndef NT_GNU_BUILD_ID
+#define NT_GNU_BUILD_ID 3
+#endif
+
+#define ElfW(type)      _ElfW (Elf, ELFSIZE, type)
+#define _ElfW(e,w,t)    _ElfW_1 (e, w, _##t)
+#define _ElfW_1(e,w,t)  e##w##t
+
+//FIXME
+extern "C" {
+  extern char*  basename(const char*  path);
+};
+#else
+
+# include <link.h>
+#endif
+
+
+namespace lul {
+
+// Traits classes so consumers can write templatized code to deal
+// with specific ELF bits.
+struct ElfClass32 {
+  typedef Elf32_Addr Addr;
+  typedef Elf32_Ehdr Ehdr;
+  typedef Elf32_Nhdr Nhdr;
+  typedef Elf32_Phdr Phdr;
+  typedef Elf32_Shdr Shdr;
+  typedef Elf32_Half Half;
+  typedef Elf32_Off Off;
+  typedef Elf32_Word Word;
+  static const int kClass = ELFCLASS32;
+  static const size_t kAddrSize = sizeof(Elf32_Addr);
+};
+
+struct ElfClass64 {
+  typedef Elf64_Addr Addr;
+  typedef Elf64_Ehdr Ehdr;
+  typedef Elf64_Nhdr Nhdr;
+  typedef Elf64_Phdr Phdr;
+  typedef Elf64_Shdr Shdr;
+  typedef Elf64_Half Half;
+  typedef Elf64_Off Off;
+  typedef Elf64_Word Word;
+  static const int kClass = ELFCLASS64;
+  static const size_t kAddrSize = sizeof(Elf64_Addr);
+};
+
+bool IsValidElf(const void* elf_header);
+int ElfClass(const void* elf_base);
+
+// Attempt to find a section named |section_name| of type |section_type|
+// in the ELF binary data at |elf_mapped_base|. On success, returns true
+// and sets |*section_start| to point to the start of the section data,
+// and |*section_size| to the size of the section's data. If |elfclass|
+// is not NULL, set |*elfclass| to the ELF file class.
+bool FindElfSection(const void *elf_mapped_base,
+                    const char *section_name,
+                    uint32_t section_type,
+                    const void **section_start,
+                    int *section_size,
+                    int *elfclass);
+
+// Internal helper method, exposed for convenience for callers
+// that already have more info.
+template<typename ElfClass>
+const typename ElfClass::Shdr*
+FindElfSectionByName(const char* name,
+                     typename ElfClass::Word section_type,
+                     const typename ElfClass::Shdr* sections,
+                     const char* section_names,
+                     const char* names_end,
+                     int nsection);
+
+// Attempt to find the first segment of type |segment_type| in the ELF
+// binary data at |elf_mapped_base|. On success, returns true and sets
+// |*segment_start| to point to the start of the segment data, and
+// and |*segment_size| to the size of the segment's data. If |elfclass|
+// is not NULL, set |*elfclass| to the ELF file class.
+bool FindElfSegment(const void *elf_mapped_base,
+                    uint32_t segment_type,
+                    const void **segment_start,
+                    int *segment_size,
+                    int *elfclass);
+
+// Convert an offset from an Elf header into a pointer to the mapped
+// address in the current process. Takes an extra template parameter
+// to specify the return type to avoid having to dynamic_cast the
+// result.
+template<typename ElfClass, typename T>
+const T*
+GetOffset(const typename ElfClass::Ehdr* elf_header,
+          typename ElfClass::Off offset);
+
+
+// (derived from)
+// file_id.h: Return a unique identifier for a file
+//
+
+static const size_t kMDGUIDSize = sizeof(MDGUID);
+
+class FileID {
+ public:
+
+  // Load the identifier for the elf file mapped into memory at |base| into
+  // |identifier|.  Return false if the identifier could not be created for the
+  // file.
+  static bool ElfFileIdentifierFromMappedFile(const void* base,
+                                              uint8_t identifier[kMDGUIDSize]);
+
+  // Convert the |identifier| data to a NULL terminated string.  The string will
+  // be formatted as a UUID (e.g., 22F065BB-FC9C-49F7-80FE-26A7CEBD7BCE).
+  // The |buffer| should be at least 37 bytes long to receive all of the data
+  // and termination.  Shorter buffers will contain truncated data.
+  static void ConvertIdentifierToString(const uint8_t identifier[kMDGUIDSize],
+                                        char* buffer, int buffer_length);
+};
+
+
+
+template<typename ElfClass, typename T>
+const T* GetOffset(const typename ElfClass::Ehdr* elf_header,
+                   typename ElfClass::Off offset) {
+  return reinterpret_cast<const T*>(reinterpret_cast<uintptr_t>(elf_header) +
+                                    offset);
+}
+
+template<typename ElfClass>
+const typename ElfClass::Shdr* FindElfSectionByName(
+    const char* name,
+    typename ElfClass::Word section_type,
+    const typename ElfClass::Shdr* sections,
+    const char* section_names,
+    const char* names_end,
+    int nsection) {
+  MOZ_ASSERT(name != NULL);
+  MOZ_ASSERT(sections != NULL);
+  MOZ_ASSERT(nsection > 0);
+
+  int name_len = strlen(name);
+  if (name_len == 0)
+    return NULL;
+
+  for (int i = 0; i < nsection; ++i) {
+    const char* section_name = section_names + sections[i].sh_name;
+    if (sections[i].sh_type == section_type &&
+        names_end - section_name >= name_len + 1 &&
+        strcmp(name, section_name) == 0) {
+      return sections + i;
+    }
+  }
+  return NULL;
+}
+
+} // namespace lul
+
+
+// And finally, the external interface, offered to LulMain.cpp
+#include "LulElfExt.h"
+
+#endif // LulElfInt_h
new file mode 100644
--- /dev/null
+++ b/tools/profiler/LulExidx.cpp
@@ -0,0 +1,690 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+
+/* libunwind - a platform-independent unwind library
+   Copyright 2011 Linaro Limited
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
+
+
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+// Derived from libunwind, with extensive modifications.
+
+// This file translates EXIDX unwind information into the same format
+// that LUL uses for CFI information.  Hence LUL's CFI unwinding
+// abilities also become usable for EXIDX.
+//
+// See: "Exception Handling ABI for the ARM Architecture", ARM IHI 0038A
+// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038a/IHI0038A_ehabi.pdf
+
+// EXIDX data is presented in two parts:
+//
+// * an index table.  This contains two words per routine,
+//   the first of which identifies the routine, and the second
+//   of which is a reference to the unwind bytecode.  If the
+//   bytecode is very compact -- 3 bytes or less -- it can be
+//   stored directly in the second word.
+//
+// * an area containing the unwind bytecodes.
+//
+// General flow is: ExceptionTableInfo::Start iterates over all
+// of the index table entries (pairs).  For each entry, it:
+//
+// * calls ExceptionTableInfo::ExtabEntryExtract to copy the bytecode
+//   out into an intermediate buffer.
+
+// * uses ExceptionTableInfo::ExtabEntryDecode to parse the intermediate
+//   buffer.  Each bytecode instruction is bundled into a
+//   arm_ex_to_module::extab_data structure, and handed to ..
+//
+// * .. ARMExToModule::ImproveStackFrame, which in turn hands it to
+//   ARMExToModule::TranslateCmd, and that generates the pseudo-CFI
+//   records that Breakpad stores.
+
+// This file is derived from the following files in
+// toolkit/crashreporter/google-breakpad:
+//   src/common/arm_ex_to_module.cc
+//   src/common/arm_ex_reader.cc
+
+#include "mozilla/Assertions.h"
+#include "mozilla/NullPtr.h"
+
+#include "LulExidxExt.h"
+
+
+#define ARM_EXBUF_START(x) (((x) >> 4) & 0x0f)
+#define ARM_EXBUF_COUNT(x) ((x) & 0x0f)
+#define ARM_EXBUF_END(x)   (ARM_EXBUF_START(x) + ARM_EXBUF_COUNT(x))
+
+namespace lul {
+
+// Translate command from extab_data to command for Module.
+int ARMExToModule::TranslateCmd(const struct extab_data* edata,
+                                LExpr& vsp) {
+  int ret = 0;
+  switch (edata->cmd) {
+    case ARM_EXIDX_CMD_FINISH:
+      /* Copy LR to PC if there isn't currently a rule for PC in force. */
+      if (curr_rules_.mR15expr.mHow == LExpr::UNKNOWN) {
+        if (curr_rules_.mR14expr.mHow == LExpr::UNKNOWN) {
+          curr_rules_.mR15expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R14, 0);
+        } else {
+          curr_rules_.mR15expr = curr_rules_.mR14expr;
+        }
+      }
+      break;
+    case ARM_EXIDX_CMD_SUB_FROM_VSP:
+      vsp = vsp.add_delta(- static_cast<long>(edata->data));
+      break;
+    case ARM_EXIDX_CMD_ADD_TO_VSP:
+      vsp = vsp.add_delta(static_cast<long>(edata->data));
+      break;
+    case ARM_EXIDX_CMD_REG_POP:
+      for (unsigned int i = 0; i < 16; i++) {
+        if (edata->data & (1 << i)) {
+          // See if we're summarising for int register |i|.  If so,
+          // describe how to pull it off the stack.  The cast of |i| is
+          // a bit of a kludge but works because DW_REG_ARM_Rn has the
+          // value |n|, for 0 <= |n| <= 15 -- that is, for the ARM 
+          // general-purpose registers.
+          LExpr* regI_exprP = curr_rules_.ExprForRegno((DW_REG_NUMBER)i);
+          if (regI_exprP) {
+            *regI_exprP = vsp.deref();
+          }
+          vsp = vsp.add_delta(4);
+        }
+      }
+      /* Set cfa in case the SP got popped. */
+      if (edata->data & (1 << 13)) {
+        vsp = curr_rules_.mR13expr;
+      }
+      break;
+    case ARM_EXIDX_CMD_REG_TO_SP: {
+      MOZ_ASSERT (edata->data < 16);
+      int    reg_no    = edata->data;
+      // Same comment as above, re the casting of |reg_no|, applies.
+      LExpr* reg_exprP = curr_rules_.ExprForRegno((DW_REG_NUMBER)reg_no);
+      if (reg_exprP) {
+        if (reg_exprP->mHow == LExpr::UNKNOWN) {
+          curr_rules_.mR13expr = LExpr(LExpr::NODEREF, reg_no, 0);
+        } else {
+          curr_rules_.mR13expr = *reg_exprP;
+        }
+        vsp = curr_rules_.mR13expr;
+      }
+      break;
+    }
+    case ARM_EXIDX_CMD_VFP_POP:
+      /* Don't recover VFP registers, but be sure to adjust the stack
+         pointer. */
+      for (unsigned int i = ARM_EXBUF_START(edata->data);
+           i <= ARM_EXBUF_END(edata->data); i++) {
+        vsp = vsp.add_delta(8);
+      }
+      if (!(edata->data & ARM_EXIDX_VFP_FSTMD)) {
+        vsp = vsp.add_delta(4);
+      }
+      break;
+    case ARM_EXIDX_CMD_WREG_POP:
+      for (unsigned int i = ARM_EXBUF_START(edata->data);
+           i <= ARM_EXBUF_END(edata->data); i++) {
+        vsp = vsp.add_delta(8);
+      }
+      break;
+    case ARM_EXIDX_CMD_WCGR_POP:
+      // Pop wCGR registers under mask {wCGR3,2,1,0}, hence "i < 4"
+      for (unsigned int i = 0; i < 4; i++) {
+        if (edata->data & (1 << i)) {
+          vsp = vsp.add_delta(4);
+        }
+      }
+      break;
+    case ARM_EXIDX_CMD_REFUSED:
+    case ARM_EXIDX_CMD_RESERVED:
+      ret = -1;
+      break;
+  }
+  return ret;
+}
+
+void ARMExToModule::AddStackFrame(uintptr_t addr, size_t size) {
+  // Here we are effectively reinitialising the EXIDX summariser for a
+  // new code address range.  smap_ stays unchanged.  All other fields
+  // are reinitialised.
+  vsp_ = LExpr(LExpr::NODEREF, DW_REG_ARM_R13, 0);
+  (void) new (&curr_rules_) RuleSet();
+  curr_rules_.mAddr = (uintptr_t)addr;
+  curr_rules_.mLen  = (uintptr_t)size;
+  if (0) {
+    char buf[100];
+    sprintf(buf, "  AddStackFrame    %llx .. %llx",
+            (uint64_t)addr, (uint64_t)(addr + size - 1));
+    log_(buf);
+  }
+}
+
+int ARMExToModule::ImproveStackFrame(const struct extab_data* edata) {
+  return TranslateCmd(edata, vsp_) ;
+}
+
+void ARMExToModule::DeleteStackFrame() {
+}
+
+void ARMExToModule::SubmitStackFrame() {
+  // JRS: I'm really not sure what this means, or if it is necessary
+  // return address always winds up in pc
+  //stack_frame_entry_->initial_rules[ustr__ZDra()] // ".ra"
+  //  = stack_frame_entry_->initial_rules[ustr__pc()];
+  // maybe don't need to do anything here?
+
+  // the final value of vsp is the new value of sp
+  curr_rules_.mR13expr = vsp_;
+
+  // Finally, add the completed RuleSet to the SecMap
+  if (curr_rules_.mLen > 0) {
+
+    // Futz with the rules for r4 .. r11 in the same way as happens
+    // with the CFI summariser:
+    /* Mark callee-saved registers (r4 .. r11) as unchanged, if there is
+       no other information about them.  FIXME: do this just once, at
+       the point where the ruleset is committed. */
+    if (curr_rules_.mR7expr.mHow == LExpr::UNKNOWN) {
+      curr_rules_.mR7expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R7, 0);
+    }
+    if (curr_rules_.mR11expr.mHow == LExpr::UNKNOWN) {
+      curr_rules_.mR11expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R11, 0);
+    }
+    if (curr_rules_.mR12expr.mHow == LExpr::UNKNOWN) {
+      curr_rules_.mR12expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R12, 0);
+    }
+    if (curr_rules_.mR14expr.mHow == LExpr::UNKNOWN) {
+      curr_rules_.mR14expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R14, 0);
+    }
+
+    // And add them
+    smap_->AddRuleSet(&curr_rules_);
+
+    if (0) {
+      curr_rules_.Print(log_);
+    }
+    if (0) {
+      char buf[100];
+      sprintf(buf, "  SubmitStackFrame %llx .. %llx",
+              (uint64_t)curr_rules_.mAddr,
+              (uint64_t)(curr_rules_.mAddr + curr_rules_.mLen - 1));
+      log_(buf);
+    }
+  }
+}
+
+
+#define ARM_EXIDX_CANT_UNWIND 0x00000001
+#define ARM_EXIDX_COMPACT     0x80000000
+#define ARM_EXTBL_OP_FINISH   0xb0
+#define ARM_EXIDX_TABLE_LIMIT (255*4)
+
+using lul::ARM_EXIDX_CMD_FINISH;
+using lul::ARM_EXIDX_CMD_SUB_FROM_VSP;
+using lul::ARM_EXIDX_CMD_ADD_TO_VSP;
+using lul::ARM_EXIDX_CMD_REG_POP;
+using lul::ARM_EXIDX_CMD_REG_TO_SP;
+using lul::ARM_EXIDX_CMD_VFP_POP;
+using lul::ARM_EXIDX_CMD_WREG_POP;
+using lul::ARM_EXIDX_CMD_WCGR_POP;
+using lul::ARM_EXIDX_CMD_RESERVED;
+using lul::ARM_EXIDX_CMD_REFUSED;
+using lul::exidx_entry;
+using lul::ARM_EXIDX_VFP_SHIFT_16;
+using lul::ARM_EXIDX_VFP_FSTMD;
+using lul::MemoryRange;
+
+
+static void* Prel31ToAddr(const void* addr)
+{
+  uint32_t offset32 = *reinterpret_cast<const uint32_t*>(addr);
+  // sign extend offset32[30:0] to 64 bits -- copy bit 30 to positions
+  // 63:31 inclusive.
+  uint64_t offset64 = offset32;
+  if (offset64 & (1ULL << 30))
+    offset64 |= 0xFFFFFFFF80000000ULL;
+  else
+    offset64 &= 0x000000007FFFFFFFULL;
+  return ((char*)addr) + (uintptr_t)offset64;
+}
+
+
+// Extract unwind bytecode for the function denoted by |entry| into |buf|,
+// and return the number of bytes of |buf| written, along with a code
+// indicating the outcome.
+
+ExceptionTableInfo::ExExtractResult
+ExceptionTableInfo::ExtabEntryExtract(const struct exidx_entry* entry,
+                                      uint8_t* buf, size_t buf_size,
+                                      /*OUT*/size_t* buf_used)
+{
+  MemoryRange mr_out(buf, buf_size);
+
+  *buf_used = 0;
+
+# define PUT_BUF_U8(_byte) \
+  do { if (!mr_out.Covers(*buf_used, 1)) return ExOutBufOverflow; \
+       buf[(*buf_used)++] = (_byte); } while (0)
+
+# define GET_EX_U32(_lval, _addr, _sec_mr) \
+  do { if (!(_sec_mr).Covers(reinterpret_cast<const uint8_t*>(_addr) \
+                             - (_sec_mr).data(), 4)) \
+         return ExInBufOverflow; \
+       (_lval) = *(reinterpret_cast<const uint32_t*>(_addr)); } while (0)
+
+# define GET_EXIDX_U32(_lval, _addr) \
+            GET_EX_U32(_lval, _addr, mr_exidx_)
+# define GET_EXTAB_U32(_lval, _addr) \
+            GET_EX_U32(_lval, _addr, mr_extab_)
+
+  uint32_t data;
+  GET_EXIDX_U32(data, &entry->data);
+
+  // A function can be marked CANT_UNWIND if (eg) it is known to be
+  // at the bottom of the stack.
+  if (data == ARM_EXIDX_CANT_UNWIND)
+    return ExCantUnwind;
+
+  uint32_t  pers;          // personality number
+  uint32_t  extra;         // number of extra data words required
+  uint32_t  extra_allowed; // number of extra data words allowed
+  uint32_t* extbl_data;    // the handler entry, if not inlined
+
+  if (data & ARM_EXIDX_COMPACT) {
+    // The handler table entry has been inlined into the index table entry.
+    // In this case it can only be an ARM-defined compact model, since
+    // bit 31 is 1.  Only personalities 0, 1 and 2 are defined for the
+    // ARM compact model, but 1 and 2 are "Long format" and may require
+    // extra data words.  Hence the allowable personalities here are:
+    //   personality 0, in which case 'extra' has no meaning
+    //   personality 1, with zero extra words
+    //   personality 2, with zero extra words
+    extbl_data = nullptr;
+    pers  = (data >> 24) & 0x0F;
+    extra = (data >> 16) & 0xFF;
+    extra_allowed = 0;
+  }
+  else {
+    // The index table entry is a pointer to the handler entry.  Note
+    // that Prel31ToAddr will read the given address, but we already
+    // range-checked above.
+    extbl_data = reinterpret_cast<uint32_t*>(Prel31ToAddr(&entry->data));
+    GET_EXTAB_U32(data, extbl_data);
+    if (!(data & ARM_EXIDX_COMPACT)) {
+      // This denotes a "generic model" handler.  That will involve
+      // executing arbitary machine code, which is something we
+      // can't represent here; hence reject it.
+      return ExCantRepresent;
+    }
+    // So we have a compact model representation.  Again, 3 possible
+    // personalities, but this time up to 255 allowable extra words.
+    pers  = (data >> 24) & 0x0F;
+    extra = (data >> 16) & 0xFF;
+    extra_allowed = 255;
+    extbl_data++;
+  }
+
+  // Now look at the the handler table entry.  The first word is
+  // |data| and subsequent words start at |*extbl_data|.  The number
+  // of extra words to use is |extra|, provided that the personality
+  // allows extra words.  Even if it does, none may be available --
+  // extra_allowed is the maximum number of extra words allowed. */
+  if (pers == 0) {
+    // "Su16" in the documentation -- 3 unwinding insn bytes
+    // |extra| has no meaning here; instead that byte is an unwind-info byte
+    PUT_BUF_U8(data >> 16);
+    PUT_BUF_U8(data >> 8);
+    PUT_BUF_U8(data);
+  }
+  else if ((pers == 1 || pers == 2) && extra <= extra_allowed) {
+    // "Lu16" or "Lu32" respectively -- 2 unwinding insn bytes,
+    // and up to 255 extra words.
+    PUT_BUF_U8(data >> 8);
+    PUT_BUF_U8(data);
+    for (uint32_t j = 0; j < extra; j++) {
+      GET_EXTAB_U32(data, extbl_data);
+      extbl_data++;
+      PUT_BUF_U8(data >> 24);
+      PUT_BUF_U8(data >> 16);
+      PUT_BUF_U8(data >> 8);
+      PUT_BUF_U8(data >> 0);
+    }
+  }
+  else {
+    // The entry is invalid.
+    return ExInvalid;
+  }
+
+  // Make sure the entry is terminated with "FINISH"
+  if (*buf_used > 0 && buf[(*buf_used) - 1] != ARM_EXTBL_OP_FINISH)
+    PUT_BUF_U8(ARM_EXTBL_OP_FINISH);
+
+  return ExSuccess;
+
+# undef GET_EXTAB_U32
+# undef GET_EXIDX_U32
+# undef GET_U32
+# undef PUT_BUF_U8
+}
+
+
+// Take the unwind information extracted by ExtabEntryExtract
+// and parse it into frame-unwind instructions.  These are as
+// specified in "Table 4, ARM-defined frame-unwinding instructions"
+// in the specification document detailed in comments at the top
+// of this file.
+//
+// This reads from |buf[0, +data_size)|.  It checks for overruns of
+// the input buffer and returns a negative value if that happens, or
+// for any other failure cases.  It returns zero in case of success.
+int ExceptionTableInfo::ExtabEntryDecode(const uint8_t* buf, size_t buf_size)
+{
+  if (buf == nullptr || buf_size == 0)
+    return -1;
+
+  MemoryRange mr_in(buf, buf_size);
+  const uint8_t* buf_initially = buf;
+
+# define GET_BUF_U8(_lval) \
+  do { if (!mr_in.Covers(buf - buf_initially, 1)) return -1; \
+       (_lval) = *(buf++); } while (0)
+
+  const uint8_t* end = buf + buf_size;
+
+  while (buf < end) {
+    struct lul::extab_data edata;
+    memset(&edata, 0, sizeof(edata));
+
+    uint8_t op;
+    GET_BUF_U8(op);
+    if ((op & 0xc0) == 0x00) {
+      // vsp = vsp + (xxxxxx << 2) + 4
+      edata.cmd = ARM_EXIDX_CMD_ADD_TO_VSP;
+      edata.data = (((int)op & 0x3f) << 2) + 4;
+    }
+    else if ((op & 0xc0) == 0x40) {
+      // vsp = vsp - (xxxxxx << 2) - 4
+      edata.cmd = ARM_EXIDX_CMD_SUB_FROM_VSP;
+      edata.data = (((int)op & 0x3f) << 2) + 4;
+    }
+    else if ((op & 0xf0) == 0x80) {
+      uint8_t op2;
+      GET_BUF_U8(op2);
+      if (op == 0x80 && op2 == 0x00) {
+        // Refuse to unwind
+        edata.cmd = ARM_EXIDX_CMD_REFUSED;
+      } else {
+        // Pop up to 12 integer registers under masks {r15-r12},{r11-r4}
+        edata.cmd = ARM_EXIDX_CMD_REG_POP;
+        edata.data = ((op & 0xf) << 8) | op2;
+        edata.data = edata.data << 4;
+      }
+    }
+    else if ((op & 0xf0) == 0x90) {
+      if (op == 0x9d || op == 0x9f) {
+        // 9d: Reserved as prefix for ARM register to register moves
+        // 9f: Reserved as perfix for Intel Wireless MMX reg to reg moves
+        edata.cmd = ARM_EXIDX_CMD_RESERVED;
+      } else {
+        // Set vsp = r[nnnn]
+        edata.cmd = ARM_EXIDX_CMD_REG_TO_SP;
+        edata.data = op & 0x0f;
+      }
+    }
+    else if ((op & 0xf0) == 0xa0) {
+      // Pop r4 to r[4+nnn],          or
+      // Pop r4 to r[4+nnn] and r14   or
+      unsigned end = (op & 0x07);
+      edata.data = (1 << (end + 1)) - 1;
+      edata.data = edata.data << 4;
+      if (op & 0x08) edata.data |= 1 << 14;
+      edata.cmd = ARM_EXIDX_CMD_REG_POP;
+    }
+    else if (op == ARM_EXTBL_OP_FINISH) {
+      // Finish
+      edata.cmd = ARM_EXIDX_CMD_FINISH;
+      buf = end;
+    }
+    else if (op == 0xb1) {
+      uint8_t op2;
+      GET_BUF_U8(op2);
+      if (op2 == 0 || (op2 & 0xf0)) {
+        // Spare
+        edata.cmd = ARM_EXIDX_CMD_RESERVED;
+      } else {
+        // Pop integer registers under mask {r3,r2,r1,r0}
+        edata.cmd = ARM_EXIDX_CMD_REG_POP;
+        edata.data = op2 & 0x0f;
+      }
+    }
+    else if (op == 0xb2) {
+      // vsp = vsp + 0x204 + (uleb128 << 2)
+      uint64_t offset = 0;
+      uint8_t byte, shift = 0;
+      do {
+        GET_BUF_U8(byte);
+        offset |= (byte & 0x7f) << shift;
+        shift += 7;
+      } while ((byte & 0x80) && buf < end);
+      edata.data = offset * 4 + 0x204;
+      edata.cmd = ARM_EXIDX_CMD_ADD_TO_VSP;
+    }
+    else if (op == 0xb3 || op == 0xc8 || op == 0xc9) {
+      // b3: Pop VFP regs D[ssss]    to D[ssss+cccc],    FSTMFDX-ishly
+      // c8: Pop VFP regs D[16+ssss] to D[16+ssss+cccc], FSTMFDD-ishly
+      // c9: Pop VFP regs D[ssss]    to D[ssss+cccc],    FSTMFDD-ishly
+      edata.cmd = ARM_EXIDX_CMD_VFP_POP;
+      GET_BUF_U8(edata.data);
+      if (op == 0xc8) edata.data |= ARM_EXIDX_VFP_SHIFT_16;
+      if (op != 0xb3) edata.data |= ARM_EXIDX_VFP_FSTMD;
+    }
+    else if ((op & 0xf8) == 0xb8 || (op & 0xf8) == 0xd0) {
+      // b8: Pop VFP regs D[8] to D[8+nnn], FSTMFDX-ishly
+      // d0: Pop VFP regs D[8] to D[8+nnn], FSTMFDD-ishly
+      edata.cmd = ARM_EXIDX_CMD_VFP_POP;
+      edata.data = 0x80 | (op & 0x07);
+      if ((op & 0xf8) == 0xd0) edata.data |= ARM_EXIDX_VFP_FSTMD;
+    }
+    else if (op >= 0xc0 && op <= 0xc5) {
+      // Intel Wireless MMX pop wR[10]-wr[10+nnn], nnn != 6,7
+      edata.cmd = ARM_EXIDX_CMD_WREG_POP;
+      edata.data = 0xa0 | (op & 0x07);
+    }
+    else if (op == 0xc6) {
+      // Intel Wireless MMX pop wR[ssss] to wR[ssss+cccc]
+      edata.cmd = ARM_EXIDX_CMD_WREG_POP;
+      GET_BUF_U8(edata.data);
+    }
+    else if (op == 0xc7) {
+      uint8_t op2;
+      GET_BUF_U8(op2);
+      if (op2 == 0 || (op2 & 0xf0)) {
+        // Spare
+        edata.cmd = ARM_EXIDX_CMD_RESERVED;
+      } else {
+        // Intel Wireless MMX pop wCGR registers under mask {wCGR3,2,1,0}
+        edata.cmd = ARM_EXIDX_CMD_WCGR_POP;
+        edata.data = op2 & 0x0f;
+      }
+    }
+    else {
+      // Spare
+      edata.cmd = ARM_EXIDX_CMD_RESERVED;
+    }
+
+    int ret = handler_->ImproveStackFrame(&edata);
+    if (ret < 0) return ret;
+  }
+  return 0;
+
+# undef GET_BUF_U8
+}
+
+void ExceptionTableInfo::Start()
+{
+  const struct exidx_entry* start
+    = reinterpret_cast<const struct exidx_entry*>(mr_exidx_.data());
+  const struct exidx_entry* end
+    = reinterpret_cast<const struct exidx_entry*>(mr_exidx_.data()
+                                                  + mr_exidx_.length());
+
+  // Iterate over each of the EXIDX entries (pairs of 32-bit words).
+  // These occupy the entire .exidx section.
+  for (const struct exidx_entry* entry = start; entry < end; ++entry) {
+
+    // Figure out the code address range that this table entry is
+    // associated with.
+    //
+    // I don't claim to understand the biasing here.  It appears that
+    //   (Prel31ToAddr(&entry->addr))
+    //    - mapping_addr_ + loading_addr_) & 0x7fffffff
+    // produces a SVMA.  Adding the text_bias_ gives plausible AVMAs.
+    uint32_t svma = (reinterpret_cast<char*>(Prel31ToAddr(&entry->addr))
+                     - mapping_addr_ + loading_addr_) & 0x7fffffff;
+    uint32_t next_svma;
+    if (entry < end - 1) {
+      next_svma = (reinterpret_cast<char*>(Prel31ToAddr(&((entry + 1)->addr)))
+                   - mapping_addr_ + loading_addr_) & 0x7fffffff;
+    } else {
+      // This is the last EXIDX entry in the sequence, so we don't
+      // have an address for the start of the next function, to limit
+      // this one.  Instead use the address of the last byte of the
+      // text section associated with this .exidx section, that we
+      // have been given.  So as to avoid junking up the CFI unwind
+      // tables with absurdly large address ranges in the case where
+      // text_last_svma_ is wrong, only use the value if it is nonzero
+      // and within one page of |svma|.  Otherwise assume a length of 1.
+      //
+      // In some cases, gcc has been observed to finish the exidx
+      // section with an entry of length 1 marked CANT_UNWIND,
+      // presumably exactly for the purpose of giving a definite
+      // length for the last real entry, without having to look at
+      // text segment boundaries.
+      bool plausible = false;
+      next_svma = svma + 1;
+      if (text_last_svma_ != 0) {
+        uint32_t maybe_next_svma = text_last_svma_ + 1;
+        if (maybe_next_svma > svma && maybe_next_svma - svma <= 4096) {
+          next_svma = maybe_next_svma;
+          plausible = true;
+        }
+      }
+      if (!plausible) {
+        char buf[100];
+        snprintf(buf, sizeof(buf),
+                 "ExceptionTableInfo: implausible EXIDX last entry size %d"
+                 "; using 1 instead.", (int32_t)(text_last_svma_ - svma));
+        buf[sizeof(buf)-1] = 0;
+        log_(buf);
+      }
+    }
+
+    // Extract the unwind info into |buf|.  This might fail for
+    // various reasons.  It involves reading both the .exidx and
+    // .extab sections.  All accesses to those sections are
+    // bounds-checked.
+    uint8_t buf[ARM_EXIDX_TABLE_LIMIT];
+    size_t buf_used = 0;
+    ExExtractResult res = ExtabEntryExtract(entry, buf, sizeof(buf), &buf_used);
+    if (res != ExSuccess) {
+      // Couldn't extract the unwind info, for some reason.  Move on.
+      switch (res) {
+        case ExInBufOverflow:
+          log_("ExtabEntryExtract: .exidx/.extab section overrun");
+          break;
+        case ExOutBufOverflow:
+          log_("ExtabEntryExtract: bytecode buffer overflow");
+          break;
+        case ExCantUnwind:
+          log_("ExtabEntryExtract: function is marked CANT_UNWIND");
+          break;
+        case ExCantRepresent:
+          log_("ExtabEntryExtract: bytecode can't be represented");
+          break;
+        case ExInvalid:
+          log_("ExtabEntryExtract: index table entry is invalid");
+          break;
+        default: {
+          char buf[100];
+          snprintf(buf, sizeof(buf),
+                   "ExtabEntryExtract: unknown error: %d", (int)res);
+          buf[sizeof(buf)-1] = 0;
+          log_(buf);
+          break;
+        }
+      }
+      continue;
+    }
+
+    // Finally, work through the unwind instructions in |buf| and
+    // create CFI entries that Breakpad can use.  This can also fail.
+    // First, add a new stack frame entry, into which ExtabEntryDecode
+    // will write the CFI entries.
+    handler_->AddStackFrame(svma + text_bias_, next_svma - svma);
+    int ret = ExtabEntryDecode(buf, buf_used);
+    if (ret < 0) {
+      handler_->DeleteStackFrame();
+      char buf[100];
+      snprintf(buf, sizeof(buf),
+               "ExtabEntryDecode: failed with error code: %d", ret);
+      buf[sizeof(buf)-1] = 0;
+      log_(buf);
+      continue;
+    }
+    handler_->SubmitStackFrame();
+  } /* iterating over .exidx */
+}
+
+} // namespace lul
new file mode 100644
--- /dev/null
+++ b/tools/profiler/LulExidxExt.h
@@ -0,0 +1,233 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+
+/* libunwind - a platform-independent unwind library
+   Copyright 2011 Linaro Limited
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
+
+
+// Copyright (c) 2010, 2011 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+// Derived from libunwind, with extensive modifications.
+// This file is derived from the following files in
+// toolkit/crashreporter/google-breakpad:
+//   src/common/arm_ex_to_module.h
+//   src/common/memory_range.h
+//   src/common/arm_ex_reader.h
+
+#ifndef LulExidxExt_h
+#define LulExidxExt_h
+
+#include "LulMainInt.h"
+
+using lul::LExpr;
+using lul::RuleSet;
+using lul::SecMap;
+
+namespace lul {
+
+typedef enum extab_cmd {
+  ARM_EXIDX_CMD_FINISH,
+  ARM_EXIDX_CMD_SUB_FROM_VSP,
+  ARM_EXIDX_CMD_ADD_TO_VSP,
+  ARM_EXIDX_CMD_REG_POP,
+  ARM_EXIDX_CMD_REG_TO_SP,
+  ARM_EXIDX_CMD_VFP_POP,
+  ARM_EXIDX_CMD_WREG_POP,
+  ARM_EXIDX_CMD_WCGR_POP,
+  ARM_EXIDX_CMD_RESERVED,
+  ARM_EXIDX_CMD_REFUSED,
+} extab_cmd_t;
+
+struct exidx_entry {
+  uint32_t addr;
+  uint32_t data;
+};
+
+struct extab_data {
+  extab_cmd_t cmd;
+  uint32_t data;
+};
+
+enum extab_cmd_flags {
+  ARM_EXIDX_VFP_SHIFT_16 = 1 << 16,
+  ARM_EXIDX_VFP_FSTMD = 1 << 17, // distinguishes FSTMxxD from FSTMxxX
+};
+
+// Receives information from arm_ex_reader::ExceptionTableInfo
+// and adds it to the SecMap object
+// This is in effect the EXIDX summariser.
+class ARMExToModule {
+ public:
+   ARMExToModule(SecMap* smap, void(*log)(const char*)) : smap_(smap)
+                                                        , log_(log) { }
+  ~ARMExToModule() { }
+  void AddStackFrame(uintptr_t addr, size_t size);
+  int  ImproveStackFrame(const struct extab_data* edata);
+  void DeleteStackFrame();
+  void SubmitStackFrame();
+ private:
+  SecMap* smap_;
+  LExpr   vsp_;        // Always appears to be of the form "sp + offset"
+  RuleSet curr_rules_; // Also holds the address range being summarised
+  // debugging message sink
+  void (*log_)(const char*);
+  int TranslateCmd(const struct extab_data* edata, LExpr& vsp);
+};
+
+
+// (derived from)
+// memory_range.h: Define the google_breakpad::MemoryRange class, which
+// is a lightweight wrapper with a pointer and a length to encapsulate
+// a contiguous range of memory.
+
+// A lightweight wrapper with a pointer and a length to encapsulate a
+// contiguous range of memory. It provides helper methods for checked
+// access of a subrange of the memory. Its implemementation does not
+// allocate memory or call into libc functions, and is thus safer to use
+// in a crashed environment.
+class MemoryRange {
+ public:
+
+  MemoryRange(const void* data, size_t length) {
+    Set(data, length);
+  }
+
+  // Sets this memory range to point to |data| and its length to |length|.
+  void Set(const void* data, size_t length) {
+    data_ = reinterpret_cast<const uint8_t*>(data);
+    // Always set |length_| to zero if |data_| is NULL.
+    length_ = data ? length : 0;
+  }
+
+  // Returns true if this range covers a subrange of |sub_length| bytes
+  // at |sub_offset| bytes of this memory range, or false otherwise.
+  bool Covers(size_t sub_offset, size_t sub_length) const {
+    // The following checks verify that:
+    // 1. sub_offset is within [ 0 .. length_ - 1 ]
+    // 2. sub_offset + sub_length is within
+    //    [ sub_offset .. length_ ]
+    return sub_offset < length_ &&
+           sub_offset + sub_length >= sub_offset &&
+           sub_offset + sub_length <= length_;
+  }
+
+  // Returns a pointer to the beginning of this memory range.
+  const uint8_t* data() const { return data_; }
+
+  // Returns the length, in bytes, of this memory range.
+  size_t length() const { return length_; }
+
+ private:
+  // Pointer to the beginning of this memory range.
+  const uint8_t* data_;
+
+  // Length, in bytes, of this memory range.
+  size_t length_;
+};
+
+
+// This class is a reader for ARM unwind information
+// from .ARM.exidx and .ARM.extab sections.
+class ExceptionTableInfo {
+ public:
+  ExceptionTableInfo(const char* exidx, size_t exidx_size,
+                     const char* extab, size_t extab_size,
+                     uint32_t text_last_svma,
+                     lul::ARMExToModule* handler,
+                     const char* mapping_addr,
+                     uint32_t loading_addr,
+                     uintptr_t text_bias,
+                     void (*log)(const char*))
+      : mr_exidx_(lul::MemoryRange(exidx, exidx_size)),
+        mr_extab_(lul::MemoryRange(extab, extab_size)),
+        text_last_svma_(text_last_svma),
+        handler_(handler), mapping_addr_(mapping_addr),
+        loading_addr_(loading_addr),
+        text_bias_(text_bias),
+        log_(log) { }
+
+  ~ExceptionTableInfo() { }
+
+  // Parses the entries in .ARM.exidx and possibly
+  // in .ARM.extab tables, reports what we find to
+  // arm_ex_to_module::ARMExToModule.
+  void Start();
+
+ private:
+  lul::MemoryRange mr_exidx_;
+  lul::MemoryRange mr_extab_;
+  uint32_t text_last_svma_;
+  lul::ARMExToModule* handler_;
+  const char* mapping_addr_;
+  uint32_t loading_addr_;
+  uintptr_t text_bias_;
+  // debugging message sink
+  void (*log_)(const char*);
+  enum ExExtractResult {
+    ExSuccess,        // success
+    ExInBufOverflow,  // out-of-range while reading .exidx
+    ExOutBufOverflow, // output buffer is too small
+    ExCantUnwind,     // this function is marked CANT_UNWIND
+    ExCantRepresent,  // entry valid, but we can't represent it
+    ExInvalid         // entry is invalid
+  };
+  ExExtractResult
+    ExtabEntryExtract(const struct lul::exidx_entry* entry,
+                      uint8_t* buf, size_t buf_size,
+                      /*OUT*/size_t* buf_used);
+
+  int ExtabEntryDecode(const uint8_t* buf, size_t buf_size);
+};
+
+} // namespace lul
+
+#endif // LulExidxExt_h
new file mode 100644
--- /dev/null
+++ b/tools/profiler/LulMain.cpp
@@ -0,0 +1,1878 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LulMain.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <algorithm>  // std::sort
+#include <string>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/MemoryChecking.h"
+
+#include "LulCommonExt.h"
+#include "LulElfExt.h"
+
+#include "LulMainInt.h"
+
+// Set this to 1 for verbose logging
+#define DEBUG_MAIN 0
+
+
+namespace lul {
+
+using std::string;
+using std::vector;
+
+
+////////////////////////////////////////////////////////////////
+// AutoLulRWLocker                                            //
+////////////////////////////////////////////////////////////////
+
+// This is a simple RAII class that manages acquisition and release of
+// LulRWLock reader-writer locks.
+
+class AutoLulRWLocker {
+public:
+  enum AcqMode { FOR_READING, FOR_WRITING };
+  AutoLulRWLocker(LulRWLock* aRWLock, AcqMode mode)
+    : mRWLock(aRWLock)
+  {
+    if (mode == FOR_WRITING) {
+      aRWLock->WrLock();
+    } else {
+      aRWLock->RdLock();
+    }
+  }
+  ~AutoLulRWLocker()
+  {
+    mRWLock->Unlock();
+  }
+
+private:
+  LulRWLock* mRWLock;
+};
+
+
+////////////////////////////////////////////////////////////////
+// RuleSet                                                    //
+////////////////////////////////////////////////////////////////
+
+static const char* 
+NameOf_DW_REG(int16_t aReg)
+{
+  switch (aReg) {
+    case DW_REG_CFA:       return "cfa";
+#if defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86)
+    case DW_REG_INTEL_XBP: return "xbp";
+    case DW_REG_INTEL_XSP: return "xsp";
+    case DW_REG_INTEL_XIP: return "xip";
+#elif defined(LUL_ARCH_arm)
+    case DW_REG_ARM_R7:    return "r7";
+    case DW_REG_ARM_R11:   return "r11";
+    case DW_REG_ARM_R12:   return "r12";
+    case DW_REG_ARM_R13:   return "r13";
+    case DW_REG_ARM_R14:   return "r14";
+    case DW_REG_ARM_R15:   return "r15";
+#else
+# error "Unsupported arch"
+#endif
+    default: return "???";
+  }
+}
+
+static string
+ShowRule(const char* aNewReg, LExpr aExpr)
+{
+  char buf[64];
+  string res = string(aNewReg) + "=";
+  switch (aExpr.mHow) {
+    case LExpr::UNKNOWN:
+      res += "Unknown";
+      break;
+    case LExpr::NODEREF:
+      sprintf(buf, "%s+%d", NameOf_DW_REG(aExpr.mReg), (int)aExpr.mOffset);
+      res += buf;
+      break;
+    case LExpr::DEREF:
+      sprintf(buf, "*(%s+%d)", NameOf_DW_REG(aExpr.mReg), (int)aExpr.mOffset);
+      res += buf;
+      break;
+    default:
+      res += "???";
+      break;
+  }
+  return res;
+}
+
+void
+RuleSet::Print(void(*aLog)(const char*))
+{
+  char buf[96];
+  sprintf(buf, "[%llx .. %llx]: let ",
+          (unsigned long long int)mAddr,
+          (unsigned long long int)(mAddr + mLen - 1));
+  string res = string(buf);
+  res += ShowRule("cfa", mCfaExpr);
+  res += " in";
+  // For each reg we care about, print the recovery expression.
+#if defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86)
+  res += ShowRule(" RA", mXipExpr);
+  res += ShowRule(" SP", mXspExpr);
+  res += ShowRule(" BP", mXbpExpr);
+#elif defined(LUL_ARCH_arm)
+  res += ShowRule(" R15", mR15expr);
+  res += ShowRule(" R7",  mR7expr);
+  res += ShowRule(" R11", mR11expr);
+  res += ShowRule(" R12", mR12expr);
+  res += ShowRule(" R13", mR13expr);
+  res += ShowRule(" R14", mR14expr);
+#else
+# error "Unsupported arch"
+#endif
+  aLog(res.c_str());
+}
+
+LExpr*
+RuleSet::ExprForRegno(DW_REG_NUMBER aRegno) {
+  switch (aRegno) {
+    case DW_REG_CFA: return &mCfaExpr;
+#   if defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86)
+    case DW_REG_INTEL_XIP: return &mXipExpr;
+    case DW_REG_INTEL_XSP: return &mXspExpr;
+    case DW_REG_INTEL_XBP: return &mXbpExpr;
+#   elif defined(LUL_ARCH_arm)
+    case DW_REG_ARM_R15:   return &mR15expr;
+    case DW_REG_ARM_R14:   return &mR14expr;
+    case DW_REG_ARM_R13:   return &mR13expr;
+    case DW_REG_ARM_R12:   return &mR12expr;
+    case DW_REG_ARM_R11:   return &mR11expr;
+    case DW_REG_ARM_R7:    return &mR7expr;
+#   else
+#     error "Unknown arch"
+#   endif
+    default: return nullptr;
+  }
+}
+
+RuleSet::RuleSet()
+{
+  mAddr = 0;
+  mLen  = 0;
+  // The only other fields are of type LExpr and those are initialised
+  // by LExpr::LExpr().
+}
+
+
+////////////////////////////////////////////////////////////////
+// SecMap                                                     //
+////////////////////////////////////////////////////////////////
+
+// See header file LulMainInt.h for comments about invariants.
+
+SecMap::SecMap(void(*aLog)(const char*))
+  : mSummaryMinAddr(1)
+  , mSummaryMaxAddr(0)
+  , mUsable(true)
+  , mLog(aLog)
+{}
+
+SecMap::~SecMap() {
+  mRuleSets.clear();
+}
+
+RuleSet*
+SecMap::FindRuleSet(uintptr_t ia) {
+  // Binary search mRuleSets to find one that brackets |ia|.
+  // lo and hi need to be signed, else the loop termination tests
+  // don't work properly.  Note that this works correctly even when
+  // mRuleSets.size() == 0.
+
+  // Can't do this until the array has been sorted and preened.
+  MOZ_ASSERT(mUsable);
+
+  long int lo = 0;
+  long int hi = (long int)mRuleSets.size() - 1;
+  while (true) {
+    // current unsearched space is from lo to hi, inclusive.
+    if (lo > hi) {
+      // not found
+      return nullptr;
+    }
+    long int  mid         = lo + ((hi - lo) / 2);
+    RuleSet*  mid_ruleSet = &mRuleSets[mid];
+    uintptr_t mid_minAddr = mid_ruleSet->mAddr;
+    uintptr_t mid_maxAddr = mid_minAddr + mid_ruleSet->mLen - 1;
+    if (ia < mid_minAddr) { hi = mid-1; continue; }
+    if (ia > mid_maxAddr) { lo = mid+1; continue; }
+    MOZ_ASSERT(mid_minAddr <= ia && ia <= mid_maxAddr);
+    return mid_ruleSet;
+  }
+  // NOTREACHED
+}
+
+// Add a RuleSet to the collection.  The rule is copied in.  Calling
+// this makes the map non-searchable.
+void
+SecMap::AddRuleSet(RuleSet* rs) {
+  mUsable = false;
+  mRuleSets.push_back(*rs);
+}
+
+
+static bool
+CmpRuleSetsByAddrLE(const RuleSet& rs1, const RuleSet& rs2) {
+  return rs1.mAddr < rs2.mAddr;
+}
+
+// Prepare the map for searching.  Completely remove any which don't
+// fall inside the specified range [start, +len).
+void
+SecMap::PrepareRuleSets(uintptr_t aStart, size_t aLen)
+{
+  if (mRuleSets.empty()) {
+    return;
+  }
+
+  MOZ_ASSERT(aLen > 0);
+  if (aLen == 0) {
+    // This should never happen.
+    mRuleSets.clear();
+    return;
+  }
+
+  // Sort by start addresses.
+  std::sort(mRuleSets.begin(), mRuleSets.end(), CmpRuleSetsByAddrLE);
+
+  // Detect any entry not completely contained within [start, +len).
+  // Set its length to zero, so that the next pass will remove it.
+  for (size_t i = 0; i < mRuleSets.size(); ++i) {
+    RuleSet* rs = &mRuleSets[i];
+    if (rs->mLen > 0 &&
+        (rs->mAddr < aStart || rs->mAddr + rs->mLen > aStart + aLen)) {
+      rs->mLen = 0;
+    }
+  }
+
+  // Iteratively truncate any overlaps and remove any zero length
+  // entries that might result, or that may have been present
+  // initially.  Unless the input is seriously screwy, this is
+  // expected to iterate only once.
+  while (true) {
+    size_t i;
+    size_t n = mRuleSets.size();
+    size_t nZeroLen = 0;
+
+    if (n == 0) {
+      break;
+    }
+
+    for (i = 1; i < n; ++i) {
+      RuleSet* prev = &mRuleSets[i-1];
+      RuleSet* here = &mRuleSets[i];
+      MOZ_ASSERT(prev->mAddr <= here->mAddr);
+      if (prev->mAddr + prev->mLen > here->mAddr) {
+        prev->mLen = here->mAddr - prev->mAddr;
+      }
+      if (prev->mLen == 0)
+        nZeroLen++;
+    }
+
+    if (mRuleSets[n-1].mLen == 0) {
+      nZeroLen++;
+    }
+
+    // At this point, the entries are in-order and non-overlapping.
+    // If none of them are zero-length, we are done.
+    if (nZeroLen == 0) {
+      break;
+    }
+
+    // Slide back the entries to remove the zero length ones.
+    size_t j = 0;  // The write-point.
+    for (i = 0; i < n; ++i) {
+      if (mRuleSets[i].mLen == 0) {
+        continue;
+      }
+      if (j != i) mRuleSets[j] = mRuleSets[i];
+      ++j;
+    }
+    MOZ_ASSERT(i == n);
+    MOZ_ASSERT(nZeroLen <= n);
+    MOZ_ASSERT(j == n - nZeroLen);
+    while (nZeroLen > 0) {
+      mRuleSets.pop_back();
+      nZeroLen--;
+    }
+
+    MOZ_ASSERT(mRuleSets.size() == j);
+  }
+
+  size_t n = mRuleSets.size();
+
+#ifdef DEBUG
+  // Do a final check on the rules: their address ranges must be
+  // ascending, non overlapping, non zero sized.
+  if (n > 0) {
+    MOZ_ASSERT(mRuleSets[0].mLen > 0);
+    for (size_t i = 1; i < n; ++i) {
+      RuleSet* prev = &mRuleSets[i-1];
+      RuleSet* here = &mRuleSets[i];
+      MOZ_ASSERT(prev->mAddr < here->mAddr);
+      MOZ_ASSERT(here->mLen > 0);
+      MOZ_ASSERT(prev->mAddr + prev->mLen <= here->mAddr);
+    }
+  }
+#endif
+
+  // Set the summary min and max address values.
+  if (n == 0) {
+    // Use the values defined in comments in the class declaration.
+    mSummaryMinAddr = 1;
+    mSummaryMaxAddr = 0;
+  } else {
+    mSummaryMinAddr = mRuleSets[0].mAddr;
+    mSummaryMaxAddr = mRuleSets[n-1].mAddr + mRuleSets[n-1].mLen - 1;
+  }
+  char buf[150];
+  snprintf(buf, sizeof(buf),
+           "PrepareRuleSets: %d entries, smin/smax 0x%llx, 0x%llx\n",
+           (int)n, (unsigned long long int)mSummaryMinAddr,
+                   (unsigned long long int)mSummaryMaxAddr);
+  buf[sizeof(buf)-1] = 0;
+  mLog(buf);
+
+  // Is now usable for binary search.
+  mUsable = true;
+
+  if (0) {
+    mLog("\nRulesets after preening\n");
+    for (size_t i = 0; i < mRuleSets.size(); ++i) {
+      mRuleSets[i].Print(mLog);
+      mLog("\n");
+    }
+    mLog("\n");
+  }
+}
+
+bool SecMap::IsEmpty() {
+  return mRuleSets.empty();
+}
+
+
+////////////////////////////////////////////////////////////////
+// SegArray                                                   //
+////////////////////////////////////////////////////////////////
+
+// A SegArray holds a set of address ranges that together exactly
+// cover an address range, with no overlaps or holes.  Each range has
+// an associated value, which in this case has been specialised to be
+// a simple boolean.  The representation is kept to minimal canonical
+// form in which adjacent ranges with the same associated value are
+// merged together.  Each range is represented by a |struct Seg|.
+//
+// SegArrays are used to keep track of which parts of the address
+// space are known to contain instructions.
+class SegArray {
+
+ public:
+  void add(uintptr_t lo, uintptr_t hi, bool val) {
+    if (lo > hi) {
+      return;
+    }
+    split_at(lo);
+    if (hi < UINTPTR_MAX) {
+      split_at(hi+1);
+    }
+    std::vector<Seg>::size_type iLo, iHi, i;
+    iLo = find(lo);
+    iHi = find(hi);
+    for (i = iLo; i <= iHi; ++i) {
+      mSegs[i].val = val;
+    }
+    preen();
+  }
+
+  bool getBoundingCodeSegment(/*OUT*/uintptr_t* rx_min,
+                              /*OUT*/uintptr_t* rx_max, uintptr_t addr) {
+    std::vector<Seg>::size_type i = find(addr);
+    if (!mSegs[i].val) {
+      return false;
+    }
+    *rx_min = mSegs[i].lo;
+    *rx_max = mSegs[i].hi;
+    return true;
+  }
+
+  SegArray() {
+    Seg s(0, UINTPTR_MAX, false);
+    mSegs.push_back(s);
+  }
+
+ private:
+  struct Seg {
+    Seg(uintptr_t lo, uintptr_t hi, bool val) : lo(lo), hi(hi), val(val) {}
+    uintptr_t lo;
+    uintptr_t hi;
+    bool val;
+  };
+
+  void preen() {
+    for (std::vector<Seg>::iterator iter = mSegs.begin();
+         iter < mSegs.end()-1;
+         ++iter) {
+      if (iter[0].val != iter[1].val) {
+        continue;
+      }
+      iter[0].hi = iter[1].hi;
+      mSegs.erase(iter+1);
+      // Back up one, so as not to miss an opportunity to merge
+      // with the entry after this one.
+      --iter;
+    }
+  }
+
+  std::vector<Seg>::size_type find(uintptr_t a) {
+    long int lo = 0;
+    long int hi = (long int)mSegs.size();
+    while (true) {
+      // The unsearched space is lo .. hi inclusive.
+      if (lo > hi) {
+        // Not found.  This can't happen.
+        return (std::vector<Seg>::size_type)(-1);
+      }
+      long int  mid    = lo + ((hi - lo) / 2);
+      uintptr_t mid_lo = mSegs[mid].lo;
+      uintptr_t mid_hi = mSegs[mid].hi;
+      if (a < mid_lo) { hi = mid-1; continue; }
+      if (a > mid_hi) { lo = mid+1; continue; }
+      return (std::vector<Seg>::size_type)mid;
+    }
+  }
+
+  void split_at(uintptr_t a) {
+    std::vector<Seg>::size_type i = find(a);
+    if (mSegs[i].lo == a) {
+      return;
+    }
+    mSegs.insert( mSegs.begin()+i+1, mSegs[i] );
+    mSegs[i].hi = a-1;
+    mSegs[i+1].lo = a;
+  }
+
+  void show() {
+    printf("<< %d entries:\n", (int)mSegs.size());
+    for (std::vector<Seg>::iterator iter = mSegs.begin();
+         iter < mSegs.end();
+         ++iter) {
+      printf("  %016llx  %016llx  %s\n",
+             (unsigned long long int)(*iter).lo,
+             (unsigned long long int)(*iter).hi,
+             (*iter).val ? "true" : "false");
+    }
+    printf(">>\n");
+  }
+
+  std::vector<Seg> mSegs;
+};
+
+
+////////////////////////////////////////////////////////////////
+// PriMap                                                     //
+////////////////////////////////////////////////////////////////
+
+class PriMap {
+ public:
+  PriMap(void (*aLog)(const char*))
+    : mLog(aLog)
+  {}
+
+  ~PriMap() {
+    for (std::vector<SecMap*>::iterator iter = mSecMaps.begin();
+         iter != mSecMaps.end();
+         ++iter) {
+      delete *iter;
+    }
+    mSecMaps.clear();
+  }
+
+  // This can happen with the global lock held for reading.
+  RuleSet* Lookup(uintptr_t ia) {
+    SecMap* sm = FindSecMap(ia);
+    return sm ? sm->FindRuleSet(ia) : nullptr;
+  }
+
+  // Add a secondary map.  No overlaps allowed w.r.t. existing
+  // secondary maps.  Global lock must be held for writing.
+  void AddSecMap(SecMap* aSecMap) {
+    // We can't add an empty SecMap to the PriMap.  But that's OK
+    // since we'd never be able to find anything in it anyway.
+    if (aSecMap->IsEmpty()) {
+      return;
+    }
+
+    // Iterate through the SecMaps and find the right place for this
+    // one.  At the same time, ensure that the in-order
+    // non-overlapping invariant is preserved (and, generally, holds).
+    // FIXME: this gives a cost that is O(N^2) in the total number of
+    // shared objects in the system.  ToDo: better.
+    MOZ_ASSERT(aSecMap->mSummaryMinAddr <= aSecMap->mSummaryMaxAddr);
+
+    size_t num_secMaps = mSecMaps.size();
+    uintptr_t i;
+    for (i = 0; i < num_secMaps; ++i) {
+      SecMap* sm_i = mSecMaps[i];
+      MOZ_ASSERT(sm_i->mSummaryMinAddr <= sm_i->mSummaryMaxAddr);
+      if (aSecMap->mSummaryMinAddr < sm_i->mSummaryMaxAddr) {
+        // |aSecMap| needs to be inserted immediately before mSecMaps[i].
+        break;
+      }
+    }
+    MOZ_ASSERT(i <= num_secMaps);
+    if (i == num_secMaps) {
+      // It goes at the end.
+      mSecMaps.push_back(aSecMap);
+    } else {
+      std::vector<SecMap*>::iterator iter = mSecMaps.begin() + i;
+      mSecMaps.insert(iter, aSecMap);
+    }
+    char buf[100];
+    snprintf(buf, sizeof(buf), "AddSecMap: now have %d SecMaps\n",
+             (int)mSecMaps.size());
+    buf[sizeof(buf)-1] = 0;
+    mLog(buf);
+  }
+
+  // Remove and delete any SecMaps in the mapping, that intersect
+  // with the specified address range.
+  void RemoveSecMapsInRange(uintptr_t avma_min, uintptr_t avma_max) {
+    MOZ_ASSERT(avma_min <= avma_max);
+    size_t num_secMaps = mSecMaps.size();
+    if (num_secMaps > 0) {
+      intptr_t i;
+      // Iterate from end to start over the vector, so as to ensure
+      // that the special case where |avma_min| and |avma_max| denote
+      // the entire address space, can be completed in time proportional
+      // to the number of elements in the map.
+      for (i = (intptr_t)num_secMaps-1; i >= 0; i--) {
+        SecMap* sm_i = mSecMaps[i];
+        if (sm_i->mSummaryMaxAddr < avma_min ||
+            avma_max < sm_i->mSummaryMinAddr) {
+          // There's no overlap.  Move on.
+          continue;
+        }
+        // We need to remove mSecMaps[i] and slide all those above it
+        // downwards to cover the hole.
+        mSecMaps.erase(mSecMaps.begin() + i);
+        delete sm_i;
+      }
+    }
+  }
+
+  // Return the number of currently contained SecMaps.
+  size_t CountSecMaps() {
+    return mSecMaps.size();
+  }
+
+  // Assess heuristically whether the given address is an instruction
+  // immediately following a call instruction.  The caller is required
+  // to hold the global lock for reading.
+  bool MaybeIsReturnPoint(TaggedUWord aInstrAddr, SegArray* aSegArray) {
+    if (!aInstrAddr.Valid()) {
+      return false;
+    }
+
+    uintptr_t ia = aInstrAddr.Value();
+
+    // Assume that nobody would be crazy enough to put code in the
+    // first or last page.
+    if (ia < 4096 || ((uintptr_t)(-ia)) < 4096) {
+      return false;
+    }
+
+    // See if it falls inside a known r-x mapped area.  Poking around
+    // outside such places risks segfaulting.
+    uintptr_t insns_min, insns_max;
+    bool b = aSegArray->getBoundingCodeSegment(&insns_min, &insns_max, ia);
+    if (!b) {
+      // no code (that we know about) at this address
+      return false;
+    }
+
+    // |ia| falls within an r-x range.  So we can
+    // safely poke around in [insns_min, insns_max].
+
+#if defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86)
+    // Is the previous instruction recognisably a CALL?  This is
+    // common for the 32- and 64-bit versions, except for the
+    // simm32(%rip) case, which is 64-bit only.
+    //
+    // For all other cases, the 64 bit versions are either identical
+    // to the 32 bit versions, or have an optional extra leading REX.W
+    // byte (0x41).  Since the extra 0x41 is optional we have to
+    // ignore it, with the convenient result that the same matching
+    // logic works for both 32- and 64-bit cases.
+
+    uint8_t* p = (uint8_t*)ia;
+#   if defined(LUL_ARCH_x64)
+    // CALL simm32(%rip)  == FF15 simm32
+    if (ia - 6 >= insns_min && p[-6] == 0xFF && p[-5] == 0x15) {
+      return true;
+    }
+#   endif
+    // CALL rel32  == E8 rel32  (both 32- and 64-bit)
+    if (ia - 5 >= insns_min && p[-5] == 0xE8) {
+      return true;
+    }
+    // CALL *%eax .. CALL *%edi  ==   FFD0 ..   FFD7  (32-bit)
+    // CALL *%rax .. CALL *%rdi  ==   FFD0 ..   FFD7  (64-bit)
+    // CALL *%r8  .. CALL *%r15  == 41FFD0 .. 41FFD7  (64-bit)
+    if (ia - 2 >= insns_min &&
+        p[-2] == 0xFF && p[-1] >= 0xD0 && p[-1] <= 0xD7) {
+      return true;
+    }
+    // Almost all of the remaining cases that occur in practice are
+    // of the form CALL *simm8(reg) or CALL *simm32(reg).
+    //
+    // 64 bit cases:
+    //
+    // call  *simm8(%rax)         FF50   simm8
+    // call  *simm8(%rcx)         FF51   simm8
+    // call  *simm8(%rdx)         FF52   simm8
+    // call  *simm8(%rbx)         FF53   simm8
+    // call  *simm8(%rsp)         FF5424 simm8
+    // call  *simm8(%rbp)         FF55   simm8
+    // call  *simm8(%rsi)         FF56   simm8
+    // call  *simm8(%rdi)         FF57   simm8
+    //
+    // call  *simm8(%r8)        41FF50   simm8
+    // call  *simm8(%r9)        41FF51   simm8
+    // call  *simm8(%r10)       41FF52   simm8
+    // call  *simm8(%r11)       41FF53   simm8
+    // call  *simm8(%r12)       41FF5424 simm8
+    // call  *simm8(%r13)       41FF55   simm8
+    // call  *simm8(%r14)       41FF56   simm8
+    // call  *simm8(%r15)       41FF57   simm8
+    //
+    // call  *simm32(%rax)        FF90   simm32
+    // call  *simm32(%rcx)        FF91   simm32
+    // call  *simm32(%rdx)        FF92   simm32
+    // call  *simm32(%rbx)        FF93   simm32
+    // call  *simm32(%rsp)        FF9424 simm32
+    // call  *simm32(%rbp)        FF95   simm32
+    // call  *simm32(%rsi)        FF96   simm32
+    // call  *simm32(%rdi)        FF97   simm32
+    //
+    // call  *simm32(%r8)       41FF90   simm32
+    // call  *simm32(%r9)       41FF91   simm32
+    // call  *simm32(%r10)      41FF92   simm32
+    // call  *simm32(%r11)      41FF93   simm32
+    // call  *simm32(%r12)      41FF9424 simm32
+    // call  *simm32(%r13)      41FF95   simm32
+    // call  *simm32(%r14)      41FF96   simm32
+    // call  *simm32(%r15)      41FF97   simm32
+    //
+    // 32 bit cases:
+    //
+    // call  *simm8(%eax)         FF50   simm8
+    // call  *simm8(%ecx)         FF51   simm8
+    // call  *simm8(%edx)         FF52   simm8
+    // call  *simm8(%ebx)         FF53   simm8
+    // call  *simm8(%esp)         FF5424 simm8
+    // call  *simm8(%ebp)         FF55   simm8
+    // call  *simm8(%esi)         FF56   simm8
+    // call  *simm8(%edi)         FF57   simm8
+    //
+    // call  *simm32(%eax)        FF90   simm32
+    // call  *simm32(%ecx)        FF91   simm32
+    // call  *simm32(%edx)        FF92   simm32
+    // call  *simm32(%ebx)        FF93   simm32
+    // call  *simm32(%esp)        FF9424 simm32
+    // call  *simm32(%ebp)        FF95   simm32
+    // call  *simm32(%esi)        FF96   simm32
+    // call  *simm32(%edi)        FF97   simm32
+    if (ia - 3 >= insns_min &&
+        p[-3] == 0xFF &&
+        (p[-2] >= 0x50 && p[-2] <= 0x57 && p[-2] != 0x54)) {
+      // imm8 case, not including %esp/%rsp
+      return true;
+    }
+    if (ia - 4 >= insns_min &&
+        p[-4] == 0xFF && p[-3] == 0x54 && p[-2] == 0x24) {
+      // imm8 case for %esp/%rsp
+      return true;
+    }
+    if (ia - 6 >= insns_min &&
+        p[-6] == 0xFF &&
+        (p[-5] >= 0x90 && p[-5] <= 0x97 && p[-5] != 0x94)) {
+      // imm32 case, not including %esp/%rsp
+      return true;
+    }
+    if (ia - 7 >= insns_min &&
+        p[-7] == 0xFF && p[-6] == 0x94 && p[-5] == 0x24) {
+      // imm32 case for %esp/%rsp
+      return true;
+    }
+
+#elif defined(LUL_ARCH_arm)
+    if (ia & 1) {
+      uint16_t w0 = 0, w1 = 0;
+      // The return address has its lowest bit set, indicating a return
+      // to Thumb code.
+      ia &= ~(uintptr_t)1;
+      if (ia - 2 >= insns_min && ia - 1 <= insns_max) {
+        w1 = *(uint16_t*)(ia - 2);
+      }
+      if (ia - 4 >= insns_min && ia - 1 <= insns_max) {
+        w0 = *(uint16_t*)(ia - 4);
+      }
+      // Is it a 32-bit Thumb call insn?
+      // BL  simm26 (Encoding T1)
+      if ((w0 & 0xF800) == 0xF000 && (w1 & 0xC000) == 0xC000) {
+        return true;
+      }
+      // BLX simm26 (Encoding T2)
+      if ((w0 & 0xF800) == 0xF000 && (w1 & 0xC000) == 0xC000) {
+        return true;
+      }
+      // Other possible cases:
+      // (BLX Rm, Encoding T1).
+      // BLX Rm (encoding T1, 16 bit, inspect w1 and ignore w0.)
+      // 0100 0111 1 Rm 000
+    } else {
+      // Returning to ARM code.
+      uint32_t a0 = 0;
+      if ((ia & 3) == 0 && ia - 4 >= insns_min && ia - 1 <= insns_max) {
+        a0 = *(uint32_t*)(ia - 4);
+      }
+      // Leading E forces unconditional only -- fix.  It could be
+      // anything except F, which is the deprecated NV code.
+      // BL simm26 (Encoding A1)
+      if ((a0 & 0xFF000000) == 0xEB000000) {
+        return true;
+      }
+      // Other possible cases:
+      // BLX simm26 (Encoding A2)
+      //if ((a0 & 0xFE000000) == 0xFA000000)
+      //  return true;
+      // BLX (register) (A1): BLX <c> <Rm>
+      // cond 0001 0010 1111 1111 1111 0011 Rm
+      // again, cond can be anything except NV (0xF)
+    }
+
+#else
+# error "Unsupported arch"
+#endif
+
+    // Not an insn we recognise.
+    return false;
+  }
+
+ private:
+  // FindSecMap's caller must hold the global lock for reading or writing.
+  SecMap* FindSecMap(uintptr_t ia) {
+    // Binary search mSecMaps to find one that brackets |ia|.
+    // lo and hi need to be signed, else the loop termination tests
+    // don't work properly.
+    long int lo = 0;
+    long int hi = (long int)mSecMaps.size() - 1;
+    while (true) {
+      // current unsearched space is from lo to hi, inclusive.
+      if (lo > hi) {
+        // not found
+        return nullptr;
+      }
+      long int  mid         = lo + ((hi - lo) / 2);
+      SecMap*   mid_secMap  = mSecMaps[mid];
+      uintptr_t mid_minAddr = mid_secMap->mSummaryMinAddr;
+      uintptr_t mid_maxAddr = mid_secMap->mSummaryMaxAddr;
+      if (ia < mid_minAddr) { hi = mid-1; continue; }
+      if (ia > mid_maxAddr) { lo = mid+1; continue; }
+      MOZ_ASSERT(mid_minAddr <= ia && ia <= mid_maxAddr);
+      return mid_secMap;
+    }
+    // NOTREACHED
+  }
+
+ private:
+  // sorted array of per-object ranges, non overlapping, non empty
+  std::vector<SecMap*> mSecMaps;
+
+  // a logging sink, for debugging.
+  void (*mLog)(const char*);
+};
+
+
+////////////////////////////////////////////////////////////////
+// CFICache                                                   //
+////////////////////////////////////////////////////////////////
+
+// This is the thread-local cache.  It maps individual insn AVMAs to
+// the associated CFI record, which live in LUL::mPriMap.
+//
+// The cache is a direct map hash table indexed by address.
+// It has to distinguish 3 cases: