1use crate::sys::{AUDIT_ARCH_AARCH64, AUDIT_ARCH_ARM, AUDIT_ARCH_I386, AUDIT_ARCH_X86_64};
12use crate::syscall::nr::*;
13
14#[derive(Clone, Copy, Debug, PartialEq, Eq)]
16pub enum Abi {
17 X86_64,
18 I386,
19 Aarch64,
20 Arm,
22}
23
24impl Abi {
25 pub fn from_audit(arch: u32) -> Option<Abi> {
27 match arch {
28 AUDIT_ARCH_X86_64 => Some(Abi::X86_64),
29 AUDIT_ARCH_I386 => Some(Abi::I386),
30 AUDIT_ARCH_AARCH64 => Some(Abi::Aarch64),
31 AUDIT_ARCH_ARM => Some(Abi::Arm),
32 _ => None,
33 }
34 }
35
36 pub fn native() -> Abi {
38 if cfg!(target_arch = "aarch64") {
39 Abi::Aarch64
40 } else {
41 Abi::X86_64
42 }
43 }
44
45 pub fn compat() -> Abi {
47 if cfg!(target_arch = "aarch64") {
48 Abi::Arm
49 } else {
50 Abi::I386
51 }
52 }
53
54 pub fn audit(self) -> u32 {
56 match self {
57 Abi::X86_64 => AUDIT_ARCH_X86_64,
58 Abi::I386 => AUDIT_ARCH_I386,
59 Abi::Aarch64 => AUDIT_ARCH_AARCH64,
60 Abi::Arm => AUDIT_ARCH_ARM,
61 }
62 }
63
64 pub fn ptr_size(self) -> usize {
66 match self {
67 Abi::X86_64 | Abi::Aarch64 => 8,
68 Abi::I386 | Abi::Arm => 4,
69 }
70 }
71
72 pub fn to_canonical(self, nr: i64) -> Option<(i64, Variant)> {
81 match self {
82 Abi::X86_64 => Some((nr, Variant::Same)),
83 Abi::I386 => i386_to_x86_64(nr),
84 Abi::Aarch64 => aarch64_to_x86_64(nr).map(|n| (n, Variant::Same)),
85 Abi::Arm => arm_to_x86_64(nr),
86 }
87 }
88
89 pub fn open_flags_arg(self, nr: i64) -> Option<usize> {
92 match (self, nr) {
93 (Abi::X86_64, OPEN) | (Abi::I386, 5) | (Abi::Arm, 5) => Some(1),
94 (Abi::X86_64, OPENAT) | (Abi::I386, 295) | (Abi::Aarch64, 56) | (Abi::Arm, 322) => Some(2),
95 _ => None,
96 }
97 }
98
99 pub fn open_flags_to_host(self, flags: i64) -> i64 {
108 match self {
109 Abi::Arm => {
110 const ARM_O_DIRECTORY: i64 = 0o40000;
111 const ARM_O_NOFOLLOW: i64 = 0o100000;
112 const ARM_O_DIRECT: i64 = 0o200000;
113 const ARM_O_LARGEFILE: i64 = 0o400000;
114 let mut out = flags & !(ARM_O_DIRECTORY | ARM_O_NOFOLLOW | ARM_O_DIRECT | ARM_O_LARGEFILE);
115 if flags & ARM_O_DIRECTORY != 0 {
116 out |= libc::O_DIRECTORY as i64;
117 }
118 if flags & ARM_O_NOFOLLOW != 0 {
119 out |= libc::O_NOFOLLOW as i64;
120 }
121 if flags & ARM_O_DIRECT != 0 {
122 out |= libc::O_DIRECT as i64;
123 }
124 out
125 }
126 _ => flags,
127 }
128 }
129
130 pub fn socketcall_nr(self) -> Option<i64> {
135 match self {
136 Abi::I386 => Some(102),
137 _ => None,
138 }
139 }
140
141 pub fn numbers_for(self, canonical: &[i64]) -> Vec<i64> {
146 match self {
147 Abi::X86_64 => canonical.to_vec(),
148 _ => (0..512)
149 .filter(|n| {
150 self.to_canonical(*n)
151 .map(|(x, _)| canonical.contains(&x))
152 .unwrap_or(false)
153 })
154 .collect(),
155 }
156 }
157}
158
159pub fn aarch64_to_x86_64(nr: i64) -> Option<i64> {
164 Some(match nr {
165 5 => SETXATTR,
166 6 => LSETXATTR,
167 8 => GETXATTR,
168 9 => LGETXATTR,
169 11 => LISTXATTR,
170 12 => LLISTXATTR,
171 14 => REMOVEXATTR,
172 15 => LREMOVEXATTR,
173 17 => GETCWD,
174 27 => INOTIFY_ADD_WATCH,
175 33 => MKNODAT,
176 34 => MKDIRAT,
177 35 => UNLINKAT,
178 36 => SYMLINKAT,
179 37 => LINKAT,
180 38 => RENAMEAT,
181 43 => STATFS,
182 45 => TRUNCATE,
183 48 => FACCESSAT,
184 49 => CHDIR,
185 50 => FCHDIR,
186 97 => UNSHARE,
187 51 => CHROOT,
188 53 => FCHMODAT,
189 54 => FCHOWNAT,
190 56 => OPENAT,
191 78 => READLINKAT,
192 79 => NEWFSTATAT,
193 88 => UTIMENSAT,
194 160 => UNAME,
195 167 => PRCTL,
196 200 => BIND,
197 203 => CONNECT,
198 220 => CLONE,
199 268 => SETNS,
200 221 => EXECVE,
201 264 => NAME_TO_HANDLE_AT,
202 276 => RENAMEAT2,
203 281 => EXECVEAT,
204 435 => CLONE3,
205 291 => STATX,
206 437 => OPENAT2,
207 439 => FACCESSAT2,
208 452 => FCHMODAT2,
209 55 => FCHOWN,
210 91 => CAPSET,
211 143 => SETREGID,
212 144 => SETGID,
213 145 => SETREUID,
214 146 => SETUID,
215 147 => SETRESUID,
216 148 => GETRESUID,
217 149 => SETRESGID,
218 150 => GETRESGID,
219 151 => SETFSUID,
220 152 => SETFSGID,
221 158 => GETGROUPS,
222 159 => SETGROUPS,
223 174 => GETUID,
224 175 => GETEUID,
225 176 => GETGID,
226 177 => GETEGID,
227 _ => return None,
228 })
229}
230
231#[derive(Clone, Copy, Debug, PartialEq, Eq)]
233pub enum Variant {
234 Same,
236 Stat64,
238 OldStat,
240 Statfs64,
242 OldStatfs,
244 Truncate64,
246 Truncate64Arm,
248 ArmStat64,
250 Id16,
252 Timespec32,
254 Time32,
256}
257
258pub fn i386_to_x86_64(nr: i64) -> Option<(i64, Variant)> {
267 use Variant::*;
268 Some(match nr {
269 5 => (OPEN, Same),
270 8 => (CREAT, Same),
271 9 => (LINK, Same),
272 10 => (UNLINK, Same),
273 11 => (EXECVE, Same),
274 2 => (FORK, Same),
275 120 => (CLONE, Same),
276 133 => (FCHDIR, Same),
277 190 => (VFORK, Same),
278 310 => (UNSHARE, Same),
279 346 => (SETNS, Same),
280 435 => (CLONE3, Same),
281 12 => (CHDIR, Same),
282 14 => (MKNOD, Same),
283 15 => (CHMOD, Same),
284 16 => (LCHOWN, Same),
285 30 => (UTIME, Time32),
286 33 => (ACCESS, Same),
287 38 => (RENAME, Same),
288 39 => (MKDIR, Same),
289 40 => (RMDIR, Same),
290 61 => (CHROOT, Same),
291 83 => (SYMLINK, Same),
292 85 => (READLINK, Same),
293 92 => (TRUNCATE, Same),
294 99 => (STATFS, OldStatfs),
295 106 => (STAT, OldStat),
296 107 => (LSTAT, OldStat),
297 122 => (UNAME, Same),
298 172 => (PRCTL, Same),
299 182 => (CHOWN, Same),
300 183 => (GETCWD, Same),
301 193 => (TRUNCATE, Truncate64),
302 195 => (STAT, Stat64),
303 196 => (LSTAT, Stat64),
304 198 => (LCHOWN, Same),
305 212 => (CHOWN, Same),
306 226 => (SETXATTR, Same),
307 227 => (LSETXATTR, Same),
308 229 => (GETXATTR, Same),
309 230 => (LGETXATTR, Same),
310 232 => (LISTXATTR, Same),
311 233 => (LLISTXATTR, Same),
312 235 => (REMOVEXATTR, Same),
313 236 => (LREMOVEXATTR, Same),
314 268 => (STATFS, Statfs64),
315 271 => (UTIMES, Time32),
316 292 => (INOTIFY_ADD_WATCH, Same),
317 295 => (OPENAT, Same),
318 296 => (MKDIRAT, Same),
319 297 => (MKNODAT, Same),
320 298 => (FCHOWNAT, Same),
321 299 => (FUTIMESAT, Time32),
322 300 => (NEWFSTATAT, Stat64),
323 301 => (UNLINKAT, Same),
324 302 => (RENAMEAT, Same),
325 303 => (LINKAT, Same),
326 304 => (SYMLINKAT, Same),
327 305 => (READLINKAT, Same),
328 306 => (FCHMODAT, Same),
329 307 => (FACCESSAT, Same),
330 320 => (UTIMENSAT, Timespec32),
331 341 => (NAME_TO_HANDLE_AT, Same),
332 353 => (RENAMEAT2, Same),
333 358 => (EXECVEAT, Same),
334 361 => (BIND, Same),
335 362 => (CONNECT, Same),
336 383 => (STATX, Same),
337 412 => (UTIMENSAT, Same),
338 437 => (OPENAT2, Same),
339 439 => (FACCESSAT2, Same),
340 452 => (FCHMODAT2, Same),
341 23 => (SETUID, Same),
342 24 => (GETUID, Same),
343 46 => (SETGID, Same),
344 47 => (GETGID, Same),
345 49 => (GETEUID, Same),
346 50 => (GETEGID, Same),
347 70 => (SETREUID, Same),
348 71 => (SETREGID, Same),
349 80 => (GETGROUPS, Id16),
350 81 => (SETGROUPS, Same),
351 95 => (FCHOWN, Same),
352 138 => (SETFSUID, Same),
353 139 => (SETFSGID, Same),
354 164 => (SETRESUID, Same),
355 165 => (GETRESUID, Id16),
356 170 => (SETRESGID, Same),
357 171 => (GETRESGID, Id16),
358 185 => (CAPSET, Same),
359 199 => (GETUID, Same),
360 200 => (GETGID, Same),
361 201 => (GETEUID, Same),
362 202 => (GETEGID, Same),
363 203 => (SETREUID, Same),
364 204 => (SETREGID, Same),
365 205 => (GETGROUPS, Same),
366 206 => (SETGROUPS, Same),
367 207 => (FCHOWN, Same),
368 208 => (SETRESUID, Same),
369 209 => (GETRESUID, Same),
370 210 => (SETRESGID, Same),
371 211 => (GETRESGID, Same),
372 213 => (SETUID, Same),
373 214 => (SETGID, Same),
374 215 => (SETFSUID, Same),
375 216 => (SETFSGID, Same),
376 _ => return None,
377 })
378}
379
380pub fn arm_to_x86_64(nr: i64) -> Option<(i64, Variant)> {
385 use Variant::*;
386 Some(match nr {
387 5 => (OPEN, Same),
388 8 => (CREAT, Same),
389 9 => (LINK, Same),
390 10 => (UNLINK, Same),
391 11 => (EXECVE, Same),
392 2 => (FORK, Same),
393 120 => (CLONE, Same),
394 133 => (FCHDIR, Same),
395 190 => (VFORK, Same),
396 337 => (UNSHARE, Same),
397 375 => (SETNS, Same),
398 435 => (CLONE3, Same),
399 12 => (CHDIR, Same),
400 14 => (MKNOD, Same),
401 15 => (CHMOD, Same),
402 16 => (LCHOWN, Same),
403 33 => (ACCESS, Same),
404 38 => (RENAME, Same),
405 39 => (MKDIR, Same),
406 40 => (RMDIR, Same),
407 61 => (CHROOT, Same),
408 83 => (SYMLINK, Same),
409 85 => (READLINK, Same),
410 92 => (TRUNCATE, Same),
411 99 => (STATFS, OldStatfs),
412 106 => (STAT, OldStat),
413 107 => (LSTAT, OldStat),
414 122 => (UNAME, Same),
415 172 => (PRCTL, Same),
416 182 => (CHOWN, Same),
417 183 => (GETCWD, Same),
418 193 => (TRUNCATE, Truncate64Arm),
419 195 => (STAT, ArmStat64),
420 196 => (LSTAT, ArmStat64),
421 198 => (LCHOWN, Same),
422 212 => (CHOWN, Same),
423 226 => (SETXATTR, Same),
424 227 => (LSETXATTR, Same),
425 229 => (GETXATTR, Same),
426 230 => (LGETXATTR, Same),
427 232 => (LISTXATTR, Same),
428 233 => (LLISTXATTR, Same),
429 235 => (REMOVEXATTR, Same),
430 236 => (LREMOVEXATTR, Same),
431 266 => (STATFS, Statfs64),
432 269 => (UTIMES, Time32),
433 282 => (BIND, Same),
434 283 => (CONNECT, Same),
435 317 => (INOTIFY_ADD_WATCH, Same),
436 322 => (OPENAT, Same),
437 323 => (MKDIRAT, Same),
438 324 => (MKNODAT, Same),
439 325 => (FCHOWNAT, Same),
440 326 => (FUTIMESAT, Time32),
441 327 => (NEWFSTATAT, ArmStat64),
442 328 => (UNLINKAT, Same),
443 329 => (RENAMEAT, Same),
444 330 => (LINKAT, Same),
445 331 => (SYMLINKAT, Same),
446 332 => (READLINKAT, Same),
447 333 => (FCHMODAT, Same),
448 334 => (FACCESSAT, Same),
449 348 => (UTIMENSAT, Timespec32),
450 370 => (NAME_TO_HANDLE_AT, Same),
451 382 => (RENAMEAT2, Same),
452 387 => (EXECVEAT, Same),
453 397 => (STATX, Same),
454 412 => (UTIMENSAT, Same),
455 437 => (OPENAT2, Same),
456 439 => (FACCESSAT2, Same),
457 452 => (FCHMODAT2, Same),
458 23 => (SETUID, Same),
459 24 => (GETUID, Same),
460 46 => (SETGID, Same),
461 47 => (GETGID, Same),
462 49 => (GETEUID, Same),
463 50 => (GETEGID, Same),
464 70 => (SETREUID, Same),
465 71 => (SETREGID, Same),
466 80 => (GETGROUPS, Id16),
467 81 => (SETGROUPS, Same),
468 95 => (FCHOWN, Same),
469 138 => (SETFSUID, Same),
470 139 => (SETFSGID, Same),
471 164 => (SETRESUID, Same),
472 165 => (GETRESUID, Id16),
473 170 => (SETRESGID, Same),
474 171 => (GETRESGID, Id16),
475 185 => (CAPSET, Same),
476 199 => (GETUID, Same),
477 200 => (GETGID, Same),
478 201 => (GETEUID, Same),
479 202 => (GETEGID, Same),
480 203 => (SETREUID, Same),
481 204 => (SETREGID, Same),
482 205 => (GETGROUPS, Same),
483 206 => (SETGROUPS, Same),
484 207 => (FCHOWN, Same),
485 208 => (SETRESUID, Same),
486 209 => (GETRESUID, Same),
487 210 => (SETRESGID, Same),
488 211 => (GETRESGID, Same),
489 213 => (SETUID, Same),
490 214 => (SETGID, Same),
491 215 => (SETFSUID, Same),
492 216 => (SETFSGID, Same),
493 _ => return None,
494 })
495}
496
497struct Out<const N: usize>([u8; N]);
499
500impl<const N: usize> Out<N> {
501 fn new() -> Self {
502 Out([0u8; N])
503 }
504 fn u64(&mut self, off: usize, v: u64) {
505 self.0[off..off + 8].copy_from_slice(&v.to_ne_bytes());
506 }
507 fn u32(&mut self, off: usize, v: u64) {
508 self.0[off..off + 4].copy_from_slice(&(v as u32).to_ne_bytes());
509 }
510 fn u16(&mut self, off: usize, v: u64) {
511 self.0[off..off + 2].copy_from_slice(&(v as u16).to_ne_bytes());
512 }
513}
514
515#[allow(clippy::unnecessary_cast)]
522pub fn stat_to_stat64(st: &libc::stat) -> [u8; 96] {
523 let mut o = Out::<96>::new();
524 o.u64(0, st.st_dev as u64);
525 o.u32(12, st.st_ino as u64);
526 o.u32(16, st.st_mode as u64);
527 o.u32(20, st.st_nlink as u64);
528 o.u32(24, st.st_uid as u64);
529 o.u32(28, st.st_gid as u64);
530 o.u64(32, st.st_rdev as u64);
531 o.u64(44, st.st_size as u64);
532 o.u32(52, st.st_blksize as u64);
533 o.u64(56, st.st_blocks as u64);
534 for (off, v) in [
535 (64usize, st.st_atime),
536 (68, st.st_atime_nsec),
537 (72, st.st_mtime),
538 (76, st.st_mtime_nsec),
539 (80, st.st_ctime),
540 (84, st.st_ctime_nsec),
541 ] {
542 o.u32(off, v as u64);
543 }
544 o.u64(88, st.st_ino as u64);
545 o.0
546}
547
548#[allow(clippy::unnecessary_cast)]
554pub fn stat_to_arm_stat64(st: &libc::stat) -> [u8; 104] {
555 let mut o = Out::<104>::new();
556 o.u64(0, st.st_dev as u64);
557 o.u32(12, st.st_ino as u64);
558 o.u32(16, st.st_mode as u64);
559 o.u32(20, st.st_nlink as u64);
560 o.u32(24, st.st_uid as u64);
561 o.u32(28, st.st_gid as u64);
562 o.u64(32, st.st_rdev as u64);
563 o.u64(48, st.st_size as u64);
564 o.u32(56, st.st_blksize as u64);
565 o.u64(64, st.st_blocks as u64);
566 for (off, v) in [
567 (72usize, st.st_atime),
568 (76, st.st_atime_nsec),
569 (80, st.st_mtime),
570 (84, st.st_mtime_nsec),
571 (88, st.st_ctime),
572 (92, st.st_ctime_nsec),
573 ] {
574 o.u32(off, v as u64);
575 }
576 o.u64(96, st.st_ino as u64);
577 o.0
578}
579
580#[allow(clippy::unnecessary_cast)]
585pub fn stat_to_old_stat(st: &libc::stat) -> Result<[u8; 64], i32> {
586 let (ino, size) = (st.st_ino as u64, st.st_size as i64);
587 if ino > u32::MAX as u64 || size > i32::MAX as i64 || size < i32::MIN as i64 {
588 return Err(libc::EOVERFLOW);
589 }
590 let mut o = Out::<64>::new();
594 o.u32(0, old_encode_dev(st.st_dev as u64) as u64);
595 o.u32(4, ino);
596 o.u16(8, st.st_mode as u64);
597 o.u16(10, st.st_nlink as u64);
598 o.u16(12, st.st_uid as u64);
599 o.u16(14, st.st_gid as u64);
600 o.u32(16, old_encode_dev(st.st_rdev as u64) as u64);
601 o.u32(20, size as u64);
602 o.u32(24, st.st_blksize as u64);
603 o.u32(28, st.st_blocks as u64);
604 for (off, v) in [
605 (32usize, st.st_atime),
606 (36, st.st_atime_nsec),
607 (40, st.st_mtime),
608 (44, st.st_mtime_nsec),
609 (48, st.st_ctime),
610 (52, st.st_ctime_nsec),
611 ] {
612 o.u32(off, v as u64);
613 }
614 Ok(o.0)
615}
616
617fn old_encode_dev(dev: u64) -> u32 {
619 let major = ((dev >> 8) & 0xfff) as u32;
620 let minor = ((dev & 0xff) | ((dev >> 12) & 0xfff00)) as u32;
621 ((major & 0xff) << 8) | (minor & 0xff)
622}
623
624fn statfs_flags(sf: &libc::statfs) -> u64 {
629 let p = sf as *const _ as *const u8;
630 let b = unsafe { std::slice::from_raw_parts(p.add(80), 8) };
631 u64::from_ne_bytes(b.try_into().unwrap())
632}
633
634fn fsid_bytes(sf: &libc::statfs) -> [u8; 8] {
636 let p = &sf.f_fsid as *const _ as *const u8;
637 let mut b = [0u8; 8];
638 b.copy_from_slice(unsafe { std::slice::from_raw_parts(p, 8) });
639 b
640}
641
642#[allow(clippy::unnecessary_cast)]
648pub fn statfs_to_statfs64(sf: &libc::statfs) -> [u8; 84] {
649 let mut o = Out::<84>::new();
650 o.u32(0, sf.f_type as u64);
651 o.u32(4, sf.f_bsize as u64);
652 o.u64(8, sf.f_blocks as u64);
653 o.u64(16, sf.f_bfree as u64);
654 o.u64(24, sf.f_bavail as u64);
655 o.u64(32, sf.f_files as u64);
656 o.u64(40, sf.f_ffree as u64);
657 o.0[48..56].copy_from_slice(&fsid_bytes(sf));
658 o.u32(56, sf.f_namelen as u64);
659 o.u32(60, sf.f_frsize as u64);
660 o.u32(64, statfs_flags(sf));
661 o.0
662}
663
664#[allow(clippy::unnecessary_cast)]
667pub fn statfs_to_old_statfs(sf: &libc::statfs) -> [u8; 64] {
668 let mut o = Out::<64>::new();
669 for (i, v) in [
670 sf.f_type as u64,
671 sf.f_bsize as u64,
672 sf.f_blocks as u64,
673 sf.f_bfree as u64,
674 sf.f_bavail as u64,
675 sf.f_files as u64,
676 sf.f_ffree as u64,
677 ]
678 .into_iter()
679 .enumerate()
680 {
681 o.u32(i * 4, v);
682 }
683 o.0[28..36].copy_from_slice(&fsid_bytes(sf));
684 o.u32(36, sf.f_namelen as u64);
685 o.u32(40, sf.f_frsize as u64);
686 o.u32(44, statfs_flags(sf));
687 o.0
688}
689
690#[cfg(test)]
691mod tests {
692 use super::*;
693
694 #[test]
695 fn maps_common_numbers() {
696 assert_eq!(i386_to_x86_64(5), Some((OPEN, Variant::Same)));
697 assert_eq!(i386_to_x86_64(195), Some((STAT, Variant::Stat64)));
698 assert_eq!(i386_to_x86_64(300), Some((NEWFSTATAT, Variant::Stat64)));
699 assert_eq!(i386_to_x86_64(11), Some((EXECVE, Variant::Same)));
700 assert_eq!(i386_to_x86_64(1), None);
701 assert!(Abi::I386.numbers_for(&[OPEN, OPENAT]).contains(&295));
702 assert_eq!(
703 Abi::Aarch64.numbers_for(&[OPENAT, NEWFSTATAT, OPEN]),
704 vec![56, 79]
705 );
706 assert_eq!(Abi::Aarch64.to_canonical(221), Some((EXECVE, Variant::Same)));
707 assert_eq!(Abi::Arm.to_canonical(5), Some((OPEN, Variant::Same)));
708 }
709
710 #[test]
711 fn stat64_layouts_carry_key_fields() {
712 let mut st: libc::stat = unsafe { std::mem::zeroed() };
713 st.st_ino = 0x1122334455u64 as _;
714 st.st_mode = 0o100644;
715 st.st_size = 5_000_000_000i64 as _;
716 st.st_atime = 1_700_000_000 as _;
717 let s = stat_to_stat64(&st);
718 assert_eq!(u64::from_ne_bytes(s[88..96].try_into().unwrap()), 0x1122334455);
719 assert_eq!(u32::from_ne_bytes(s[16..20].try_into().unwrap()), 0o100644);
720 assert_eq!(u64::from_ne_bytes(s[44..52].try_into().unwrap()), 5_000_000_000);
721 assert_eq!(u32::from_ne_bytes(s[64..68].try_into().unwrap()), 1_700_000_000);
722 let a = stat_to_arm_stat64(&st);
723 assert_eq!(u64::from_ne_bytes(a[96..104].try_into().unwrap()), 0x1122334455);
724 assert_eq!(u64::from_ne_bytes(a[48..56].try_into().unwrap()), 5_000_000_000);
725 assert_eq!(u32::from_ne_bytes(a[72..76].try_into().unwrap()), 1_700_000_000);
726 assert_eq!(stat_to_old_stat(&st), Err(libc::EOVERFLOW));
727 }
728
729 #[test]
730 fn arm_numbers_and_flags() {
731 assert_eq!(Abi::Arm.to_canonical(322), Some((OPENAT, Variant::Same)));
732 assert_eq!(Abi::Arm.to_canonical(327), Some((NEWFSTATAT, Variant::ArmStat64)));
733 assert_eq!(Abi::Arm.to_canonical(283), Some((CONNECT, Variant::Same)));
734 assert!(Abi::Arm.numbers_for(&[OPENAT, STAT]).contains(&195));
735 let arm_nofollow_dir = 0o100000 | 0o40000 | 0o2;
736 let host = Abi::Arm.open_flags_to_host(arm_nofollow_dir);
737 assert_eq!(host & libc::O_NOFOLLOW as i64, libc::O_NOFOLLOW as i64);
738 assert_eq!(host & libc::O_DIRECTORY as i64, libc::O_DIRECTORY as i64);
739 assert_eq!(host & 0o2, 0o2);
740 assert_eq!(
741 Abi::X86_64.open_flags_to_host(libc::O_NOFOLLOW as i64),
742 libc::O_NOFOLLOW as i64
743 );
744 }
745}