diff --git a/crates/aarch64/src/lib.rs b/crates/aarch64/src/lib.rs index 3e384df..05e0e3b 100644 --- a/crates/aarch64/src/lib.rs +++ b/crates/aarch64/src/lib.rs @@ -37,6 +37,9 @@ const NEON_BITWISE_SELECT_USER_OP: &str = "NEON_bsl"; const NEON_UNSIGNED_MINIMUM_ACROSS_USER_OP: &str = "NEON_uminv"; const NEON_COMPARE_LESS_THAN_USER_OP: &str = "NEON_cmlt"; const NEON_BITWISE_INSERT_USER_OP: &str = "NEON_bit"; +const NEON_MINIMUM_NUMBER_USER_OP: &str = "NEON_fminnm"; +const NEON_ROUND_INTEGRAL_USER_OP: &str = "NEON_frint"; +const NEON_UNSIGNED_MULTIPLY_LONG_USER_OP: &str = "NEON_umull"; const SLEIGH_SOURCES: [(&str, &str); 7] = [ ( @@ -491,6 +494,11 @@ enum Operation { left: Value, right: Value, }, + SignedDivide { + destination: Place, + left: Value, + right: Value, + }, BitwiseAnd { destination: Place, left: Value, @@ -527,6 +535,16 @@ enum Operation { right: Value, comparison: FloatComparison, }, + FloatMinimumNumber { + destination: Place, + left: Value, + right: Value, + }, + FloatRoundIntegral { + destination: Place, + source: Value, + mode: Value, + }, ShiftLeft { destination: Place, left: Value, @@ -679,6 +697,12 @@ enum Operation { mask: Value, element_size: Value, }, + NeonUnsignedMultiplyLong { + destination: Place, + left: Value, + right: Value, + element_size: Value, + }, SupervisorCall { immediate: Value, }, @@ -761,6 +785,7 @@ fn lower_pcode( | PcodeOp::IntSub | PcodeOp::IntMul | PcodeOp::IntDiv + | PcodeOp::IntSignedDiv | PcodeOp::IntAnd | PcodeOp::IntOr | PcodeOp::IntXor @@ -875,11 +900,9 @@ fn lower_binary_operation( left, right, }, - PcodeOp::IntDiv => Operation::Divide { - destination, - left, - right, - }, + operation @ (PcodeOp::IntDiv | PcodeOp::IntSignedDiv) => { + lower_divide_operation(operation, destination, left, right) + } PcodeOp::IntAnd => Operation::BitwiseAnd { destination, left, @@ -959,6 +982,27 @@ fn lower_binary_operation( } } +fn lower_divide_operation( + operation: PcodeOp, + destination: Place, + left: Value, + right: Value, +) -> Operation { + if operation == PcodeOp::IntSignedDiv { + Operation::SignedDivide { + destination, + left, + right, + } + } else { + Operation::Divide { + destination, + left, + right, + } + } +} + fn lower_unary_pcode( language: &SleighData, operation: PcodeOp, @@ -1073,6 +1117,24 @@ fn lower_control_pcode( mask: arguments[0]?, element_size: arguments[1]?, }), + NEON_MINIMUM_NUMBER_USER_OP if arguments[0].is_none() => { + Some(Operation::FloatMinimumNumber { + destination: lower_place(language, output)?, + left: lower_value(language, left)?, + right: lower_value(language, right)?, + }) + } + NEON_ROUND_INTEGRAL_USER_OP => Some(Operation::FloatRoundIntegral { + destination: lower_place(language, output)?, + source: lower_value(language, left)?, + mode: lower_value(language, right)?, + }), + NEON_UNSIGNED_MULTIPLY_LONG_USER_OP => Some(Operation::NeonUnsignedMultiplyLong { + destination: lower_place(language, output)?, + left: lower_value(language, left)?, + right: lower_value(language, right)?, + element_size: arguments[0]?, + }), _ => None, }, _ => None, @@ -1185,6 +1247,7 @@ fn execute_operations( | Operation::Subtract { .. } | Operation::Multiply { .. } | Operation::Divide { .. } + | Operation::SignedDivide { .. } | Operation::BitwiseAnd { .. } | Operation::BitwiseOr { .. } | Operation::BitwiseXor { .. } @@ -1192,6 +1255,8 @@ fn execute_operations( | Operation::FloatToInteger { .. } | Operation::FloatArithmetic { .. } | Operation::FloatCompare { .. } + | Operation::FloatMinimumNumber { .. } + | Operation::FloatRoundIntegral { .. } | Operation::ShiftLeft { .. } | Operation::ShiftRight { .. } | Operation::SignedShiftRight { .. } @@ -1218,7 +1283,8 @@ fn execute_operations( | Operation::NeonBitwiseSelect { .. } | Operation::NeonUnsignedMinimumAcross { .. } | Operation::NeonCompareLessThan { .. } - | Operation::NeonBitwiseInsert { .. } => { + | Operation::NeonBitwiseInsert { .. } + | Operation::NeonUnsignedMultiplyLong { .. } => { execute_value_operation(state, &mut scratch, operation, pc, encoding)?; } Operation::Load { .. } | Operation::Store { .. } => { @@ -1290,6 +1356,7 @@ fn execute_value_operation( | Operation::Subtract { .. } | Operation::Multiply { .. } | Operation::Divide { .. } + | Operation::SignedDivide { .. } | Operation::BitwiseAnd { .. } | Operation::BitwiseOr { .. } | Operation::BitwiseXor { .. }) => { @@ -1298,7 +1365,9 @@ fn execute_value_operation( operation @ (Operation::IntegerToFloat { .. } | Operation::FloatToInteger { .. } | Operation::FloatArithmetic { .. } - | Operation::FloatCompare { .. }) => { + | Operation::FloatCompare { .. } + | Operation::FloatMinimumNumber { .. } + | Operation::FloatRoundIntegral { .. }) => { execute_float_value_operation(state, scratch, &operation, pc, encoding)? } operation @ (Operation::ShiftLeft { .. } @@ -1331,7 +1400,8 @@ fn execute_value_operation( | Operation::NeonBitwiseSelect { .. } | Operation::NeonUnsignedMinimumAcross { .. } | Operation::NeonCompareLessThan { .. } - | Operation::NeonBitwiseInsert { .. }) => { + | Operation::NeonBitwiseInsert { .. } + | Operation::NeonUnsignedMultiplyLong { .. }) => { execute_neon_value_operation(state, scratch, &operation, pc, encoding)? } Operation::Load { .. } @@ -1374,6 +1444,11 @@ fn execute_integer_value_operation( left, right, } + | Operation::SignedDivide { + destination, + left, + right, + } | Operation::BitwiseAnd { destination, left, @@ -1392,6 +1467,8 @@ fn execute_integer_value_operation( else { return Err(unsupported()); }; + let left_size = left.size; + let right_size = right.size; let left = read(left)?; let right = read(right)?; let value = match operation { @@ -1399,6 +1476,16 @@ fn execute_integer_value_operation( Operation::Subtract { .. } => left.wrapping_sub(right), Operation::Multiply { .. } => left.wrapping_mul(right), Operation::Divide { .. } => left.checked_div(right).unwrap_or(0), + Operation::SignedDivide { .. } => { + let left = signed_value(left, left_size).ok_or_else(unsupported)?; + let right = signed_value(right, right_size).ok_or_else(unsupported)?; + let quotient = if right == 0 { + 0 + } else { + left.checked_div(right).unwrap_or(left) + }; + u128::from_ne_bytes(quotient.to_ne_bytes()) + } Operation::BitwiseAnd { .. } => left & right, Operation::BitwiseOr { .. } => left | right, Operation::BitwiseXor { .. } => left ^ right, @@ -1456,6 +1543,24 @@ fn execute_float_value_operation( .ok_or_else(unsupported)?, ), )), + Operation::FloatMinimumNumber { + destination, + left, + right, + } => Ok(( + destination, + minimum_number_float_bits(read(left)?, read(right)?, destination.size) + .ok_or_else(unsupported)?, + )), + Operation::FloatRoundIntegral { + destination, + source, + mode, + } => Ok(( + destination, + round_integral_float_bits(read(source)?, source.size, read(mode)?, state.fpcr()) + .ok_or_else(unsupported)?, + )), _ => Err(unsupported()), } } @@ -1531,6 +1636,75 @@ fn float_arithmetic_bits( } } +fn minimum_number_float_bits(left: u128, right: u128, size: u8) -> Option { + match size { + 4 => Some(u128::from( + f32::from_bits(u32::try_from(left).ok()?) + .min(f32::from_bits(u32::try_from(right).ok()?)) + .to_bits(), + )), + 8 => Some(u128::from( + f64::from_bits(u64::try_from(left).ok()?) + .min(f64::from_bits(u64::try_from(right).ok()?)) + .to_bits(), + )), + _ => None, + } +} + +fn round_integral_float_bits(value: u128, size: u8, mode: u128, fpcr: u32) -> Option { + let current_mode = (fpcr >> 22) & 0b11; + match size { + 4 => { + let value = f32::from_bits(u32::try_from(value).ok()?); + let rounded = match mode { + 0 => value.round(), + 1 | 5 => round_current_f32(value, current_mode), + 2 => value.floor(), + 3 => value.round_ties_even(), + 4 => value.ceil(), + 6 => value.trunc(), + _ => return None, + }; + Some(u128::from(rounded.to_bits())) + } + 8 => { + let value = f64::from_bits(u64::try_from(value).ok()?); + let rounded = match mode { + 0 => value.round(), + 1 | 5 => round_current_f64(value, current_mode), + 2 => value.floor(), + 3 => value.round_ties_even(), + 4 => value.ceil(), + 6 => value.trunc(), + _ => return None, + }; + Some(u128::from(rounded.to_bits())) + } + _ => None, + } +} + +fn round_current_f32(value: f32, mode: u32) -> f32 { + match mode { + 0 => value.round_ties_even(), + 1 => value.ceil(), + 2 => value.floor(), + 3 => value.trunc(), + _ => unreachable!("FPCR rounding mode is two bits"), + } +} + +fn round_current_f64(value: f64, mode: u32) -> f64 { + match mode { + 0 => value.round_ties_even(), + 1 => value.ceil(), + 2 => value.floor(), + 3 => value.trunc(), + _ => unreachable!("FPCR rounding mode is two bits"), + } +} + fn compare_float_bits( left: u128, right: u128, @@ -1634,17 +1808,29 @@ fn execute_neon_value_operation( ) -> Result<(Place, u128), Trap> { let unsupported = || Trap::UnsupportedInstruction { pc, encoding }; let read = |value| read_value(state, scratch, value).ok_or_else(unsupported); - let (destination, source, element_size, count_bits) = match *operation { + match *operation { Operation::NeonCount { destination, source, element_size, - } => (destination, source, element_size, true), + } => execute_neon_byte_reduction( + state, + scratch, + (destination, source, element_size), + true, + (pc, encoding), + ), Operation::NeonAddAcross { destination, source, element_size, - } => (destination, source, element_size, false), + } => execute_neon_byte_reduction( + state, + scratch, + (destination, source, element_size), + false, + (pc, encoding), + ), Operation::NeonCompareEqual { destination, left, @@ -1658,13 +1844,13 @@ fn execute_neon_value_operation( element_size, } => { let signed_less_than = matches!(operation, Operation::NeonCompareLessThan { .. }); - return execute_neon_comparison( + execute_neon_comparison( state, scratch, (destination, left, right, element_size), signed_less_than, (pc, encoding), - ); + ) } Operation::NeonUnsignedMaximumAcross { destination, @@ -1680,7 +1866,7 @@ fn execute_neon_value_operation( let maximum = matches!(operation, Operation::NeonUnsignedMaximumAcross { .. }); let result = unsigned_lane_extreme(read(source)?, source.size, element_size, maximum) .ok_or_else(unsupported)?; - return Ok((destination, result)); + Ok((destination, result)) } Operation::NeonBitwiseSelect { destination, @@ -1688,30 +1874,50 @@ fn execute_neon_value_operation( when_set, when_clear, element_size, - } => { - return execute_neon_bitwise_insert( - state, - scratch, - (destination, when_clear, when_set, selector, element_size), - (pc, encoding), - ); - } + } => execute_neon_bitwise_insert( + state, + scratch, + (destination, when_clear, when_set, selector, element_size), + (pc, encoding), + ), Operation::NeonBitwiseInsert { destination, original, source, mask, element_size, - } => { - return execute_neon_bitwise_insert( - state, - scratch, - (destination, original, source, mask, element_size), - (pc, encoding), - ); - } - _ => return Err(unsupported()), - }; + } => execute_neon_bitwise_insert( + state, + scratch, + (destination, original, source, mask, element_size), + (pc, encoding), + ), + Operation::NeonUnsignedMultiplyLong { + destination, + left, + right, + element_size, + } => execute_neon_unsigned_multiply_long( + state, + scratch, + (destination, left, right, element_size), + (pc, encoding), + ), + _ => Err(unsupported()), + } +} + +fn execute_neon_byte_reduction( + state: &Aarch64State, + scratch: &ScratchValues, + operands: (Place, Value, Value), + count_bits: bool, + location: (GuestAddress, u32), +) -> Result<(Place, u128), Trap> { + let (destination, source, element_size) = operands; + let (pc, encoding) = location; + let unsupported = || Trap::UnsupportedInstruction { pc, encoding }; + let read = |value| read_value(state, scratch, value).ok_or_else(unsupported); if read(element_size)? != 1 { return Err(unsupported()); } @@ -1729,6 +1935,22 @@ fn execute_neon_value_operation( Ok((destination, result)) } +fn execute_neon_unsigned_multiply_long( + state: &Aarch64State, + scratch: &ScratchValues, + operands: (Place, Value, Value, Value), + location: (GuestAddress, u32), +) -> Result<(Place, u128), Trap> { + let (destination, left, right, element_size) = operands; + let (pc, encoding) = location; + let unsupported = || Trap::UnsupportedInstruction { pc, encoding }; + let read = |value| read_value(state, scratch, value).ok_or_else(unsupported); + let element_size = u8::try_from(read(element_size)?).map_err(|_| unsupported())?; + let result = unsigned_multiply_long_lanes(read(left)?, read(right)?, left.size, element_size) + .ok_or_else(unsupported)?; + Ok((destination, result)) +} + fn execute_neon_bitwise_insert( state: &Aarch64State, scratch: &ScratchValues, @@ -1776,6 +1998,34 @@ fn unsigned_lane_extreme( Some(result) } +fn unsigned_multiply_long_lanes( + left: u128, + right: u128, + source_size: u8, + element_size: u8, +) -> Option { + if element_size == 0 || !source_size.is_multiple_of(element_size) { + return None; + } + let result_element_size = element_size.checked_mul(2)?; + let lane_count = source_size / element_size; + if lane_count.checked_mul(result_element_size)? > 16 { + return None; + } + let source_mask = value_mask(element_size)?; + let result_mask = value_mask(result_element_size)?; + let mut result = 0; + for lane in 0..lane_count { + let source_shift = u32::from(lane) * u32::from(element_size) * 8; + let result_shift = u32::from(lane) * u32::from(result_element_size) * 8; + let product = ((left >> source_shift) & source_mask) + .wrapping_mul((right >> source_shift) & source_mask) + & result_mask; + result |= product << result_shift; + } + Some(result) +} + fn execute_neon_comparison( state: &Aarch64State, scratch: &ScratchValues, @@ -1972,6 +2222,7 @@ fn execute_memory_operation( | Operation::Subtract { .. } | Operation::Multiply { .. } | Operation::Divide { .. } + | Operation::SignedDivide { .. } | Operation::BitwiseAnd { .. } | Operation::BitwiseOr { .. } | Operation::BitwiseXor { .. } @@ -1979,6 +2230,8 @@ fn execute_memory_operation( | Operation::FloatToInteger { .. } | Operation::FloatArithmetic { .. } | Operation::FloatCompare { .. } + | Operation::FloatMinimumNumber { .. } + | Operation::FloatRoundIntegral { .. } | Operation::ShiftLeft { .. } | Operation::ShiftRight { .. } | Operation::SignedShiftRight { .. } @@ -2010,6 +2263,7 @@ fn execute_memory_operation( | Operation::NeonUnsignedMinimumAcross { .. } | Operation::NeonCompareLessThan { .. } | Operation::NeonBitwiseInsert { .. } + | Operation::NeonUnsignedMultiplyLong { .. } | Operation::SupervisorCall { .. } => Err(unsupported()), } } @@ -2223,6 +2477,9 @@ mod tests { 0x49, 0x7c, 0xa9, 0x9b, // umull x9, w2, w9 0x09, 0x09, 0xc0, 0x1a, // udiv w9, w8, w0 ]; + const SIGNED_DIVIDE_CODE: &[u8] = &[ + 0x08, 0x0d, 0xc3, 0x9a, // sdiv x8, x8, x3 + ]; const RUST_VECTOR_STORE_CODE: &[u8] = &[ 0x00, 0xe4, 0x00, 0x6f, // movi v0.2d, #0 0xe0, 0x83, 0x00, 0xad, // stp q0, q0, [sp, #0x10] @@ -2465,6 +2722,13 @@ mod tests { interpreter.step(&mut state, &mut memory).unwrap(); } assert_eq!(state.x(9), Some(7)); + + let mut memory = executable_memory(SIGNED_DIVIDE_CODE); + let mut state = Aarch64State::new(CODE_ADDRESS, GuestAddress::new(0x8000)); + state.set_x(8, (-7_i64).cast_unsigned()).unwrap(); + state.set_x(3, 3).unwrap(); + interpreter.step(&mut state, &mut memory).unwrap(); + assert_eq!(state.x(8), Some((-2_i64).cast_unsigned())); } #[test] @@ -2589,6 +2853,40 @@ mod tests { assert_eq!(state.vector(0), Some(0xaa0f_aa0f_aa0f_aa0f)); } + #[test] + fn executes_musl_time_conversion_vector_semantics() { + const CODE: &[u8] = &[ + 0x01, 0xc0, 0xa1, 0x2e, // umull v1.2d, v0.2s, v1.2s + 0x21, 0x04, 0x5b, 0x6f, // ushr v1.2d, v1.2d, #37 + 0x21, 0x28, 0xa1, 0x0e, // xtn v1.2s, v1.2d + 0x20, 0x94, 0xa2, 0x2e, // mls v0.2s, v1.2s, v2.2s + ]; + let mut memory = executable_memory(CODE); + let mut state = Aarch64State::new(CODE_ADDRESS, GuestAddress::new(0x8000)); + let packed_maximum = u128::from(u32::MAX) | (u128::from(u32::MAX) << 32); + state.set_vector(0, packed_maximum).unwrap(); + state.set_vector(1, packed_maximum).unwrap(); + state.set_vector(2, 7_u128 | (9_u128 << 32)).unwrap(); + let mut interpreter = Interpreter::new().unwrap(); + let product = u128::from(u64::from(u32::MAX) * u64::from(u32::MAX)); + interpreter.step(&mut state, &mut memory).unwrap(); + assert_eq!(state.vector(1), Some(product | (product << 64))); + let shifted = product >> 37; + interpreter.step(&mut state, &mut memory).unwrap(); + assert_eq!(state.vector(1), Some(shifted | (shifted << 64))); + interpreter.step(&mut state, &mut memory).unwrap(); + let narrowed = shifted & u128::from(u32::MAX); + assert_eq!(state.vector(1), Some(narrowed | (narrowed << 32))); + interpreter.step(&mut state, &mut memory).unwrap(); + let lane = u32::try_from(narrowed).unwrap(); + let expected_low = u32::MAX.wrapping_sub(lane.wrapping_mul(7)); + let expected_high = u32::MAX.wrapping_sub(lane.wrapping_mul(9)); + assert_eq!( + state.vector(0), + Some(u128::from(expected_low) | (u128::from(expected_high) << 32)) + ); + } + #[test] fn executes_cpython_scalar_float_conversion_arithmetic_and_comparison() { const CODE: &[u8] = &[ @@ -2604,6 +2902,10 @@ mod tests { const READ_FPCR: &[u8] = &[ 0x08, 0x44, 0x3b, 0xd5, // mrs x8, fpcr ]; + const MINIMUM_AND_FLOOR: &[u8] = &[ + 0x00, 0x78, 0x61, 0x1e, // fminnm d0, d0, d1 + 0x00, 0x40, 0x65, 0x1e, // frintm d0, d0 + ]; let mut memory = executable_memory(CODE); let mut state = Aarch64State::new(CODE_ADDRESS, GuestAddress::new(0x8000)); state.set_x(8, 3).unwrap(); @@ -2635,6 +2937,15 @@ mod tests { state.set_fpcr(0x40_0000); interpreter.step(&mut state, &mut memory).unwrap(); assert_eq!(state.x(8), Some(0x40_0000)); + + let mut memory = executable_memory(MINIMUM_AND_FLOOR); + let mut state = Aarch64State::new(CODE_ADDRESS, GuestAddress::new(0x8000)); + state.set_vector(0, u128::from(9.75_f64.to_bits())).unwrap(); + state.set_vector(1, u128::from(8.5_f64.to_bits())).unwrap(); + interpreter.step(&mut state, &mut memory).unwrap(); + assert_eq!(state.vector(0), Some(u128::from(8.5_f64.to_bits()))); + interpreter.step(&mut state, &mut memory).unwrap(); + assert_eq!(state.vector(0), Some(u128::from(8.0_f64.to_bits()))); } #[test] diff --git a/crates/linux-abi/src/lib.rs b/crates/linux-abi/src/lib.rs index 08660bf..8ea73af 100644 --- a/crates/linux-abi/src/lib.rs +++ b/crates/linux-abi/src/lib.rs @@ -4,6 +4,7 @@ #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[repr(u64)] pub enum Syscall { + Getcwd = 17, Mkdirat = 34, Unlinkat = 35, Renameat = 38, @@ -34,6 +35,7 @@ impl Syscall { #[must_use] pub const fn from_number(number: u64) -> Option { match number { + 17 => Some(Self::Getcwd), 34 => Some(Self::Mkdirat), 35 => Some(Self::Unlinkat), 38 => Some(Self::Renameat), @@ -83,6 +85,7 @@ pub enum Errno { InvalidArgument = 22, TooManyOpenFiles = 24, NoSpace = 28, + Range = 34, NoSystemCall = 38, DirectoryNotEmpty = 39, } @@ -101,6 +104,7 @@ mod tests { #[test] fn decodes_the_minimal_aarch64_table() { + assert_eq!(Syscall::from_number(17), Some(Syscall::Getcwd)); assert_eq!(Syscall::from_number(34), Some(Syscall::Mkdirat)); assert_eq!(Syscall::from_number(35), Some(Syscall::Unlinkat)); assert_eq!(Syscall::from_number(38), Some(Syscall::Renameat)); diff --git a/crates/linux-runtime/src/lib.rs b/crates/linux-runtime/src/lib.rs index 48dca3e..fcab2da 100644 --- a/crates/linux-runtime/src/lib.rs +++ b/crates/linux-runtime/src/lib.rs @@ -27,8 +27,16 @@ const OPEN_EXCLUSIVE: u64 = 0x80; const OPEN_TRUNCATE: u64 = 0x200; const OPEN_APPEND: u64 = 0x400; const OPEN_DIRECTORY: u64 = 0x1_0000; -const SUPPORTED_OPEN_FLAGS: u64 = - OPEN_ACCESS_MASK | OPEN_CREATE | OPEN_EXCLUSIVE | OPEN_TRUNCATE | OPEN_APPEND | OPEN_DIRECTORY; +const OPEN_NOFOLLOW: u64 = 0x2_0000; +const OPEN_CLOEXEC: u64 = 0x8_0000; +const SUPPORTED_OPEN_FLAGS: u64 = OPEN_ACCESS_MASK + | OPEN_CREATE + | OPEN_EXCLUSIVE + | OPEN_TRUNCATE + | OPEN_APPEND + | OPEN_DIRECTORY + | OPEN_NOFOLLOW + | OPEN_CLOEXEC; const AT_REMOVE_DIRECTORY: u64 = 0x200; const DIRECTORY_ENTRY_HEADER_SIZE: usize = 19; const DIRECTORY_ENTRY_ALIGNMENT: usize = 8; @@ -57,6 +65,7 @@ const SIG_SETMASK: u64 = 2; const SIGKILL_BIT: u64 = 1 << (9 - 1); const SIGSTOP_BIT: u64 = 1 << (19 - 1); const CPU_AFFINITY_BYTES: u64 = 8; +const INITIAL_CURRENT_DIRECTORY: &[u8] = b"/project"; #[derive(Clone, Copy, Debug, Default)] struct SignalAction { @@ -131,6 +140,11 @@ impl fmt::Display for SyscallEvent { | Syscall::Getdents64), ) => format_vfs_syscall(formatter, syscall, self.arguments)?, Some(Syscall::Close) => write!(formatter, "close(fd={})", self.arguments[0])?, + Some(Syscall::Getcwd) => write!( + formatter, + "getcwd(buf={:#x}, size={})", + self.arguments[0], self.arguments[1], + )?, Some(Syscall::Lseek) => write!( formatter, "lseek(fd={}, offset={}, whence={})", @@ -194,26 +208,9 @@ impl fmt::Display for SyscallEvent { "rt_sigprocmask(how={}, set={:#x}, old_set={:#x}, sigsetsize={})", self.arguments[0], self.arguments[1], self.arguments[2], self.arguments[3], )?, - Some(Syscall::Munmap) => write!( - formatter, - "munmap(address={:#x}, length={})", - self.arguments[0], self.arguments[1], - )?, - Some(Syscall::Mmap) => write!( - formatter, - "mmap(address={:#x}, length={}, protection={:#x}, flags={:#x}, fd={}, offset={:#x})", - self.arguments[0], - self.arguments[1], - self.arguments[2], - self.arguments[3], - self.arguments[4].cast_signed(), - self.arguments[5], - )?, - Some(Syscall::Mprotect) => write!( - formatter, - "mprotect(address={:#x}, length={}, protection={:#x})", - self.arguments[0], self.arguments[1], self.arguments[2], - )?, + Some(syscall @ (Syscall::Munmap | Syscall::Mmap | Syscall::Mprotect)) => { + format_memory_syscall(formatter, syscall, self.arguments)?; + } None => write!(formatter, "syscall({})", self.number)?, } match self.outcome { @@ -223,6 +220,36 @@ impl fmt::Display for SyscallEvent { } } +fn format_memory_syscall( + formatter: &mut fmt::Formatter<'_>, + syscall: Syscall, + arguments: [u64; 6], +) -> fmt::Result { + match syscall { + Syscall::Munmap => write!( + formatter, + "munmap(address={:#x}, length={})", + arguments[0], arguments[1], + ), + Syscall::Mmap => write!( + formatter, + "mmap(address={:#x}, length={}, protection={:#x}, flags={:#x}, fd={}, offset={:#x})", + arguments[0], + arguments[1], + arguments[2], + arguments[3], + arguments[4].cast_signed(), + arguments[5], + ), + Syscall::Mprotect => write!( + formatter, + "mprotect(address={:#x}, length={}, protection={:#x})", + arguments[0], arguments[1], arguments[2], + ), + _ => unreachable!("non-memory syscall passed to memory formatter"), + } +} + fn format_vfs_syscall( formatter: &mut fmt::Formatter<'_>, syscall: Syscall, @@ -326,6 +353,7 @@ pub struct Process { signal_mask: u64, next_mmap_address: GuestAddress, file_descriptors: BTreeMap, + current_directory: Vec, trace: Vec, pending_input: Option, } @@ -352,6 +380,7 @@ impl Process { signal_mask: 0, next_mmap_address: GuestAddress::new(MMAP_ARENA_START), file_descriptors: BTreeMap::new(), + current_directory: INITIAL_CURRENT_DIRECTORY.to_vec(), trace: Vec::new(), pending_input: None, }) @@ -526,6 +555,7 @@ impl Process { output_bytes: self.output_bytes, }))); } + Some(Syscall::Getcwd) => self.dispatch_getcwd(), Some(Syscall::Mkdirat) => self.dispatch_mkdirat(filesystem), Some(Syscall::Unlinkat) => self.dispatch_unlinkat(filesystem), Some(Syscall::Renameat) => self.dispatch_renameat(filesystem), @@ -568,6 +598,25 @@ impl Process { Ok(None) } + fn dispatch_getcwd(&mut self) { + let buffer = GuestAddress::new(self.register(0)); + let size = self.register(1); + let required = u64::try_from(self.current_directory.len()) + .expect("guest current-directory length fits u64") + + 1; + if size < required { + self.set_return(Errno::Range.return_value()); + return; + } + let mut bytes = self.current_directory.clone(); + bytes.push(0); + if self.memory.write(buffer, &bytes).is_err() { + self.set_return(Errno::Fault.return_value()); + return; + } + self.set_return(required); + } + fn dispatch_write( &mut self, terminal: &mut T, @@ -1853,6 +1902,32 @@ mod tests { } } + #[test] + fn getcwd_reports_the_project_directory_and_validates_the_buffer() { + let image = load_hello(ProcessConfig::default(), 1, MESSAGE_ADDRESS); + let mut process = Process::new(image).unwrap(); + let buffer = process.state.sp().checked_sub(32).unwrap(); + process.state.set_x(0, buffer.get()).unwrap(); + process.state.set_x(1, 32).unwrap(); + + process.dispatch_getcwd(); + + assert_eq!(process.register(0), 9); + let mut bytes = [0; 9]; + process.memory.read_exact(buffer, &mut bytes).unwrap(); + assert_eq!(&bytes, b"/project\0"); + + process.state.set_x(0, buffer.get()).unwrap(); + process.state.set_x(1, 8).unwrap(); + process.dispatch_getcwd(); + assert_eq!(process.register(0), Errno::Range.return_value()); + + process.state.set_x(0, u64::MAX).unwrap(); + process.state.set_x(1, 32).unwrap(); + process.dispatch_getcwd(); + assert_eq!(process.register(0), Errno::Fault.return_value()); + } + #[test] fn anonymous_mmap_honors_an_available_address_hint() { let image = load_hello(ProcessConfig::default(), 1, MESSAGE_ADDRESS); diff --git a/third_party/ghidra-aarch64/languages/AARCH64instructions.sinc b/third_party/ghidra-aarch64/languages/AARCH64instructions.sinc index bfe3b99..9b4bae0 100644 --- a/third_party/ghidra-aarch64/languages/AARCH64instructions.sinc +++ b/third_party/ghidra-aarch64/languages/AARCH64instructions.sinc @@ -3316,6 +3316,7 @@ define pcodeop NEON_fminv; define pcodeop NEON_fmov; define pcodeop NEON_fmsub; define pcodeop NEON_fmulx; +define pcodeop NEON_frint; define pcodeop NEON_fnmadd; define pcodeop NEON_fnmsub; define pcodeop NEON_frecpe; @@ -4003,4 +4004,3 @@ is b_0031=0x00200c20 unimpl } # end with ImmS_ImmR_TestSet=1 - diff --git a/third_party/ghidra-aarch64/languages/AARCH64neon.sinc b/third_party/ghidra-aarch64/languages/AARCH64neon.sinc index 65a3b3b..7cb10f8 100644 --- a/third_party/ghidra-aarch64/languages/AARCH64neon.sinc +++ b/third_party/ghidra-aarch64/languages/AARCH64neon.sinc @@ -9510,13 +9510,13 @@ is b_31=0 & b_30=1 & b_29 & b_2428=0b01110 & b_23 & b_22=0b1 & b_1321=0b10000110 # C7.2.141 FRINTA (scalar) page C7-1315 line 76515 KEEPWITH # FP rounding instruction (not implemented) -frint_smode: "a" is b_1517=0b100 { } -frint_smode: "i" is b_1517=0b111 { } -frint_smode: "m" is b_1517=0b010 { } -frint_smode: "n" is b_1517=0b000 { } -frint_smode: "p" is b_1517=0b001 { } -frint_smode: "x" is b_1517=0b110 { } -frint_smode: "z" is b_1517=0b011 { } +frint_smode: "a" is b_1517=0b100 { export 0:1; } +frint_smode: "i" is b_1517=0b111 { export 1:1; } +frint_smode: "m" is b_1517=0b010 { export 2:1; } +frint_smode: "n" is b_1517=0b000 { export 3:1; } +frint_smode: "p" is b_1517=0b001 { export 4:1; } +frint_smode: "x" is b_1517=0b110 { export 5:1; } +frint_smode: "z" is b_1517=0b011 { export 6:1; } # C7.2.156 FRINTA (scalar) page C7-2371 line 138539 MATCH x1e264000/mask=xff3ffc00 # C7.2.158 FRINTI (scalar) page C7-2375 line 138773 MATCH x1e27c000/mask=xff3ffc00 @@ -9534,7 +9534,7 @@ frint_smode: "z" is b_1517=0b011 { } :frint^frint_smode Rd_FPR16, Rn_FPR16 is b_2431=0b00011110 & b_2223=0b11 & b_1821=0b1001 & b_1517 & b_1014=0b10000 & frint_smode & Rd_FPR16 & Rn_FPR16 & Zd { - Rd_FPR16 = trunc(Rn_FPR16); + Rd_FPR16 = NEON_frint(Rn_FPR16, frint_smode); zext_zh(Zd); # zero upper 30 bytes of Zd } @@ -9554,7 +9554,7 @@ is b_2431=0b00011110 & b_2223=0b11 & b_1821=0b1001 & b_1517 & b_1014=0b10000 & f :frint^frint_smode Rd_FPR32, Rn_FPR32 is b_2431=0b00011110 & b_2223=0b00 & b_1821=0b1001 & b_1517 & b_1014=0b10000 & frint_smode & Rd_FPR32 & Rn_FPR32 & Zd { - Rd_FPR32 = trunc(Rn_FPR32); + Rd_FPR32 = NEON_frint(Rn_FPR32, frint_smode); zext_zs(Zd); # zero upper 28 bytes of Zd } @@ -9574,7 +9574,7 @@ is b_2431=0b00011110 & b_2223=0b00 & b_1821=0b1001 & b_1517 & b_1014=0b10000 & f :frint^frint_smode Rd_FPR64, Rn_FPR64 is b_2431=0b00011110 & b_2223=0b01 & b_1821=0b1001 & b_1517 & b_1014=0b10000 & frint_smode & Rd_FPR64 & Rn_FPR64 & Zd { - Rd_FPR64 = trunc(Rn_FPR64); + Rd_FPR64 = NEON_frint(Rn_FPR64, frint_smode); zext_zd(Zd); # zero upper 24 bytes of Zd }