use proc_macro::TokenStream;
use quote::{quote, ToTokens};
use syn::{parse::Parse, FnArg, Signature, Token, Visibility};
struct Syscall {
signature: Signature,
id: syn::Expr,
vis: Visibility,
}
impl Parse for Syscall {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let id = input.parse::<syn::Expr>().expect("id");
input.parse::<Token![,]>().expect("comma");
let vis = input.parse::<Visibility>().expect("visibility");
let signature = input.parse::<Signature>().expect("signature");
Ok(Syscall { signature, id, vis })
}
}
#[proc_macro]
pub fn syscall(input: TokenStream) -> TokenStream {
let Syscall { signature, id, vis } = syn::parse_macro_input!(input as Syscall);
let ret = signature.output.clone().into_token_stream();
let syscall_function = {
let mut syscall_function: Signature =
syn::parse_quote! {extern "C" fn syscall(id: u64) #ret};
for i in 0..signature.inputs.len() {
let arg_name = syn::parse_str::<syn::Ident>(format!("arg{}", i).as_str()).unwrap();
let arg: FnArg = syn::parse_quote! {#arg_name: usize};
syscall_function.inputs.push(arg);
}
quote! {
#[naked]
#[allow(improper_ctypes)]
#syscall_function {
unsafe {
core::arch::naked_asm!(
"mov r10,rcx",
"syscall",
"ret",
)
}
}
}
};
let calling_args = {
let mut token_stream = proc_macro2::TokenStream::new();
for i in 0..signature.inputs.len() {
let arg = signature.inputs[i].clone();
let name = match arg {
FnArg::Receiver(_) => panic!("syscall cannot have receiver"),
FnArg::Typed(arg) => arg.pat.clone(),
};
token_stream.extend(quote! {
#name as usize,
});
}
token_stream
};
quote! {
#vis #signature {
#syscall_function
syscall(#id as u64, #calling_args)
}
}
.into()
}
#[proc_macro]
pub fn syscall_noret(input: TokenStream) -> TokenStream {
let Syscall { signature, id, vis } = syn::parse_macro_input!(input as Syscall);
let ret = signature.output.clone().into_token_stream();
let syscall_function = {
let mut syscall_function: Signature =
syn::parse_quote! {extern "C" fn syscall(id: u64) #ret};
for i in 0..signature.inputs.len() {
let arg_name = syn::parse_str::<syn::Ident>(format!("arg{}", i).as_str()).unwrap();
let arg: FnArg = syn::parse_quote! {#arg_name: usize};
syscall_function.inputs.push(arg);
}
quote! {
#[naked]
#[allow(improper_ctypes)]
#syscall_function {
unsafe {
core::arch::naked_asm!(
"mov r10,rcx",
"syscall",
"_b:",
"jmp _b",
)
}
}
}
};
let calling_args = {
let mut token_stream = proc_macro2::TokenStream::new();
for i in 0..signature.inputs.len() {
let arg = signature.inputs[i].clone();
let name = match arg {
FnArg::Receiver(_) => panic!("syscall cannot have receiver"),
FnArg::Typed(arg) => arg.pat.clone(),
};
token_stream.extend(quote! {
#name,
});
}
token_stream
};
quote! {
#vis #signature {
#syscall_function
syscall(#id as u64, #calling_args)
}
}
.into()
}