Bug 1283121 - Handle SIGBUS for unaligned FP load/store on ARM Linux. r=luke, r=jseward
authorLars T Hansen <lhansen@mozilla.com>
Wed, 09 Jan 2019 13:51:12 +0100
changeset 511189 1230184adda1d000a3b339a599416c015eea4119
parent 511188 3b1fbe2830f3a4b385f07ae0d2403611e666a821
child 511190 2d1497d9bb0402cb5873759ff4e038352dda2d6e
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke, jseward
bugs1283121
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1283121 - Handle SIGBUS for unaligned FP load/store on ARM Linux. r=luke, r=jseward Accesses that are not flagged as unaligned in the wasm bytecode may still be unaligned in practice; this is allowed by the wasm spec. This is not a problem on x86 but may be on ARM (depending on the chip and the OS), since ARM has more stringent alignment requirements; however, our initial ARM work knowingly ignored the problem, as we had insufficient data. Now we have data, and it turns out that 32-bit ARM Linux and Android usually signal SIGBUS for unaligned floating-point accesses, this is observed broadly on current (2017-2018) devices. It is still chip-dependent, in principle, but I've found no recent device or Android version where the signal does not occur. So we have to handle the signal or avoid it being generated in the first place. As we expect unaligned FP accesses to be infrequent (multibyte data read directly out of packed data streams, etc) it provides best performance overall to emulate the faulting instruction within the trap handler. The main problem with doing so is that the Linux sigcontext does not quite expose the FP registers. However, the sigcontext contains a data structure that has the data we need; it is self-identifying; and it has been stable for nearly a decade and many Linux versions, at least back to the 2.6 line. Android has merged the code in question. A secondary problem is that we don't want a general disassembler in the signal handler, so code generation becomes slightly constrained by this, but only on ARM, and not in a way that required any changes.
js/src/jit-test/tests/wasm/float-unaligned.js
js/src/jit-test/tests/wasm/spec/address.wast.js
js/src/jit-test/tests/wasm/spec/call_indirect.wast.js
js/src/jit-test/tests/wasm/spec/float_memory.wast.js
js/src/jit-test/tests/wasm/spec/memory_redundancy.wast.js
js/src/jit/arm/MacroAssembler-arm.cpp
js/src/wasm/WasmInstance.cpp
js/src/wasm/WasmInstance.h
js/src/wasm/WasmSignalHandlers.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/float-unaligned.js
@@ -0,0 +1,118 @@
+// Various tests for unaligned float accesses.  These are specifically meant to
+// test the SIGBUS handling on 32-bit ARM by exercising odd addresses and odd
+// offsets.
+
+// For a triple of (numBallast, ty, offset), create the text for a pair of
+// functions "get_ty_offset" and "set_ty_offset" where each has numBallast live
+// dummy values across the operation of interest to force the use of different
+// register numbers.  (This is primarily for the FP registers as ARM code
+// generation currently always uses the same scratch register for the base
+// address of the access.)
+//
+// These must be augmented with a memory.  Memory addresses 0-255 are reserved
+// for internal use by these functions.  The memory must start as zero.
+
+function makeLoadStore(numBallast, ty, offset) {
+    // The general idea of the ballast is that we occupy some FP registers and
+    // some int registers with non-dead values before we perform an operation,
+    // and then we consume the occupied registers after.
+    //
+    // In the case of load, the loaded result is stored back in memory before we
+    // consume the ballast, thus the ion regalloc will not simply always load
+    // the result into d0, but usually into some temp other than d0.  Thus the
+    // amount of ballast affects the register.  (Ditto baseline though the
+    // reasoning is simpler.)
+    //
+    // In the case of store, we keep the parameter value live until the end so
+    // that the tmp that we compute for the store is moved into a different
+    // register.  The tmp has the same value as the parameter value but a
+    // non-JIT compiler can't know that.
+
+    let loadtxt =
+      `(func (export "get_${ty}_${offset}") (param $p i32) (result ${ty})
+         ${ballast(() => `
+              (i32.const 8)
+              (i32.store (i32.const 8) (i32.add (i32.load (i32.const 8)) (i32.const 1)))
+              (${ty}.load (i32.const 8))`)}
+
+         (${ty}.store (i32.const 0) (${ty}.load offset=${offset} (get_local $p)))
+
+         ${ballast(() => `
+             ${ty}.store`)}
+
+         (${ty}.load (i32.const 0)))`;
+
+    // This will assume the value at mem[16] is zero.
+    let storetxt =
+      `(func (export "set_${ty}_${offset}") (param $p i32) (param $v ${ty})
+         (local $tmp ${ty})
+         ${ballast(() => `
+              (i32.const 8)
+              (i32.store (i32.const 8) (i32.add (i32.load (i32.const 8)) (i32.const 1)))
+              (${ty}.load (i32.const 8))`)}
+
+         (set_local $tmp (${ty}.add (get_local $v) (${ty}.load (i32.const 16))))
+         (${ty}.store offset=${offset} (get_local $p) (get_local $tmp))
+
+         ${ballast(() => `
+             ${ty}.store`)}
+         (${ty}.store (i32.const 8) (get_local $v)))`;
+
+    return `${loadtxt}
+            ${storetxt}`;
+
+    function ballast(thunk) {
+        let s = "";
+        for ( let i=0 ; i < numBallast; i++ )
+            s += thunk();
+        return s;
+    }
+}
+
+// The complexity here comes from trying to force the source/target FP registers
+// in the FP access instruction to vary.  For Baseline this is not hard; for Ion
+// trickier.
+
+function makeInstance(numBallast, offset) {
+    let txt =
+        `(module
+           (memory (export "memory") 1 1)
+           ${makeLoadStore(numBallast, 'f64', offset)}
+           ${makeLoadStore(numBallast, 'f32', offset)})`;
+    return new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(txt)));
+}
+
+// `offset` corresponds to the "offset" directive in the instruction
+for ( let offset=0 ; offset < 8; offset++ ) {
+
+    // `numBallast` represents the amount of ballast registers we're trying to use,
+    // see comments above.
+    for ( let numBallast=0; numBallast < 16; numBallast++ ) {
+        let ins = makeInstance(numBallast, offset);
+        let mem = ins.exports.memory;
+        let buf = new DataView(mem.buffer);
+
+        // `i` represents the offset in the pointer from a proper boundary
+        for ( let i=0; i < 9; i++ ) {
+	    let offs = 256+i;
+            let val = Math.PI+i;
+
+            buf.setFloat64(offs + offset, val, true);
+            assertEq(ins.exports["get_f64_" + offset](offs), val);
+
+            ins.exports["set_f64_" + offset](offs + 32, val);
+            assertEq(buf.getFloat64(offs + 32 + offset, true), val);
+        }
+
+        for ( let i=0; i < 9; i++ ) {
+            let offs = 512+i;
+            let val = Math.fround(Math.PI+i);
+
+            buf.setFloat32(offs + offset, val, true);
+            assertEq(ins.exports["get_f32_" + offset](offs), val);
+
+            ins.exports["set_f32_" + offset](offs + 32, val);
+            assertEq(buf.getFloat32(offs + 32 + offset, true), val);
+        }
+    }
+}
--- a/js/src/jit-test/tests/wasm/spec/address.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/address.wast.js
@@ -1,10 +1,8 @@
-// |jit-test| skip-if: getBuildConfiguration()['arm']
-// skip due to bug 1517351
 
 // address.wast:3
 let $1 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x8a\x80\x80\x80\x00\x02\x60\x01\x7f\x01\x7f\x60\x01\x7f\x00\x03\x9f\x80\x80\x80\x00\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x01\x05\x83\x80\x80\x80\x00\x01\x00\x01\x07\xcd\x82\x80\x80\x00\x1e\x08\x38\x75\x5f\x67\x6f\x6f\x64\x31\x00\x00\x08\x38\x75\x5f\x67\x6f\x6f\x64\x32\x00\x01\x08\x38\x75\x5f\x67\x6f\x6f\x64\x33\x00\x02\x08\x38\x75\x5f\x67\x6f\x6f\x64\x34\x00\x03\x08\x38\x75\x5f\x67\x6f\x6f\x64\x35\x00\x04\x08\x38\x73\x5f\x67\x6f\x6f\x64\x31\x00\x05\x08\x38\x73\x5f\x67\x6f\x6f\x64\x32\x00\x06\x08\x38\x73\x5f\x67\x6f\x6f\x64\x33\x00\x07\x08\x38\x73\x5f\x67\x6f\x6f\x64\x34\x00\x08\x08\x38\x73\x5f\x67\x6f\x6f\x64\x35\x00\x09\x09\x31\x36\x75\x5f\x67\x6f\x6f\x64\x31\x00\x0a\x09\x31\x36\x75\x5f\x67\x6f\x6f\x64\x32\x00\x0b\x09\x31\x36\x75\x5f\x67\x6f\x6f\x64\x33\x00\x0c\x09\x31\x36\x75\x5f\x67\x6f\x6f\x64\x34\x00\x0d\x09\x31\x36\x75\x5f\x67\x6f\x6f\x64\x35\x00\x0e\x09\x31\x36\x73\x5f\x67\x6f\x6f\x64\x31\x00\x0f\x09\x31\x36\x73\x5f\x67\x6f\x6f\x64\x32\x00\x10\x09\x31\x36\x73\x5f\x67\x6f\x6f\x64\x33\x00\x11\x09\x31\x36\x73\x5f\x67\x6f\x6f\x64\x34\x00\x12\x09\x31\x36\x73\x5f\x67\x6f\x6f\x64\x35\x00\x13\x08\x33\x32\x5f\x67\x6f\x6f\x64\x31\x00\x14\x08\x33\x32\x5f\x67\x6f\x6f\x64\x32\x00\x15\x08\x33\x32\x5f\x67\x6f\x6f\x64\x33\x00\x16\x08\x33\x32\x5f\x67\x6f\x6f\x64\x34\x00\x17\x08\x33\x32\x5f\x67\x6f\x6f\x64\x35\x00\x18\x06\x38\x75\x5f\x62\x61\x64\x00\x19\x06\x38\x73\x5f\x62\x61\x64\x00\x1a\x07\x31\x36\x75\x5f\x62\x61\x64\x00\x1b\x07\x31\x36\x73\x5f\x62\x61\x64\x00\x1c\x06\x33\x32\x5f\x62\x61\x64\x00\x1d\x0a\x82\x83\x80\x80\x00\x1e\x87\x80\x80\x80\x00\x00\x20\x00\x2d\x00\x00\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x2d\x00\x00\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x2d\x00\x01\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x2d\x00\x02\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x2d\x00\x19\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x2c\x00\x00\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x2c\x00\x00\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x2c\x00\x01\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x2c\x00\x02\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x2c\x00\x19\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x2f\x01\x00\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x2f\x00\x00\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x2f\x00\x01\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x2f\x01\x02\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x2f\x01\x19\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x2e\x01\x00\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x2e\x00\x00\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x2e\x00\x01\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x2e\x01\x02\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x2e\x01\x19\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x28\x02\x00\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x28\x00\x00\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x28\x00\x01\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x28\x01\x02\x0b\x87\x80\x80\x80\x00\x00\x20\x00\x28\x02\x19\x0b\x8c\x80\x80\x80\x00\x00\x20\x00\x2d\x00\xff\xff\xff\xff\x0f\x1a\x0b\x8c\x80\x80\x80\x00\x00\x20\x00\x2c\x00\xff\xff\xff\xff\x0f\x1a\x0b\x8c\x80\x80\x80\x00\x00\x20\x00\x2f\x01\xff\xff\xff\xff\x0f\x1a\x0b\x8c\x80\x80\x80\x00\x00\x20\x00\x2e\x01\xff\xff\xff\xff\x0f\x1a\x0b\x8c\x80\x80\x80\x00\x00\x20\x00\x28\x02\xff\xff\xff\xff\x0f\x1a\x0b\x0b\xa0\x80\x80\x80\x00\x01\x00\x41\x00\x0b\x1a\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a");
 
 // address.wast:104
 assert_return(() => call($1, "8u_good1", [0]), 97);
 
 // address.wast:105
--- a/js/src/jit-test/tests/wasm/spec/call_indirect.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/call_indirect.wast.js
@@ -1,10 +1,8 @@
-// |jit-test| skip-if: getBuildConfiguration()['arm']
-// skip due to bug 1517351
 
 // call_indirect.wast:3
 let $1 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\xfb\x80\x80\x80\x00\x18\x60\x00\x00\x60\x00\x01\x7f\x60\x00\x01\x7e\x60\x00\x01\x7d\x60\x00\x01\x7c\x60\x01\x7f\x01\x7f\x60\x01\x7e\x01\x7e\x60\x01\x7d\x01\x7d\x60\x01\x7c\x01\x7c\x60\x02\x7d\x7f\x01\x7f\x60\x02\x7f\x7e\x01\x7e\x60\x02\x7c\x7d\x01\x7d\x60\x02\x7e\x7c\x01\x7c\x60\x01\x7f\x01\x7f\x60\x01\x7e\x01\x7e\x60\x01\x7d\x01\x7d\x60\x01\x7c\x01\x7c\x60\x04\x7e\x7c\x7f\x7e\x01\x7f\x60\x01\x7e\x01\x7f\x60\x04\x7e\x7c\x7f\x7e\x00\x60\x01\x7e\x00\x60\x01\x7f\x01\x7e\x60\x01\x7f\x01\x7d\x60\x01\x7f\x01\x7c\x03\xca\x80\x80\x80\x00\x49\x01\x02\x03\x04\x05\x06\x07\x08\x0a\x0c\x09\x0b\x0d\x0e\x0f\x10\x00\x01\x02\x03\x04\x02\x01\x02\x03\x04\x01\x02\x03\x04\x0a\x15\x05\x16\x17\x06\x06\x05\x07\x08\x05\x07\x08\x05\x05\x00\x00\x00\x01\x01\x01\x01\x02\x01\x03\x01\x00\x00\x01\x01\x00\x03\x04\x04\x04\x01\x03\x01\x01\x01\x01\x01\x02\x04\x85\x80\x80\x80\x00\x01\x70\x01\x1d\x1d\x05\x83\x80\x80\x80\x00\x01\x00\x01\x06\x8d\x80\x80\x80\x00\x01\x7c\x01\x44\x00\x00\x00\x00\x00\x00\x24\x40\x0b\x07\xfc\x86\x80\x80\x00\x37\x08\x74\x79\x70\x65\x2d\x69\x33\x32\x00\x11\x08\x74\x79\x70\x65\x2d\x69\x36\x34\x00\x12\x08\x74\x79\x70\x65\x2d\x66\x33\x32\x00\x13\x08\x74\x79\x70\x65\x2d\x66\x36\x34\x00\x14\x0a\x74\x79\x70\x65\x2d\x69\x6e\x64\x65\x78\x00\x15\x0e\x74\x79\x70\x65\x2d\x66\x69\x72\x73\x74\x2d\x69\x33\x32\x00\x16\x0e\x74\x79\x70\x65\x2d\x66\x69\x72\x73\x74\x2d\x69\x36\x34\x00\x17\x0e\x74\x79\x70\x65\x2d\x66\x69\x72\x73\x74\x2d\x66\x33\x32\x00\x18\x0e\x74\x79\x70\x65\x2d\x66\x69\x72\x73\x74\x2d\x66\x36\x34\x00\x19\x0f\x74\x79\x70\x65\x2d\x73\x65\x63\x6f\x6e\x64\x2d\x69\x33\x32\x00\x1a\x0f\x74\x79\x70\x65\x2d\x73\x65\x63\x6f\x6e\x64\x2d\x69\x36\x34\x00\x1b\x0f\x74\x79\x70\x65\x2d\x73\x65\x63\x6f\x6e\x64\x2d\x66\x33\x32\x00\x1c\x0f\x74\x79\x70\x65\x2d\x73\x65\x63\x6f\x6e\x64\x2d\x66\x36\x34\x00\x1d\x08\x64\x69\x73\x70\x61\x74\x63\x68\x00\x1e\x17\x64\x69\x73\x70\x61\x74\x63\x68\x2d\x73\x74\x72\x75\x63\x74\x75\x72\x61\x6c\x2d\x69\x36\x34\x00\x1f\x17\x64\x69\x73\x70\x61\x74\x63\x68\x2d\x73\x74\x72\x75\x63\x74\x75\x72\x61\x6c\x2d\x69\x33\x32\x00\x20\x17\x64\x69\x73\x70\x61\x74\x63\x68\x2d\x73\x74\x72\x75\x63\x74\x75\x72\x61\x6c\x2d\x66\x33\x32\x00\x21\x17\x64\x69\x73\x70\x61\x74\x63\x68\x2d\x73\x74\x72\x75\x63\x74\x75\x72\x61\x6c\x2d\x66\x36\x34\x00\x22\x07\x66\x61\x63\x2d\x69\x36\x34\x00\x23\x07\x66\x69\x62\x2d\x69\x36\x34\x00\x24\x07\x66\x61\x63\x2d\x69\x33\x32\x00\x25\x07\x66\x61\x63\x2d\x66\x33\x32\x00\x26\x07\x66\x61\x63\x2d\x66\x36\x34\x00\x27\x07\x66\x69\x62\x2d\x69\x33\x32\x00\x28\x07\x66\x69\x62\x2d\x66\x33\x32\x00\x29\x07\x66\x69\x62\x2d\x66\x36\x34\x00\x2a\x04\x65\x76\x65\x6e\x00\x2b\x03\x6f\x64\x64\x00\x2c\x07\x72\x75\x6e\x61\x77\x61\x79\x00\x2d\x0e\x6d\x75\x74\x75\x61\x6c\x2d\x72\x75\x6e\x61\x77\x61\x79\x00\x2e\x0f\x61\x73\x2d\x73\x65\x6c\x65\x63\x74\x2d\x66\x69\x72\x73\x74\x00\x30\x0d\x61\x73\x2d\x73\x65\x6c\x65\x63\x74\x2d\x6d\x69\x64\x00\x31\x0e\x61\x73\x2d\x73\x65\x6c\x65\x63\x74\x2d\x6c\x61\x73\x74\x00\x32\x0f\x61\x73\x2d\x69\x66\x2d\x63\x6f\x6e\x64\x69\x74\x69\x6f\x6e\x00\x33\x0e\x61\x73\x2d\x62\x72\x5f\x69\x66\x2d\x66\x69\x72\x73\x74\x00\x34\x0d\x61\x73\x2d\x62\x72\x5f\x69\x66\x2d\x6c\x61\x73\x74\x00\x35\x11\x61\x73\x2d\x62\x72\x5f\x74\x61\x62\x6c\x65\x2d\x66\x69\x72\x73\x74\x00\x36\x10\x61\x73\x2d\x62\x72\x5f\x74\x61\x62\x6c\x65\x2d\x6c\x61\x73\x74\x00\x37\x0e\x61\x73\x2d\x73\x74\x6f\x72\x65\x2d\x66\x69\x72\x73\x74\x00\x38\x0d\x61\x73\x2d\x73\x74\x6f\x72\x65\x2d\x6c\x61\x73\x74\x00\x39\x14\x61\x73\x2d\x6d\x65\x6d\x6f\x72\x79\x2e\x67\x72\x6f\x77\x2d\x76\x61\x6c\x75\x65\x00\x3a\x0f\x61\x73\x2d\x72\x65\x74\x75\x72\x6e\x2d\x76\x61\x6c\x75\x65\x00\x3b\x0f\x61\x73\x2d\x64\x72\x6f\x70\x2d\x6f\x70\x65\x72\x61\x6e\x64\x00\x3c\x0b\x61\x73\x2d\x62\x72\x2d\x76\x61\x6c\x75\x65\x00\x3d\x12\x61\x73\x2d\x6c\x6f\x63\x61\x6c\x2e\x73\x65\x74\x2d\x76\x61\x6c\x75\x65\x00\x3e\x12\x61\x73\x2d\x6c\x6f\x63\x61\x6c\x2e\x74\x65\x65\x2d\x76\x61\x6c\x75\x65\x00\x3f\x13\x61\x73\x2d\x67\x6c\x6f\x62\x61\x6c\x2e\x73\x65\x74\x2d\x76\x61\x6c\x75\x65\x00\x40\x0f\x61\x73\x2d\x6c\x6f\x61\x64\x2d\x6f\x70\x65\x72\x61\x6e\x64\x00\x41\x10\x61\x73\x2d\x75\x6e\x61\x72\x79\x2d\x6f\x70\x65\x72\x61\x6e\x64\x00\x42\x0e\x61\x73\x2d\x62\x69\x6e\x61\x72\x79\x2d\x6c\x65\x66\x74\x00\x43\x0f\x61\x73\x2d\x62\x69\x6e\x61\x72\x79\x2d\x72\x69\x67\x68\x74\x00\x44\x0f\x61\x73\x2d\x74\x65\x73\x74\x2d\x6f\x70\x65\x72\x61\x6e\x64\x00\x45\x0f\x61\x73\x2d\x63\x6f\x6d\x70\x61\x72\x65\x2d\x6c\x65\x66\x74\x00\x46\x10\x61\x73\x2d\x63\x6f\x6d\x70\x61\x72\x65\x2d\x72\x69\x67\x68\x74\x00\x47\x12\x61\x73\x2d\x63\x6f\x6e\x76\x65\x72\x74\x2d\x6f\x70\x65\x72\x61\x6e\x64\x00\x48\x09\xa3\x80\x80\x80\x00\x01\x00\x41\x00\x0b\x1d\x00\x01\x02\x03\x04\x05\x06\x07\x0a\x08\x0b\x09\x23\x24\x2b\x2c\x2d\x2e\x2f\x0c\x0d\x0e\x0f\x25\x26\x27\x28\x29\x2a\x0a\xba\x8b\x80\x80\x00\x49\x85\x80\x80\x80\x00\x00\x41\xb2\x02\x0b\x85\x80\x80\x80\x00\x00\x42\xe4\x02\x0b\x87\x80\x80\x80\x00\x00\x43\x00\x20\x73\x45\x0b\x8b\x80\x80\x80\x00\x00\x44\x00\x00\x00\x00\x00\xc8\xae\x40\x0b\x84\x80\x80\x80\x00\x00\x20\x00\x0b\x84\x80\x80\x80\x00\x00\x20\x00\x0b\x84\x80\x80\x80\x00\x00\x20\x00\x0b\x84\x80\x80\x80\x00\x00\x20\x00\x0b\x84\x80\x80\x80\x00\x00\x20\x01\x0b\x84\x80\x80\x80\x00\x00\x20\x01\x0b\x84\x80\x80\x80\x00\x00\x20\x01\x0b\x84\x80\x80\x80\x00\x00\x20\x01\x0b\x84\x80\x80\x80\x00\x00\x20\x00\x0b\x84\x80\x80\x80\x00\x00\x20\x00\x0b\x84\x80\x80\x80\x00\x00\x20\x00\x0b\x84\x80\x80\x80\x00\x00\x20\x00\x0b\xdd\x80\x80\x80\x00\x00\x41\x00\x11\x00\x00\x42\x00\x41\x00\x11\x14\x00\x42\x00\x44\x00\x00\x00\x00\x00\x00\x00\x00\x41\x00\x42\x00\x41\x00\x11\x13\x00\x41\x00\x11\x00\x00\x41\x00\x11\x01\x00\x45\x1a\x41\x00\x11\x01\x00\x45\x1a\x42\x00\x41\x00\x11\x12\x00\x45\x1a\x42\x00\x44\x00\x00\x00\x00\x00\x00\x00\x00\x41\x00\x42\x00\x41\x00\x11\x11\x00\x45\x1a\x42\x00\x41\x00\x11\x06\x00\x50\x1a\x0b\x87\x80\x80\x80\x00\x00\x41\x00\x11\x01\x00\x0b\x87\x80\x80\x80\x00\x00\x41\x01\x11\x02\x00\x0b\x87\x80\x80\x80\x00\x00\x41\x02\x11\x03\x00\x0b\x87\x80\x80\x80\x00\x00\x41\x03\x11\x04\x00\x0b\x8a\x80\x80\x80\x00\x00\x42\xe4\x00\x41\x05\x11\x06\x00\x0b\x89\x80\x80\x80\x00\x00\x41\x20\x41\x04\x11\x05\x00\x0b\x8a\x80\x80\x80\x00\x00\x42\xc0\x00\x41\x05\x11\x06\x00\x0b\x8c\x80\x80\x80\x00\x00\x43\xc3\xf5\xa8\x3f\x41\x06\x11\x07\x00\x0b\x90\x80\x80\x80\x00\x00\x44\x3d\x0a\xd7\xa3\x70\x3d\xfa\x3f\x41\x07\x11\x08\x00\x0b\x8e\x80\x80\x80\x00\x00\x43\x66\x66\x00\x42\x41\x20\x41\x08\x11\x09\x00\x0b\x8c\x80\x80\x80\x00\x00\x41\x20\x42\xc0\x00\x41\x09\x11\x0a\x00\x0b\x95\x80\x80\x80\x00\x00\x44\x00\x00\x00\x00\x00\x00\x50\x40\x43\x00\x00\x00\x42\x41\x0a\x11\x0b\x00\x0b\x93\x80\x80\x80\x00\x00\x42\xc0\x00\x44\x66\x66\x66\x66\x66\x06\x50\x40\x41\x0b\x11\x0c\x00\x0b\x89\x80\x80\x80\x00\x00\x20\x01\x20\x00\x11\x06\x00\x0b\x89\x80\x80\x80\x00\x00\x42\x09\x20\x00\x11\x0e\x00\x0b\x89\x80\x80\x80\x00\x00\x41\x09\x20\x00\x11\x0d\x00\x0b\x8c\x80\x80\x80\x00\x00\x43\x00\x00\x10\x41\x20\x00\x11\x0f\x00\x0b\x90\x80\x80\x80\x00\x00\x44\x00\x00\x00\x00\x00\x00\x22\x40\x20\x00\x11\x10\x00\x0b\x98\x80\x80\x80\x00\x00\x20\x00\x50\x04\x7e\x42\x01\x05\x20\x00\x20\x00\x42\x01\x7d\x41\x0c\x11\x06\x00\x7e\x0b\x0b\xa2\x80\x80\x80\x00\x00\x20\x00\x42\x01\x58\x04\x7e\x42\x01\x05\x20\x00\x42\x02\x7d\x41\x0d\x11\x06\x00\x20\x00\x42\x01\x7d\x41\x0d\x11\x06\x00\x7c\x0b\x0b\x98\x80\x80\x80\x00\x00\x20\x00\x45\x04\x7f\x41\x01\x05\x20\x00\x20\x00\x41\x01\x6b\x41\x17\x11\x05\x00\x6c\x0b\x0b\xa3\x80\x80\x80\x00\x00\x20\x00\x43\x00\x00\x00\x00\x5b\x04\x7d\x43\x00\x00\x80\x3f\x05\x20\x00\x20\x00\x43\x00\x00\x80\x3f\x93\x41\x18\x11\x07\x00\x94\x0b\x0b\xaf\x80\x80\x80\x00\x00\x20\x00\x44\x00\x00\x00\x00\x00\x00\x00\x00\x61\x04\x7c\x44\x00\x00\x00\x00\x00\x00\xf0\x3f\x05\x20\x00\x20\x00\x44\x00\x00\x00\x00\x00\x00\xf0\x3f\xa1\x41\x19\x11\x08\x00\xa2\x0b\x0b\xa2\x80\x80\x80\x00\x00\x20\x00\x41\x01\x4d\x04\x7f\x41\x01\x05\x20\x00\x41\x02\x6b\x41\x1a\x11\x05\x00\x20\x00\x41\x01\x6b\x41\x1a\x11\x05\x00\x6a\x0b\x0b\xae\x80\x80\x80\x00\x00\x20\x00\x43\x00\x00\x80\x3f\x5f\x04\x7d\x43\x00\x00\x80\x3f\x05\x20\x00\x43\x00\x00\x00\x40\x93\x41\x1b\x11\x07\x00\x20\x00\x43\x00\x00\x80\x3f\x93\x41\x1b\x11\x07\x00\x92\x0b\x0b\xbe\x80\x80\x80\x00\x00\x20\x00\x44\x00\x00\x00\x00\x00\x00\xf0\x3f\x65\x04\x7c\x44\x00\x00\x00\x00\x00\x00\xf0\x3f\x05\x20\x00\x44\x00\x00\x00\x00\x00\x00\x00\x40\xa1\x41\x1c\x11\x08\x00\x20\x00\x44\x00\x00\x00\x00\x00\x00\xf0\x3f\xa1\x41\x1c\x11\x08\x00\xa0\x0b\x0b\x95\x80\x80\x80\x00\x00\x20\x00\x45\x04\x7f\x41\x2c\x05\x20\x00\x41\x01\x6b\x41\x0f\x11\x05\x00\x0b\x0b\x96\x80\x80\x80\x00\x00\x20\x00\x45\x04\x7f\x41\xe3\x00\x05\x20\x00\x41\x01\x6b\x41\x0e\x11\x05\x00\x0b\x0b\x87\x80\x80\x80\x00\x00\x41\x10\x11\x00\x00\x0b\x87\x80\x80\x80\x00\x00\x41\x12\x11\x00\x00\x0b\x87\x80\x80\x80\x00\x00\x41\x11\x11\x00\x00\x0b\x8c\x80\x80\x80\x00\x00\x41\x00\x11\x01\x00\x41\x02\x41\x03\x1b\x0b\x8c\x80\x80\x80\x00\x00\x41\x02\x41\x00\x11\x01\x00\x41\x03\x1b\x0b\x8c\x80\x80\x80\x00\x00\x41\x02\x41\x03\x41\x00\x11\x01\x00\x1b\x0b\x8f\x80\x80\x80\x00\x00\x41\x00\x11\x01\x00\x04\x7f\x41\x01\x05\x41\x02\x0b\x0b\x8e\x80\x80\x80\x00\x00\x02\x7e\x41\x01\x11\x02\x00\x41\x02\x0d\x00\x0b\x0b\x8e\x80\x80\x80\x00\x00\x02\x7f\x41\x02\x41\x00\x11\x01\x00\x0d\x00\x0b\x0b\x90\x80\x80\x80\x00\x00\x02\x7d\x41\x02\x11\x03\x00\x41\x02\x0e\x01\x00\x00\x0b\x0b\x90\x80\x80\x80\x00\x00\x02\x7f\x41\x02\x41\x00\x11\x01\x00\x0e\x01\x00\x00\x0b\x0b\x8c\x80\x80\x80\x00\x00\x41\x00\x11\x01\x00\x41\x01\x36\x02\x00\x0b\x8c\x80\x80\x80\x00\x00\x41\x0a\x41\x03\x11\x04\x00\x39\x03\x00\x0b\x89\x80\x80\x80\x00\x00\x41\x00\x11\x01\x00\x40\x00\x0b\x8a\x80\x80\x80\x00\x00\x41\x01\x41\x04\x11\x05\x00\x0f\x0b\x8a\x80\x80\x80\x00\x00\x42\x01\x41\x05\x11\x06\x00\x1a\x0b\x91\x80\x80\x80\x00\x00\x02\x7d\x43\x00\x00\x80\x3f\x41\x06\x11\x07\x00\x0c\x00\x0b\x0b\x96\x80\x80\x80\x00\x01\x01\x7c\x44\x00\x00\x00\x00\x00\x00\xf0\x3f\x41\x07\x11\x08\x00\x21\x00\x20\x00\x0b\x94\x80\x80\x80\x00\x01\x01\x7c\x44\x00\x00\x00\x00\x00\x00\xf0\x3f\x41\x07\x11\x08\x00\x22\x00\x0b\x94\x80\x80\x80\x00\x00\x44\x00\x00\x00\x00\x00\x00\xf0\x3f\x41\x07\x11\x08\x00\x24\x00\x23\x00\x0b\x8a\x80\x80\x80\x00\x00\x41\x00\x11\x01\x00\x28\x02\x00\x0b\x90\x80\x80\x80\x00\x00\x02\x7d\x43\x00\x00\x00\x00\x41\x06\x11\x07\x00\x91\x0b\x0b\x8f\x80\x80\x80\x00\x00\x02\x7f\x41\x01\x41\x04\x11\x05\x00\x41\x0a\x6a\x0b\x0b\x8f\x80\x80\x80\x00\x00\x02\x7f\x41\x0a\x41\x01\x41\x04\x11\x05\x00\x6b\x0b\x0b\x8d\x80\x80\x80\x00\x00\x02\x7f\x41\x01\x41\x04\x11\x05\x00\x45\x0b\x0b\x8f\x80\x80\x80\x00\x00\x02\x7f\x41\x01\x41\x04\x11\x05\x00\x41\x0a\x4d\x0b\x0b\x8f\x80\x80\x80\x00\x00\x02\x7f\x41\x0a\x41\x01\x41\x04\x11\x05\x00\x47\x0b\x0b\x8d\x80\x80\x80\x00\x00\x02\x7e\x41\x01\x41\x04\x11\x05\x00\xac\x0b\x0b");
 
 // call_indirect.wast:447
 assert_return(() => call($1, "type-i32", []), 306);
 
 // call_indirect.wast:448
--- a/js/src/jit-test/tests/wasm/spec/float_memory.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/float_memory.wast.js
@@ -1,10 +1,8 @@
-// |jit-test| skip-if: getBuildConfiguration()['arm']
-// skip arm7 due to bug 1513231
 
 // float_memory.wast:5
 let $1 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x8c\x80\x80\x80\x00\x03\x60\x00\x01\x7d\x60\x00\x01\x7f\x60\x00\x00\x03\x86\x80\x80\x80\x00\x05\x00\x01\x02\x02\x02\x05\x84\x80\x80\x80\x00\x01\x01\x01\x01\x07\xb7\x80\x80\x80\x00\x05\x08\x66\x33\x32\x2e\x6c\x6f\x61\x64\x00\x00\x08\x69\x33\x32\x2e\x6c\x6f\x61\x64\x00\x01\x09\x66\x33\x32\x2e\x73\x74\x6f\x72\x65\x00\x02\x09\x69\x33\x32\x2e\x73\x74\x6f\x72\x65\x00\x03\x05\x72\x65\x73\x65\x74\x00\x04\x0a\xca\x80\x80\x80\x00\x05\x87\x80\x80\x80\x00\x00\x41\x00\x2a\x02\x00\x0b\x87\x80\x80\x80\x00\x00\x41\x00\x28\x02\x00\x0b\x8c\x80\x80\x80\x00\x00\x41\x00\x43\x00\x00\xa0\x7f\x38\x02\x00\x0b\x8d\x80\x80\x80\x00\x00\x41\x00\x41\x80\x80\x80\xfd\x07\x36\x02\x00\x0b\x89\x80\x80\x80\x00\x00\x41\x00\x41\x00\x36\x02\x00\x0b\x0b\x8a\x80\x80\x80\x00\x01\x00\x41\x00\x0b\x04\x00\x00\xa0\x7f");
 
 // float_memory.wast:15
 assert_return(() => call($1, "i32.load", []), 2141192192);
 
 // float_memory.wast:16
--- a/js/src/jit-test/tests/wasm/spec/memory_redundancy.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/memory_redundancy.wast.js
@@ -1,10 +1,8 @@
-// |jit-test| skip-if: getBuildConfiguration()['arm']
-// skip arm7 due to bug 1513231
 
 // memory_redundancy.wast:5
 let $1 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x91\x80\x80\x80\x00\x04\x60\x00\x00\x60\x00\x01\x7f\x60\x00\x01\x7d\x60\x01\x7f\x01\x7f\x03\x87\x80\x80\x80\x00\x06\x00\x01\x01\x02\x03\x01\x05\x84\x80\x80\x80\x00\x01\x01\x01\x01\x07\xeb\x80\x80\x80\x00\x06\x0f\x7a\x65\x72\x6f\x5f\x65\x76\x65\x72\x79\x74\x68\x69\x6e\x67\x00\x00\x12\x74\x65\x73\x74\x5f\x73\x74\x6f\x72\x65\x5f\x74\x6f\x5f\x6c\x6f\x61\x64\x00\x01\x13\x74\x65\x73\x74\x5f\x72\x65\x64\x75\x6e\x64\x61\x6e\x74\x5f\x6c\x6f\x61\x64\x00\x02\x0f\x74\x65\x73\x74\x5f\x64\x65\x61\x64\x5f\x73\x74\x6f\x72\x65\x00\x03\x06\x6d\x61\x6c\x6c\x6f\x63\x00\x04\x0f\x6d\x61\x6c\x6c\x6f\x63\x5f\x61\x6c\x69\x61\x73\x69\x6e\x67\x00\x05\x0a\xbd\x81\x80\x80\x00\x06\x9e\x80\x80\x80\x00\x00\x41\x00\x41\x00\x36\x02\x00\x41\x04\x41\x00\x36\x02\x00\x41\x08\x41\x00\x36\x02\x00\x41\x0c\x41\x00\x36\x02\x00\x0b\x98\x80\x80\x80\x00\x00\x41\x08\x41\x00\x36\x02\x00\x41\x05\x43\x00\x00\x00\x80\x38\x02\x00\x41\x08\x28\x02\x00\x0b\xa2\x80\x80\x80\x00\x01\x02\x7f\x41\x08\x28\x02\x00\x21\x00\x41\x05\x41\x80\x80\x80\x80\x78\x36\x02\x00\x41\x08\x28\x02\x00\x21\x01\x20\x00\x20\x01\x6a\x0b\x9f\x80\x80\x80\x00\x01\x01\x7d\x41\x08\x41\xa3\xc6\x8c\x99\x02\x36\x02\x00\x41\x0b\x2a\x02\x00\x21\x00\x41\x08\x41\x00\x36\x02\x00\x20\x00\x0b\x84\x80\x80\x80\x00\x00\x41\x10\x0b\xa3\x80\x80\x80\x00\x01\x02\x7f\x41\x04\x10\x04\x21\x00\x41\x04\x10\x04\x21\x01\x20\x00\x41\x2a\x36\x02\x00\x20\x01\x41\x2b\x36\x02\x00\x20\x00\x28\x02\x00\x0b");
 
 // memory_redundancy.wast:59
 assert_return(() => call($1, "test_store_to_load", []), 128);
 
 // memory_redundancy.wast:60
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -5902,16 +5902,18 @@ void MacroAssemblerARM::wasmLoadImpl(con
     }
   } else {
     bool isFloat = output.isFloat();
     if (isFloat) {
       MOZ_ASSERT((byteSize == 4) == output.fpu().isSingle());
       ScratchRegisterScope scratch(asMasm());
       ma_add(memoryBase, ptr, scratch);
 
+      // See HandleUnalignedTrap() in WasmSignalHandler.cpp.  We depend on this
+      // being a single, unconditional VLDR with a base pointer other than PC.
       load = ma_vldr(Operand(Address(scratch, 0)).toVFPAddr(), output.fpu());
       append(access, load.getOffset());
     } else {
       load = ma_dataTransferN(IsLoad, byteSize * 8, isSigned, memoryBase, ptr,
                               output.gpr());
       append(access, load.getOffset());
     }
   }
@@ -5956,16 +5958,18 @@ void MacroAssemblerARM::wasmStoreImpl(co
     append(access, store.getOffset());
   } else {
     if (value.isFloat()) {
       ScratchRegisterScope scratch(asMasm());
       FloatRegister val = value.fpu();
       MOZ_ASSERT((byteSize == 4) == val.isSingle());
       ma_add(memoryBase, ptr, scratch);
 
+      // See HandleUnalignedTrap() in WasmSignalHandler.cpp.  We depend on this
+      // being a single, unconditional VLDR with a base pointer other than PC.
       store = ma_vstr(val, Operand(Address(scratch, 0)).toVFPAddr());
       append(access, store.getOffset());
     } else {
       bool isSigned = type == Scalar::Uint32 ||
                       type == Scalar::Int32;  // see AsmJSStoreHeap;
       Register val = value.gpr();
 
       store = ma_dataTransferN(IsStore, 8 * byteSize /* bits */, isSigned,
--- a/js/src/wasm/WasmInstance.cpp
+++ b/js/src/wasm/WasmInstance.cpp
@@ -1138,16 +1138,46 @@ bool Instance::memoryAccessInGuardRegion
     return false;
   }
 
   size_t lastByteOffset = addr - base + (numBytes - 1);
   return lastByteOffset >= memory()->volatileMemoryLength() &&
          lastByteOffset < memoryMappedSize();
 }
 
+bool Instance::memoryAccessInBounds(uint8_t* addr,
+                                    unsigned numBytes) const {
+  MOZ_ASSERT(numBytes > 0 && numBytes <= sizeof(double));
+
+  if (!metadata().usesMemory()) {
+    return false;
+  }
+
+  uint8_t* base = memoryBase().unwrap(/* comparison */);
+  if (addr < base) {
+    return false;
+  }
+
+  uint32_t length = memory()->volatileMemoryLength();
+  if (addr >= base + length) {
+    return false;
+  }
+
+  // The pointer points into the memory.  Now check for partial OOB.
+  //
+  // This calculation can't wrap around because the access is small and there
+  // always is a guard page following the memory.
+  size_t lastByteOffset = addr - base + (numBytes - 1);
+  if (lastByteOffset >= length) {
+    return false;
+  }
+
+  return true;
+}
+
 void Instance::tracePrivate(JSTracer* trc) {
   // This method is only called from WasmInstanceObject so the only reason why
   // TraceEdge is called is so that the pointer can be updated during a moving
   // GC. TraceWeakEdge may sound better, but it is less efficient given that
   // we know object_ is already marked.
   MOZ_ASSERT(!gc::IsAboutToBeFinalized(&object_));
   TraceEdge(trc, &object_, "wasm instance object");
 
--- a/js/src/wasm/WasmInstance.h
+++ b/js/src/wasm/WasmInstance.h
@@ -108,16 +108,17 @@ class Instance {
   const Metadata& metadata() const { return code_->metadata(); }
   bool isAsmJS() const { return metadata().isAsmJS(); }
   const SharedTableVector& tables() const { return tables_; }
   SharedMem<uint8_t*> memoryBase() const;
   WasmMemoryObject* memory() const;
   size_t memoryMappedSize() const;
   SharedArrayRawBuffer* sharedMemoryBuffer() const;  // never null
   bool memoryAccessInGuardRegion(uint8_t* addr, unsigned numBytes) const;
+  bool memoryAccessInBounds(uint8_t* addr, unsigned numBytes) const;
   const StructTypeVector& structTypes() const { return code_->structTypes(); }
 
   static constexpr size_t offsetOfJSJitArgsRectifier() {
     return offsetof(Instance, jsJitArgsRectifier_);
   }
   static constexpr size_t offsetOfJSJitExceptionHandler() {
     return offsetof(Instance, jsJitExceptionHandler_);
   }
--- a/js/src/wasm/WasmSignalHandlers.cpp
+++ b/js/src/wasm/WasmSignalHandlers.cpp
@@ -208,16 +208,20 @@ using mozilla::DebugOnly;
 #define R11_sig(p) ((p)->thread.__r[11])
 #define R13_sig(p) ((p)->thread.__sp)
 #define R14_sig(p) ((p)->thread.__lr)
 #define R15_sig(p) ((p)->thread.__pc)
 #else
 #error "Don't know how to read/write to the thread state via the mcontext_t."
 #endif
 
+#if defined(__linux__) && defined(__arm__)
+#include <sys/user.h>
+#endif
+
 #if defined(ANDROID)
 // Not all versions of the Android NDK define ucontext_t or mcontext_t.
 // Detect this and provide custom but compatible definitions. Note that these
 // follow the GLibc naming convention to access register values from
 // mcontext_t.
 //
 // See: https://chromiumcodereview.appspot.com/10829122/
 // See: http://code.google.com/p/android/issues/detail?id=34784
@@ -432,17 +436,207 @@ struct AutoHandlingTrap {
   }
 
   ~AutoHandlingTrap() {
     MOZ_ASSERT(sAlreadyHandlingTrap.get());
     sAlreadyHandlingTrap.set(false);
   }
 };
 
+#if defined(__linux__) && defined(__arm__)
+
+// Code to handle SIGBUS for unaligned floating point accesses on 32-bit ARM.
+
+static uintptr_t ReadGPR(CONTEXT* context, uint32_t rn) {
+  switch (rn) {
+    case 0: return context->uc_mcontext.arm_r0;
+    case 1: return context->uc_mcontext.arm_r1;
+    case 2: return context->uc_mcontext.arm_r2;
+    case 3: return context->uc_mcontext.arm_r3;
+    case 4: return context->uc_mcontext.arm_r4;
+    case 5: return context->uc_mcontext.arm_r5;
+    case 6: return context->uc_mcontext.arm_r6;
+    case 7: return context->uc_mcontext.arm_r7;
+    case 8: return context->uc_mcontext.arm_r8;
+    case 9: return context->uc_mcontext.arm_r9;
+    case 10: return context->uc_mcontext.arm_r10;
+    case 11: return context->uc_mcontext.arm_fp;
+    case 12: return context->uc_mcontext.arm_ip;
+    case 13: return context->uc_mcontext.arm_sp;
+    case 14: return context->uc_mcontext.arm_lr;
+    case 15: return context->uc_mcontext.arm_pc;
+    default: MOZ_CRASH();
+  }
+}
+
+// Linux kernel data structures.
+//
+// The vfp_sigframe is a kernel type overlaid on the uc_regspace field of the
+// ucontext_t if the first word of the uc_regspace is VFP_MAGIC.  (user_vfp and
+// user_vfp_exc are defined in sys/user.h and are stable.)
+//
+// VFP_MAGIC appears to have been stable since a commit to Linux on 2010-04-11,
+// when it was changed from being 0x56465001 on ARMv6 and earlier and 0x56465002
+// on ARMv7 and later, to being 0x56465001 on all CPU versions.  This was in
+// Kernel 2.6.34-rc5.
+//
+// My best interpretation of the Android commit history is that Android has had
+// vfp_sigframe and VFP_MAGIC in this form since at least Android 3.4 / 2012;
+// Firefox requires Android 4.0 at least and we're probably safe here.
+
+struct vfp_sigframe
+{
+  unsigned long       magic;
+  unsigned long       size;
+  struct user_vfp     ufp;
+  struct user_vfp_exc ufp_exc;
+};
+
+#define VFP_MAGIC 0x56465001
+
+static vfp_sigframe* GetVFPFrame(CONTEXT* context) {
+  if (context->uc_regspace[0] != VFP_MAGIC) {
+    return nullptr;
+  }
+  return (vfp_sigframe*)&context->uc_regspace;
+}
+
+static bool ReadFPR64(CONTEXT* context, uint32_t vd, double* val) {
+  MOZ_ASSERT(vd < 32);
+  vfp_sigframe* frame = GetVFPFrame(context);
+  if (frame) {
+    *val = ((double*)frame->ufp.fpregs)[vd];
+    return true;
+  }
+  return false;
+}
+
+static bool WriteFPR64(CONTEXT* context, uint32_t vd, double val) {
+  MOZ_ASSERT(vd < 32);
+  vfp_sigframe* frame = GetVFPFrame(context);
+  if (frame) {
+    ((double*)frame->ufp.fpregs)[vd] = val;
+    return true;
+  }
+  return false;
+}
+
+static bool ReadFPR32(CONTEXT* context, uint32_t vd, float* val) {
+  MOZ_ASSERT(vd < 32);
+  vfp_sigframe* frame = GetVFPFrame(context);
+  if (frame) {
+    *val = ((float*)frame->ufp.fpregs)[vd];
+    return true;
+  }
+  return false;
+}
+
+static bool WriteFPR32(CONTEXT* context, uint32_t vd, float val) {
+  MOZ_ASSERT(vd < 32);
+  vfp_sigframe* frame = GetVFPFrame(context);
+  if (frame) {
+    ((float*)frame->ufp.fpregs)[vd] = val;
+    return true;
+  }
+  return false;
+}
+
+static bool HandleUnalignedTrap(CONTEXT* context, uint8_t* pc,
+                                Instance* instance) {
+  // ARM only, no Thumb.
+  MOZ_RELEASE_ASSERT(uintptr_t(pc) % 4 == 0);
+
+  // wasmLoadImpl() and wasmStoreImpl() in MacroAssembler-arm.cpp emit plain,
+  // unconditional VLDR and VSTR instructions that do not use the PC as the base
+  // register.
+  uint32_t instr = *(uint32_t*)pc;
+  uint32_t masked = instr & 0x0F300E00;
+  bool isVLDR = masked == 0x0D100A00;
+  bool isVSTR = masked == 0x0D000A00;
+
+  if (!isVLDR && !isVSTR) {
+    // Three obvious cases if we don't get our expected instructions:
+    // - masm is generating other FP access instructions than it should
+    // - we're encountering a device that traps on new kinds of accesses,
+    //   perhaps unaligned integer accesses
+    // - general code generation bugs that lead to SIGBUS
+#  ifdef ANDROID
+    __android_log_print(ANDROID_LOG_ERROR, "WASM", "Bad SIGBUS instr %08x", instr);
+#  endif
+#  ifdef DEBUG
+    MOZ_CRASH("Unexpected instruction");
+#  endif
+    return false;
+  }
+
+  bool isUnconditional = (instr >> 28) == 0xE;
+  bool isDouble = (instr & 0x00000100) != 0;
+  bool isAdd = (instr & 0x00800000) != 0;
+  uint32_t dBit = (instr >> 22) & 1;
+  uint32_t offs = (instr & 0xFF) << 2;
+  uint32_t rn = (instr >> 16) & 0xF;
+
+  MOZ_RELEASE_ASSERT(isUnconditional);
+  MOZ_RELEASE_ASSERT(rn != 15);
+
+  uint8_t* p = (uint8_t*)ReadGPR(context, rn) + (isAdd ? offs : -offs);
+
+  if (!instance->memoryAccessInBounds(p, isDouble ? sizeof(double)
+                                                  : sizeof(float))) {
+    return false;
+  }
+
+  if (isDouble) {
+    uint32_t vd = ((instr >> 12) & 0xF) | (dBit << 4);
+    double val;
+    if (isVLDR) {
+      memcpy(&val, p, sizeof(val));
+      if (WriteFPR64(context, vd, val)) {
+        SetContextPC(context, pc + 4);
+        return true;
+      }
+    } else {
+      if (ReadFPR64(context, vd, &val)) {
+        memcpy(p, &val, sizeof(val));
+        SetContextPC(context, pc + 4);
+        return true;
+      }
+    }
+  } else {
+    uint32_t vd = ((instr >> 11) & (0xF << 1)) | dBit;
+    float val;
+    if (isVLDR) {
+      memcpy(&val, p, sizeof(val));
+      if (WriteFPR32(context, vd, val)) {
+        SetContextPC(context, pc + 4);
+        return true;
+      }
+    } else {
+      if (ReadFPR32(context, vd, &val)) {
+        memcpy(p, &val, sizeof(val));
+        SetContextPC(context, pc + 4);
+        return true;
+      }
+    }
+  }
+
+#ifdef DEBUG
+  MOZ_CRASH("SIGBUS handler could not access FP register, incompatible kernel?");
+#endif
+  return false;
+}
+#else // __linux__ && __arm__
+static bool HandleUnalignedTrap(CONTEXT* context, uint8_t* pc,
+                                Instance* instance) {
+  return false;
+}
+#endif // __linux__ && __arm__
+
 static MOZ_MUST_USE bool HandleTrap(CONTEXT* context,
+                                    bool isUnalignedSignal = false,
                                     JSContext* assertCx = nullptr) {
   MOZ_ASSERT(sAlreadyHandlingTrap.get());
 
   uint8_t* pc = ContextToPC(context);
   const CodeSegment* codeSegment = LookupCodeSegment(pc);
   if (!codeSegment || !codeSegment->isModule()) {
     return false;
   }
@@ -458,16 +652,26 @@ static MOZ_MUST_USE bool HandleTrap(CONT
   // We have a safe, expected wasm trap, so fp is well-defined to be a Frame*.
   // For the first sanity check, the Trap::IndirectCallBadSig special case is
   // due to this trap occurring in the indirect call prologue, while fp points
   // to the caller's Frame which can be in a different Module. In any case,
   // though, the containing JSContext is the same.
   Instance* instance = ((Frame*)ContextToFP(context))->tls->instance;
   MOZ_RELEASE_ASSERT(&instance->code() == &segment.code() ||
                      trap == Trap::IndirectCallBadSig);
+
+  if (isUnalignedSignal) {
+    if (trap != Trap::OutOfBounds) {
+      return false;
+    }
+    if (HandleUnalignedTrap(context, pc, instance)) {
+      return true;
+    }
+  }
+
   JSContext* cx =
       instance->realm()->runtimeFromAnyThread()->mainContextFromAnyThread();
   MOZ_RELEASE_ASSERT(!assertCx || cx == assertCx);
 
   // JitActivation::startWasmTrap() stores enough register state from the
   // point of the trap to allow stack unwinding or resumption, both of which
   // will call finishWasmTrap().
   jit::JitActivation* activation = cx->activation()->asJit();
@@ -498,17 +702,17 @@ static LONG WINAPI WasmTrapHandler(LPEXC
   AutoHandlingTrap aht;
 
   EXCEPTION_RECORD* record = exception->ExceptionRecord;
   if (record->ExceptionCode != EXCEPTION_ACCESS_VIOLATION &&
       record->ExceptionCode != EXCEPTION_ILLEGAL_INSTRUCTION) {
     return EXCEPTION_CONTINUE_SEARCH;
   }
 
-  if (!HandleTrap(exception->ContextRecord, TlsContext.get())) {
+  if (!HandleTrap(exception->ContextRecord, false, TlsContext.get())) {
     return EXCEPTION_CONTINUE_SEARCH;
   }
 
   return EXCEPTION_CONTINUE_EXECUTION;
 }
 
 #elif defined(XP_DARWIN)
 // On OSX we are forced to use the lower-level Mach exception mechanism instead
@@ -666,17 +870,17 @@ static struct sigaction sPrevSEGVHandler
 static struct sigaction sPrevSIGBUSHandler;
 static struct sigaction sPrevWasmTrapHandler;
 
 static void WasmTrapHandler(int signum, siginfo_t* info, void* context) {
   if (!sAlreadyHandlingTrap.get()) {
     AutoHandlingTrap aht;
     MOZ_RELEASE_ASSERT(signum == SIGSEGV || signum == SIGBUS ||
                        signum == kWasmTrapSignal);
-    if (HandleTrap((CONTEXT*)context, TlsContext.get())) {
+    if (HandleTrap((CONTEXT*)context, signum == SIGBUS, TlsContext.get())) {
       return;
     }
   }
 
   struct sigaction* previousSignal = nullptr;
   switch (signum) {
     case SIGSEGV:
       previousSignal = &sPrevSEGVHandler;