Improved calling convention support for x86, x86_64, arm and aarch64 for libafl-qemu (#3013)
* Improved calling convention support for x86, x86_64, arm and aarch64 * fix * fix write_function_arguments * fix argument_error * follow clippy advice * last change? * fix guestaddr issue * add code block for cargo-fmt * default to default convention * fix mistake * add pub * fix * changes after review * last change? --------- Co-authored-by: celian <cglenaz>
This commit is contained in:
parent
7c83be2408
commit
40c0a8c57c
@ -30,9 +30,8 @@ use libafl_bolts::{
|
|||||||
#[cfg(feature = "fork")]
|
#[cfg(feature = "fork")]
|
||||||
use libafl_qemu::QemuForkExecutor;
|
use libafl_qemu::QemuForkExecutor;
|
||||||
use libafl_qemu::{
|
use libafl_qemu::{
|
||||||
elf::EasyElf, modules::edges::StdEdgeCoverageChildModule, ArchExtras, CallingConvention,
|
elf::EasyElf, modules::edges::StdEdgeCoverageChildModule, ArchExtras, Emulator, GuestAddr,
|
||||||
Emulator, GuestAddr, GuestReg, MmapPerms, QemuExitError, QemuExitReason, QemuShutdownCause,
|
GuestReg, MmapPerms, QemuExitError, QemuExitReason, QemuShutdownCause, Regs,
|
||||||
Regs,
|
|
||||||
};
|
};
|
||||||
#[cfg(feature = "snapshot")]
|
#[cfg(feature = "snapshot")]
|
||||||
use libafl_qemu::{modules::SnapshotModule, QemuExecutor};
|
use libafl_qemu::{modules::SnapshotModule, QemuExecutor};
|
||||||
@ -232,10 +231,8 @@ pub fn fuzz() -> Result<(), Error> {
|
|||||||
qemu.write_reg(Regs::Pc, test_one_input_ptr).unwrap();
|
qemu.write_reg(Regs::Pc, test_one_input_ptr).unwrap();
|
||||||
qemu.write_reg(Regs::Sp, stack_ptr).unwrap();
|
qemu.write_reg(Regs::Sp, stack_ptr).unwrap();
|
||||||
qemu.write_return_address(ret_addr).unwrap();
|
qemu.write_return_address(ret_addr).unwrap();
|
||||||
qemu.write_function_argument(CallingConvention::Cdecl, 0, input_addr)
|
qemu.write_function_argument(0, input_addr).unwrap();
|
||||||
.unwrap();
|
qemu.write_function_argument(1, len).unwrap();
|
||||||
qemu.write_function_argument(CallingConvention::Cdecl, 1, len)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
match qemu.run() {
|
match qemu.run() {
|
||||||
Ok(QemuExitReason::Breakpoint(_)) => {}
|
Ok(QemuExitReason::Breakpoint(_)) => {}
|
||||||
@ -268,10 +265,8 @@ pub fn fuzz() -> Result<(), Error> {
|
|||||||
qemu.write_reg(Regs::Pc, test_one_input_ptr).unwrap();
|
qemu.write_reg(Regs::Pc, test_one_input_ptr).unwrap();
|
||||||
qemu.write_reg(Regs::Sp, stack_ptr).unwrap();
|
qemu.write_reg(Regs::Sp, stack_ptr).unwrap();
|
||||||
qemu.write_return_address(ret_addr).unwrap();
|
qemu.write_return_address(ret_addr).unwrap();
|
||||||
qemu.write_function_argument(CallingConvention::Cdecl, 0, input_addr)
|
qemu.write_function_argument(0, input_addr).unwrap();
|
||||||
.unwrap();
|
qemu.write_function_argument(1, len).unwrap();
|
||||||
qemu.write_function_argument(CallingConvention::Cdecl, 1, len)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
match qemu.run() {
|
match qemu.run() {
|
||||||
Ok(QemuExitReason::Breakpoint(_)) => {}
|
Ok(QemuExitReason::Breakpoint(_)) => {}
|
||||||
|
@ -29,8 +29,8 @@ use libafl_bolts::{
|
|||||||
use libafl_qemu::{
|
use libafl_qemu::{
|
||||||
elf::EasyElf,
|
elf::EasyElf,
|
||||||
modules::{drcov::DrCovModule, SnapshotModule},
|
modules::{drcov::DrCovModule, SnapshotModule},
|
||||||
ArchExtras, CallingConvention, Emulator, GuestAddr, GuestReg, MmapPerms, Qemu, QemuExecutor,
|
ArchExtras, Emulator, GuestAddr, GuestReg, MmapPerms, Qemu, QemuExecutor, QemuExitReason,
|
||||||
QemuExitReason, QemuMappingsViewer, QemuRWError, QemuShutdownCause, Regs,
|
QemuMappingsViewer, QemuRWError, QemuShutdownCause, Regs,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -179,8 +179,8 @@ pub fn fuzz() {
|
|||||||
qemu.write_reg(Regs::Pc, test_one_input_ptr)?;
|
qemu.write_reg(Regs::Pc, test_one_input_ptr)?;
|
||||||
qemu.write_reg(Regs::Sp, stack_ptr)?;
|
qemu.write_reg(Regs::Sp, stack_ptr)?;
|
||||||
qemu.write_return_address(ret_addr)?;
|
qemu.write_return_address(ret_addr)?;
|
||||||
qemu.write_function_argument(CallingConvention::Cdecl, 0, input_addr)?;
|
qemu.write_function_argument(0, input_addr)?;
|
||||||
qemu.write_function_argument(CallingConvention::Cdecl, 1, len)?;
|
qemu.write_function_argument(1, len)?;
|
||||||
|
|
||||||
match qemu.run() {
|
match qemu.run() {
|
||||||
Ok(QemuExitReason::Breakpoint(_)) => {}
|
Ok(QemuExitReason::Breakpoint(_)) => {}
|
||||||
|
@ -4,9 +4,7 @@ use libafl::{
|
|||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
use libafl_bolts::AsSlice;
|
use libafl_bolts::AsSlice;
|
||||||
use libafl_qemu::{
|
use libafl_qemu::{elf::EasyElf, ArchExtras, GuestAddr, GuestReg, MmapPerms, Qemu, Regs};
|
||||||
elf::EasyElf, ArchExtras, CallingConvention, GuestAddr, GuestReg, MmapPerms, Qemu, Regs,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct Harness {
|
pub struct Harness {
|
||||||
qemu: Qemu,
|
qemu: Qemu,
|
||||||
@ -118,11 +116,11 @@ impl Harness {
|
|||||||
.map_err(|e| Error::unknown(format!("Failed to write return address: {e:?}")))?;
|
.map_err(|e| Error::unknown(format!("Failed to write return address: {e:?}")))?;
|
||||||
|
|
||||||
self.qemu
|
self.qemu
|
||||||
.write_function_argument(CallingConvention::Cdecl, 0, self.input_addr)
|
.write_function_argument(0, self.input_addr)
|
||||||
.map_err(|e| Error::unknown(format!("Failed to write argument 0: {e:?}")))?;
|
.map_err(|e| Error::unknown(format!("Failed to write argument 0: {e:?}")))?;
|
||||||
|
|
||||||
self.qemu
|
self.qemu
|
||||||
.write_function_argument(CallingConvention::Cdecl, 1, len)
|
.write_function_argument(1, len)
|
||||||
.map_err(|e| Error::unknown(format!("Failed to write argument 1: {e:?}")))?;
|
.map_err(|e| Error::unknown(format!("Failed to write argument 1: {e:?}")))?;
|
||||||
unsafe {
|
unsafe {
|
||||||
let _ = self.qemu.run();
|
let _ = self.qemu.run();
|
||||||
|
@ -8,7 +8,12 @@ use pyo3::prelude::*;
|
|||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
pub use syscall_numbers::aarch64::*;
|
pub use syscall_numbers::aarch64::*;
|
||||||
|
|
||||||
use crate::{sync_exit::ExitArgs, CallingConvention, QemuRWError, QemuRWErrorKind};
|
use crate::{sync_exit::ExitArgs, CallingConvention, GuestAddr, QemuRWError, QemuRWErrorKind};
|
||||||
|
|
||||||
|
#[expect(non_upper_case_globals)]
|
||||||
|
impl CallingConvention {
|
||||||
|
pub const Default: CallingConvention = CallingConvention::Aapcs64;
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
@ -94,47 +99,83 @@ impl crate::ArchExtras for crate::CPU {
|
|||||||
self.write_reg(Regs::Lr, val)
|
self.write_reg(Regs::Lr, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_function_argument(
|
fn read_function_argument_with_cc(
|
||||||
&self,
|
&self,
|
||||||
conv: CallingConvention,
|
|
||||||
idx: u8,
|
idx: u8,
|
||||||
|
conv: CallingConvention,
|
||||||
) -> Result<GuestReg, QemuRWError> {
|
) -> Result<GuestReg, QemuRWError> {
|
||||||
QemuRWError::check_conv(QemuRWErrorKind::Read, CallingConvention::Cdecl, conv)?;
|
QemuRWError::check_conv(QemuRWErrorKind::Read, CallingConvention::Aapcs64, conv)?;
|
||||||
|
|
||||||
let reg_id = match idx {
|
match idx {
|
||||||
0 => Regs::X0,
|
0 => self.read_reg(Regs::X0),
|
||||||
1 => Regs::X1,
|
1 => self.read_reg(Regs::X1),
|
||||||
2 => Regs::X2,
|
2 => self.read_reg(Regs::X2),
|
||||||
3 => Regs::X3,
|
3 => self.read_reg(Regs::X3),
|
||||||
4 => Regs::X4,
|
4 => self.read_reg(Regs::X4),
|
||||||
5 => Regs::X5,
|
5 => self.read_reg(Regs::X5),
|
||||||
r => {
|
6 => self.read_reg(Regs::X6),
|
||||||
return Err(QemuRWError::new_argument_error(
|
7 => self.read_reg(Regs::X7),
|
||||||
QemuRWErrorKind::Read,
|
_ => {
|
||||||
i32::from(r),
|
const SIZE: usize = size_of::<GuestReg>();
|
||||||
))
|
let stack_ptr: GuestAddr = self.read_reg(Regs::Rsp)?;
|
||||||
|
/*
|
||||||
|
* Stack is full and descending. SP points to return address, arguments
|
||||||
|
* are in reverse order above that. 8th argument is at SP + 8.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let offset = (SIZE as GuestAddr) * (GuestAddr::from(idx) - 7);
|
||||||
|
let mut buf = [0; SIZE];
|
||||||
|
self.read_mem(stack_ptr + offset, &mut buf)?;
|
||||||
|
|
||||||
|
#[cfg(feature = "be")]
|
||||||
|
{
|
||||||
|
Ok(GuestReg::from_le_bytes(buf).into())
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "be"))]
|
||||||
|
Ok(GuestReg::from_le_bytes(buf).into())
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
self.read_reg(reg_id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_function_argument<T>(
|
fn write_function_argument_with_cc<T>(
|
||||||
&self,
|
&self,
|
||||||
conv: CallingConvention,
|
idx: u8,
|
||||||
idx: i32,
|
|
||||||
val: T,
|
val: T,
|
||||||
|
conv: CallingConvention,
|
||||||
) -> Result<(), QemuRWError>
|
) -> Result<(), QemuRWError>
|
||||||
where
|
where
|
||||||
T: Into<GuestReg>,
|
T: Into<GuestReg>,
|
||||||
{
|
{
|
||||||
QemuRWError::check_conv(QemuRWErrorKind::Write, CallingConvention::Cdecl, conv)?;
|
QemuRWError::check_conv(QemuRWErrorKind::Write, CallingConvention::Aapcs64, conv)?;
|
||||||
|
|
||||||
let val: GuestReg = val.into();
|
let val: GuestReg = val.into();
|
||||||
match idx {
|
match idx {
|
||||||
0 => self.write_reg(Regs::X0, val),
|
0 => self.write_reg(Regs::X0, val),
|
||||||
1 => self.write_reg(Regs::X1, val),
|
1 => self.write_reg(Regs::X1, val),
|
||||||
r => Err(QemuRWError::new_argument_error(QemuRWErrorKind::Write, r)),
|
2 => self.write_reg(Regs::X2, val),
|
||||||
|
3 => self.write_reg(Regs::X3, val),
|
||||||
|
4 => self.write_reg(Regs::X4, val),
|
||||||
|
5 => self.write_reg(Regs::X5, val),
|
||||||
|
6 => self.write_reg(Regs::X6, val),
|
||||||
|
7 => self.write_reg(Regs::X7, val),
|
||||||
|
_ => {
|
||||||
|
let val: GuestReg = val.into();
|
||||||
|
let stack_ptr: GuestAddr = self.read_reg(Regs::Sp)?;
|
||||||
|
/*
|
||||||
|
* Stack is full and descending. SP points to return address, arguments
|
||||||
|
* are in reverse order above that. 8th argument is at SP + 8.
|
||||||
|
*/
|
||||||
|
let size: GuestAddr = size_of::<GuestReg>() as GuestAddr;
|
||||||
|
let offset = size * (GuestAddr::from(idx) - 7);
|
||||||
|
#[cfg(feature = "be")]
|
||||||
|
let arg = val.to_be_bytes();
|
||||||
|
|
||||||
|
#[cfg(not(feature = "be"))]
|
||||||
|
let arg = val.to_le_bytes();
|
||||||
|
self.write_mem(stack_ptr + offset, &arg)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,12 @@ use pyo3::prelude::*;
|
|||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
pub use syscall_numbers::arm::*;
|
pub use syscall_numbers::arm::*;
|
||||||
|
|
||||||
use crate::{sync_exit::ExitArgs, CallingConvention, QemuRWError, QemuRWErrorKind};
|
use crate::{sync_exit::ExitArgs, CallingConvention, GuestAddr, QemuRWError, QemuRWErrorKind};
|
||||||
|
|
||||||
|
#[expect(non_upper_case_globals)]
|
||||||
|
impl CallingConvention {
|
||||||
|
pub const Default: CallingConvention = CallingConvention::Aapcs;
|
||||||
|
}
|
||||||
|
|
||||||
/// Registers for the ARM instruction set.
|
/// Registers for the ARM instruction set.
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
@ -91,46 +96,77 @@ impl crate::ArchExtras for crate::CPU {
|
|||||||
self.write_reg(Regs::Lr, val)
|
self.write_reg(Regs::Lr, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_function_argument(
|
fn read_function_argument_with_cc(
|
||||||
&self,
|
&self,
|
||||||
conv: CallingConvention,
|
|
||||||
idx: u8,
|
idx: u8,
|
||||||
|
conv: CallingConvention,
|
||||||
) -> Result<GuestReg, QemuRWError> {
|
) -> Result<GuestReg, QemuRWError> {
|
||||||
QemuRWError::check_conv(QemuRWErrorKind::Read, CallingConvention::Cdecl, conv)?;
|
QemuRWError::check_conv(QemuRWErrorKind::Read, CallingConvention::Aapcs, conv)?;
|
||||||
|
|
||||||
let reg_id = match idx {
|
match idx {
|
||||||
0 => Regs::R0,
|
0 => self.read_reg(Regs::R0),
|
||||||
1 => Regs::R1,
|
1 => self.read_reg(Regs::R1),
|
||||||
2 => Regs::R2,
|
2 => self.read_reg(Regs::R2),
|
||||||
3 => Regs::R3,
|
3 => self.read_reg(Regs::R3),
|
||||||
// 4.. would be on the stack, let's not do this for now
|
_ => {
|
||||||
r => {
|
const SIZE: usize = size_of::<GuestReg>();
|
||||||
return Err(QemuRWError::new_argument_error(
|
let stack_ptr: GuestAddr = self.read_reg(Regs::Sp)?;
|
||||||
QemuRWErrorKind::Read,
|
/*
|
||||||
i32::from(r),
|
* Stack is full and descending. SP points to return address, arguments
|
||||||
))
|
* are in reverse order above that. 4th argument is at SP + 8.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let offset = (SIZE as GuestAddr) * (GuestAddr::from(idx) - 3);
|
||||||
|
let mut buf = [0; SIZE];
|
||||||
|
self.read_mem(stack_ptr + offset, &mut buf)?;
|
||||||
|
|
||||||
|
#[cfg(feature = "be")]
|
||||||
|
{
|
||||||
|
Ok(GuestReg::from_le_bytes(buf).into())
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "be"))]
|
||||||
|
Ok(GuestReg::from_le_bytes(buf).into())
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
self.read_reg(reg_id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_function_argument<T>(
|
fn write_function_argument_with_cc<T>(
|
||||||
&self,
|
&self,
|
||||||
conv: CallingConvention,
|
idx: u8,
|
||||||
idx: i32,
|
|
||||||
val: T,
|
val: T,
|
||||||
|
conv: CallingConvention,
|
||||||
) -> Result<(), QemuRWError>
|
) -> Result<(), QemuRWError>
|
||||||
where
|
where
|
||||||
T: Into<GuestReg>,
|
T: Into<GuestReg>,
|
||||||
{
|
{
|
||||||
QemuRWError::check_conv(QemuRWErrorKind::Write, CallingConvention::Cdecl, conv)?;
|
QemuRWError::check_conv(QemuRWErrorKind::Write, CallingConvention::Aapcs, conv)?;
|
||||||
|
|
||||||
let val: GuestReg = val.into();
|
let val: GuestReg = val.into();
|
||||||
match idx {
|
match idx {
|
||||||
0 => self.write_reg(Regs::R0, val),
|
0 => self.write_reg(Regs::R0, val),
|
||||||
1 => self.write_reg(Regs::R1, val),
|
1 => self.write_reg(Regs::R1, val),
|
||||||
r => Err(QemuRWError::new_argument_error(QemuRWErrorKind::Write, r)),
|
2 => self.write_reg(Regs::R2, val),
|
||||||
|
3 => self.write_reg(Regs::R3, val),
|
||||||
|
_ => {
|
||||||
|
let val: GuestReg = val.into();
|
||||||
|
let stack_ptr: GuestAddr = self.read_reg(Regs::Sp)?;
|
||||||
|
/*
|
||||||
|
* Stack is full and descending. SP points to return address, arguments
|
||||||
|
* are in reverse order above that. 4th argument is at SP + 4.
|
||||||
|
*/
|
||||||
|
let size: GuestAddr = size_of::<GuestReg>() as GuestAddr;
|
||||||
|
let offset = size * (GuestAddr::from(idx) - 3);
|
||||||
|
|
||||||
|
#[cfg(feature = "be")]
|
||||||
|
let arg = val.to_be_bytes();
|
||||||
|
|
||||||
|
#[cfg(not(feature = "be"))]
|
||||||
|
let arg = val.to_le_bytes();
|
||||||
|
|
||||||
|
self.write_mem(stack_ptr + offset, &arg)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,11 @@ pub use strum_macros::EnumIter;
|
|||||||
|
|
||||||
use crate::{sync_exit::ExitArgs, CallingConvention, QemuRWError, QemuRWErrorKind};
|
use crate::{sync_exit::ExitArgs, CallingConvention, QemuRWError, QemuRWErrorKind};
|
||||||
|
|
||||||
|
#[expect(non_upper_case_globals)]
|
||||||
|
impl CallingConvention {
|
||||||
|
pub const Default: CallingConvention = CallingConvention::Hexagon;
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
pub enum Regs {
|
pub enum Regs {
|
||||||
@ -103,12 +108,12 @@ impl crate::ArchExtras for crate::CPU {
|
|||||||
self.write_reg(Regs::Lr, val)
|
self.write_reg(Regs::Lr, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_function_argument(
|
fn read_function_argument_with_cc(
|
||||||
&self,
|
&self,
|
||||||
conv: CallingConvention,
|
|
||||||
idx: u8,
|
idx: u8,
|
||||||
|
conv: CallingConvention,
|
||||||
) -> Result<GuestReg, QemuRWError> {
|
) -> Result<GuestReg, QemuRWError> {
|
||||||
QemuRWError::check_conv(QemuRWErrorKind::Read, CallingConvention::Cdecl, conv)?;
|
QemuRWError::check_conv(QemuRWErrorKind::Read, CallingConvention::Hexagon, conv)?;
|
||||||
|
|
||||||
// Note that 64 bit values may be passed in two registers (and may have padding), then this mapping is off.
|
// Note that 64 bit values may be passed in two registers (and may have padding), then this mapping is off.
|
||||||
let reg_id = match idx {
|
let reg_id = match idx {
|
||||||
@ -118,29 +123,33 @@ impl crate::ArchExtras for crate::CPU {
|
|||||||
3 => Regs::R3,
|
3 => Regs::R3,
|
||||||
4 => Regs::R4,
|
4 => Regs::R4,
|
||||||
5 => Regs::R5,
|
5 => Regs::R5,
|
||||||
r => {
|
r => return Err(QemuRWError::new_argument_error(QemuRWErrorKind::Read, r)),
|
||||||
return Err(QemuRWError::new_argument_error(
|
|
||||||
QemuRWErrorKind::Read,
|
|
||||||
i32::from(r),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
self.read_reg(reg_id)
|
self.read_reg(reg_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_function_argument<T>(
|
fn write_function_argument_with_cc<T>(
|
||||||
&self,
|
&self,
|
||||||
|
idx: u8,
|
||||||
|
val: T,
|
||||||
conv: CallingConvention,
|
conv: CallingConvention,
|
||||||
idx: i32,
|
|
||||||
_val: T,
|
|
||||||
) -> Result<(), QemuRWError>
|
) -> Result<(), QemuRWError>
|
||||||
where
|
where
|
||||||
T: Into<GuestReg>,
|
T: Into<GuestReg>,
|
||||||
{
|
{
|
||||||
QemuRWError::check_conv(QemuRWErrorKind::Write, CallingConvention::Cdecl, conv)?;
|
QemuRWError::check_conv(QemuRWErrorKind::Write, CallingConvention::Hexagon, conv)?;
|
||||||
|
|
||||||
// TODO
|
// Note that 64 bit values may be passed in two registers (and may have padding), then this mapping is off.
|
||||||
Err(QemuRWError::new_argument_error(QemuRWErrorKind::Write, idx))
|
let val: GuestReg = val.into();
|
||||||
|
match idx {
|
||||||
|
0 => self.write_reg(Regs::R0, val),
|
||||||
|
1 => self.write_reg(Regs::R1, val),
|
||||||
|
2 => self.write_reg(Regs::R2, val),
|
||||||
|
3 => self.write_reg(Regs::R3, val),
|
||||||
|
4 => self.write_reg(Regs::R4, val),
|
||||||
|
5 => self.write_reg(Regs::R5, val),
|
||||||
|
r => Err(QemuRWError::new_argument_error(QemuRWErrorKind::Write, r)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,11 @@ pub use syscall_numbers::x86::*;
|
|||||||
|
|
||||||
use crate::{sync_exit::ExitArgs, CallingConvention, GuestAddr, QemuRWError, QemuRWErrorKind};
|
use crate::{sync_exit::ExitArgs, CallingConvention, GuestAddr, QemuRWError, QemuRWErrorKind};
|
||||||
|
|
||||||
|
#[expect(non_upper_case_globals)]
|
||||||
|
impl CallingConvention {
|
||||||
|
pub const Default: CallingConvention = CallingConvention::Cdecl;
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
pub enum Regs {
|
pub enum Regs {
|
||||||
@ -77,43 +82,37 @@ impl crate::ArchExtras for crate::CPU {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_function_argument(
|
fn read_function_argument_with_cc(
|
||||||
&self,
|
&self,
|
||||||
conv: CallingConvention,
|
|
||||||
idx: u8,
|
idx: u8,
|
||||||
|
conv: CallingConvention,
|
||||||
) -> Result<GuestReg, QemuRWError> {
|
) -> Result<GuestReg, QemuRWError> {
|
||||||
QemuRWError::check_conv(QemuRWErrorKind::Read, CallingConvention::Cdecl, conv)?;
|
QemuRWError::check_conv(QemuRWErrorKind::Read, CallingConvention::Cdecl, conv)?;
|
||||||
|
|
||||||
match idx {
|
match idx {
|
||||||
0..=1 => {
|
_ => {
|
||||||
|
const SIZE: usize = size_of::<GuestReg>();
|
||||||
let stack_ptr: GuestAddr = self.read_reg(Regs::Sp)?;
|
let stack_ptr: GuestAddr = self.read_reg(Regs::Sp)?;
|
||||||
/*
|
/*
|
||||||
* Stack is full and descending. SP points to return address, arguments
|
* Stack is full and descending. SP points to return address, arguments
|
||||||
* are in reverse order above that.
|
* are in reverse order above that.
|
||||||
*/
|
*/
|
||||||
let size: GuestAddr = size_of::<GuestReg>() as GuestAddr;
|
|
||||||
let offset = size * (idx as GuestAddr + 1);
|
|
||||||
|
|
||||||
let mut val = [0u8; size_of::<GuestReg>()];
|
let offset = (SIZE as GuestAddr) * (GuestAddr::from(idx) + 1);
|
||||||
|
let mut val = [0u8; SIZE];
|
||||||
unsafe {
|
unsafe {
|
||||||
self.read_mem(stack_ptr + offset, &mut val);
|
self.read_mem(stack_ptr + offset, &mut val);
|
||||||
}
|
}
|
||||||
Ok(GuestReg::from_le_bytes(val).into())
|
Ok(GuestReg::from_le_bytes(val).into())
|
||||||
}
|
}
|
||||||
r => {
|
|
||||||
return Err(QemuRWError::new_argument_error(
|
|
||||||
QemuRWErrorKind::Read,
|
|
||||||
i32::from(r),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_function_argument<T>(
|
fn write_function_argument_with_cc<T>(
|
||||||
&self,
|
&self,
|
||||||
conv: CallingConvention,
|
idx: u8,
|
||||||
idx: i32,
|
|
||||||
val: T,
|
val: T,
|
||||||
|
conv: CallingConvention,
|
||||||
) -> Result<(), QemuRWError>
|
) -> Result<(), QemuRWError>
|
||||||
where
|
where
|
||||||
T: Into<GuestReg>,
|
T: Into<GuestReg>,
|
||||||
@ -121,7 +120,7 @@ impl crate::ArchExtras for crate::CPU {
|
|||||||
QemuRWError::check_conv(QemuRWErrorKind::Write, CallingConvention::Cdecl, conv)?;
|
QemuRWError::check_conv(QemuRWErrorKind::Write, CallingConvention::Cdecl, conv)?;
|
||||||
|
|
||||||
match idx {
|
match idx {
|
||||||
0..=1 => {
|
_ => {
|
||||||
let val: GuestReg = val.into();
|
let val: GuestReg = val.into();
|
||||||
let stack_ptr: GuestAddr = self.read_reg(Regs::Sp)?;
|
let stack_ptr: GuestAddr = self.read_reg(Regs::Sp)?;
|
||||||
/*
|
/*
|
||||||
@ -129,7 +128,7 @@ impl crate::ArchExtras for crate::CPU {
|
|||||||
* are in reverse order above that.
|
* are in reverse order above that.
|
||||||
*/
|
*/
|
||||||
let size: GuestAddr = size_of::<GuestReg>() as GuestAddr;
|
let size: GuestAddr = size_of::<GuestReg>() as GuestAddr;
|
||||||
let offset = size * (idx as GuestAddr + 1);
|
let offset = size * (GuestAddr::from(idx) + 1);
|
||||||
|
|
||||||
let arg = val.to_le_bytes();
|
let arg = val.to_le_bytes();
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -9,6 +9,11 @@ pub use syscall_numbers::mips::*;
|
|||||||
|
|
||||||
use crate::{sync_exit::ExitArgs, CallingConvention, QemuRWError, QemuRWErrorKind};
|
use crate::{sync_exit::ExitArgs, CallingConvention, QemuRWError, QemuRWErrorKind};
|
||||||
|
|
||||||
|
#[expect(non_upper_case_globals)]
|
||||||
|
impl CallingConvention {
|
||||||
|
pub const Default: CallingConvention = CallingConvention::MipsO32;
|
||||||
|
}
|
||||||
|
|
||||||
/// Registers for the MIPS instruction set.
|
/// Registers for the MIPS instruction set.
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
@ -91,12 +96,12 @@ impl crate::ArchExtras for crate::CPU {
|
|||||||
self.write_reg(Regs::Ra, val)
|
self.write_reg(Regs::Ra, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_function_argument(
|
fn read_function_argument_with_cc(
|
||||||
&self,
|
&self,
|
||||||
conv: CallingConvention,
|
|
||||||
idx: u8,
|
idx: u8,
|
||||||
|
conv: CallingConvention,
|
||||||
) -> Result<GuestReg, QemuRWError> {
|
) -> Result<GuestReg, QemuRWError> {
|
||||||
QemuRWError::check_conv(QemuRWErrorKind::Read, CallingConvention::Cdecl, conv)?;
|
QemuRWError::check_conv(QemuRWErrorKind::Read, CallingConvention::MipsO32, conv)?;
|
||||||
|
|
||||||
let reg_id = match idx {
|
let reg_id = match idx {
|
||||||
0 => Regs::A0,
|
0 => Regs::A0,
|
||||||
@ -104,32 +109,30 @@ impl crate::ArchExtras for crate::CPU {
|
|||||||
2 => Regs::A2,
|
2 => Regs::A2,
|
||||||
3 => Regs::A3,
|
3 => Regs::A3,
|
||||||
// 4.. would be on the stack, let's not do this for now
|
// 4.. would be on the stack, let's not do this for now
|
||||||
r => {
|
r => return Err(QemuRWError::new_argument_error(QemuRWErrorKind::Read, r)),
|
||||||
return Err(QemuRWError::new_argument_error(
|
|
||||||
QemuRWErrorKind::Read,
|
|
||||||
i32::from(r),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
self.read_reg(reg_id)
|
self.read_reg(reg_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_function_argument<T>(
|
fn write_function_argument_with_cc<T>(
|
||||||
&self,
|
&self,
|
||||||
conv: CallingConvention,
|
idx: u8,
|
||||||
idx: i32,
|
|
||||||
val: T,
|
val: T,
|
||||||
|
conv: CallingConvention,
|
||||||
) -> Result<(), QemuRWError>
|
) -> Result<(), QemuRWError>
|
||||||
where
|
where
|
||||||
T: Into<GuestReg>,
|
T: Into<GuestReg>,
|
||||||
{
|
{
|
||||||
QemuRWError::check_conv(QemuRWErrorKind::Write, CallingConvention::Cdecl, conv)?;
|
QemuRWError::check_conv(QemuRWErrorKind::Write, CallingConvention::MipsO32, conv)?;
|
||||||
|
|
||||||
let val: GuestReg = val.into();
|
let val: GuestReg = val.into();
|
||||||
match idx {
|
match idx {
|
||||||
0 => self.write_reg(Regs::A0, val),
|
0 => self.write_reg(Regs::A0, val),
|
||||||
1 => self.write_reg(Regs::A1, val),
|
1 => self.write_reg(Regs::A1, val),
|
||||||
|
2 => self.write_reg(Regs::A2, val),
|
||||||
|
3 => self.write_reg(Regs::A3, val),
|
||||||
|
// 4.. would be on the stack, let's not do this for now
|
||||||
r => Err(QemuRWError::new_argument_error(QemuRWErrorKind::Write, r)),
|
r => Err(QemuRWError::new_argument_error(QemuRWErrorKind::Write, r)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,11 @@ pub use syscall_numbers::powerpc::*;
|
|||||||
|
|
||||||
use crate::{sync_exit::ExitArgs, CallingConvention, QemuRWError, QemuRWErrorKind};
|
use crate::{sync_exit::ExitArgs, CallingConvention, QemuRWError, QemuRWErrorKind};
|
||||||
|
|
||||||
|
#[expect(non_upper_case_globals)]
|
||||||
|
impl CallingConvention {
|
||||||
|
pub const Default: CallingConvention = CallingConvention::Ppc32;
|
||||||
|
}
|
||||||
|
|
||||||
/// Registers for the MIPS instruction set.
|
/// Registers for the MIPS instruction set.
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
@ -131,12 +136,12 @@ impl crate::ArchExtras for crate::CPU {
|
|||||||
self.write_reg(Regs::Lr, val)
|
self.write_reg(Regs::Lr, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_function_argument(
|
fn read_function_argument_with_cc(
|
||||||
&self,
|
&self,
|
||||||
conv: CallingConvention,
|
|
||||||
idx: u8,
|
idx: u8,
|
||||||
|
conv: CallingConvention,
|
||||||
) -> Result<GuestReg, QemuRWError> {
|
) -> Result<GuestReg, QemuRWError> {
|
||||||
QemuRWError::check_conv(QemuRWErrorKind::Read, CallingConvention::Cdecl, conv)?;
|
QemuRWError::check_conv(QemuRWErrorKind::Read, CallingConvention::Ppc32, conv)?;
|
||||||
|
|
||||||
let reg_id = match idx {
|
let reg_id = match idx {
|
||||||
0 => Regs::R3,
|
0 => Regs::R3,
|
||||||
@ -145,27 +150,22 @@ impl crate::ArchExtras for crate::CPU {
|
|||||||
3 => Regs::R6,
|
3 => Regs::R6,
|
||||||
4 => Regs::R7,
|
4 => Regs::R7,
|
||||||
5 => Regs::R8,
|
5 => Regs::R8,
|
||||||
r => {
|
r => return Err(QemuRWError::new_argument_error(QemuRWErrorKind::Read, r)),
|
||||||
return Err(QemuRWError::new_argument_error(
|
|
||||||
QemuRWErrorKind::Read,
|
|
||||||
i32::from(r),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
self.read_reg(reg_id)
|
self.read_reg(reg_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_function_argument<T>(
|
fn write_function_argument_with_cc<T>(
|
||||||
&self,
|
&self,
|
||||||
conv: CallingConvention,
|
idx: u8,
|
||||||
idx: i32,
|
|
||||||
val: T,
|
val: T,
|
||||||
|
conv: CallingConvention,
|
||||||
) -> Result<(), QemuRWError>
|
) -> Result<(), QemuRWError>
|
||||||
where
|
where
|
||||||
T: Into<GuestReg>,
|
T: Into<GuestReg>,
|
||||||
{
|
{
|
||||||
QemuRWError::check_conv(QemuRWErrorKind::Write, CallingConvention::Cdecl, conv)?;
|
QemuRWError::check_conv(QemuRWErrorKind::Write, CallingConvention::Ppc32, conv)?;
|
||||||
|
|
||||||
let val: GuestReg = val.into();
|
let val: GuestReg = val.into();
|
||||||
match idx {
|
match idx {
|
||||||
|
@ -22,6 +22,11 @@ pub const SYS_riscv_hwprobe: c_long = SYS_arch_specific_syscall + 14;
|
|||||||
|
|
||||||
use crate::{sync_exit::ExitArgs, CallingConvention, QemuRWError, QemuRWErrorKind};
|
use crate::{sync_exit::ExitArgs, CallingConvention, QemuRWError, QemuRWErrorKind};
|
||||||
|
|
||||||
|
#[expect(non_upper_case_globals)]
|
||||||
|
impl CallingConvention {
|
||||||
|
pub const Default: CallingConvention = CallingConvention::RiscVilp32;
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
pub enum Regs {
|
pub enum Regs {
|
||||||
@ -106,12 +111,12 @@ impl crate::ArchExtras for crate::CPU {
|
|||||||
self.write_reg(Regs::Ra, val)
|
self.write_reg(Regs::Ra, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_function_argument(
|
fn read_function_argument_with_cc(
|
||||||
&self,
|
&self,
|
||||||
conv: CallingConvention,
|
|
||||||
idx: u8,
|
idx: u8,
|
||||||
|
conv: CallingConvention,
|
||||||
) -> Result<GuestReg, QemuRWError> {
|
) -> Result<GuestReg, QemuRWError> {
|
||||||
QemuRWError::check_conv(QemuRWErrorKind::Read, CallingConvention::Cdecl, conv)?;
|
QemuRWError::check_conv(QemuRWErrorKind::Read, CallingConvention::RiscVilp32, conv)?;
|
||||||
|
|
||||||
// Note that 64 bit values may be passed in two registers (and are even-odd eg. A0, A2 and A3 where A1 is empty), then this mapping is off.
|
// Note that 64 bit values may be passed in two registers (and are even-odd eg. A0, A2 and A3 where A1 is empty), then this mapping is off.
|
||||||
// Note: This does not consider the floating point registers.
|
// Note: This does not consider the floating point registers.
|
||||||
@ -125,27 +130,22 @@ impl crate::ArchExtras for crate::CPU {
|
|||||||
5 => Regs::A5, // argument value
|
5 => Regs::A5, // argument value
|
||||||
6 => Regs::A6, // argument value
|
6 => Regs::A6, // argument value
|
||||||
7 => Regs::A7, // argument value
|
7 => Regs::A7, // argument value
|
||||||
r => {
|
r => return Err(QemuRWError::new_argument_error(QemuRWErrorKind::Read, r)),
|
||||||
return Err(QemuRWError::new_argument_error(
|
|
||||||
QemuRWErrorKind::Read,
|
|
||||||
i32::from(r),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
self.read_reg(reg_id)
|
self.read_reg(reg_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_function_argument<T>(
|
fn write_function_argument_with_cc<T>(
|
||||||
&self,
|
&self,
|
||||||
conv: CallingConvention,
|
idx: u8,
|
||||||
idx: i32,
|
|
||||||
val: T,
|
val: T,
|
||||||
|
conv: CallingConvention,
|
||||||
) -> Result<(), QemuRWError>
|
) -> Result<(), QemuRWError>
|
||||||
where
|
where
|
||||||
T: Into<GuestReg>,
|
T: Into<GuestReg>,
|
||||||
{
|
{
|
||||||
QemuRWError::check_conv(QemuRWErrorKind::Write, CallingConvention::Cdecl, conv)?;
|
QemuRWError::check_conv(QemuRWErrorKind::Write, CallingConvention::RiscVilp32, conv)?;
|
||||||
|
|
||||||
let val: GuestReg = val.into();
|
let val: GuestReg = val.into();
|
||||||
match idx {
|
match idx {
|
||||||
|
@ -6,7 +6,12 @@ use num_enum::{IntoPrimitive, TryFromPrimitive};
|
|||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
pub use syscall_numbers::x86_64::*;
|
pub use syscall_numbers::x86_64::*;
|
||||||
|
|
||||||
use crate::{sync_exit::ExitArgs, CallingConvention, QemuRWError, QemuRWErrorKind};
|
use crate::{sync_exit::ExitArgs, CallingConvention, GuestAddr, QemuRWError, QemuRWErrorKind};
|
||||||
|
|
||||||
|
#[expect(non_upper_case_globals)]
|
||||||
|
impl CallingConvention {
|
||||||
|
pub const Default: CallingConvention = CallingConvention::SystemV;
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
@ -84,47 +89,68 @@ impl crate::ArchExtras for crate::CPU {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_function_argument(
|
fn read_function_argument_with_cc(
|
||||||
&self,
|
&self,
|
||||||
conv: CallingConvention,
|
|
||||||
idx: u8,
|
idx: u8,
|
||||||
|
conv: CallingConvention,
|
||||||
) -> Result<GuestReg, QemuRWError> {
|
) -> Result<GuestReg, QemuRWError> {
|
||||||
QemuRWError::check_conv(QemuRWErrorKind::Read, CallingConvention::Cdecl, conv)?;
|
QemuRWError::check_conv(QemuRWErrorKind::Read, CallingConvention::SystemV, conv)?;
|
||||||
|
|
||||||
let reg_id = match idx {
|
match idx {
|
||||||
0 => Regs::Rdi,
|
0 => self.read_reg(Regs::Rdi),
|
||||||
1 => Regs::Rsi,
|
1 => self.read_reg(Regs::Rsi),
|
||||||
2 => Regs::Rdx,
|
2 => self.read_reg(Regs::Rdx),
|
||||||
3 => Regs::Rcx,
|
3 => self.read_reg(Regs::Rcx),
|
||||||
4 => Regs::R8,
|
4 => self.read_reg(Regs::R8),
|
||||||
5 => Regs::R9,
|
5 => self.read_reg(Regs::R9),
|
||||||
r => {
|
_ => {
|
||||||
return Err(QemuRWError::new_argument_error(
|
const SIZE: usize = size_of::<GuestReg>();
|
||||||
QemuRWErrorKind::Read,
|
let stack_ptr: GuestAddr = self.read_reg(Regs::Sp)?;
|
||||||
i32::from(r),
|
/*
|
||||||
))
|
* Stack is full and descending. SP points to return address, arguments
|
||||||
|
* are in reverse order above that. 6th argument is at SP + 8.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let offset = (SIZE as GuestAddr) * (GuestAddr::from(idx) - 5);
|
||||||
|
let mut buf = [0; SIZE];
|
||||||
|
self.read_mem(stack_ptr + offset, &mut buf)?;
|
||||||
|
|
||||||
|
Ok(GuestAddr::from_le_bytes(buf))
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
self.read_reg(reg_id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_function_argument<T>(
|
fn write_function_argument_with_cc<T>(
|
||||||
&self,
|
&self,
|
||||||
conv: CallingConvention,
|
idx: u8,
|
||||||
idx: i32,
|
|
||||||
val: T,
|
val: T,
|
||||||
|
conv: CallingConvention,
|
||||||
) -> Result<(), QemuRWError>
|
) -> Result<(), QemuRWError>
|
||||||
where
|
where
|
||||||
T: Into<GuestReg>,
|
T: Into<GuestReg>,
|
||||||
{
|
{
|
||||||
QemuRWError::check_conv(QemuRWErrorKind::Write, CallingConvention::Cdecl, conv)?;
|
QemuRWError::check_conv(QemuRWErrorKind::Write, CallingConvention::SystemV, conv)?;
|
||||||
|
|
||||||
let val: GuestReg = val.into();
|
let val: GuestReg = val.into();
|
||||||
match idx {
|
match idx {
|
||||||
0 => self.write_reg(Regs::Rdi, val),
|
0 => self.write_reg(Regs::Rdi, val),
|
||||||
1 => self.write_reg(Regs::Rsi, val),
|
1 => self.write_reg(Regs::Rsi, val),
|
||||||
r => Err(QemuRWError::new_argument_error(QemuRWErrorKind::Write, r)),
|
2 => self.write_reg(Regs::Rdx, val),
|
||||||
|
3 => self.write_reg(Regs::Rcx, val),
|
||||||
|
4 => self.write_reg(Regs::R8, val),
|
||||||
|
5 => self.write_reg(Regs::R9, val),
|
||||||
|
_ => {
|
||||||
|
let val: GuestReg = val.into();
|
||||||
|
let stack_ptr: GuestAddr = self.read_reg(Regs::Rsp)?;
|
||||||
|
/*
|
||||||
|
* Stack is full and descending. SP points to return address, arguments
|
||||||
|
* are in reverse order above that. 6th argument is at SP + 8.
|
||||||
|
*/
|
||||||
|
let size: GuestAddr = size_of::<GuestReg>() as GuestAddr;
|
||||||
|
let offset = size * (GuestAddr::from(idx) - 5);
|
||||||
|
let arg = val.to_le_bytes();
|
||||||
|
self.write_mem(stack_ptr + offset, &arg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,10 +12,10 @@ pub use libafl_targets::{
|
|||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
|
use crate::capstone;
|
||||||
#[cfg(feature = "systemmode")]
|
#[cfg(feature = "systemmode")]
|
||||||
use crate::modules::utils::filters::{NopPageFilter, NOP_PAGE_FILTER};
|
use crate::modules::utils::filters::{NopPageFilter, NOP_PAGE_FILTER};
|
||||||
#[cfg(feature = "usermode")]
|
|
||||||
use crate::{capstone, qemu::ArchExtras, CallingConvention};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
emu::EmulatorModules,
|
emu::EmulatorModules,
|
||||||
modules::{
|
modules::{
|
||||||
@ -301,12 +301,8 @@ impl CmpLogRoutinesModule {
|
|||||||
|
|
||||||
let qemu = Qemu::get().unwrap();
|
let qemu = Qemu::get().unwrap();
|
||||||
|
|
||||||
let a0: GuestAddr = qemu
|
let a0: GuestAddr = qemu.read_function_argument(0).unwrap_or(0);
|
||||||
.read_function_argument(CallingConvention::Cdecl, 0)
|
let a1: GuestAddr = qemu.read_function_argument(1).unwrap_or(0);
|
||||||
.unwrap_or(0);
|
|
||||||
let a1: GuestAddr = qemu
|
|
||||||
.read_function_argument(CallingConvention::Cdecl, 1)
|
|
||||||
.unwrap_or(0);
|
|
||||||
|
|
||||||
if a0 == 0 || a1 == 0 {
|
if a0 == 0 || a1 == 0 {
|
||||||
return;
|
return;
|
||||||
|
@ -228,7 +228,7 @@ impl InjectionModule {
|
|||||||
let reg: GuestAddr = qemu
|
let reg: GuestAddr = qemu
|
||||||
.current_cpu()
|
.current_cpu()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read_function_argument(CallingConvention::Cdecl, parameter)
|
.read_function_argument_with_cc(parameter, CallingConvention::Default)
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let module = emulator_modules.get_mut::<Self>().unwrap();
|
let module = emulator_modules.get_mut::<Self>().unwrap();
|
||||||
|
@ -36,7 +36,7 @@ pub enum QemuRWErrorKind {
|
|||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum QemuRWErrorCause {
|
pub enum QemuRWErrorCause {
|
||||||
WrongCallingConvention(CallingConvention, CallingConvention), // expected, given
|
WrongCallingConvention(CallingConvention, CallingConvention), // expected, given
|
||||||
WrongArgument(i32),
|
WrongArgument(u8),
|
||||||
CurrentCpuNotFound,
|
CurrentCpuNotFound,
|
||||||
Reg(i32),
|
Reg(i32),
|
||||||
WrongMemoryLocation(GuestAddr, usize), // addr, size
|
WrongMemoryLocation(GuestAddr, usize), // addr, size
|
||||||
@ -121,8 +121,8 @@ impl QemuRWError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new_argument_error(kind: QemuRWErrorKind, reg_id: i32) -> Self {
|
pub fn new_argument_error(kind: QemuRWErrorKind, arg_id: u8) -> Self {
|
||||||
Self::new(kind, QemuRWErrorCause::WrongArgument(reg_id), None)
|
Self::new(kind, QemuRWErrorCause::WrongArgument(arg_id), None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_conv(
|
pub fn check_conv(
|
||||||
|
@ -74,16 +74,17 @@ pub trait ArchExtras {
|
|||||||
fn write_return_address<T>(&self, val: T) -> Result<(), QemuRWError>
|
fn write_return_address<T>(&self, val: T) -> Result<(), QemuRWError>
|
||||||
where
|
where
|
||||||
T: Into<GuestReg>;
|
T: Into<GuestReg>;
|
||||||
fn read_function_argument(
|
fn read_function_argument_with_cc(
|
||||||
&self,
|
&self,
|
||||||
conv: CallingConvention,
|
|
||||||
idx: u8,
|
idx: u8,
|
||||||
) -> Result<GuestReg, QemuRWError>;
|
|
||||||
fn write_function_argument<T>(
|
|
||||||
&self,
|
|
||||||
conv: CallingConvention,
|
conv: CallingConvention,
|
||||||
idx: i32,
|
) -> Result<GuestReg, QemuRWError>;
|
||||||
|
|
||||||
|
fn write_function_argument_with_cc<T>(
|
||||||
|
&self,
|
||||||
|
idx: u8,
|
||||||
val: T,
|
val: T,
|
||||||
|
conv: CallingConvention,
|
||||||
) -> Result<(), QemuRWError>
|
) -> Result<(), QemuRWError>
|
||||||
where
|
where
|
||||||
T: Into<GuestReg>;
|
T: Into<GuestReg>;
|
||||||
@ -155,7 +156,14 @@ pub struct CPU {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum CallingConvention {
|
pub enum CallingConvention {
|
||||||
|
SystemV,
|
||||||
Cdecl,
|
Cdecl,
|
||||||
|
Aapcs64,
|
||||||
|
Aapcs,
|
||||||
|
Hexagon,
|
||||||
|
MipsO32,
|
||||||
|
Ppc32,
|
||||||
|
RiscVilp32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -937,6 +945,65 @@ impl Qemu {
|
|||||||
pub fn is_running(&self) -> bool {
|
pub fn is_running(&self) -> bool {
|
||||||
unsafe { QEMU_IS_RUNNING }
|
unsafe { QEMU_IS_RUNNING }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Write the function arguments by following default calling convention.
|
||||||
|
/// Assume that every arguments has integer/pointer type, otherwise the value
|
||||||
|
/// may be stored at wrong place because of different rules for complex types.
|
||||||
|
/// Note that the stack pointer register must point the top of the stack at the start
|
||||||
|
/// of the called function, in case the argument is written in the stack.
|
||||||
|
/// Support downward-growing stack only.
|
||||||
|
/// If you need to specify a calling convention, use [`Self::write_function_arguments_with_cc`].
|
||||||
|
pub fn write_function_arguments<T>(&mut self, val: &[T]) -> Result<(), QemuRWError>
|
||||||
|
where
|
||||||
|
T: Into<GuestReg> + Copy,
|
||||||
|
{
|
||||||
|
self.write_function_arguments_with_cc(val, &CallingConvention::Default)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write the function arguments by following calling convention `conv`.
|
||||||
|
/// Assume that every arguments has integer/pointer type, otherwise the value
|
||||||
|
/// may be stored at wrong place because of different rules for complex types.
|
||||||
|
/// Note that the stack pointer register must point the top of the stack at the start
|
||||||
|
/// of the called function, in case the argument is written in the stack.
|
||||||
|
/// Support downward-growing stack only.
|
||||||
|
pub fn write_function_arguments_with_cc<T>(
|
||||||
|
&mut self,
|
||||||
|
val: &[T],
|
||||||
|
conv: &CallingConvention,
|
||||||
|
) -> Result<(), QemuRWError>
|
||||||
|
where
|
||||||
|
T: Into<GuestReg> + Copy,
|
||||||
|
{
|
||||||
|
for (idx, elem) in val.iter().enumerate() {
|
||||||
|
self.write_function_argument_with_cc(idx as u8, elem.to_owned(), conv.clone())?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read the function `idx` argument by following default calling convention.
|
||||||
|
/// Assume that this argument and every prior arguments has integer/pointer type, otherwise
|
||||||
|
/// it may return a wrong value because of different rules for complex types.
|
||||||
|
/// Note that the stack pointer register must point the top of the stack at the start
|
||||||
|
/// of the called function, in case the value is in the stack.
|
||||||
|
/// Support downward-growing stack only.
|
||||||
|
/// If you need to specify a calling convention, use [`Self::read_function_argument_with_cc`].
|
||||||
|
pub fn read_function_argument(&self, idx: u8) -> Result<GuestReg, QemuRWError> {
|
||||||
|
self.read_function_argument_with_cc(idx, CallingConvention::Default)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write the function `val` into `idx` argument by following default calling convention.
|
||||||
|
/// Assume that `val` and every prior arguments has integer/pointer type, otherwise the value
|
||||||
|
/// may be stored at wrong place because of different rules for complex types.
|
||||||
|
/// Note that the stack pointer register must point the top of the stack at the start
|
||||||
|
/// of the called function, in case the argument is written in the stack.
|
||||||
|
/// Support downward-growing stack only.
|
||||||
|
/// If you need to specify a calling convention, use [`Self::write_function_argument_with_cc`].
|
||||||
|
pub fn write_function_argument<T>(&self, idx: u8, val: T) -> Result<(), QemuRWError>
|
||||||
|
where
|
||||||
|
T: Into<GuestReg>,
|
||||||
|
{
|
||||||
|
self.write_function_argument_with_cc(idx, val, CallingConvention::Default)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ArchExtras for Qemu {
|
impl ArchExtras for Qemu {
|
||||||
@ -955,28 +1022,40 @@ impl ArchExtras for Qemu {
|
|||||||
.write_return_address::<T>(val)
|
.write_return_address::<T>(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_function_argument(
|
/// Read the function `idx` argument by following calling convention `conv`.
|
||||||
|
/// Assume that this argument and every prior arguments has integer/pointer type, otherwise
|
||||||
|
/// it may return a wrong value because of different rules for complex types.
|
||||||
|
/// Note that the stack pointer register must point the top of the stack at the start
|
||||||
|
/// of the called function, in case the value is in the stack.
|
||||||
|
/// Support downward-growing stack only.
|
||||||
|
fn read_function_argument_with_cc(
|
||||||
&self,
|
&self,
|
||||||
conv: CallingConvention,
|
|
||||||
idx: u8,
|
idx: u8,
|
||||||
|
conv: CallingConvention,
|
||||||
) -> Result<GuestReg, QemuRWError> {
|
) -> Result<GuestReg, QemuRWError> {
|
||||||
self.current_cpu()
|
self.current_cpu()
|
||||||
.ok_or(QemuRWError::current_cpu_not_found(QemuRWErrorKind::Read))?
|
.ok_or(QemuRWError::current_cpu_not_found(QemuRWErrorKind::Read))?
|
||||||
.read_function_argument(conv, idx)
|
.read_function_argument_with_cc(idx, conv)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_function_argument<T>(
|
/// Write the function `val` into `idx` argument by following calling convention `conv`.
|
||||||
|
/// Assume that `val` and every prior arguments has integer/pointer type, otherwise the value
|
||||||
|
/// may be stored at wrong place because of different rules for complex types.
|
||||||
|
/// Note that the stack pointer register must point the top of the stack at the start
|
||||||
|
/// of the called function, in case the argument is written in the stack.
|
||||||
|
/// Support downward-growing stack only.
|
||||||
|
fn write_function_argument_with_cc<T>(
|
||||||
&self,
|
&self,
|
||||||
conv: CallingConvention,
|
idx: u8,
|
||||||
idx: i32,
|
|
||||||
val: T,
|
val: T,
|
||||||
|
conv: CallingConvention,
|
||||||
) -> Result<(), QemuRWError>
|
) -> Result<(), QemuRWError>
|
||||||
where
|
where
|
||||||
T: Into<GuestReg>,
|
T: Into<GuestReg>,
|
||||||
{
|
{
|
||||||
self.current_cpu()
|
self.current_cpu()
|
||||||
.ok_or(QemuRWError::current_cpu_not_found(QemuRWErrorKind::Write))?
|
.ok_or(QemuRWError::current_cpu_not_found(QemuRWErrorKind::Write))?
|
||||||
.write_function_argument::<T>(conv, idx, val)
|
.write_function_argument_with_cc::<T>(idx, val, conv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user