Skip to main content

ptrace_syscalls_macros/
lib.rs

1use convert_case::{Case, Casing};
2use proc_macro::TokenStream;
3use proc_macro2::Span;
4use proc_macro_crate::{crate_name, FoundCrate};
5use quote::{format_ident, quote, quote_spanned, ToTokens};
6use syn::{
7  braced, bracketed, parenthesized, parse::Parse, parse_macro_input, punctuated::Punctuated, spanned::Spanned, token,
8  Expr, GenericArgument, Ident, PathArguments, Token, Type,
9};
10
11#[allow(dead_code)]
12struct ModifiedArgsExpr {
13  plus: Token![+],
14  brace_token: token::Brace,
15  args: Punctuated<ArgField, Token![,]>,
16}
17
18impl Parse for ModifiedArgsExpr {
19  fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
20    let content;
21    Ok(Self {
22      plus: input.parse()?,
23      brace_token: braced!(content in input),
24      args: content.parse_terminated(ArgField::parse, Token![,])?,
25    })
26  }
27}
28
29#[allow(dead_code)]
30struct Decoder {
31  at: Token![@],
32  func: Ident,
33  paren_token: token::Paren,
34  args: Punctuated<Expr, Token![,]>,
35}
36
37impl Decoder {
38  fn decoded(&self, arg_name: &Ident, arg_type: proc_macro2::TokenStream, span: Span) -> proc_macro2::TokenStream {
39    let mut counter = 0u32;
40    let func: &Ident = &self.func;
41    let (target_trait, is_result) = match func.to_string().as_str() {
42      "sized_by" => (format_ident!("InspectDynSizedFromPid"), false),
43      "counted_by" => (format_ident!("InspectCountedFromPid"), false),
44      "sized_by_result" => (format_ident!("InspectDynSizedFromPid"), true),
45      "counted_by_result" => (format_ident!("InspectCountedFromPid"), true),
46      _ => panic!("Unsupported decoder function: {:?}", func),
47    };
48    let args = &self.args;
49    if !is_result {
50      quote_spanned! {
51        span =>
52        let #arg_name = <#arg_type as #target_trait>::inspect_from(inspectee_pid, raw_args.#arg_name as AddressType, (#args) as usize);
53      }
54    } else {
55      counter += 1;
56      let tmp_name = format_ident!("tmp_{}", counter);
57      quote_spanned! {
58        span =>
59        let #arg_name = if let Ok(&#tmp_name) = (#args).as_ref() {
60          <#arg_type as #target_trait>::inspect_from(inspectee_pid, raw_args.#arg_name as AddressType, #tmp_name as usize)
61        } else {
62          Err(InspectError::DependencyInspectFailure { field: stringify!(#arg_name) })
63        };
64      }
65    }
66  }
67}
68
69impl Parse for Decoder {
70  fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
71    let content;
72    Ok(Self {
73      at: input.parse()?,
74      func: input.parse()?,
75      paren_token: parenthesized!(content in input),
76      args: content.parse_terminated(Expr::parse, Token![,])?,
77    })
78  }
79}
80
81#[allow(dead_code)]
82struct ArgField {
83  ident: Ident,
84  colon_token: Token![:],
85  ty: Type,
86  decoder: Option<Decoder>,
87}
88
89impl Parse for ArgField {
90  fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
91    let ident = input.parse()?;
92    let colon_token = input.parse()?;
93    let ty = input.parse()?;
94    let lookahead = input.lookahead1();
95    let decoder = if lookahead.peek(Token![@]) {
96      Some(input.parse()?)
97    } else {
98      None
99    };
100    Ok(Self {
101      ident,
102      colon_token,
103      ty,
104      decoder,
105    })
106  }
107}
108
109#[allow(dead_code)]
110struct SyscallEntry {
111  name: syn::Ident,
112  paren_token: token::Paren,
113  raw_args: Punctuated<ArgField, Token![,]>,
114  separator: Token![/],
115  brace_token: token::Brace,
116  args: Punctuated<ArgField, Token![,]>,
117  arrow: Token![->],
118  result: Ident,
119  modified_args: Option<ModifiedArgsExpr>,
120  for_token: Token![for],
121  bracket_token: token::Bracket,
122  archs: Punctuated<Arch, Token![,]>,
123  group_token: Token![~],
124  bracket_token_2: token::Bracket,
125  groups: Punctuated<Ident, Token![,]>,
126  span: Option<Span>,
127}
128
129impl Parse for SyscallEntry {
130  fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
131    let content;
132    let raw_args;
133    let archs_content;
134    let groups_content;
135    let start;
136    let end;
137    Ok(SyscallEntry {
138      name: {
139        let name: Ident = input.parse()?;
140        start = name.span();
141        name
142      },
143      paren_token: parenthesized!(raw_args in input),
144      raw_args: raw_args.parse_terminated(ArgField::parse, Token![,])?,
145      separator: input.parse()?,
146      brace_token: braced!(content in input),
147      args: content.parse_terminated(ArgField::parse, Token![,])?,
148      arrow: input.parse()?,
149      result: input.parse()?,
150      modified_args: {
151        let lookahead = input.lookahead1();
152        if lookahead.peek(token::Plus) {
153          Some(input.parse()?)
154        } else {
155          None
156        }
157      },
158      group_token: input.parse()?,
159      bracket_token_2: bracketed!(groups_content in input),
160      groups: groups_content.parse_terminated(Ident::parse, Token![,])?,
161      for_token: input.parse()?,
162      bracket_token: bracketed!(archs_content in input),
163      archs: {
164        let archs = archs_content.parse_terminated(Arch::parse, Token![,])?;
165        end = archs.last().unwrap().span;
166        archs
167      },
168      span: end.and_then(|end| start.join(end)),
169    })
170  }
171}
172
173#[allow(dead_code)]
174struct Arch {
175  name: syn::Ident,
176  colon: Token![:],
177  number: syn::LitInt,
178  span: Option<Span>,
179}
180
181impl Parse for Arch {
182  fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
183    let start;
184    let end;
185    Ok(Arch {
186      name: {
187        let name: Ident = input.parse()?;
188        start = name.span();
189        name
190      },
191      colon: input.parse()?,
192      number: {
193        let number: syn::LitInt = input.parse()?;
194        end = number.span();
195        number
196      },
197      span: start.join(end),
198    })
199  }
200}
201
202struct GenSyscallArgsStructResult {
203  args_struct: proc_macro2::TokenStream,
204  raw_args_struct: proc_macro2::TokenStream,
205  modified_args_struct: proc_macro2::TokenStream,
206  name: Ident,
207  args_struct_type: Ident,
208  raw_args_struct_type: Ident,
209  modified_args_struct_type: Ident,
210  archs: Vec<String>,
211  syscall_number: Ident,
212  syscall_const_name: Ident,
213}
214
215fn gen_syscall_args_struct(
216  syscall: &SyscallEntry,
217  crate_token: proc_macro2::TokenStream,
218) -> GenSyscallArgsStructResult {
219  let span = syscall.span.unwrap_or_else(Span::call_site);
220  let name = &syscall.name;
221  let args = &syscall.args;
222  let groups_idents = &syscall.groups.iter().collect::<Vec<_>>();
223  let groups = if groups_idents.is_empty() {
224    quote_spanned! { span =>
225      #crate_token::SyscallGroups::empty()
226    }
227  } else {
228    quote_spanned! { span =>
229      #(#crate_token::SyscallGroups::#groups_idents)|*
230    }
231  };
232  let arch_name_0 = syscall.archs.first().as_ref().unwrap().name.to_string();
233  let arch_number_0 = &syscall.archs.first().as_ref().unwrap().number;
234  let (mut arch_names, numbers): (Vec<_>, Vec<_>) = syscall
235    .archs
236    .iter()
237    .skip(1)
238    .map(|x| (x.name.to_string(), x.number.clone()))
239    .unzip();
240  let syscall_number = quote_spanned! { span =>
241    if cfg!(target_arch = #arch_name_0) {
242      #arch_number_0
243    }
244    #(
245    else if cfg!(target_arch = #arch_names) {
246      #numbers
247    }
248    )*
249    else {
250      unreachable!()
251    }
252  };
253  arch_names.insert(0, arch_name_0);
254  let mut camel_case_name = name.to_string().to_case(Case::UpperCamel);
255  let camel_case_ident = Ident::new(&camel_case_name, name.span());
256  camel_case_name.push_str("Args");
257  let camel_case_args_type = Ident::new(&camel_case_name, name.span());
258  camel_case_name.replace_range((camel_case_name.len() - 4).., "RawArgs");
259  let camel_case_raw_args_type = Ident::new(&camel_case_name, name.span());
260  camel_case_name.replace_range((camel_case_name.len() - 7).., "ModifiedArgs");
261  let camel_case_modified_args_type = Ident::new(&camel_case_name, name.span());
262  let mut inspects = vec![];
263  let mut arg_names = vec![];
264  let mut wrapped_arg_types = vec![];
265  for arg in args.iter() {
266    let arg_name = &arg.ident;
267    arg_names.push(arg_name.clone());
268    let arg_type = &arg.ty;
269    let (wrapped_arg_type, need_memory_inspection) = wrap_syscall_arg_type(arg_type, span);
270    wrapped_arg_types.push(wrapped_arg_type.clone());
271    if !need_memory_inspection {
272      let inspect = quote_spanned! { span =>
273        let #arg_name = raw_args.#arg_name as #wrapped_arg_type;
274      };
275      inspects.push(inspect);
276    } else if let Some(decoder) = &arg.decoder {
277      let decoded = decoder.decoded(arg_name, wrapped_arg_type, span);
278      inspects.push(decoded);
279    } else {
280      inspects.push(quote_spanned! { span =>
281          let #arg_name: #wrapped_arg_type = #crate_token::InspectFromPid::inspect_from(inspectee_pid, raw_args.#arg_name as #crate_token::AddressType);
282        });
283    }
284  }
285  let mut raw_arg_names = vec![];
286  let mut raw_arg_types = vec![];
287  let mut inspect_raw_args = vec![];
288  for (i, raw_arg) in syscall.raw_args.iter().enumerate() {
289    let arg_name = &raw_arg.ident;
290    raw_arg_names.push(arg_name.clone());
291    let arg_type = &raw_arg.ty;
292    raw_arg_types.push(arg_type.clone());
293    let literal_i = syn::LitInt::new(&i.to_string(), arg_type.span());
294    inspect_raw_args.push(quote_spanned! { span =>
295      let #arg_name = syscall_arg!(regs, #literal_i) as #arg_type;
296    });
297  }
298  let mut modified_arg_names = vec![];
299  let mut modified_arg_names_err = vec![];
300  let mut modified_arg_types = vec![];
301  let mut inspect_modified_args = vec![];
302  let syscall_result_type = &syscall.result;
303  modified_arg_names.push(format_ident!("syscall_result"));
304  modified_arg_types.push(quote_spanned! { span => #syscall_result_type });
305  let (inspect_syscall_result, is_syscall_failed) = if syscall_result_type != "Unit" {
306    (
307      quote_spanned! {
308        span =>
309        let syscall_result = syscall_res_from_regs!(regs) as #syscall_result_type;
310      },
311      quote_spanned! { span => (syscall_result as isize) < 0 },
312    )
313  } else {
314    (quote_spanned! { span => let syscall_result = (); }, quote_spanned! { span => false })
315  };
316  if let Some(modified_args) = &syscall.modified_args {
317    for modified_arg in modified_args.args.iter() {
318      let arg_name = &modified_arg.ident;
319      modified_arg_names.push(arg_name.clone());
320      modified_arg_names_err.push(quote_spanned! { span =>
321        #arg_name: Err(InspectError::SyscallFailure)
322      });
323      let arg_type = &modified_arg.ty;
324      let (wrapped_arg_type, need_memory_inspection) = wrap_syscall_arg_type(arg_type, span);
325      modified_arg_types.push(wrapped_arg_type.clone());
326      if !need_memory_inspection {
327        let inspect = quote_spanned! { span =>
328          let #arg_name = raw_args.#arg_name as #wrapped_arg_type;
329        };
330        inspect_modified_args.push(inspect);
331      } else if let Some(decoder) = &modified_arg.decoder {
332        let decoded = decoder.decoded(arg_name, wrapped_arg_type, span);
333        inspect_modified_args.push(decoded);
334      } else {
335        inspect_modified_args.push(quote_spanned! { span =>
336            let #arg_name: #wrapped_arg_type = #crate_token::InspectFromPid::inspect_from(inspectee_pid, raw_args.#arg_name as #crate_token::AddressType);
337        });
338      }
339    }
340  }
341  let syscall_const_name = format_ident!("SYS_{}", name);
342  GenSyscallArgsStructResult {
343    syscall_number: syscall_const_name.clone(),
344    raw_args_struct: quote_spanned! { span =>
345      #[cfg(any(#(target_arch = #arch_names),*))]
346      pub const #syscall_const_name: isize = #syscall_number;
347
348      #[cfg(any(#(target_arch = #arch_names),*))]
349      #[derive(Debug, Clone, Copy, PartialEq)]
350      pub struct #camel_case_raw_args_type {
351        #(pub #raw_arg_names: #raw_arg_types),*
352      }
353
354      #[cfg(any(#(target_arch = #arch_names),*))]
355      impl #camel_case_raw_args_type {
356        fn from_regs(regs: &#crate_token::arch::PtraceRegisters) -> Self {
357          use #crate_token::arch::syscall_arg;
358          #(#inspect_raw_args)*
359          Self {
360            #(#raw_arg_names),*
361          }
362        }
363      }
364
365      #[cfg(any(#(target_arch = #arch_names),*))]
366      impl #crate_token::SyscallNumber for #camel_case_raw_args_type {
367        #[inline(always)]
368        fn syscall_number(&self) -> isize {
369          #syscall_const_name
370        }
371      }
372
373      #[cfg(any(#(target_arch = #arch_names),*))]
374      impl #crate_token::SyscallGroupsGetter for #camel_case_raw_args_type {
375        #[inline(always)]
376        fn syscall_groups(&self) -> ::enumflags2::BitFlags<#crate_token::SyscallGroups> {
377          use ::enumflags2::BitFlag;
378          {
379            #groups
380          }.into()
381        }
382      }
383
384      #[cfg(any(#(target_arch = #arch_names),*))]
385      impl #crate_token::SyscallStopInspect for #camel_case_raw_args_type {
386        type Args = #camel_case_args_type;
387        type Result = #camel_case_modified_args_type;
388        fn inspect_sysenter(self, inspectee_pid: Pid) -> Self::Args {
389          let raw_args = self;
390          #(#inspects)*
391          Self::Args {
392            #(#arg_names),*
393          }
394        }
395        fn inspect_sysexit(self, inspectee_pid: Pid, regs: &PtraceRegisters) -> Self::Result {
396          let raw_args = self;
397          #inspect_syscall_result
398          if #is_syscall_failed {
399            Self::Result {
400              syscall_result,
401              #(#modified_arg_names_err),*
402            }
403          } else {
404            #(#inspect_modified_args)*
405            Self::Result {
406              #(#modified_arg_names),*
407            }
408          }
409        }
410      }
411    },
412    args_struct: quote_spanned! { span =>
413      #[cfg(any(#(target_arch = #arch_names),*))]
414      #[derive(Debug, Clone, PartialEq)]
415      pub struct #camel_case_args_type {
416        #(pub #arg_names: #wrapped_arg_types),*
417      }
418
419      #[cfg(any(#(target_arch = #arch_names),*))]
420      impl #crate_token::SyscallNumber for #camel_case_args_type {
421        #[inline(always)]
422        fn syscall_number(&self) -> isize {
423          #syscall_const_name
424        }
425      }
426
427      #[cfg(any(#(target_arch = #arch_names),*))]
428      impl #crate_token::SyscallGroupsGetter for #camel_case_args_type {
429        #[inline(always)]
430        fn syscall_groups(&self) -> ::enumflags2::BitFlags<#crate_token::SyscallGroups> {
431          use ::enumflags2::BitFlag;
432          {
433            #groups
434          }.into()
435        }
436      }
437
438      #[cfg(any(#(target_arch = #arch_names),*))]
439      impl From<#camel_case_args_type> for #crate_token::SyscallArgs {
440        fn from(args: #camel_case_args_type) -> Self {
441          #crate_token::SyscallArgs::#camel_case_ident(args)
442        }
443      }
444    },
445    modified_args_struct: quote_spanned! { span =>
446      #[cfg(any(#(target_arch = #arch_names),*))]
447      #[derive(Debug, Clone, PartialEq)]
448      pub struct #camel_case_modified_args_type {
449        #(pub #modified_arg_names: #modified_arg_types),*
450      }
451
452      #[cfg(any(#(target_arch = #arch_names),*))]
453      impl #crate_token::SyscallNumber for #camel_case_modified_args_type {
454        #[inline(always)]
455        fn syscall_number(&self) -> isize {
456          #syscall_const_name
457        }
458      }
459
460      #[cfg(any(#(target_arch = #arch_names),*))]
461      impl #crate_token::SyscallGroupsGetter for #camel_case_modified_args_type {
462        #[inline(always)]
463        fn syscall_groups(&self) -> ::enumflags2::BitFlags<#crate_token::SyscallGroups> {
464          use ::enumflags2::BitFlag;
465          {
466            #groups
467          }.into()
468        }
469      }
470
471      #[cfg(any(#(target_arch = #arch_names),*))]
472      impl From<#camel_case_modified_args_type> for #crate_token::SyscallModifiedArgs {
473        fn from(args: #camel_case_modified_args_type) -> Self {
474          #crate_token::SyscallModifiedArgs::#camel_case_ident(args)
475        }
476      }
477
478    },
479    name: camel_case_ident,
480    args_struct_type: camel_case_args_type,
481    raw_args_struct_type: camel_case_raw_args_type,
482    modified_args_struct_type: camel_case_modified_args_type,
483    archs: arch_names.clone(),
484    syscall_const_name,
485  }
486}
487
488#[proc_macro]
489pub fn gen_syscalls(input: TokenStream) -> TokenStream {
490  let input = parse_macro_input!(input with Punctuated::<SyscallEntry, syn::Token![,]>::parse_terminated);
491  let mut arg_structs = vec![];
492  let mut raw_arg_structs = vec![];
493  let mut modified_arg_structs = vec![];
494  let mut names = vec![];
495  let mut raw_arg_struct_types = vec![];
496  let mut arg_struct_types = vec![];
497  let mut modified_arg_struct_types = vec![];
498  let mut supported_archs = vec![];
499  let mut syscall_numbers = vec![];
500  let crate_token = get_crate("ptrace-syscalls");
501  let mut syscall_names_dedup = vec![];
502  let mut supported_archs_dedup = vec![];
503  let mut syscall_consts = vec![];
504  for syscall in &input {
505    let GenSyscallArgsStructResult {
506      args_struct,
507      name,
508      args_struct_type,
509      archs,
510      syscall_number,
511      raw_args_struct,
512      raw_args_struct_type,
513      modified_args_struct,
514      modified_args_struct_type,
515      syscall_const_name,
516    } = gen_syscall_args_struct(syscall, crate_token.clone());
517    arg_structs.push(args_struct);
518    raw_arg_structs.push(raw_args_struct);
519    modified_arg_structs.push(modified_args_struct);
520    arg_struct_types.push(args_struct_type);
521    raw_arg_struct_types.push(raw_args_struct_type);
522    modified_arg_struct_types.push(modified_args_struct_type);
523    names.push(name.clone());
524    supported_archs.push(archs.clone());
525    syscall_numbers.push(syscall_number.clone());
526    syscall_consts.push(syscall_const_name);
527    if syscall_names_dedup.last() != Some(&syscall.name) {
528      syscall_names_dedup.push(syscall.name.clone());
529      supported_archs_dedup.push(archs.clone());
530    } else {
531      supported_archs_dedup.last_mut().unwrap().extend(archs);
532    }
533  }
534  TokenStream::from(quote::quote! {
535    #(#raw_arg_structs)*
536    #(#arg_structs)*
537    #(#modified_arg_structs)*
538    #[non_exhaustive]
539    #[derive(Debug, Clone, Copy, PartialEq)]
540    pub enum SyscallRawArgs {
541      #(
542        #[cfg(any(#(target_arch = #supported_archs),*))]
543        #names(#raw_arg_struct_types),
544      )*
545      Unknown(#crate_token::UnknownArgs),
546    }
547
548    #[non_exhaustive]
549    #[derive(Debug, Clone, PartialEq)]
550    pub enum SyscallArgs {
551      #(
552        #[cfg(any(#(target_arch = #supported_archs),*))]
553        #names(#arg_struct_types),
554      )*
555      Unknown(#crate_token::UnknownArgs),
556    }
557
558    #[non_exhaustive]
559    #[derive(Debug, Clone, PartialEq)]
560    pub enum SyscallModifiedArgs {
561      #(
562        #[cfg(any(#(target_arch = #supported_archs),*))]
563        #names(#modified_arg_struct_types),
564      )*
565      Unknown(#crate_token::UnknownArgs),
566    }
567
568    impl #crate_token::SyscallNumber for SyscallRawArgs {
569      #[inline(always)]
570      fn syscall_number(&self) -> isize {
571        match self {
572          #(
573            #[cfg(any(#(target_arch = #supported_archs),*))]
574            Self::#names(args) => args.syscall_number(),
575          )*
576          Self::Unknown(args) => args.syscall_number(),
577        }
578      }
579    }
580
581    impl #crate_token::SyscallGroupsGetter for SyscallRawArgs {
582      #[inline(always)]
583      fn syscall_groups(&self) -> ::enumflags2::BitFlags<#crate_token::SyscallGroups> {
584        match self {
585          #(
586            #[cfg(any(#(target_arch = #supported_archs),*))]
587            Self::#names(args) => args.syscall_groups(),
588          )*
589          Self::Unknown(args) => args.syscall_groups(),
590        }
591      }
592    }
593
594    impl #crate_token::SyscallNumber for SyscallArgs {
595      #[inline(always)]
596      fn syscall_number(&self) -> isize {
597        match self {
598          #(
599            #[cfg(any(#(target_arch = #supported_archs),*))]
600            Self::#names(args) => args.syscall_number(),
601          )*
602          Self::Unknown(args) => args.syscall_number(),
603        }
604      }
605    }
606
607    impl #crate_token::SyscallGroupsGetter for SyscallArgs {
608      #[inline(always)]
609      fn syscall_groups(&self) -> ::enumflags2::BitFlags<#crate_token::SyscallGroups> {
610        match self {
611          #(
612            #[cfg(any(#(target_arch = #supported_archs),*))]
613            Self::#names(args) => args.syscall_groups(),
614          )*
615          Self::Unknown(args) => args.syscall_groups(),
616        }
617      }
618    }
619
620    impl SyscallRawArgs {
621      fn from_regs(regs: &#crate_token::arch::PtraceRegisters) -> Self {
622        use #crate_token::arch::syscall_no_from_regs;
623        match syscall_no_from_regs!(regs) as isize {
624          #(
625            #[cfg(any(#(target_arch = #supported_archs),*))]
626            #syscall_numbers => {
627              Self::#names(#raw_arg_struct_types::from_regs(regs))
628            },
629          )*
630          _ => {
631            Self::Unknown(UnknownArgs::from_regs(regs))
632          }
633        }
634      }
635    }
636
637    impl SyscallStopInspect for SyscallRawArgs {
638      type Args = SyscallArgs;
639      type Result = SyscallModifiedArgs;
640
641      fn inspect_sysenter(self, inspectee_pid: Pid) -> Self::Args {
642        match self {
643          #(
644            #[cfg(any(#(target_arch = #supported_archs),*))]
645            Self::#names(raw_args) => {
646              SyscallArgs::#names(raw_args.inspect_sysenter(inspectee_pid))
647            },
648          )*
649          Self::Unknown(unknown) => {
650            SyscallArgs::Unknown(unknown)
651          }
652        }
653      }
654
655      fn inspect_sysexit(self, inspectee_pid: Pid, regs: &PtraceRegisters) -> Self::Result {
656        match self {
657          #(
658            #[cfg(any(#(target_arch = #supported_archs),*))]
659            Self::#names(raw_args) => {
660              SyscallModifiedArgs::#names(raw_args.inspect_sysexit(inspectee_pid, regs))
661            },
662          )*
663          Self::Unknown(unknown) => {
664            SyscallModifiedArgs::Unknown(unknown)
665          }
666        }
667      }
668    }
669
670    // This is not going to work. TODO: maybe create a rust issue for #[cfg(macro!(...))]?
671    // #[macro_export]
672    // macro_rules! cfg_if_has_syscall {
673    //   // match if/else chains with a final `else`
674    //   (
675    //       $(
676    //           if #[has $i_syscall:ident] { $( $i_tokens:tt )* }
677    //       ) else+
678    //       else { $( $e_tokens:tt )* }
679    //   ) => {
680    //     ::cfg_if::cfg_if! {
681    //       $(
682    //           if #[cfg($crate::cfg_if_has_syscall!(__internal__, $i_syscall))] { $( $i_tokens:tt )* }
683    //       ) else+
684    //       else { $( $e_tokens:tt )* }
685    //     }
686    //   };
687
688    //   // match if/else chains lacking a final `else`
689    //   (
690    //       if #[has $i_syscall:ident] { $( $i_tokens:tt )* }
691    //       $(
692    //           else if #[has $e_syscall:ident] { $( $e_tokens:tt )* }
693    //       )*
694    //   ) => {
695    //     ::cfg_if::cfg_if! {
696    //       if #[cfg($crate::cfg_if_has_syscall!(__internal__, $i_syscall))] { $( $i_tokens:tt )* }
697    //       $(
698    //           else if #[cfg($crate::cfg_if_has_syscall!(__internal__, $e_syscall))] { $( $e_tokens:tt )* }
699    //       )*
700    //     }
701    //   };
702    //   #(
703    //     (__internal__, #syscall_names_dedup) => {
704    //       any(#(target_arch = #supported_archs_dedup),*)
705    //     }
706    //   );*
707    // }
708  })
709}
710
711fn get_crate(name: &str) -> proc_macro2::TokenStream {
712  let found_crate = crate_name(name).unwrap_or_else(|_| panic!("`{}` not found in `Cargo.toml`", name));
713
714  match found_crate {
715    FoundCrate::Itself => quote!(crate),
716    FoundCrate::Name(name) => {
717      let ident = format_ident!("{}", &name);
718      quote!( #ident )
719    }
720  }
721}
722
723/// returns: (wrapped type, needs memory inspection)
724fn wrap_syscall_arg_type(ty: &Type, span: Span) -> (proc_macro2::TokenStream, bool) {
725  match ty {
726    Type::Array(ty) => {
727      let element_ty = &ty.elem;
728      (quote_spanned!(span =>Result<#ty, InspectError<Vec<#element_ty>>>), true)
729    }
730    Type::Path(ty) => {
731      assert_eq!(ty.path.segments.len(), 1);
732      let ty = &ty.path.segments[0];
733      let ty_str = ty.to_token_stream().to_string();
734      match ty_str.as_str() {
735        "Unit" => (quote_spanned!(span => ()), false),
736        // Primitive types
737        "RawFd" | "socklen_t" | "c_int" | "c_uint" | "c_ulong" | "c_long" | "i16" | "i32" | "i64" | "u64" | "usize"
738        | "isize" | "size_t" | "key_serial_t" | "AddressType" | "mode_t" | "uid_t" | "pid_t" | "gid_t" | "off_t"
739        | "u32" | "clockid_t" | "id_t" | "key_t" | "mqd_t" | "aio_context_t" | "dev_t" | "nfds_t" | "loff_t"
740        | "qid_t" | "idtype_t" | "time_t" | "timer_t" => (ty.to_token_stream(), false),
741        // Types that need to be wrapped
742        "sockaddr"
743        | "CString"
744        | "PathBuf"
745        | "iovec"
746        | "timex"
747        | "cap_user_header"
748        | "cap_user_data"
749        | "timespec"
750        | "clone_args"
751        | "epoll_event"
752        | "sigset_t"
753        | "stat"
754        | "statfs"
755        | "futex_waitv"
756        | "user_desc"
757        | "itimerval"
758        | "rlimit"
759        | "rusage"
760        | "timeval"
761        | "__aio_sigset"
762        | "timezone"
763        | "iocb"
764        | "io_event"
765        | "io_uring_params"
766        | "open_how"
767        | "landlock_ruleset_attr"
768        | "mq_attr"
769        | "sigevent"
770        | "msqid_ds"
771        | "pollfd"
772        | "__mount_arg"
773        | "msghdr"
774        | "riscv_hwprobe"
775        | "siginfo_t"
776        | "sched_attr"
777        | "sched_param"
778        | "stack_t"
779        | "mnt_id_req"
780        | "statx"
781        | "sysinfo"
782        | "itimerspec"
783        | "tms"
784        | "utsname"
785        | "ustat"
786        | "shmid_ds"
787        | "cachestat"
788        | "cachestat_range" => (quote_spanned!(span => InspectResult<#ty>), true),
789        _ => {
790          if ty.ident == "Option" {
791            let PathArguments::AngleBracketed(arg) = &ty.arguments else {
792              panic!("Unsupported syscall arg type: {:?}", ty_str);
793            };
794            let argstr = arg.args.to_token_stream().to_string();
795            match argstr.as_str() {
796              "PathBuf" | "timespec" | "Vec < CString >" | "CString" | "Vec < c_ulong >" | "Vec < c_uint >"
797              | "Vec < gid_t >" | "timezone" | "mq_attr" | "siginfo_t" | "sigset_t" | "iovec" | "rlimit64"
798              | "fd_set" | "sockaddr" | "sigaction" | "timeval" | "itimerval" | "stack_t" | "timer_t" | "time_t"
799              | "sigevent" | "itimerspec" | "utimbuf" | "rusage" => (quote_spanned!(span => InspectResult<#ty>), true),
800              "[timespec; 2]" | "[timeval; 2]" | "[timespec ; 2]" | "[timeval ; 2]" => {
801                let GenericArgument::Type(inner) = arg.args.first().unwrap() else {
802                  panic!("Unsupported inner syscall arg type: {:?}", argstr);
803                };
804                let Type::Array(inner) = inner else {
805                  panic!("Unsupported inner syscall arg type: {:?}", argstr)
806                };
807                let element_ty = &inner.elem;
808                (quote_spanned!(span => Result<#ty, InspectError<Vec<#element_ty>>>), true)
809              }
810              _ => panic!("Unsupported inner syscall arg type: {:?}", argstr),
811            }
812          } else if ty.ident == "Vec" {
813            let PathArguments::AngleBracketed(arg) = &ty.arguments else {
814              panic!("Unsupported inner syscall arg type: {:?}", ty_str);
815            };
816            let arg = arg.args.to_token_stream().to_string();
817            match arg.as_str() {
818              "c_int" | "u8" | "CString" | "epoll_event" | "futex_waitv" | "c_ulong" | "linux_dirent" | "io_event"
819              | "linux_dirent64" | "gid_t" | "AddressType" | "kexec_segment" | "c_uchar" | "u64" | "mount_attr"
820              | "pollfd" | "iovec" | "riscv_hwprobe" | "mmsghdr" | "sembuf" => {
821                (quote_spanned!(span => InspectResult<#ty>), true)
822              }
823              _ => panic!("Unsupported inner syscall arg type: {:?}", arg),
824            }
825          } else if ty.ident == "InspectResult" {
826            (quote_spanned!(span => #ty), true)
827          } else if ty.ident == "Arc" {
828            let PathArguments::AngleBracketed(arg) = &ty.arguments else {
829              panic!("Unsupported inner syscall arg type: {:?}", ty_str);
830            };
831            let arg = arg.args.to_token_stream().to_string();
832            match arg.as_str() {
833              "rseq" | "statmount" | "msgbuf" | "file_handle" | "xattr_args" | "mount_attr" => {
834                (quote_spanned!(span => InspectResult<#ty>), true)
835              }
836              _ => panic!("Unsupported inner syscall arg type: {:?}", arg),
837            }
838          } else {
839            panic!("Unsupported syscall arg type: {:?}", ty_str);
840          }
841        }
842      }
843    }
844    _ => panic!("Multi segment path is not supported here"),
845  }
846}