Commit b6cf8ee2 authored by mbaxter's avatar mbaxter Committed by GitHub

cannon: Use 32-bit futex values (#13453)

* cannon: Update futex-related tests - use 32-bit futex values

* cannon: Update go vm implementation - use 32-bit futex values

* cannon: Update sol 64-bit vm implementation - use 32-bit futex values

* cannon: Update sol 32-bit vm implementation - use 32-bit futex values

* cannon: Update MIPS64 version

* cannon: Update MIPS2 version

* cannon: Run semver lock

* cannon: Update semver comments to match actual version

* cannon: Run semver-lock

* cannon: Randomize futex value in register when testing

* cannon: Tweak comment

* cannon: Ignore the upper bytes in the futex value register

* cannon: Make FutexVal thread field 32-bit

* cannon: Run semver-lock

* cannon: Fix some inconsistencies, run semver-lock

* cannon: Rename testutil method
parent bf7e95ce
...@@ -109,17 +109,18 @@ func (m *InstrumentedState) handleSyscall() error { ...@@ -109,17 +109,18 @@ func (m *InstrumentedState) handleSyscall() error {
return nil return nil
case arch.SysFutex: case arch.SysFutex:
// args: a0 = addr, a1 = op, a2 = val, a3 = timeout // args: a0 = addr, a1 = op, a2 = val, a3 = timeout
effAddr := a0 & arch.AddressMask // Futex value is 32-bit, so clear the lower 2 bits to get an effective address targeting a 4-byte value
effFutexAddr := a0 & ^Word(0x3)
switch a1 { switch a1 {
case exec.FutexWaitPrivate: case exec.FutexWaitPrivate:
m.memoryTracker.TrackMemAccess(effAddr) futexVal := m.getFutexValue(effFutexAddr)
mem := m.state.Memory.GetWord(effAddr) targetVal := uint32(a2)
if mem != a2 { if futexVal != targetVal {
v0 = exec.SysErrorSignal v0 = exec.SysErrorSignal
v1 = exec.MipsEAGAIN v1 = exec.MipsEAGAIN
} else { } else {
thread.FutexAddr = effAddr thread.FutexAddr = effFutexAddr
thread.FutexVal = a2 thread.FutexVal = targetVal
if a3 == 0 { if a3 == 0 {
thread.FutexTimeoutStep = exec.FutexNoTimeout thread.FutexTimeoutStep = exec.FutexNoTimeout
} else { } else {
...@@ -130,7 +131,7 @@ func (m *InstrumentedState) handleSyscall() error { ...@@ -130,7 +131,7 @@ func (m *InstrumentedState) handleSyscall() error {
} }
case exec.FutexWakePrivate: case exec.FutexWakePrivate:
// Trigger a wakeup traversal // Trigger a wakeup traversal
m.state.Wakeup = effAddr m.state.Wakeup = effFutexAddr
// Don't indicate to the program that we've woken up a waiting thread, as there are no guarantees. // Don't indicate to the program that we've woken up a waiting thread, as there are no guarantees.
// The woken up thread should indicate this in userspace. // The woken up thread should indicate this in userspace.
v0 = 0 v0 = 0
...@@ -283,10 +284,8 @@ func (m *InstrumentedState) doMipsStep() error { ...@@ -283,10 +284,8 @@ func (m *InstrumentedState) doMipsStep() error {
m.onWaitComplete(thread, true) m.onWaitComplete(thread, true)
return nil return nil
} else { } else {
effAddr := thread.FutexAddr & arch.AddressMask futexVal := m.getFutexValue(thread.FutexAddr)
m.memoryTracker.TrackMemAccess(effAddr) if thread.FutexVal == futexVal {
mem := m.state.Memory.GetWord(effAddr)
if thread.FutexVal == mem {
// still got expected value, continue sleeping, try next thread. // still got expected value, continue sleeping, try next thread.
m.preemptThread(thread) m.preemptThread(thread)
m.statsTracker.trackWakeupFail() m.statsTracker.trackWakeupFail()
...@@ -488,3 +487,8 @@ func (m *InstrumentedState) popThread() { ...@@ -488,3 +487,8 @@ func (m *InstrumentedState) popThread() {
func (m *InstrumentedState) lastThreadRemaining() bool { func (m *InstrumentedState) lastThreadRemaining() bool {
return m.state.ThreadCount() == 1 return m.state.ThreadCount() == 1
} }
func (m *InstrumentedState) getFutexValue(vAddr Word) uint32 {
subword := exec.LoadSubWord(m.state.GetMemory(), vAddr, Word(4), false, m.memoryTracker)
return uint32(subword)
}
...@@ -399,7 +399,7 @@ func TestStateWitnessSize(t *testing.T) { ...@@ -399,7 +399,7 @@ func TestStateWitnessSize(t *testing.T) {
func TestThreadStateWitnessSize(t *testing.T) { func TestThreadStateWitnessSize(t *testing.T) {
expectedWitnessSize := 166 expectedWitnessSize := 166
if !arch.IsMips32 { if !arch.IsMips32 {
expectedWitnessSize = 322 expectedWitnessSize = 318
} }
require.Equal(t, expectedWitnessSize, SERIALIZED_THREAD_SIZE) require.Equal(t, expectedWitnessSize, SERIALIZED_THREAD_SIZE)
} }
...@@ -48,7 +48,7 @@ type ExpectedThreadState struct { ...@@ -48,7 +48,7 @@ type ExpectedThreadState struct {
ExitCode uint8 ExitCode uint8
Exited bool Exited bool
FutexAddr arch.Word FutexAddr arch.Word
FutexVal arch.Word FutexVal uint32
FutexTimeoutStep uint64 FutexTimeoutStep uint64
PC arch.Word PC arch.Word
NextPC arch.Word NextPC arch.Word
......
...@@ -18,12 +18,12 @@ const ( ...@@ -18,12 +18,12 @@ const (
THREAD_EXITED_WITNESS_OFFSET = THREAD_EXIT_CODE_WITNESS_OFFSET + 1 THREAD_EXITED_WITNESS_OFFSET = THREAD_EXIT_CODE_WITNESS_OFFSET + 1
THREAD_FUTEX_ADDR_WITNESS_OFFSET = THREAD_EXITED_WITNESS_OFFSET + 1 THREAD_FUTEX_ADDR_WITNESS_OFFSET = THREAD_EXITED_WITNESS_OFFSET + 1
THREAD_FUTEX_VAL_WITNESS_OFFSET = THREAD_FUTEX_ADDR_WITNESS_OFFSET + arch.WordSizeBytes THREAD_FUTEX_VAL_WITNESS_OFFSET = THREAD_FUTEX_ADDR_WITNESS_OFFSET + arch.WordSizeBytes
THREAD_FUTEX_TIMEOUT_STEP_WITNESS_OFFSET = THREAD_FUTEX_VAL_WITNESS_OFFSET + arch.WordSizeBytes THREAD_FUTEX_TIMEOUT_STEP_WITNESS_OFFSET = THREAD_FUTEX_VAL_WITNESS_OFFSET + 4
THREAD_FUTEX_CPU_WITNESS_OFFSET = THREAD_FUTEX_TIMEOUT_STEP_WITNESS_OFFSET + 8 THREAD_FUTEX_CPU_WITNESS_OFFSET = THREAD_FUTEX_TIMEOUT_STEP_WITNESS_OFFSET + 8
THREAD_REGISTERS_WITNESS_OFFSET = THREAD_FUTEX_CPU_WITNESS_OFFSET + (4 * arch.WordSizeBytes) THREAD_REGISTERS_WITNESS_OFFSET = THREAD_FUTEX_CPU_WITNESS_OFFSET + (4 * arch.WordSizeBytes)
// SERIALIZED_THREAD_SIZE is the size of a serialized ThreadState object // SERIALIZED_THREAD_SIZE is the size of a serialized ThreadState object
// 166 and 322 bytes for 32 and 64-bit respectively // 166 and 318 bytes for 32 and 64-bit respectively
SERIALIZED_THREAD_SIZE = THREAD_REGISTERS_WITNESS_OFFSET + (32 * arch.WordSizeBytes) SERIALIZED_THREAD_SIZE = THREAD_REGISTERS_WITNESS_OFFSET + (32 * arch.WordSizeBytes)
// THREAD_WITNESS_SIZE is the size of a thread witness encoded in bytes. // THREAD_WITNESS_SIZE is the size of a thread witness encoded in bytes.
...@@ -41,7 +41,7 @@ type ThreadState struct { ...@@ -41,7 +41,7 @@ type ThreadState struct {
ExitCode uint8 `json:"exit"` ExitCode uint8 `json:"exit"`
Exited bool `json:"exited"` Exited bool `json:"exited"`
FutexAddr Word `json:"futexAddr"` FutexAddr Word `json:"futexAddr"`
FutexVal Word `json:"futexVal"` FutexVal uint32 `json:"futexVal"`
FutexTimeoutStep uint64 `json:"futexTimeoutStep"` FutexTimeoutStep uint64 `json:"futexTimeoutStep"`
Cpu mipsevm.CpuScalars `json:"cpu"` Cpu mipsevm.CpuScalars `json:"cpu"`
Registers [32]Word `json:"registers"` Registers [32]Word `json:"registers"`
...@@ -73,7 +73,7 @@ func (t *ThreadState) serializeThread() []byte { ...@@ -73,7 +73,7 @@ func (t *ThreadState) serializeThread() []byte {
out = append(out, t.ExitCode) out = append(out, t.ExitCode)
out = mipsevm.AppendBoolToWitness(out, t.Exited) out = mipsevm.AppendBoolToWitness(out, t.Exited)
out = arch.ByteOrderWord.AppendWord(out, t.FutexAddr) out = arch.ByteOrderWord.AppendWord(out, t.FutexAddr)
out = arch.ByteOrderWord.AppendWord(out, t.FutexVal) out = binary.BigEndian.AppendUint32(out, t.FutexVal)
out = binary.BigEndian.AppendUint64(out, t.FutexTimeoutStep) out = binary.BigEndian.AppendUint64(out, t.FutexTimeoutStep)
out = arch.ByteOrderWord.AppendWord(out, t.Cpu.PC) out = arch.ByteOrderWord.AppendWord(out, t.Cpu.PC)
......
...@@ -4,11 +4,13 @@ package testutil ...@@ -4,11 +4,13 @@ package testutil
import ( import (
"bytes" "bytes"
"fmt"
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch" "github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
) )
...@@ -42,6 +44,20 @@ func SetMemoryUint64(t require.TestingT, mem *memory.Memory, addr Word, value ui ...@@ -42,6 +44,20 @@ func SetMemoryUint64(t require.TestingT, mem *memory.Memory, addr Word, value ui
require.Equal(t, Word(value), actual) require.Equal(t, Word(value), actual)
} }
// RandomizeWordAndSetUint32 writes a uint32 value and randomizes the rest of the Word containing the uint32 in memory
func RandomizeWordAndSetUint32(mem *memory.Memory, addr Word, val uint32, randomizeWordSeed int64) {
if addr&0x3 != 0 {
panic(fmt.Errorf("unaligned memory access: %x", addr))
}
// Randomize the Word containing the target uint32 - only makes a difference for 64-bit architectures
rand := NewRandHelper(randomizeWordSeed)
wordAddr := addr & arch.AddressMask
mem.SetWord(wordAddr, rand.Word())
exec.StoreSubWord(mem, addr, 4, Word(val), new(exec.NoopMemoryTracker))
}
// ToSignedInteger converts the unsigend Word to a SignedInteger. // ToSignedInteger converts the unsigend Word to a SignedInteger.
// Useful for avoiding Go compiler warnings for literals that don't fit in a signed type // Useful for avoiding Go compiler warnings for literals that don't fit in a signed type
func ToSignedInteger(x Word) arch.SignedInteger { func ToSignedInteger(x Word) arch.SignedInteger {
......
...@@ -140,12 +140,12 @@ ...@@ -140,12 +140,12 @@
"sourceCodeHash": "0x6c45dd23cb0d6f9bf4f84855ad0caf70e53dee3fe6c41454f7bf8df52ec3a9af" "sourceCodeHash": "0x6c45dd23cb0d6f9bf4f84855ad0caf70e53dee3fe6c41454f7bf8df52ec3a9af"
}, },
"src/cannon/MIPS2.sol": { "src/cannon/MIPS2.sol": {
"initCodeHash": "0x4971f62a6aecf91bd795fa44b5ce3cb77a987719af4f351d4aec5b6c3bf81387", "initCodeHash": "0x70ecbb2327fadd6205312aa93279a6340242dfeb44a00b98caa7b56688c46bdc",
"sourceCodeHash": "0x8da8be0b7d60af0eb11bd58653f1854d56a8f0616f3aeaeba7ab9ec340d02ac7" "sourceCodeHash": "0x84506539c40b72b1d40c9d07418650b30c27d2b219b40e55f61edcd31365157f"
}, },
"src/cannon/MIPS64.sol": { "src/cannon/MIPS64.sol": {
"initCodeHash": "0xdef0bd64af2541644e1fba56cb443650749dee201a38053c02464584f2e806a9", "initCodeHash": "0xa2a42c50d2fac71d93e44ad4871e5d838f1c630b9d1abc4c89971d36b0ae44bb",
"sourceCodeHash": "0x16b46d17dff5b772675bdd280dddea482e85c707a8779694f10cc78b05de871d" "sourceCodeHash": "0xdb771f1b92c7612b120e0bce31967f0c8a7ce332dbb426bc9cfc52b47be21c4d"
}, },
"src/cannon/PreimageOracle.sol": { "src/cannon/PreimageOracle.sol": {
"initCodeHash": "0xf08736a5af9277a4f3498dfee84a40c9b05f1a2ba3177459bebe2b0b54f99343", "initCodeHash": "0xf08736a5af9277a4f3498dfee84a40c9b05f1a2ba3177459bebe2b0b54f99343",
......
...@@ -63,8 +63,8 @@ contract MIPS2 is ISemver { ...@@ -63,8 +63,8 @@ contract MIPS2 is ISemver {
} }
/// @notice The semantic version of the MIPS2 contract. /// @notice The semantic version of the MIPS2 contract.
/// @custom:semver 1.0.0-beta.26 /// @custom:semver 1.0.0-beta.27
string public constant version = "1.0.0-beta.26"; string public constant version = "1.0.0-beta.27";
/// @notice The preimage oracle contract. /// @notice The preimage oracle contract.
IPreimageOracle internal immutable ORACLE; IPreimageOracle internal immutable ORACLE;
...@@ -245,10 +245,8 @@ contract MIPS2 is ISemver { ...@@ -245,10 +245,8 @@ contract MIPS2 is ISemver {
// timeout! Allow execution // timeout! Allow execution
return onWaitComplete(thread, true); return onWaitComplete(thread, true);
} else { } else {
uint32 mem = MIPSMemory.readMem( uint32 futexVal = getFutexValue(thread.futexAddr);
state.memRoot, thread.futexAddr & 0xFFffFFfc, MIPSMemory.memoryProofOffset(MEM_PROOF_OFFSET, 1) if (thread.futexVal == futexVal) {
);
if (thread.futexVal == mem) {
// still got expected value, continue sleeping, try next thread. // still got expected value, continue sleeping, try next thread.
preemptThread(state, thread); preemptThread(state, thread);
return outputState(); return outputState();
...@@ -488,16 +486,16 @@ contract MIPS2 is ISemver { ...@@ -488,16 +486,16 @@ contract MIPS2 is ISemver {
return outputState(); return outputState();
} else if (syscall_no == sys.SYS_FUTEX) { } else if (syscall_no == sys.SYS_FUTEX) {
// args: a0 = addr, a1 = op, a2 = val, a3 = timeout // args: a0 = addr, a1 = op, a2 = val, a3 = timeout
uint32 effAddr = a0 & 0xFFffFFfc; uint32 effFutexAddr = a0 & 0xFFffFFfc;
if (a1 == sys.FUTEX_WAIT_PRIVATE) { if (a1 == sys.FUTEX_WAIT_PRIVATE) {
uint32 mem = uint32 futexVal = getFutexValue(effFutexAddr);
MIPSMemory.readMem(state.memRoot, effAddr, MIPSMemory.memoryProofOffset(MEM_PROOF_OFFSET, 1)); uint32 targetValue = a2;
if (mem != a2) { if (futexVal != targetValue) {
v0 = sys.SYS_ERROR_SIGNAL; v0 = sys.SYS_ERROR_SIGNAL;
v1 = sys.EAGAIN; v1 = sys.EAGAIN;
} else { } else {
thread.futexAddr = effAddr; thread.futexAddr = effFutexAddr;
thread.futexVal = a2; thread.futexVal = targetValue;
thread.futexTimeoutStep = a3 == 0 ? sys.FUTEX_NO_TIMEOUT : state.step + sys.FUTEX_TIMEOUT_STEPS; thread.futexTimeoutStep = a3 == 0 ? sys.FUTEX_NO_TIMEOUT : state.step + sys.FUTEX_TIMEOUT_STEPS;
// Leave cpu scalars as-is. This instruction will be completed by `onWaitComplete` // Leave cpu scalars as-is. This instruction will be completed by `onWaitComplete`
updateCurrentThreadRoot(); updateCurrentThreadRoot();
...@@ -506,7 +504,7 @@ contract MIPS2 is ISemver { ...@@ -506,7 +504,7 @@ contract MIPS2 is ISemver {
} else if (a1 == sys.FUTEX_WAKE_PRIVATE) { } else if (a1 == sys.FUTEX_WAKE_PRIVATE) {
// Trigger thread traversal starting from the left stack until we find one waiting on the wakeup // Trigger thread traversal starting from the left stack until we find one waiting on the wakeup
// address // address
state.wakeup = effAddr; state.wakeup = effFutexAddr;
// Don't indicate to the program that we've woken up a waiting thread, as there are no guarantees. // Don't indicate to the program that we've woken up a waiting thread, as there are no guarantees.
// The woken up thread should indicate this in userspace. // The woken up thread should indicate this in userspace.
v0 = 0; v0 = 0;
...@@ -942,4 +940,16 @@ contract MIPS2 is ISemver { ...@@ -942,4 +940,16 @@ contract MIPS2 is ISemver {
// verify we have enough calldata // verify we have enough calldata
require(s >= (THREAD_PROOF_OFFSET + 198), "insufficient calldata for thread witness"); // 166 + 32 require(s >= (THREAD_PROOF_OFFSET + 198), "insufficient calldata for thread witness"); // 166 + 32
} }
/// @notice Loads a 32-bit futex value at _vAddr
function getFutexValue(uint32 _vAddr) internal pure returns (uint32 out_) {
State memory state;
assembly {
state := STATE_MEM_OFFSET
}
uint32 effAddr = _vAddr & 0xFFffFFfc;
uint256 memProofOffset = MIPSMemory.memoryProofOffset(MEM_PROOF_OFFSET, 1);
return MIPSMemory.readMem(state.memRoot, effAddr, memProofOffset);
}
} }
...@@ -21,7 +21,7 @@ import { IPreimageOracle } from "interfaces/cannon/IPreimageOracle.sol"; ...@@ -21,7 +21,7 @@ import { IPreimageOracle } from "interfaces/cannon/IPreimageOracle.sol";
/// It differs from MIPS.sol in that it supports MIPS64 instructions and multi-tasking. /// It differs from MIPS.sol in that it supports MIPS64 instructions and multi-tasking.
contract MIPS64 is ISemver { contract MIPS64 is ISemver {
/// @notice The thread context. /// @notice The thread context.
/// Total state size: 8 + 1 + 1 + 8 + 8 + 8 + 8 + 8 + 8 + 8 + 32 * 8 = 322 bytes /// Total state size: 8 + 1 + 1 + 8 + 4 + 8 + 8 + 8 + 8 + 8 + 32 * 8 = 318 bytes
struct ThreadState { struct ThreadState {
// metadata // metadata
uint64 threadID; uint64 threadID;
...@@ -29,7 +29,7 @@ contract MIPS64 is ISemver { ...@@ -29,7 +29,7 @@ contract MIPS64 is ISemver {
bool exited; bool exited;
// state // state
uint64 futexAddr; uint64 futexAddr;
uint64 futexVal; uint32 futexVal;
uint64 futexTimeoutStep; uint64 futexTimeoutStep;
uint64 pc; uint64 pc;
uint64 nextPC; uint64 nextPC;
...@@ -38,7 +38,7 @@ contract MIPS64 is ISemver { ...@@ -38,7 +38,7 @@ contract MIPS64 is ISemver {
uint64[32] registers; uint64[32] registers;
} }
uint32 internal constant PACKED_THREAD_STATE_SIZE = 322; uint32 internal constant PACKED_THREAD_STATE_SIZE = 318;
uint8 internal constant LL_STATUS_NONE = 0; uint8 internal constant LL_STATUS_NONE = 0;
uint8 internal constant LL_STATUS_ACTIVE_32_BIT = 0x1; uint8 internal constant LL_STATUS_ACTIVE_32_BIT = 0x1;
...@@ -67,8 +67,8 @@ contract MIPS64 is ISemver { ...@@ -67,8 +67,8 @@ contract MIPS64 is ISemver {
} }
/// @notice The semantic version of the MIPS64 contract. /// @notice The semantic version of the MIPS64 contract.
/// @custom:semver 1.0.0-beta.8 /// @custom:semver 1.0.0-beta.9
string public constant version = "1.0.0-beta.8"; string public constant version = "1.0.0-beta.9";
/// @notice The preimage oracle contract. /// @notice The preimage oracle contract.
IPreimageOracle internal immutable ORACLE; IPreimageOracle internal immutable ORACLE;
...@@ -253,12 +253,8 @@ contract MIPS64 is ISemver { ...@@ -253,12 +253,8 @@ contract MIPS64 is ISemver {
// timeout! Allow execution // timeout! Allow execution
return onWaitComplete(thread, true); return onWaitComplete(thread, true);
} else { } else {
uint64 mem = MIPS64Memory.readMem( uint32 futexVal = getFutexValue(thread.futexAddr);
state.memRoot, if (thread.futexVal == futexVal) {
thread.futexAddr & arch.ADDRESS_MASK,
MIPS64Memory.memoryProofOffset(MEM_PROOF_OFFSET, 1)
);
if (thread.futexVal == mem) {
// still got expected value, continue sleeping, try next thread. // still got expected value, continue sleeping, try next thread.
preemptThread(state, thread); preemptThread(state, thread);
return outputState(); return outputState();
...@@ -530,17 +526,17 @@ contract MIPS64 is ISemver { ...@@ -530,17 +526,17 @@ contract MIPS64 is ISemver {
return outputState(); return outputState();
} else if (syscall_no == sys.SYS_FUTEX) { } else if (syscall_no == sys.SYS_FUTEX) {
// args: a0 = addr, a1 = op, a2 = val, a3 = timeout // args: a0 = addr, a1 = op, a2 = val, a3 = timeout
uint64 effAddr = a0 & arch.ADDRESS_MASK; // Futex value is 32-bit, so clear the lower 2 bits to get an effective address targeting a 4-byte value
uint64 effFutexAddr = a0 & 0xFFFFFFFFFFFFFFFC;
if (a1 == sys.FUTEX_WAIT_PRIVATE) { if (a1 == sys.FUTEX_WAIT_PRIVATE) {
uint64 mem = MIPS64Memory.readMem( uint32 futexVal = getFutexValue(effFutexAddr);
state.memRoot, effAddr, MIPS64Memory.memoryProofOffset(MEM_PROOF_OFFSET, 1) uint32 targetVal = uint32(a2);
); if (futexVal != targetVal) {
if (mem != a2) {
v0 = sys.SYS_ERROR_SIGNAL; v0 = sys.SYS_ERROR_SIGNAL;
v1 = sys.EAGAIN; v1 = sys.EAGAIN;
} else { } else {
thread.futexAddr = effAddr; thread.futexAddr = effFutexAddr;
thread.futexVal = a2; thread.futexVal = targetVal;
thread.futexTimeoutStep = a3 == 0 ? sys.FUTEX_NO_TIMEOUT : state.step + sys.FUTEX_TIMEOUT_STEPS; thread.futexTimeoutStep = a3 == 0 ? sys.FUTEX_NO_TIMEOUT : state.step + sys.FUTEX_TIMEOUT_STEPS;
// Leave cpu scalars as-is. This instruction will be completed by `onWaitComplete` // Leave cpu scalars as-is. This instruction will be completed by `onWaitComplete`
updateCurrentThreadRoot(); updateCurrentThreadRoot();
...@@ -549,7 +545,7 @@ contract MIPS64 is ISemver { ...@@ -549,7 +545,7 @@ contract MIPS64 is ISemver {
} else if (a1 == sys.FUTEX_WAKE_PRIVATE) { } else if (a1 == sys.FUTEX_WAKE_PRIVATE) {
// Trigger thread traversal starting from the left stack until we find one waiting on the wakeup // Trigger thread traversal starting from the left stack until we find one waiting on the wakeup
// address // address
state.wakeup = effAddr; state.wakeup = effFutexAddr;
// Don't indicate to the program that we've woken up a waiting thread, as there are no guarantees. // Don't indicate to the program that we've woken up a waiting thread, as there are no guarantees.
// The woken up thread should indicate this in userspace. // The woken up thread should indicate this in userspace.
v0 = 0; v0 = 0;
...@@ -897,7 +893,7 @@ contract MIPS64 is ISemver { ...@@ -897,7 +893,7 @@ contract MIPS64 is ISemver {
from, to := copyMem(from, to, 1) // exitCode from, to := copyMem(from, to, 1) // exitCode
from, to := copyMem(from, to, 1) // exited from, to := copyMem(from, to, 1) // exited
from, to := copyMem(from, to, 8) // futexAddr from, to := copyMem(from, to, 8) // futexAddr
from, to := copyMem(from, to, 8) // futexVal from, to := copyMem(from, to, 4) // futexVal
from, to := copyMem(from, to, 8) // futexTimeoutStep from, to := copyMem(from, to, 8) // futexTimeoutStep
from, to := copyMem(from, to, 8) // pc from, to := copyMem(from, to, 8) // pc
from, to := copyMem(from, to, 8) // nextPC from, to := copyMem(from, to, 8) // nextPC
...@@ -960,7 +956,7 @@ contract MIPS64 is ISemver { ...@@ -960,7 +956,7 @@ contract MIPS64 is ISemver {
c, m := putField(c, m, 1) // exitCode c, m := putField(c, m, 1) // exitCode
c, m := putField(c, m, 1) // exited c, m := putField(c, m, 1) // exited
c, m := putField(c, m, 8) // futexAddr c, m := putField(c, m, 8) // futexAddr
c, m := putField(c, m, 8) // futexVal c, m := putField(c, m, 4) // futexVal
c, m := putField(c, m, 8) // futexTimeoutStep c, m := putField(c, m, 8) // futexTimeoutStep
c, m := putField(c, m, 8) // pc c, m := putField(c, m, 8) // pc
c, m := putField(c, m, 8) // nextPC c, m := putField(c, m, 8) // nextPC
...@@ -986,4 +982,15 @@ contract MIPS64 is ISemver { ...@@ -986,4 +982,15 @@ contract MIPS64 is ISemver {
"MIPS64: insufficient calldata for thread witness" "MIPS64: insufficient calldata for thread witness"
); );
} }
/// @notice Loads a 32-bit futex value at _vAddr
function getFutexValue(uint64 _vAddr) internal pure returns (uint32 out_) {
State memory state;
assembly {
state := STATE_MEM_OFFSET
}
uint64 subword = loadSubWord(state, _vAddr, 4, false);
return uint32(subword);
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment