xref: /rust-libc-0.2.174/libc-test/build.rs (revision 5a43dd27)
1 #![deny(warnings)]
2 
3 extern crate ctest2 as ctest;
4 
5 use std::fs::File;
6 use std::io::{BufRead, BufReader, BufWriter, Write};
7 use std::path::{Path, PathBuf};
8 use std::{env, io};
9 
10 fn do_cc() {
11     let target = env::var("TARGET").unwrap();
12     if cfg!(unix) {
13         let exclude = ["redox", "wasi"];
14         if !exclude.iter().any(|x| target.contains(x)) {
15             let mut cmsg = cc::Build::new();
16 
17             cmsg.file("src/cmsg.c");
18 
19             if target.contains("solaris") || target.contains("illumos") {
20                 cmsg.define("_XOPEN_SOURCE", "700");
21             }
22             cmsg.compile("cmsg");
23         }
24 
25         if target.contains("linux")
26             || target.contains("android")
27             || target.contains("emscripten")
28             || target.contains("fuchsia")
29             || target.contains("bsd")
30         {
31             cc::Build::new().file("src/makedev.c").compile("makedev");
32         }
33     }
34     if target.contains("android") || target.contains("linux") {
35         cc::Build::new().file("src/errqueue.c").compile("errqueue");
36     }
37     if target.contains("linux")
38         || target.contains("l4re")
39         || target.contains("android")
40         || target.contains("emscripten")
41     {
42         cc::Build::new().file("src/sigrt.c").compile("sigrt");
43     }
44 }
45 
46 fn do_ctest() {
47     match &env::var("TARGET").unwrap() {
48         t if t.contains("android") => return test_android(t),
49         t if t.contains("apple") => return test_apple(t),
50         t if t.contains("dragonfly") => return test_dragonflybsd(t),
51         t if t.contains("emscripten") => return test_emscripten(t),
52         t if t.contains("freebsd") => return test_freebsd(t),
53         t if t.contains("haiku") => return test_haiku(t),
54         t if t.contains("linux") => return test_linux(t),
55         t if t.contains("netbsd") => return test_netbsd(t),
56         t if t.contains("openbsd") => return test_openbsd(t),
57         t if t.contains("redox") => return test_redox(t),
58         t if t.contains("solaris") => return test_solarish(t),
59         t if t.contains("illumos") => return test_solarish(t),
60         t if t.contains("wasi") => return test_wasi(t),
61         t if t.contains("windows") => return test_windows(t),
62         t if t.contains("vxworks") => return test_vxworks(t),
63         t if t.contains("nto-qnx") => return test_neutrino(t),
64         t => panic!("unknown target {}", t),
65     }
66 }
67 
68 fn ctest_cfg() -> ctest::TestGenerator {
69     let mut cfg = ctest::TestGenerator::new();
70     let libc_cfgs = [
71         "libc_align",
72         "libc_core_cvoid",
73         "libc_packedN",
74         "libc_thread_local",
75     ];
76     for f in &libc_cfgs {
77         cfg.cfg(f, None);
78     }
79     cfg
80 }
81 
82 fn do_semver() {
83     let mut out = PathBuf::from(env::var("OUT_DIR").unwrap());
84     out.push("semver.rs");
85     let mut output = BufWriter::new(File::create(&out).unwrap());
86 
87     let family = env::var("CARGO_CFG_TARGET_FAMILY").unwrap();
88     let vendor = env::var("CARGO_CFG_TARGET_VENDOR").unwrap();
89     let os = env::var("CARGO_CFG_TARGET_OS").unwrap();
90     let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
91     let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap();
92 
93     // `libc-test/semver` dir.
94     let mut semver_root = PathBuf::from("semver");
95 
96     // NOTE: Windows has the same `family` as `os`, no point in including it
97     // twice.
98     // NOTE: Android doesn't include the unix file (or the Linux file) because
99     // there are some many definitions missing it's actually easier just to
100     // maintain a file for Android.
101     if family != os && os != "android" {
102         process_semver_file(&mut output, &mut semver_root, &family);
103     }
104     // We don't do semver for unknown targets.
105     if vendor != "unknown" {
106         process_semver_file(&mut output, &mut semver_root, &vendor);
107     }
108     process_semver_file(&mut output, &mut semver_root, &os);
109     let os_arch = format!("{}-{}", os, arch);
110     process_semver_file(&mut output, &mut semver_root, &os_arch);
111     if target_env != "" {
112         let os_env = format!("{}-{}", os, target_env);
113         process_semver_file(&mut output, &mut semver_root, &os_env);
114 
115         let os_env_arch = format!("{}-{}-{}", os, target_env, arch);
116         process_semver_file(&mut output, &mut semver_root, &os_env_arch);
117     }
118 }
119 
120 fn process_semver_file<W: Write, P: AsRef<Path>>(output: &mut W, path: &mut PathBuf, file: P) {
121     // NOTE: `path` is reused between calls, so always remove the file again.
122     path.push(file);
123     path.set_extension("txt");
124 
125     println!("cargo:rerun-if-changed={}", path.display());
126     let input_file = match File::open(&*path) {
127         Ok(file) => file,
128         Err(ref err) if err.kind() == io::ErrorKind::NotFound => {
129             path.pop();
130             return;
131         }
132         Err(err) => panic!("unexpected error opening file: {}", err),
133     };
134     let input = BufReader::new(input_file);
135 
136     write!(output, "// Source: {}.\n", path.display()).unwrap();
137     output.write(b"use libc::{\n").unwrap();
138     for line in input.lines() {
139         let line = line.unwrap().into_bytes();
140         match line.first() {
141             // Ignore comments and empty lines.
142             Some(b'#') | None => continue,
143             _ => {
144                 output.write(b"    ").unwrap();
145                 output.write(&line).unwrap();
146                 output.write(b",\n").unwrap();
147             }
148         }
149     }
150     output.write(b"};\n\n").unwrap();
151     path.pop();
152 }
153 
154 fn main() {
155     // Avoid unnecessary re-building.
156     println!("cargo:rerun-if-changed=build.rs");
157 
158     do_cc();
159     do_ctest();
160     do_semver();
161 }
162 
163 macro_rules! headers {
164     ($cfg:ident: [$m:expr]: $header:literal) => {
165         if $m {
166             $cfg.header($header);
167         }
168     };
169     ($cfg:ident: $header:literal) => {
170         $cfg.header($header);
171     };
172     ($($cfg:ident: $([$c:expr]:)* $header:literal,)*) => {
173         $(headers!($cfg: $([$c]:)* $header);)*
174     };
175     ($cfg:ident: $( $([$c:expr]:)* $header:literal,)*) => {
176         headers!($($cfg: $([$c]:)* $header,)*);
177     };
178     ($cfg:ident: $( $([$c:expr]:)* $header:literal),*) => {
179         headers!($($cfg: $([$c]:)* $header,)*);
180     };
181 }
182 
183 fn test_apple(target: &str) {
184     assert!(target.contains("apple"));
185     let x86_64 = target.contains("x86_64");
186     let i686 = target.contains("i686");
187 
188     let mut cfg = ctest_cfg();
189     cfg.flag("-Wno-deprecated-declarations");
190     cfg.define("__APPLE_USE_RFC_3542", None);
191 
192     headers! { cfg:
193         "aio.h",
194         "CommonCrypto/CommonCrypto.h",
195         "CommonCrypto/CommonRandom.h",
196         "copyfile.h",
197         "crt_externs.h",
198         "ctype.h",
199         "dirent.h",
200         "dlfcn.h",
201         "errno.h",
202         "execinfo.h",
203         "fcntl.h",
204         "fnmatch.h",
205         "getopt.h",
206         "glob.h",
207         "grp.h",
208         "iconv.h",
209         "ifaddrs.h",
210         "langinfo.h",
211         "libgen.h",
212         "libproc.h",
213         "limits.h",
214         "locale.h",
215         "mach-o/dyld.h",
216         "mach/mach_init.h",
217         "mach/mach.h",
218         "mach/mach_time.h",
219         "mach/mach_types.h",
220         "mach/mach_vm.h",
221         "mach/thread_act.h",
222         "mach/thread_policy.h",
223         "malloc/malloc.h",
224         "net/bpf.h",
225         "net/dlil.h",
226         "net/if.h",
227         "net/if_arp.h",
228         "net/if_dl.h",
229         "net/if_utun.h",
230         "net/if_var.h",
231         "net/ndrv.h",
232         "net/route.h",
233         "netdb.h",
234         "netinet/if_ether.h",
235         "netinet/in.h",
236         "netinet/ip.h",
237         "netinet/tcp.h",
238         "netinet/udp.h",
239         "os/clock.h",
240         "os/lock.h",
241         "os/signpost.h",
242         // FIXME: Requires the macOS 14.4 SDK.
243         //"os/os_sync_wait_on_address.h",
244         "poll.h",
245         "pthread.h",
246         "pthread_spis.h",
247         "pthread/introspection.h",
248         "pthread/spawn.h",
249         "pthread/stack_np.h",
250         "pwd.h",
251         "regex.h",
252         "resolv.h",
253         "sched.h",
254         "semaphore.h",
255         "signal.h",
256         "spawn.h",
257         "stddef.h",
258         "stdint.h",
259         "stdio.h",
260         "stdlib.h",
261         "string.h",
262         "sysdir.h",
263         "sys/appleapiopts.h",
264         "sys/attr.h",
265         "sys/clonefile.h",
266         "sys/event.h",
267         "sys/file.h",
268         "sys/ioctl.h",
269         "sys/ipc.h",
270         "sys/kern_control.h",
271         "sys/mman.h",
272         "sys/mount.h",
273         "sys/proc_info.h",
274         "sys/ptrace.h",
275         "sys/quota.h",
276         "sys/random.h",
277         "sys/resource.h",
278         "sys/sem.h",
279         "sys/shm.h",
280         "sys/socket.h",
281         "sys/stat.h",
282         "sys/statvfs.h",
283         "sys/sys_domain.h",
284         "sys/sysctl.h",
285         "sys/time.h",
286         "sys/times.h",
287         "sys/timex.h",
288         "sys/types.h",
289         "sys/uio.h",
290         "sys/un.h",
291         "sys/utsname.h",
292         "sys/vsock.h",
293         "sys/wait.h",
294         "sys/xattr.h",
295         "syslog.h",
296         "termios.h",
297         "time.h",
298         "unistd.h",
299         "util.h",
300         "utime.h",
301         "utmpx.h",
302         "wchar.h",
303         "xlocale.h",
304         [x86_64]: "crt_externs.h",
305     }
306 
307     cfg.skip_struct(move |ty| {
308         if ty.starts_with("__c_anonymous_") {
309             return true;
310         }
311         match ty {
312             // FIXME: actually a union
313             "sigval" => true,
314 
315             // FIXME: The size is changed in recent macOSes.
316             "malloc_zone_t" => true,
317             // it is a moving target, changing through versions
318             // also contains bitfields members
319             "tcp_connection_info" => true,
320             // FIXME: The size is changed in recent macOSes.
321             "malloc_introspection_t" => true,
322 
323             _ => false,
324         }
325     });
326 
327     cfg.skip_type(move |ty| {
328         if ty.starts_with("__c_anonymous_") {
329             return true;
330         }
331         match ty {
332             // FIXME: Requires the macOS 14.4 SDK.
333             "os_sync_wake_by_address_flags_t" | "os_sync_wait_on_address_flags_t" => true,
334 
335             _ => false,
336         }
337     });
338 
339     cfg.skip_const(move |name| {
340         // They're declared via `deprecated_mach` and we don't support it anymore.
341         if name.starts_with("VM_FLAGS_") {
342             return true;
343         }
344         match name {
345             // These OSX constants are removed in Sierra.
346             // https://developer.apple.com/library/content/releasenotes/General/APIDiffsMacOS10_12/Swift/Darwin.html
347             "KERN_KDENABLE_BG_TRACE" | "KERN_KDDISABLE_BG_TRACE" => true,
348             // FIXME: the value has been changed since Catalina (0xffff0000 -> 0x3fff0000).
349             "SF_SETTABLE" => true,
350 
351             // FIXME: XCode 13.1 doesn't have it.
352             "TIOCREMOTE" => true,
353 
354             // FIXME: Requires the macOS 14.4 SDK.
355             "OS_SYNC_WAKE_BY_ADDRESS_NONE"
356             | "OS_SYNC_WAKE_BY_ADDRESS_SHARED"
357             | "OS_SYNC_WAIT_ON_ADDRESS_NONE"
358             | "OS_SYNC_WAIT_ON_ADDRESS_SHARED" => true,
359 
360             _ => false,
361         }
362     });
363 
364     cfg.skip_fn(move |name| {
365         // skip those that are manually verified
366         match name {
367             // FIXME: https://github.com/rust-lang/libc/issues/1272
368             "execv" | "execve" | "execvp" => true,
369 
370             // close calls the close_nocancel system call
371             "close" => true,
372 
373             // FIXME: std removed libresolv support: https://github.com/rust-lang/rust/pull/102766
374             "res_init" => true,
375 
376             // FIXME: remove once the target in CI is updated
377             "pthread_jit_write_freeze_callbacks_np" => true,
378 
379             // FIXME: ABI has been changed on recent macOSes.
380             "os_unfair_lock_assert_owner" | "os_unfair_lock_assert_not_owner" => true,
381 
382             // FIXME: Once the SDK get updated to Ventura's level
383             "freadlink" | "mknodat" | "mkfifoat" => true,
384 
385             // FIXME: Requires the macOS 14.4 SDK.
386             "os_sync_wake_by_address_any"
387             | "os_sync_wake_by_address_all"
388             | "os_sync_wake_by_address_flags_t"
389             | "os_sync_wait_on_address"
390             | "os_sync_wait_on_address_flags_t"
391             | "os_sync_wait_on_address_with_deadline"
392             | "os_sync_wait_on_address_with_timeout" => true,
393 
394             _ => false,
395         }
396     });
397 
398     cfg.skip_field(move |struct_, field| {
399         match (struct_, field) {
400             // FIXME: the array size has been changed since macOS 10.15 ([8] -> [7]).
401             ("statfs", "f_reserved") => true,
402             ("__darwin_arm_neon_state64", "__v") => true,
403             // MAXPATHLEN is too big for auto-derive traits on arrays.
404             ("vnode_info_path", "vip_path") => true,
405             ("ifreq", "ifr_ifru") => true,
406             ("ifkpi", "ifk_data") => true,
407             ("ifconf", "ifc_ifcu") => true,
408             // FIXME: this field has been incorporated into a resized `rmx_filler` array.
409             ("rt_metrics", "rmx_state") => true,
410             ("rt_metrics", "rmx_filler") => true,
411             _ => false,
412         }
413     });
414 
415     cfg.skip_field_type(move |struct_, field| {
416         match (struct_, field) {
417             // FIXME: actually a union
418             ("sigevent", "sigev_value") => true,
419             _ => false,
420         }
421     });
422 
423     cfg.volatile_item(|i| {
424         use ctest::VolatileItemKind::*;
425         match i {
426             StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => true,
427             _ => false,
428         }
429     });
430 
431     cfg.type_name(move |ty, is_struct, is_union| {
432         match ty {
433             // Just pass all these through, no need for a "struct" prefix
434             "FILE" | "DIR" | "Dl_info" => ty.to_string(),
435 
436             // OSX calls this something else
437             "sighandler_t" => "sig_t".to_string(),
438 
439             t if is_union => format!("union {}", t),
440             t if t.ends_with("_t") => t.to_string(),
441             t if is_struct => format!("struct {}", t),
442             t => t.to_string(),
443         }
444     });
445 
446     cfg.field_name(move |struct_, field| {
447         match field {
448             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
449                 s.replace("e_nsec", "espec.tv_nsec")
450             }
451             // FIXME: sigaction actually contains a union with two variants:
452             // a sa_sigaction with type: (*)(int, struct __siginfo *, void *)
453             // a sa_handler with type sig_t
454             "sa_sigaction" if struct_ == "sigaction" => "sa_handler".to_string(),
455             s => s.to_string(),
456         }
457     });
458 
459     cfg.skip_roundtrip(move |s| match s {
460         // FIXME: this type has the wrong ABI
461         "max_align_t" if i686 => true,
462         // Can't return an array from a C function.
463         "uuid_t" | "vol_capabilities_set_t" => true,
464         _ => false,
465     });
466     cfg.generate("../src/lib.rs", "main.rs");
467 }
468 
469 fn test_openbsd(target: &str) {
470     assert!(target.contains("openbsd"));
471 
472     let mut cfg = ctest_cfg();
473     cfg.flag("-Wno-deprecated-declarations");
474 
475     let x86_64 = target.contains("x86_64");
476 
477     headers! { cfg:
478         "elf.h",
479         "errno.h",
480         "execinfo.h",
481         "fcntl.h",
482         "fnmatch.h",
483         "getopt.h",
484         "libgen.h",
485         "limits.h",
486         "link.h",
487         "locale.h",
488         "stddef.h",
489         "stdint.h",
490         "stdio.h",
491         "stdlib.h",
492         "sys/stat.h",
493         "sys/types.h",
494         "time.h",
495         "wchar.h",
496         "ctype.h",
497         "dirent.h",
498         "sys/socket.h",
499         [x86_64]:"machine/fpu.h",
500         "net/if.h",
501         "net/route.h",
502         "net/if_arp.h",
503         "netdb.h",
504         "netinet/in.h",
505         "netinet/ip.h",
506         "netinet/tcp.h",
507         "netinet/udp.h",
508         "net/bpf.h",
509         "regex.h",
510         "resolv.h",
511         "pthread.h",
512         "dlfcn.h",
513         "search.h",
514         "spawn.h",
515         "signal.h",
516         "string.h",
517         "sys/file.h",
518         "sys/futex.h",
519         "sys/ioctl.h",
520         "sys/ipc.h",
521         "sys/mman.h",
522         "sys/param.h",
523         "sys/resource.h",
524         "sys/shm.h",
525         "sys/socket.h",
526         "sys/time.h",
527         "sys/uio.h",
528         "sys/ktrace.h",
529         "sys/un.h",
530         "sys/wait.h",
531         "unistd.h",
532         "utime.h",
533         "pwd.h",
534         "grp.h",
535         "sys/utsname.h",
536         "sys/ptrace.h",
537         "sys/mount.h",
538         "sys/uio.h",
539         "sched.h",
540         "termios.h",
541         "poll.h",
542         "syslog.h",
543         "semaphore.h",
544         "sys/statvfs.h",
545         "sys/times.h",
546         "glob.h",
547         "ifaddrs.h",
548         "langinfo.h",
549         "sys/sysctl.h",
550         "utmp.h",
551         "sys/event.h",
552         "net/if_dl.h",
553         "util.h",
554         "ufs/ufs/quota.h",
555         "pthread_np.h",
556         "sys/reboot.h",
557         "sys/syscall.h",
558         "sys/shm.h",
559         "sys/param.h",
560     }
561 
562     cfg.skip_struct(move |ty| {
563         if ty.starts_with("__c_anonymous_") {
564             return true;
565         }
566         match ty {
567             // FIXME: actually a union
568             "sigval" => true,
569 
570             _ => false,
571         }
572     });
573 
574     cfg.skip_const(move |name| {
575         match name {
576             // Removed in OpenBSD 6.0
577             "KERN_USERMOUNT" | "KERN_ARND" => true,
578             // Removed in OpenBSD 7.2
579             "KERN_NSELCOLL" => true,
580             // Good chance it's going to be wrong depending on the host release
581             "KERN_MAXID" | "NET_RT_MAXID" => true,
582             "EV_SYSFLAGS" => true,
583             _ => false,
584         }
585     });
586 
587     cfg.skip_fn(move |name| {
588         match name {
589             // FIXME: https://github.com/rust-lang/libc/issues/1272
590             "execv" | "execve" | "execvp" | "execvpe" => true,
591 
592             // Removed in OpenBSD 6.5
593             // https://marc.info/?l=openbsd-cvs&m=154723400730318
594             "mincore" => true,
595 
596             // futex() has volatile arguments, but that doesn't exist in Rust.
597             "futex" => true,
598 
599             // Available for openBSD 7.3
600             "mimmutable" => true,
601 
602             // Removed in OpenBSD 7.5
603             // https://marc.info/?l=openbsd-cvs&m=170239504300386
604             "syscall" => true,
605 
606             _ => false,
607         }
608     });
609 
610     cfg.type_name(move |ty, is_struct, is_union| {
611         match ty {
612             // Just pass all these through, no need for a "struct" prefix
613             "FILE" | "DIR" | "Dl_info" | "Elf32_Phdr" | "Elf64_Phdr" => ty.to_string(),
614 
615             // OSX calls this something else
616             "sighandler_t" => "sig_t".to_string(),
617 
618             t if is_union => format!("union {}", t),
619             t if t.ends_with("_t") => t.to_string(),
620             t if is_struct => format!("struct {}", t),
621             t => t.to_string(),
622         }
623     });
624 
625     cfg.field_name(move |struct_, field| match field {
626         "st_birthtime" if struct_.starts_with("stat") => "__st_birthtime".to_string(),
627         "st_birthtime_nsec" if struct_.starts_with("stat") => "__st_birthtimensec".to_string(),
628         s if s.ends_with("_nsec") && struct_.starts_with("stat") => s.replace("e_nsec", ".tv_nsec"),
629         "sa_sigaction" if struct_ == "sigaction" => "sa_handler".to_string(),
630         s => s.to_string(),
631     });
632 
633     cfg.skip_field_type(move |struct_, field| {
634         // type siginfo_t.si_addr changed from OpenBSD 6.0 to 6.1
635         struct_ == "siginfo_t" && field == "si_addr"
636     });
637 
638     cfg.skip_field(|struct_, field| {
639         match (struct_, field) {
640             // conflicting with `p_type` macro from <resolve.h>.
641             ("Elf32_Phdr", "p_type") => true,
642             ("Elf64_Phdr", "p_type") => true,
643             // ifr_ifru is defined is an union
644             ("ifreq", "ifr_ifru") => true,
645             _ => false,
646         }
647     });
648 
649     cfg.generate("../src/lib.rs", "main.rs");
650 }
651 
652 fn test_windows(target: &str) {
653     assert!(target.contains("windows"));
654     let gnu = target.contains("gnu");
655     let i686 = target.contains("i686");
656 
657     let mut cfg = ctest_cfg();
658     if target.contains("msvc") {
659         cfg.flag("/wd4324");
660     }
661     cfg.define("_WIN32_WINNT", Some("0x8000"));
662 
663     headers! { cfg:
664         "direct.h",
665         "errno.h",
666         "fcntl.h",
667         "io.h",
668         "limits.h",
669         "locale.h",
670         "process.h",
671         "signal.h",
672         "stddef.h",
673         "stdint.h",
674         "stdio.h",
675         "stdlib.h",
676         "sys/stat.h",
677         "sys/types.h",
678         "sys/utime.h",
679         "time.h",
680         "wchar.h",
681         [gnu]: "ws2tcpip.h",
682         [!gnu]: "Winsock2.h",
683     }
684 
685     cfg.type_name(move |ty, is_struct, is_union| {
686         match ty {
687             // Just pass all these through, no need for a "struct" prefix
688             "FILE" | "DIR" | "Dl_info" => ty.to_string(),
689 
690             // FIXME: these don't exist:
691             "time64_t" => "__time64_t".to_string(),
692             "ssize_t" => "SSIZE_T".to_string(),
693 
694             "sighandler_t" if !gnu => "_crt_signal_t".to_string(),
695             "sighandler_t" if gnu => "__p_sig_fn_t".to_string(),
696 
697             t if is_union => format!("union {}", t),
698             t if t.ends_with("_t") => t.to_string(),
699 
700             // Windows uppercase structs don't have `struct` in front:
701             t if is_struct => {
702                 if ty.chars().next().unwrap().is_uppercase() {
703                     t.to_string()
704                 } else if t == "stat" {
705                     "struct __stat64".to_string()
706                 } else if t == "utimbuf" {
707                     "struct __utimbuf64".to_string()
708                 } else {
709                     // put `struct` in front of all structs:
710                     format!("struct {}", t)
711                 }
712             }
713             t => t.to_string(),
714         }
715     });
716 
717     cfg.fn_cname(move |name, cname| cname.unwrap_or(name).to_string());
718 
719     cfg.skip_type(move |name| match name {
720         "SSIZE_T" if !gnu => true,
721         "ssize_t" if !gnu => true,
722         // FIXME: The size and alignment of this type are incorrect
723         "time_t" if gnu && i686 => true,
724         _ => false,
725     });
726 
727     cfg.skip_struct(move |ty| {
728         if ty.starts_with("__c_anonymous_") {
729             return true;
730         }
731         match ty {
732             // FIXME: The size and alignment of this struct are incorrect
733             "timespec" if gnu && i686 => true,
734             _ => false,
735         }
736     });
737 
738     cfg.skip_const(move |name| {
739         match name {
740             // FIXME: API error:
741             // SIG_ERR type is "void (*)(int)", not "int"
742             "SIG_ERR" |
743             // Similar for SIG_DFL/IGN/GET/SGE/ACK
744             "SIG_DFL" | "SIG_IGN" | "SIG_GET" | "SIG_SGE" | "SIG_ACK" => true,
745             // FIXME: newer windows-gnu environment on CI?
746             "_O_OBTAIN_DIR" if gnu => true,
747             _ => false,
748         }
749     });
750 
751     cfg.skip_field(move |s, field| match s {
752         "CONTEXT" if field == "Fp" => true,
753         _ => false,
754     });
755     // FIXME: All functions point to the wrong addresses?
756     cfg.skip_fn_ptrcheck(|_| true);
757 
758     cfg.skip_signededness(move |c| {
759         match c {
760             // windows-isms
761             n if n.starts_with("P") => true,
762             n if n.starts_with("H") => true,
763             n if n.starts_with("LP") => true,
764             "sighandler_t" if gnu => true,
765             _ => false,
766         }
767     });
768 
769     cfg.skip_fn(move |name| {
770         match name {
771             // FIXME: https://github.com/rust-lang/libc/issues/1272
772             "execv" | "execve" | "execvp" | "execvpe" => true,
773 
774             _ => false,
775         }
776     });
777 
778     cfg.generate("../src/lib.rs", "main.rs");
779 }
780 
781 fn test_redox(target: &str) {
782     assert!(target.contains("redox"));
783 
784     let mut cfg = ctest_cfg();
785     cfg.flag("-Wno-deprecated-declarations");
786 
787     headers! {
788         cfg:
789         "ctype.h",
790         "dirent.h",
791         "dlfcn.h",
792         "errno.h",
793         "fcntl.h",
794         "fnmatch.h",
795         "grp.h",
796         "limits.h",
797         "locale.h",
798         "netdb.h",
799         "netinet/in.h",
800         "netinet/ip.h",
801         "netinet/tcp.h",
802         "poll.h",
803         "pwd.h",
804         "semaphore.h",
805         "string.h",
806         "strings.h",
807         "sys/file.h",
808         "sys/ioctl.h",
809         "sys/mman.h",
810         "sys/ptrace.h",
811         "sys/resource.h",
812         "sys/socket.h",
813         "sys/stat.h",
814         "sys/statvfs.h",
815         "sys/time.h",
816         "sys/types.h",
817         "sys/uio.h",
818         "sys/un.h",
819         "sys/utsname.h",
820         "sys/wait.h",
821         "termios.h",
822         "time.h",
823         "unistd.h",
824         "utime.h",
825         "wchar.h",
826     }
827 
828     cfg.generate("../src/lib.rs", "main.rs");
829 }
830 
831 fn test_solarish(target: &str) {
832     let is_solaris = target.contains("solaris");
833     let is_illumos = target.contains("illumos");
834     assert!(is_solaris || is_illumos);
835 
836     // ctest generates arguments supported only by clang, so make sure to run with CC=clang.
837     // While debugging, "CFLAGS=-ferror-limit=<large num>" is useful to get more error output.
838     let mut cfg = ctest_cfg();
839     cfg.flag("-Wno-deprecated-declarations");
840 
841     cfg.define("_XOPEN_SOURCE", Some("700"));
842     cfg.define("__EXTENSIONS__", None);
843     cfg.define("_LCONV_C99", None);
844 
845     headers! {
846         cfg:
847         "aio.h",
848         "ctype.h",
849         "dirent.h",
850         "dlfcn.h",
851         "door.h",
852         "errno.h",
853         "execinfo.h",
854         "fcntl.h",
855         "fnmatch.h",
856         "getopt.h",
857         "glob.h",
858         "grp.h",
859         "ifaddrs.h",
860         "langinfo.h",
861         "limits.h",
862         "link.h",
863         "locale.h",
864         "mqueue.h",
865         "net/if.h",
866         "net/if_arp.h",
867         "net/route.h",
868         "netdb.h",
869         "netinet/in.h",
870         "netinet/ip.h",
871         "netinet/tcp.h",
872         "netinet/udp.h",
873         "poll.h",
874         "port.h",
875         "pthread.h",
876         "pwd.h",
877         "resolv.h",
878         "sched.h",
879         "semaphore.h",
880         "signal.h",
881         "stddef.h",
882         "stdint.h",
883         "stdio.h",
884         "stdlib.h",
885         "string.h",
886         "sys/auxv.h",
887         "sys/file.h",
888         "sys/filio.h",
889         "sys/ioctl.h",
890         "sys/lgrp_user.h",
891         "sys/loadavg.h",
892         "sys/mkdev.h",
893         "sys/mman.h",
894         "sys/mount.h",
895         "sys/priv.h",
896         "sys/pset.h",
897         "sys/random.h",
898         "sys/resource.h",
899         "sys/sendfile.h",
900         "sys/socket.h",
901         "sys/stat.h",
902         "sys/statvfs.h",
903         "sys/stropts.h",
904         "sys/shm.h",
905         "sys/systeminfo.h",
906         "sys/time.h",
907         "sys/times.h",
908         "sys/timex.h",
909         "sys/types.h",
910         "sys/uio.h",
911         "sys/un.h",
912         "sys/utsname.h",
913         "sys/wait.h",
914         "syslog.h",
915         "termios.h",
916         "thread.h",
917         "time.h",
918         "priv.h",
919         "ucontext.h",
920         "unistd.h",
921         "utime.h",
922         "utmpx.h",
923         "wchar.h",
924     }
925 
926     if is_illumos {
927         headers! { cfg:
928             "sys/epoll.h",
929             "sys/eventfd.h",
930         }
931     }
932 
933     if is_solaris {
934         headers! { cfg:
935             "sys/lgrp_user_impl.h",
936         }
937     }
938 
939     cfg.skip_type(move |ty| match ty {
940         "sighandler_t" => true,
941         _ => false,
942     });
943 
944     cfg.type_name(move |ty, is_struct, is_union| match ty {
945         "FILE" => "__FILE".to_string(),
946         "DIR" | "Dl_info" => ty.to_string(),
947         t if t.ends_with("_t") => t.to_string(),
948         t if is_struct => format!("struct {}", t),
949         t if is_union => format!("union {}", t),
950         t => t.to_string(),
951     });
952 
953     cfg.field_name(move |struct_, field| {
954         match struct_ {
955             // rust struct uses raw u64, rather than union
956             "epoll_event" if field == "u64" => "data.u64".to_string(),
957             // rust struct was committed with typo for Solaris
958             "door_arg_t" if field == "dec_num" => "desc_num".to_string(),
959             "stat" if field.ends_with("_nsec") => {
960                 // expose stat.Xtim.tv_nsec fields
961                 field.trim_end_matches("e_nsec").to_string() + ".tv_nsec"
962             }
963             _ => field.to_string(),
964         }
965     });
966 
967     cfg.skip_const(move |name| match name {
968         "DT_FIFO" | "DT_CHR" | "DT_DIR" | "DT_BLK" | "DT_REG" | "DT_LNK" | "DT_SOCK"
969         | "USRQUOTA" | "GRPQUOTA" | "PRIO_MIN" | "PRIO_MAX" => true,
970 
971         // skip sighandler_t assignments
972         "SIG_DFL" | "SIG_ERR" | "SIG_IGN" => true,
973 
974         "DT_UNKNOWN" => true,
975 
976         "_UTX_LINESIZE" | "_UTX_USERSIZE" | "_UTX_PADSIZE" | "_UTX_IDSIZE" | "_UTX_HOSTSIZE" => {
977             true
978         }
979 
980         "EADI" | "EXTPROC" | "IPC_SEAT" => true,
981 
982         // This evaluates to a sysconf() call rather than a constant
983         "PTHREAD_STACK_MIN" => true,
984 
985         // EPOLLEXCLUSIVE is a relatively recent addition to the epoll interface and may not be
986         // defined on older systems.  It is, however, safe to use on systems which do not
987         // explicitly support it. (A no-op is an acceptable implementation of EPOLLEXCLUSIVE.)
988         "EPOLLEXCLUSIVE" if is_illumos => true,
989 
990         _ => false,
991     });
992 
993     cfg.skip_struct(move |ty| {
994         if ty.starts_with("__c_anonymous_") {
995             return true;
996         }
997         // the union handling is a mess
998         if ty.contains("door_desc_t_") {
999             return true;
1000         }
1001         match ty {
1002             // union, not a struct
1003             "sigval" => true,
1004             // a bunch of solaris-only fields
1005             "utmpx" if is_illumos => true,
1006             _ => false,
1007         }
1008     });
1009 
1010     cfg.skip_field_type(move |struct_, field| {
1011         // aio_buf is "volatile void*"
1012         struct_ == "aiocb" && field == "aio_buf"
1013     });
1014 
1015     cfg.skip_field(move |s, field| {
1016         match s {
1017             // C99 sizing on this is tough
1018             "dirent" if field == "d_name" => true,
1019             // the union/macro makes this rough
1020             "sigaction" if field == "sa_sigaction" => true,
1021             // Missing in illumos
1022             "sigevent" if field == "ss_sp" => true,
1023             // Avoid sigval union issues
1024             "sigevent" if field == "sigev_value" => true,
1025             // const issues
1026             "sigevent" if field == "sigev_notify_attributes" => true,
1027 
1028             // Avoid const and union issues
1029             "door_arg" if field == "desc_ptr" => true,
1030             "door_desc_t" if field == "d_data" => true,
1031             "door_arg_t" if field.ends_with("_ptr") => true,
1032             "door_arg_t" if field.ends_with("rbuf") => true,
1033 
1034             // anonymous union challenges
1035             "fpregset_t" if field == "fp_reg_set" => true,
1036 
1037             // The LX brand (integrated into some illumos distros) commandeered several of the
1038             // `uc_filler` fields to use for brand-specific state.
1039             "ucontext_t" if is_illumos && (field == "uc_filler" || field == "uc_brand_data") => {
1040                 true
1041             }
1042 
1043             _ => false,
1044         }
1045     });
1046 
1047     cfg.skip_fn(move |name| {
1048         // skip those that are manually verified
1049         match name {
1050             // const-ness only added recently
1051             "dladdr" => true,
1052 
1053             // Definition of those functions as changed since unified headers
1054             // from NDK r14b These changes imply some API breaking changes but
1055             // are still ABI compatible. We can wait for the next major release
1056             // to be compliant with the new API.
1057             //
1058             // FIXME: unskip these for next major release
1059             "setpriority" | "personality" => true,
1060 
1061             // signal is defined in terms of sighandler_t, so ignore
1062             "signal" => true,
1063 
1064             // Currently missing
1065             "cfmakeraw" | "cfsetspeed" => true,
1066 
1067             // const-ness issues
1068             "execv" | "execve" | "execvp" | "settimeofday" | "sethostname" => true,
1069 
1070             // FIXME(1.0): https://github.com/rust-lang/libc/issues/1272
1071             "fexecve" => true,
1072 
1073             // Solaris-different
1074             "getpwent_r" | "getgrent_r" | "updwtmpx" if is_illumos => true,
1075             "madvise" | "mprotect" if is_illumos => true,
1076             "door_call" | "door_return" | "door_create" if is_illumos => true,
1077 
1078             // The compat functions use these "native" functions linked to their
1079             // non-prefixed implementations in libc.
1080             "native_getpwent_r" | "native_getgrent_r" => true,
1081 
1082             // Not visible when build with _XOPEN_SOURCE=700
1083             "mmapobj" | "mmap64" | "meminfo" | "getpagesizes" | "getpagesizes2" => true,
1084 
1085             // These functions may return int or void depending on the exact
1086             // configuration of the compilation environment, but the return
1087             // value is not useful (always 0) so we can ignore it:
1088             "setservent" | "endservent" => true,
1089 
1090             // Following illumos#3729, getifaddrs was changed to a
1091             // redefine_extname symbol in order to preserve compatibility.
1092             // Until better symbol binding story is figured out, it must be
1093             // excluded from the tests.
1094             "getifaddrs" if is_illumos => true,
1095 
1096             // FIXME: Our API is unsound. The Rust API allows aliasing
1097             // pointers, but the C API requires pointers not to alias.
1098             // We should probably be at least using `&`/`&mut` here, see:
1099             // https://github.com/gnzlbg/ctest/issues/68
1100             "lio_listio" => true,
1101 
1102             _ => false,
1103         }
1104     });
1105 
1106     cfg.generate("../src/lib.rs", "main.rs");
1107 }
1108 
1109 fn test_netbsd(target: &str) {
1110     assert!(target.contains("netbsd"));
1111     let mut cfg = ctest_cfg();
1112 
1113     cfg.flag("-Wno-deprecated-declarations");
1114     cfg.define("_NETBSD_SOURCE", Some("1"));
1115 
1116     headers! {
1117         cfg:
1118         "elf.h",
1119         "errno.h",
1120         "fcntl.h",
1121         "fnmatch.h",
1122         "getopt.h",
1123         "libgen.h",
1124         "limits.h",
1125         "link.h",
1126         "locale.h",
1127         "stddef.h",
1128         "stdint.h",
1129         "stdio.h",
1130         "stdlib.h",
1131         "sys/stat.h",
1132         "sys/types.h",
1133         "time.h",
1134         "wchar.h",
1135         "aio.h",
1136         "ctype.h",
1137         "dirent.h",
1138         "dlfcn.h",
1139         "glob.h",
1140         "grp.h",
1141         "ifaddrs.h",
1142         "langinfo.h",
1143         "net/bpf.h",
1144         "net/if.h",
1145         "net/if_arp.h",
1146         "net/if_dl.h",
1147         "net/route.h",
1148         "netdb.h",
1149         "netinet/in.h",
1150         "netinet/ip.h",
1151         "netinet/tcp.h",
1152         "netinet/udp.h",
1153         "poll.h",
1154         "pthread.h",
1155         "pwd.h",
1156         "regex.h",
1157         "resolv.h",
1158         "sched.h",
1159         "semaphore.h",
1160         "signal.h",
1161         "string.h",
1162         "sys/endian.h",
1163         "sys/exec_elf.h",
1164         "sys/xattr.h",
1165         "sys/extattr.h",
1166         "sys/file.h",
1167         "sys/ioctl.h",
1168         "sys/ioctl_compat.h",
1169         "sys/ipc.h",
1170         "sys/ktrace.h",
1171         "sys/mman.h",
1172         "sys/mount.h",
1173         "sys/ptrace.h",
1174         "sys/resource.h",
1175         "sys/shm.h",
1176         "sys/socket.h",
1177         "sys/statvfs.h",
1178         "sys/sysctl.h",
1179         "sys/time.h",
1180         "sys/times.h",
1181         "sys/timex.h",
1182         "sys/ucontext.h",
1183         "sys/ucred.h",
1184         "sys/uio.h",
1185         "sys/un.h",
1186         "sys/utsname.h",
1187         "sys/wait.h",
1188         "syslog.h",
1189         "termios.h",
1190         "ufs/ufs/quota.h",
1191         "ufs/ufs/quota1.h",
1192         "unistd.h",
1193         "util.h",
1194         "utime.h",
1195         "mqueue.h",
1196         "netinet/dccp.h",
1197         "sys/event.h",
1198         "sys/quota.h",
1199         "sys/reboot.h",
1200         "sys/shm.h",
1201         "iconv.h",
1202     }
1203 
1204     cfg.type_name(move |ty, is_struct, is_union| {
1205         match ty {
1206             // Just pass all these through, no need for a "struct" prefix
1207             "FILE" | "fd_set" | "Dl_info" | "DIR" | "Elf32_Phdr" | "Elf64_Phdr" | "Elf32_Shdr"
1208             | "Elf64_Shdr" | "Elf32_Sym" | "Elf64_Sym" | "Elf32_Ehdr" | "Elf64_Ehdr"
1209             | "Elf32_Chdr" | "Elf64_Chdr" => ty.to_string(),
1210 
1211             // OSX calls this something else
1212             "sighandler_t" => "sig_t".to_string(),
1213 
1214             t if is_union => format!("union {}", t),
1215 
1216             t if t.ends_with("_t") => t.to_string(),
1217 
1218             // put `struct` in front of all structs:.
1219             t if is_struct => format!("struct {}", t),
1220 
1221             t => t.to_string(),
1222         }
1223     });
1224 
1225     cfg.field_name(move |struct_, field| {
1226         match field {
1227             // Our stat *_nsec fields normally don't actually exist but are part
1228             // of a timeval struct
1229             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
1230                 s.replace("e_nsec", ".tv_nsec")
1231             }
1232             "u64" if struct_ == "epoll_event" => "data.u64".to_string(),
1233             s => s.to_string(),
1234         }
1235     });
1236 
1237     cfg.skip_type(move |ty| {
1238         if ty.starts_with("__c_anonymous_") {
1239             return true;
1240         }
1241         match ty {
1242             // FIXME: sighandler_t is crazy across platforms
1243             "sighandler_t" => true,
1244             _ => false,
1245         }
1246     });
1247 
1248     cfg.skip_struct(move |ty| {
1249         match ty {
1250             // This is actually a union, not a struct
1251             "sigval" => true,
1252             // These are tested as part of the linux_fcntl tests since there are
1253             // header conflicts when including them with all the other structs.
1254             "termios2" => true,
1255             _ => false,
1256         }
1257     });
1258 
1259     cfg.skip_signededness(move |c| {
1260         match c {
1261             "LARGE_INTEGER" | "float" | "double" => true,
1262             n if n.starts_with("pthread") => true,
1263             // sem_t is a struct or pointer
1264             "sem_t" => true,
1265             _ => false,
1266         }
1267     });
1268 
1269     cfg.skip_const(move |name| {
1270         match name {
1271             "SIG_DFL" | "SIG_ERR" | "SIG_IGN" => true, // sighandler_t weirdness
1272             "SIGUNUSED" => true,                       // removed in glibc 2.26
1273 
1274             // weird signed extension or something like that?
1275             "MS_NOUSER" => true,
1276             "MS_RMT_MASK" => true, // updated in glibc 2.22 and musl 1.1.13
1277             "BOTHER" => true,
1278             "GRND_RANDOM" | "GRND_INSECURE" | "GRND_NONBLOCK" => true, // netbsd 10 minimum
1279 
1280             _ => false,
1281         }
1282     });
1283 
1284     cfg.skip_fn(move |name| {
1285         match name {
1286             // FIXME: https://github.com/rust-lang/libc/issues/1272
1287             "execv" | "execve" | "execvp" => true,
1288             // FIXME: netbsd 10 minimum
1289             "getentropy" | "getrandom" => true,
1290 
1291             "getrlimit" | "getrlimit64" |    // non-int in 1st arg
1292             "setrlimit" | "setrlimit64" |    // non-int in 1st arg
1293             "prlimit" | "prlimit64" |        // non-int in 2nd arg
1294 
1295             _ => false,
1296         }
1297     });
1298 
1299     cfg.skip_field_type(move |struct_, field| {
1300         // This is a weird union, don't check the type.
1301         (struct_ == "ifaddrs" && field == "ifa_ifu") ||
1302         // sighandler_t type is super weird
1303         (struct_ == "sigaction" && field == "sa_sigaction") ||
1304         // sigval is actually a union, but we pretend it's a struct
1305         (struct_ == "sigevent" && field == "sigev_value") ||
1306         // aio_buf is "volatile void*" and Rust doesn't understand volatile
1307         (struct_ == "aiocb" && field == "aio_buf")
1308     });
1309 
1310     cfg.skip_field(|struct_, field| {
1311         match (struct_, field) {
1312             // conflicting with `p_type` macro from <resolve.h>.
1313             ("Elf32_Phdr", "p_type") => true,
1314             ("Elf64_Phdr", "p_type") => true,
1315             // pthread_spin_t is a volatile uchar
1316             ("pthread_spinlock_t", "pts_spin") => true,
1317             _ => false,
1318         }
1319     });
1320 
1321     cfg.generate("../src/lib.rs", "main.rs");
1322 }
1323 
1324 fn test_dragonflybsd(target: &str) {
1325     assert!(target.contains("dragonfly"));
1326     let mut cfg = ctest_cfg();
1327     cfg.flag("-Wno-deprecated-declarations");
1328 
1329     headers! {
1330         cfg:
1331         "aio.h",
1332         "ctype.h",
1333         "dirent.h",
1334         "dlfcn.h",
1335         "errno.h",
1336         "execinfo.h",
1337         "fcntl.h",
1338         "fnmatch.h",
1339         "getopt.h",
1340         "glob.h",
1341         "grp.h",
1342         "ifaddrs.h",
1343         "kenv.h",
1344         "kvm.h",
1345         "langinfo.h",
1346         "libgen.h",
1347         "limits.h",
1348         "link.h",
1349         "locale.h",
1350         "mqueue.h",
1351         "net/bpf.h",
1352         "net/if.h",
1353         "net/if_arp.h",
1354         "net/if_dl.h",
1355         "net/route.h",
1356         "netdb.h",
1357         "netinet/in.h",
1358         "netinet/ip.h",
1359         "netinet/tcp.h",
1360         "netinet/udp.h",
1361         "poll.h",
1362         "pthread.h",
1363         "pthread_np.h",
1364         "pwd.h",
1365         "regex.h",
1366         "resolv.h",
1367         "sched.h",
1368         "semaphore.h",
1369         "signal.h",
1370         "stddef.h",
1371         "stdint.h",
1372         "stdio.h",
1373         "stdlib.h",
1374         "string.h",
1375         "sys/event.h",
1376         "sys/file.h",
1377         "sys/ioctl.h",
1378         "sys/cpuctl.h",
1379         "sys/eui64.h",
1380         "sys/ipc.h",
1381         "sys/kinfo.h",
1382         "sys/ktrace.h",
1383         "sys/malloc.h",
1384         "sys/mman.h",
1385         "sys/mount.h",
1386         "sys/procctl.h",
1387         "sys/ptrace.h",
1388         "sys/reboot.h",
1389         "sys/resource.h",
1390         "sys/rtprio.h",
1391         "sys/sched.h",
1392         "sys/shm.h",
1393         "sys/socket.h",
1394         "sys/stat.h",
1395         "sys/statvfs.h",
1396         "sys/sysctl.h",
1397         "sys/time.h",
1398         "sys/times.h",
1399         "sys/timex.h",
1400         "sys/types.h",
1401         "sys/checkpoint.h",
1402         "sys/uio.h",
1403         "sys/un.h",
1404         "sys/utsname.h",
1405         "sys/wait.h",
1406         "syslog.h",
1407         "termios.h",
1408         "time.h",
1409         "ucontext.h",
1410         "unistd.h",
1411         "util.h",
1412         "utime.h",
1413         "utmpx.h",
1414         "vfs/ufs/quota.h",
1415         "vm/vm_map.h",
1416         "wchar.h",
1417         "iconv.h",
1418     }
1419 
1420     cfg.type_name(move |ty, is_struct, is_union| {
1421         match ty {
1422             // Just pass all these through, no need for a "struct" prefix
1423             "FILE" | "fd_set" | "Dl_info" | "DIR" | "Elf32_Phdr" | "Elf64_Phdr" | "Elf32_Shdr"
1424             | "Elf64_Shdr" | "Elf32_Sym" | "Elf64_Sym" | "Elf32_Ehdr" | "Elf64_Ehdr"
1425             | "Elf32_Chdr" | "Elf64_Chdr" => ty.to_string(),
1426 
1427             // FIXME: OSX calls this something else
1428             "sighandler_t" => "sig_t".to_string(),
1429 
1430             t if is_union => format!("union {}", t),
1431 
1432             t if t.ends_with("_t") => t.to_string(),
1433 
1434             // sigval is a struct in Rust, but a union in C:
1435             "sigval" => format!("union sigval"),
1436 
1437             // put `struct` in front of all structs:.
1438             t if is_struct => format!("struct {}", t),
1439 
1440             t => t.to_string(),
1441         }
1442     });
1443 
1444     cfg.field_name(move |struct_, field| {
1445         match field {
1446             // Our stat *_nsec fields normally don't actually exist but are part
1447             // of a timeval struct
1448             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
1449                 s.replace("e_nsec", ".tv_nsec")
1450             }
1451             "u64" if struct_ == "epoll_event" => "data.u64".to_string(),
1452             // Field is named `type` in C but that is a Rust keyword,
1453             // so these fields are translated to `type_` in the bindings.
1454             "type_" if struct_ == "rtprio" => "type".to_string(),
1455             s => s.to_string(),
1456         }
1457     });
1458 
1459     cfg.skip_type(move |ty| {
1460         match ty {
1461             // sighandler_t is crazy across platforms
1462             "sighandler_t" => true,
1463 
1464             _ => false,
1465         }
1466     });
1467 
1468     cfg.skip_struct(move |ty| {
1469         if ty.starts_with("__c_anonymous_") {
1470             return true;
1471         }
1472         match ty {
1473             // FIXME: These are tested as part of the linux_fcntl tests since
1474             // there are header conflicts when including them with all the other
1475             // structs.
1476             "termios2" => true,
1477 
1478             _ => false,
1479         }
1480     });
1481 
1482     cfg.skip_signededness(move |c| {
1483         match c {
1484             "LARGE_INTEGER" | "float" | "double" => true,
1485             // uuid_t is a struct, not an integer.
1486             "uuid_t" => true,
1487             n if n.starts_with("pthread") => true,
1488             // sem_t is a struct or pointer
1489             "sem_t" => true,
1490             // mqd_t is a pointer on DragonFly
1491             "mqd_t" => true,
1492 
1493             _ => false,
1494         }
1495     });
1496 
1497     cfg.skip_const(move |name| {
1498         match name {
1499             "SIG_DFL" | "SIG_ERR" | "SIG_IGN" => true, // sighandler_t weirdness
1500 
1501             // weird signed extension or something like that?
1502             "MS_NOUSER" => true,
1503             "MS_RMT_MASK" => true, // updated in glibc 2.22 and musl 1.1.13
1504 
1505             // These are defined for Solaris 11, but the crate is tested on
1506             // illumos, where they are currently not defined
1507             "EADI" | "PORT_SOURCE_POSTWAIT" | "PORT_SOURCE_SIGNAL" | "PTHREAD_STACK_MIN" => true,
1508 
1509             _ => false,
1510         }
1511     });
1512 
1513     cfg.skip_fn(move |name| {
1514         // skip those that are manually verified
1515         match name {
1516             // FIXME: https://github.com/rust-lang/libc/issues/1272
1517             "execv" | "execve" | "execvp" | "fexecve" => true,
1518 
1519             "getrlimit" | "getrlimit64" |    // non-int in 1st arg
1520             "setrlimit" | "setrlimit64" |    // non-int in 1st arg
1521             "prlimit" | "prlimit64"        // non-int in 2nd arg
1522              => true,
1523 
1524             _ => false,
1525         }
1526     });
1527 
1528     cfg.skip_field_type(move |struct_, field| {
1529         // This is a weird union, don't check the type.
1530         (struct_ == "ifaddrs" && field == "ifa_ifu") ||
1531         // sighandler_t type is super weird
1532         (struct_ == "sigaction" && field == "sa_sigaction") ||
1533         // sigval is actually a union, but we pretend it's a struct
1534         (struct_ == "sigevent" && field == "sigev_value") ||
1535         // aio_buf is "volatile void*" and Rust doesn't understand volatile
1536         (struct_ == "aiocb" && field == "aio_buf")
1537     });
1538 
1539     cfg.skip_field(move |struct_, field| {
1540         // this is actually a union on linux, so we can't represent it well and
1541         // just insert some padding.
1542         (struct_ == "siginfo_t" && field == "_pad") ||
1543         // sigev_notify_thread_id is actually part of a sigev_un union
1544         (struct_ == "sigevent" && field == "sigev_notify_thread_id")
1545     });
1546 
1547     cfg.generate("../src/lib.rs", "main.rs");
1548 }
1549 
1550 fn test_wasi(target: &str) {
1551     assert!(target.contains("wasi"));
1552     let p2 = target.contains("wasip2");
1553 
1554     let mut cfg = ctest_cfg();
1555     cfg.define("_GNU_SOURCE", None);
1556 
1557     headers! { cfg:
1558         "ctype.h",
1559         "dirent.h",
1560         "errno.h",
1561         "fcntl.h",
1562         "fnmatch.h",
1563         "langinfo.h",
1564         "limits.h",
1565         "locale.h",
1566         "malloc.h",
1567         [p2]: "netdb.h",
1568         [p2]: "netinet/in.h",
1569         [p2]: "netinet/tcp.h",
1570         "poll.h",
1571         "sched.h",
1572         "stdbool.h",
1573         "stddef.h",
1574         "stdint.h",
1575         "stdio.h",
1576         "stdlib.h",
1577         "string.h",
1578         "sys/ioctl.h",
1579         "sys/resource.h",
1580         "sys/select.h",
1581         "sys/socket.h",
1582         "sys/stat.h",
1583         "sys/times.h",
1584         "sys/types.h",
1585         "sys/uio.h",
1586         "sys/utsname.h",
1587         "time.h",
1588         "unistd.h",
1589         "wasi/api.h",
1590         "wasi/libc-find-relpath.h",
1591         "wasi/libc-nocwd.h",
1592         "wasi/libc.h",
1593         "wchar.h",
1594     }
1595 
1596     // Currently `ctest2` doesn't support macros-in-static-expressions and will
1597     // panic on them. That affects `CLOCK_*` defines in wasi to set this here
1598     // to omit them.
1599     cfg.cfg("libc_ctest", None);
1600 
1601     // `ctest2` has a hard-coded list of default cfgs which doesn't include
1602     // wasip2, which is why it has to be set here manually.
1603     if p2 {
1604         cfg.cfg("target_env", Some("p2"));
1605     }
1606 
1607     cfg.type_name(move |ty, is_struct, is_union| match ty {
1608         "FILE" | "fd_set" | "DIR" => ty.to_string(),
1609         t if is_union => format!("union {}", t),
1610         t if t.starts_with("__wasi") && t.ends_with("_u") => format!("union {}", t),
1611         t if t.starts_with("__wasi") && is_struct => format!("struct {}", t),
1612         t if t.ends_with("_t") => t.to_string(),
1613         t if is_struct => format!("struct {}", t),
1614         t => t.to_string(),
1615     });
1616 
1617     cfg.field_name(move |_struct, field| {
1618         match field {
1619             // deal with fields as rust keywords
1620             "type_" => "type".to_string(),
1621             s => s.to_string(),
1622         }
1623     });
1624 
1625     // These have a different and internal type in header files and are only
1626     // used here to generate a pointer to them in bindings so skip these tests.
1627     cfg.skip_static(|c| c.starts_with("_CLOCK_"));
1628 
1629     cfg.skip_const(|c| match c {
1630         // These constants aren't yet defined in wasi-libc.
1631         // Exposing them is being tracked by https://github.com/WebAssembly/wasi-libc/issues/531.
1632         "SO_BROADCAST" | "SO_LINGER" => true,
1633 
1634         _ => false,
1635     });
1636 
1637     cfg.skip_fn(|f| match f {
1638         // This function doesn't actually exist in libc's header files
1639         "__errno_location" => true,
1640 
1641         // The `timeout` argument to this function is `*const` in Rust but
1642         // mutable in C which causes a mismatch. Avoiding breakage by changing
1643         // this in wasi-libc and instead accepting that this is slightly
1644         // different.
1645         "select" => true,
1646 
1647         _ => false,
1648     });
1649 
1650     // d_name is declared as a flexible array in WASI libc, so it
1651     // doesn't support sizeof.
1652     cfg.skip_field(|s, field| s == "dirent" && field == "d_name");
1653 
1654     cfg.generate("../src/lib.rs", "main.rs");
1655 }
1656 
1657 fn test_android(target: &str) {
1658     assert!(target.contains("android"));
1659     let target_pointer_width = match target {
1660         t if t.contains("aarch64") || t.contains("x86_64") => 64,
1661         t if t.contains("i686") || t.contains("arm") => 32,
1662         t => panic!("unsupported target: {}", t),
1663     };
1664     let x86 = target.contains("i686") || target.contains("x86_64");
1665     let aarch64 = target.contains("aarch64");
1666 
1667     let mut cfg = ctest_cfg();
1668     cfg.define("_GNU_SOURCE", None);
1669 
1670     headers! { cfg:
1671                "arpa/inet.h",
1672                "ctype.h",
1673                "dirent.h",
1674                "dlfcn.h",
1675                "elf.h",
1676                "errno.h",
1677                "fcntl.h",
1678                "fnmatch.h",
1679                "getopt.h",
1680                "grp.h",
1681                "ifaddrs.h",
1682                "libgen.h",
1683                "limits.h",
1684                "link.h",
1685                "linux/sysctl.h",
1686                "locale.h",
1687                "malloc.h",
1688                "net/ethernet.h",
1689                "net/if.h",
1690                "net/if_arp.h",
1691                "net/route.h",
1692                "netdb.h",
1693                "netinet/in.h",
1694                "netinet/ip.h",
1695                "netinet/tcp.h",
1696                "netinet/udp.h",
1697                "netpacket/packet.h",
1698                "poll.h",
1699                "pthread.h",
1700                "pty.h",
1701                "pwd.h",
1702                "regex.h",
1703                "resolv.h",
1704                "sched.h",
1705                "semaphore.h",
1706                "signal.h",
1707                "spawn.h",
1708                "stddef.h",
1709                "stdint.h",
1710                "stdio.h",
1711                "stdlib.h",
1712                "string.h",
1713                "sys/auxv.h",
1714                "sys/epoll.h",
1715                "sys/eventfd.h",
1716                "sys/file.h",
1717                "sys/fsuid.h",
1718                "sys/inotify.h",
1719                "sys/ioctl.h",
1720                "sys/klog.h",
1721                "sys/mman.h",
1722                "sys/mount.h",
1723                "sys/personality.h",
1724                "sys/prctl.h",
1725                "sys/ptrace.h",
1726                "sys/random.h",
1727                "sys/reboot.h",
1728                "sys/resource.h",
1729                "sys/sendfile.h",
1730                "sys/signalfd.h",
1731                "sys/socket.h",
1732                "sys/stat.h",
1733                "sys/statvfs.h",
1734                "sys/swap.h",
1735                "sys/syscall.h",
1736                "sys/sysinfo.h",
1737                "sys/system_properties.h",
1738                "sys/time.h",
1739                "sys/timerfd.h",
1740                "sys/times.h",
1741                "sys/types.h",
1742                "sys/ucontext.h",
1743                "sys/uio.h",
1744                "sys/un.h",
1745                "sys/user.h",
1746                "sys/utsname.h",
1747                "sys/vfs.h",
1748                "sys/xattr.h",
1749                "sys/wait.h",
1750                "syslog.h",
1751                "termios.h",
1752                "time.h",
1753                "unistd.h",
1754                "utime.h",
1755                "utmp.h",
1756                "wchar.h",
1757                "xlocale.h",
1758                // time64_t is not defined for 64-bit targets If included it will
1759                // generate the error 'Your time_t is already 64-bit'
1760                [target_pointer_width == 32]: "time64.h",
1761                [x86]: "sys/reg.h",
1762     }
1763 
1764     // Include linux headers at the end:
1765     headers! { cfg:
1766                 "asm/mman.h",
1767                 "linux/auxvec.h",
1768                 "linux/dccp.h",
1769                 "linux/elf.h",
1770                 "linux/errqueue.h",
1771                 "linux/falloc.h",
1772                 "linux/filter.h",
1773                 "linux/futex.h",
1774                 "linux/fs.h",
1775                 "linux/genetlink.h",
1776                 "linux/if_alg.h",
1777                 "linux/if_addr.h",
1778                 "linux/if_ether.h",
1779                 "linux/if_link.h",
1780                 "linux/rtnetlink.h",
1781                 "linux/if_tun.h",
1782                 "linux/kexec.h",
1783                 "linux/magic.h",
1784                 "linux/membarrier.h",
1785                 "linux/memfd.h",
1786                 "linux/mempolicy.h",
1787                 "linux/module.h",
1788                 "linux/mount.h",
1789                 "linux/net_tstamp.h",
1790                 "linux/netfilter/nfnetlink.h",
1791                 "linux/netfilter/nfnetlink_log.h",
1792                 "linux/netfilter/nfnetlink_queue.h",
1793                 "linux/netfilter/nf_tables.h",
1794                 "linux/netfilter_ipv4.h",
1795                 "linux/netfilter_ipv6.h",
1796                 "linux/netfilter_ipv6/ip6_tables.h",
1797                 "linux/netlink.h",
1798                 "linux/quota.h",
1799                 "linux/reboot.h",
1800                 "linux/seccomp.h",
1801                 "linux/sched.h",
1802                 "linux/sockios.h",
1803                 "linux/uinput.h",
1804                 "linux/vm_sockets.h",
1805                 "linux/wait.h",
1806 
1807     }
1808 
1809     // Include Android-specific headers:
1810     headers! { cfg:
1811                 "android/set_abort_message.h"
1812     }
1813 
1814     cfg.type_name(move |ty, is_struct, is_union| {
1815         match ty {
1816             // Just pass all these through, no need for a "struct" prefix
1817             "FILE" | "fd_set" | "Dl_info" | "Elf32_Phdr" | "Elf64_Phdr" => ty.to_string(),
1818 
1819             t if is_union => format!("union {}", t),
1820 
1821             t if t.ends_with("_t") => t.to_string(),
1822 
1823             // sigval is a struct in Rust, but a union in C:
1824             "sigval" => format!("union sigval"),
1825 
1826             // put `struct` in front of all structs:.
1827             t if is_struct => format!("struct {}", t),
1828 
1829             t => t.to_string(),
1830         }
1831     });
1832 
1833     cfg.field_name(move |struct_, field| {
1834         match field {
1835             // Our stat *_nsec fields normally don't actually exist but are part
1836             // of a timeval struct
1837             s if s.ends_with("_nsec") && struct_.starts_with("stat") => s.to_string(),
1838             // FIXME: appears that `epoll_event.data` is an union
1839             "u64" if struct_ == "epoll_event" => "data.u64".to_string(),
1840             // The following structs have a field called `type` in C,
1841             // but `type` is a Rust keyword, so these fields are translated
1842             // to `type_` in Rust.
1843             "type_"
1844                 if struct_ == "input_event"
1845                     || struct_ == "input_mask"
1846                     || struct_ == "ff_effect" =>
1847             {
1848                 "type".to_string()
1849             }
1850 
1851             s => s.to_string(),
1852         }
1853     });
1854 
1855     cfg.skip_type(move |ty| {
1856         match ty {
1857             // FIXME: `sighandler_t` type is incorrect, see:
1858             // https://github.com/rust-lang/libc/issues/1359
1859             "sighandler_t" => true,
1860 
1861             // These are tested in the `linux_elf.rs` file.
1862             "Elf64_Phdr" | "Elf32_Phdr" => true,
1863             // These are intended to be opaque
1864             "posix_spawn_file_actions_t" => true,
1865             "posix_spawnattr_t" => true,
1866             _ => false,
1867         }
1868     });
1869 
1870     cfg.skip_struct(move |ty| {
1871         if ty.starts_with("__c_anonymous_") {
1872             return true;
1873         }
1874         match ty {
1875             // These are tested as part of the linux_fcntl tests since there are
1876             // header conflicts when including them with all the other structs.
1877             "termios2" => true,
1878             // uc_sigmask and uc_sigmask64 of ucontext_t are an anonymous union
1879             "ucontext_t" => true,
1880             // 'private' type
1881             "prop_info" => true,
1882 
1883             // These are tested in the `linux_elf.rs` file.
1884             "Elf64_Phdr" | "Elf32_Phdr" => true,
1885 
1886             // FIXME: The type of `iv` has been changed.
1887             "af_alg_iv" => true,
1888 
1889             // FIXME: The size of struct has been changed:
1890             "inotify_event" => true,
1891             // FIXME: The field has been changed:
1892             "sockaddr_vm" => true,
1893 
1894             _ => false,
1895         }
1896     });
1897 
1898     cfg.skip_const(move |name| {
1899         match name {
1900             // The IPV6 constants are tested in the `linux_ipv6.rs` tests:
1901             | "IPV6_FLOWINFO"
1902             | "IPV6_FLOWLABEL_MGR"
1903             | "IPV6_FLOWINFO_SEND"
1904             | "IPV6_FLOWINFO_FLOWLABEL"
1905             | "IPV6_FLOWINFO_PRIORITY"
1906             // The F_ fnctl constants are tested in the `linux_fnctl.rs` tests:
1907             | "F_CANCELLK"
1908             | "F_ADD_SEALS"
1909             | "F_GET_SEALS"
1910             | "F_SEAL_SEAL"
1911             | "F_SEAL_SHRINK"
1912             | "F_SEAL_GROW"
1913             | "F_SEAL_WRITE" => true,
1914 
1915             // The `ARPHRD_CAN` is tested in the `linux_if_arp.rs` tests:
1916             "ARPHRD_CAN" => true,
1917 
1918             // FIXME: deprecated: not available in any header
1919             // See: https://github.com/rust-lang/libc/issues/1356
1920             "ENOATTR" => true,
1921 
1922             // FIXME: still necessary?
1923             "SIG_DFL" | "SIG_ERR" | "SIG_IGN" => true, // sighandler_t weirdness
1924             // FIXME: deprecated - removed in glibc 2.26
1925             "SIGUNUSED" => true,
1926 
1927             // Needs a newer Android SDK for the definition
1928             "P_PIDFD" => true,
1929 
1930             // Requires Linux kernel 5.6
1931             "VMADDR_CID_LOCAL" => true,
1932 
1933             // FIXME: conflicts with standard C headers and is tested in
1934             // `linux_termios.rs` below:
1935             "BOTHER" => true,
1936             "IBSHIFT" => true,
1937             "TCGETS2" | "TCSETS2" | "TCSETSW2" | "TCSETSF2" => true,
1938 
1939             // is a private value for kernel usage normally
1940             "FUSE_SUPER_MAGIC" => true,
1941             // linux 5.12 min
1942             "MPOL_F_NUMA_BALANCING" => true,
1943 
1944             // GRND_INSECURE was added in platform-tools-30.0.0
1945             "GRND_INSECURE" => true,
1946 
1947             // kernel 5.10 minimum required
1948             "MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ" | "MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ" => true,
1949 
1950             // kernel 5.18 minimum
1951             | "MADV_COLD"
1952             | "MADV_DONTNEED_LOCKED"
1953             | "MADV_PAGEOUT"
1954             | "MADV_POPULATE_READ"
1955             | "MADV_POPULATE_WRITE" => true,
1956 
1957             // kernel 5.6 minimum required
1958             "IPPROTO_MPTCP" | "IPPROTO_ETHERNET" => true,
1959 
1960             // kernel 6.2 minimum
1961             "TUN_F_USO4" | "TUN_F_USO6" | "IFF_NO_CARRIER" => true,
1962 
1963             // FIXME: NDK r22 minimum required
1964             | "FDB_NOTIFY_BIT"
1965             | "FDB_NOTIFY_INACTIVE_BIT"
1966             | "IFLA_ALT_IFNAME"
1967             | "IFLA_PERM_ADDRESS"
1968             | "IFLA_PROP_LIST"
1969             | "IFLA_PROTO_DOWN_REASON"
1970             | "NDA_FDB_EXT_ATTRS"
1971             | "NDA_NH_ID"
1972             | "NFEA_ACTIVITY_NOTIFY"
1973             | "NFEA_DONT_REFRESH"
1974             | "NFEA_UNSPEC" => true,
1975 
1976             // FIXME: NDK r23 minimum required
1977             | "IFLA_PARENT_DEV_BUS_NAME"
1978             | "IFLA_PARENT_DEV_NAME" => true,
1979 
1980             // FIXME: NDK r25 minimum required
1981             | "IFLA_GRO_MAX_SIZE"
1982             | "NDA_FLAGS_EXT"
1983             | "NTF_EXT_MANAGED" => true,
1984 
1985             // FIXME: NDK above r25 required
1986             | "IFLA_ALLMULTI"
1987             | "IFLA_DEVLINK_PORT"
1988             | "IFLA_GRO_IPV4_MAX_SIZE"
1989             | "IFLA_GSO_IPV4_MAX_SIZE"
1990             | "IFLA_TSO_MAX_SEGS"
1991             | "IFLA_TSO_MAX_SIZE"
1992             | "NDA_NDM_STATE_MASK"
1993             | "NDA_NDM_FLAGS_MASK"
1994             | "NDTPA_INTERVAL_PROBE_TIME_MS"
1995             | "NFQA_UNSPEC"
1996             | "NTF_EXT_LOCKED"
1997             | "ALG_SET_DRBG_ENTROPY" => true,
1998 
1999             // FIXME: Something has been changed on r26b:
2000             | "IPPROTO_MAX"
2001             | "NFNL_SUBSYS_COUNT"
2002             | "NF_NETDEV_NUMHOOKS"
2003             | "NFT_MSG_MAX"
2004             | "SW_MAX"
2005             | "SW_CNT" => true,
2006 
2007             // FIXME: aarch64 env cannot find it:
2008             | "PTRACE_GETREGS"
2009             | "PTRACE_SETREGS" if aarch64 => true,
2010             // FIXME: The value has been changed on r26b:
2011             | "SYS_syscalls" if aarch64 => true,
2012 
2013             // From `<include/linux/sched.h>`.
2014             | "PF_VCPU"
2015             | "PF_IDLE"
2016             | "PF_EXITING"
2017             | "PF_POSTCOREDUMP"
2018             | "PF_IO_WORKER"
2019             | "PF_WQ_WORKER"
2020             | "PF_FORKNOEXEC"
2021             | "PF_SUPERPRIV"
2022             | "PF_DUMPCORE"
2023             | "PF_MCE_PROCESS"
2024             | "PF_SIGNALED"
2025             | "PF_MEMALLOC"
2026             | "PF_NPROC_EXCEEDED"
2027             | "PF_USED_MATH"
2028             | "PF_USER_WORKER"
2029             | "PF_NOFREEZE"
2030             | "PF_KSWAPD"
2031             | "PF_MEMALLOC_NOFS"
2032             | "PF_MEMALLOC_NOIO"
2033             | "PF_LOCAL_THROTTLE"
2034             | "PF_KTHREAD"
2035             | "PF_RANDOMIZE"
2036             | "PF_NO_SETAFFINITY"
2037             | "PF_MCE_EARLY"
2038             | "PF_MEMALLOC_PIN"
2039             | "PF_SUSPEND_TASK" => true,
2040 
2041             _ => false,
2042         }
2043     });
2044 
2045     cfg.skip_fn(move |name| {
2046         // skip those that are manually verified
2047         match name {
2048             // FIXME: https://github.com/rust-lang/libc/issues/1272
2049             "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true,
2050 
2051             // There are two versions of the sterror_r function, see
2052             //
2053             // https://linux.die.net/man/3/strerror_r
2054             //
2055             // An XSI-compliant version provided if:
2056             //
2057             // (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE
2058             //
2059             // and a GNU specific version provided if _GNU_SOURCE is defined.
2060             //
2061             // libc provides bindings for the XSI-compliant version, which is
2062             // preferred for portable applications.
2063             //
2064             // We skip the test here since here _GNU_SOURCE is defined, and
2065             // test the XSI version below.
2066             "strerror_r" => true,
2067             "reallocarray" => true,
2068             "__system_property_wait" => true,
2069 
2070             // Added in API level 30, but tests use level 28.
2071             "mlock2" => true,
2072 
2073             // Added in glibc 2.25.
2074             "getentropy" => true,
2075 
2076             // Added in API level 28, but some tests use level 24.
2077             "getrandom" => true,
2078 
2079             // Added in API level 28, but some tests use level 24.
2080             "syncfs" => true,
2081 
2082             // Added in API level 28, but some tests use level 24.
2083             "pthread_attr_getinheritsched" | "pthread_attr_setinheritsched" => true,
2084             // Added in API level 28, but some tests use level 24.
2085             "fread_unlocked" | "fwrite_unlocked" | "fgets_unlocked" | "fflush_unlocked" => true,
2086 
2087             // Added in API level 28, but some tests use level 24.
2088             "aligned_alloc" => true,
2089 
2090             // Added in API level 26, but some tests use level 24.
2091             "getgrent" => true,
2092 
2093             // Added in API level 26, but some tests use level 24.
2094             "setgrent" => true,
2095 
2096             // Added in API level 26, but some tests use level 24.
2097             "endgrent" => true,
2098 
2099             // FIXME: bad function pointers:
2100             "isalnum" | "isalpha" | "iscntrl" | "isdigit" | "isgraph" | "islower" | "isprint"
2101             | "ispunct" | "isspace" | "isupper" | "isxdigit" | "isblank" | "tolower"
2102             | "toupper" => true,
2103 
2104             _ => false,
2105         }
2106     });
2107 
2108     cfg.skip_field_type(move |struct_, field| {
2109         // This is a weird union, don't check the type.
2110         (struct_ == "ifaddrs" && field == "ifa_ifu") ||
2111         // sigval is actually a union, but we pretend it's a struct
2112         (struct_ == "sigevent" && field == "sigev_value") ||
2113         // this one is an anonymous union
2114         (struct_ == "ff_effect" && field == "u") ||
2115         // FIXME: `sa_sigaction` has type `sighandler_t` but that type is
2116         // incorrect, see: https://github.com/rust-lang/libc/issues/1359
2117         (struct_ == "sigaction" && field == "sa_sigaction") ||
2118         // signalfd had SIGSYS fields added in Android 4.19, but CI does not have that version yet.
2119         (struct_ == "signalfd_siginfo" && field == "ssi_call_addr") ||
2120         // FIXME: Seems the type has been changed on NDK r26b
2121         (struct_ == "flock64" && (field == "l_start" || field == "l_len"))
2122     });
2123 
2124     cfg.skip_field(|struct_, field| {
2125         match (struct_, field) {
2126             // conflicting with `p_type` macro from <resolve.h>.
2127             ("Elf32_Phdr", "p_type") => true,
2128             ("Elf64_Phdr", "p_type") => true,
2129 
2130             // this is actually a union on linux, so we can't represent it well and
2131             // just insert some padding.
2132             ("siginfo_t", "_pad") => true,
2133             ("ifreq", "ifr_ifru") => true,
2134             ("ifconf", "ifc_ifcu") => true,
2135 
2136             _ => false,
2137         }
2138     });
2139 
2140     cfg.generate("../src/lib.rs", "main.rs");
2141 
2142     test_linux_like_apis(target);
2143 }
2144 
2145 fn test_freebsd(target: &str) {
2146     assert!(target.contains("freebsd"));
2147     let mut cfg = ctest_cfg();
2148 
2149     let freebsd_ver = which_freebsd();
2150 
2151     match freebsd_ver {
2152         Some(12) => cfg.cfg("freebsd12", None),
2153         Some(13) => cfg.cfg("freebsd13", None),
2154         Some(14) => cfg.cfg("freebsd14", None),
2155         Some(15) => cfg.cfg("freebsd15", None),
2156         _ => &mut cfg,
2157     };
2158 
2159     // For sched linux compat fn
2160     cfg.define("_WITH_CPU_SET_T", None);
2161     // Required for `getline`:
2162     cfg.define("_WITH_GETLINE", None);
2163     // Required for making freebsd11_stat available in the headers
2164     cfg.define("_WANT_FREEBSD11_STAT", None);
2165 
2166     let freebsd13 = match freebsd_ver {
2167         Some(n) if n >= 13 => true,
2168         _ => false,
2169     };
2170     let freebsd14 = match freebsd_ver {
2171         Some(n) if n >= 14 => true,
2172         _ => false,
2173     };
2174     let freebsd15 = match freebsd_ver {
2175         Some(n) if n >= 15 => true,
2176         _ => false,
2177     };
2178 
2179     headers! { cfg:
2180                 "aio.h",
2181                 "arpa/inet.h",
2182                 "bsm/audit.h",
2183                 "ctype.h",
2184                 "dirent.h",
2185                 "dlfcn.h",
2186                 "elf.h",
2187                 "errno.h",
2188                 "execinfo.h",
2189                 "fcntl.h",
2190                 "fnmatch.h",
2191                 "getopt.h",
2192                 "glob.h",
2193                 "grp.h",
2194                 "iconv.h",
2195                 "ifaddrs.h",
2196                 "kenv.h",
2197                 "langinfo.h",
2198                 "libgen.h",
2199                 "libutil.h",
2200                 "limits.h",
2201                 "link.h",
2202                 "locale.h",
2203                 "machine/elf.h",
2204                 "machine/reg.h",
2205                 "malloc_np.h",
2206                 "memstat.h",
2207                 "mqueue.h",
2208                 "net/bpf.h",
2209                 "net/if.h",
2210                 "net/if_arp.h",
2211                 "net/if_dl.h",
2212                 "net/if_mib.h",
2213                 "net/route.h",
2214                 "netdb.h",
2215                 "netinet/ip.h",
2216                 "netinet/in.h",
2217                 "netinet/sctp.h",
2218                 "netinet/tcp.h",
2219                 "netinet/udp.h",
2220                 "poll.h",
2221                 "pthread.h",
2222                 "pthread_np.h",
2223                 "pwd.h",
2224                 "regex.h",
2225                 "resolv.h",
2226                 "sched.h",
2227                 "semaphore.h",
2228                 "signal.h",
2229                 "spawn.h",
2230                 "stddef.h",
2231                 "stdint.h",
2232                 "stdio.h",
2233                 "stdlib.h",
2234                 "string.h",
2235                 "sys/capsicum.h",
2236                 "sys/auxv.h",
2237                 "sys/cpuset.h",
2238                 "sys/domainset.h",
2239                 "sys/eui64.h",
2240                 "sys/event.h",
2241                 [freebsd13]:"sys/eventfd.h",
2242                 "sys/extattr.h",
2243                 "sys/file.h",
2244                 "sys/ioctl.h",
2245                 "sys/ipc.h",
2246                 "sys/jail.h",
2247                 "sys/mman.h",
2248                 "sys/mount.h",
2249                 "sys/msg.h",
2250                 "sys/procctl.h",
2251                 "sys/procdesc.h",
2252                 "sys/ptrace.h",
2253                 "sys/queue.h",
2254                 "sys/random.h",
2255                 "sys/reboot.h",
2256                 "sys/resource.h",
2257                 "sys/rtprio.h",
2258                 "sys/sem.h",
2259                 "sys/shm.h",
2260                 "sys/socket.h",
2261                 "sys/stat.h",
2262                 "sys/statvfs.h",
2263                 "sys/sysctl.h",
2264                 "sys/thr.h",
2265                 "sys/time.h",
2266                 [freebsd14 || freebsd15]:"sys/timerfd.h",
2267                 "sys/times.h",
2268                 "sys/timex.h",
2269                 "sys/types.h",
2270                 "sys/proc.h",
2271                 "kvm.h", // must be after "sys/types.h"
2272                 "sys/ucontext.h",
2273                 "sys/uio.h",
2274                 "sys/ktrace.h",
2275                 "sys/umtx.h",
2276                 "sys/un.h",
2277                 "sys/user.h",
2278                 "sys/utsname.h",
2279                 "sys/uuid.h",
2280                 "sys/vmmeter.h",
2281                 "sys/wait.h",
2282                 "libprocstat.h",
2283                 "devstat.h",
2284                 "syslog.h",
2285                 "termios.h",
2286                 "time.h",
2287                 "ufs/ufs/quota.h",
2288                 "unistd.h",
2289                 "utime.h",
2290                 "utmpx.h",
2291                 "wchar.h",
2292     }
2293 
2294     cfg.type_name(move |ty, is_struct, is_union| {
2295         match ty {
2296             // Just pass all these through, no need for a "struct" prefix
2297             "FILE"
2298             | "fd_set"
2299             | "Dl_info"
2300             | "DIR"
2301             | "Elf32_Phdr"
2302             | "Elf64_Phdr"
2303             | "Elf32_Auxinfo"
2304             | "Elf64_Auxinfo"
2305             | "devstat_select_mode"
2306             | "devstat_support_flags"
2307             | "devstat_type_flags"
2308             | "devstat_match_flags"
2309             | "devstat_priority" => ty.to_string(),
2310 
2311             // FIXME: https://github.com/rust-lang/libc/issues/1273
2312             "sighandler_t" => "sig_t".to_string(),
2313 
2314             t if is_union => format!("union {}", t),
2315 
2316             t if t.ends_with("_t") => t.to_string(),
2317 
2318             // sigval is a struct in Rust, but a union in C:
2319             "sigval" => format!("union sigval"),
2320 
2321             // put `struct` in front of all structs:.
2322             t if is_struct => format!("struct {}", t),
2323 
2324             t => t.to_string(),
2325         }
2326     });
2327 
2328     cfg.field_name(move |struct_, field| {
2329         match field {
2330             // Our stat *_nsec fields normally don't actually exist but are part
2331             // of a timeval struct
2332             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
2333                 s.replace("e_nsec", ".tv_nsec")
2334             }
2335             // Field is named `type` in C but that is a Rust keyword,
2336             // so these fields are translated to `type_` in the bindings.
2337             "type_" if struct_ == "rtprio" => "type".to_string(),
2338             "type_" if struct_ == "sockstat" => "type".to_string(),
2339             "type_" if struct_ == "devstat_match_table" => "type".to_string(),
2340             s => s.to_string(),
2341         }
2342     });
2343 
2344     cfg.skip_const(move |name| {
2345         match name {
2346             // These constants were introduced in FreeBSD 13:
2347             "F_ADD_SEALS" | "F_GET_SEALS" | "F_SEAL_SEAL" | "F_SEAL_SHRINK" | "F_SEAL_GROW"
2348             | "F_SEAL_WRITE"
2349                 if Some(13) > freebsd_ver =>
2350             {
2351                 true
2352             }
2353 
2354             // These constants were introduced in FreeBSD 13:
2355             "EFD_CLOEXEC" | "EFD_NONBLOCK" | "EFD_SEMAPHORE" if Some(13) > freebsd_ver => true,
2356 
2357             // These constants were introduced in FreeBSD 12:
2358             "AT_RESOLVE_BENEATH" | "O_RESOLVE_BENEATH" if Some(12) > freebsd_ver => true,
2359 
2360             // These constants were introduced in FreeBSD 13:
2361             "O_DSYNC" | "O_PATH" | "O_EMPTY_PATH" | "AT_EMPTY_PATH" if Some(13) > freebsd_ver => {
2362                 true
2363             }
2364 
2365             // These aliases were introduced in FreeBSD 13:
2366             // (note however that the constants themselves work on any version)
2367             "CLOCK_BOOTTIME" | "CLOCK_REALTIME_COARSE" | "CLOCK_MONOTONIC_COARSE"
2368                 if Some(13) > freebsd_ver =>
2369             {
2370                 true
2371             }
2372 
2373             // FIXME: These are deprecated - remove in a couple of releases.
2374             // These constants were removed in FreeBSD 11 (svn r273250) but will
2375             // still be accepted and ignored at runtime.
2376             "MAP_RENAME" | "MAP_NORESERVE" => true,
2377 
2378             // FIXME: These are deprecated - remove in a couple of releases.
2379             // These constants were removed in FreeBSD 11 (svn r262489),
2380             // and they've never had any legitimate use outside of the
2381             // base system anyway.
2382             "CTL_MAXID" | "KERN_MAXID" | "HW_MAXID" | "USER_MAXID" => true,
2383 
2384             // Deprecated and removed in FreeBSD 15.  It was never actually implemented.
2385             "TCP_MAXPEAKRATE" => true,
2386 
2387             // FIXME: This is deprecated - remove in a couple of releases.
2388             // This was removed in FreeBSD 14 (git 1b4701fe1e8) and never
2389             // should've been used anywhere anyway.
2390             "TDF_UNUSED23" => true,
2391 
2392             // Removed in FreeBSD 15
2393             "TDF_CANSWAP" | "TDF_SWAPINREQ" => true,
2394 
2395             // Unaccessible in FreeBSD 15
2396             "TDI_SWAPPED" | "P_SWAPPINGOUT" | "P_SWAPPINGIN" => true,
2397 
2398             // Removed in FreeBSD 14 (git a6b55ee6be1)
2399             "IFF_KNOWSEPOCH" => true,
2400 
2401             // Removed in FreeBSD 14 (git 7ff9ae90f0b)
2402             "IFF_NOGROUP" => true,
2403 
2404             // FIXME: These are deprecated - remove in a couple of releases.
2405             // These symbols are not stable across OS-versions.  They were
2406             // changed for FreeBSD 14 in git revisions b62848b0c3f and
2407             // 2cf7870864e.
2408             "PRI_MAX_ITHD" | "PRI_MIN_REALTIME" | "PRI_MAX_REALTIME" | "PRI_MIN_KERN"
2409             | "PRI_MAX_KERN" | "PSWP" | "PVM" | "PINOD" | "PRIBIO" | "PVFS" | "PZERO" | "PSOCK"
2410             | "PWAIT" | "PLOCK" | "PPAUSE" | "PRI_MIN_TIMESHARE" | "PUSER" | "PI_AV" | "PI_NET"
2411             | "PI_DISK" | "PI_TTY" | "PI_DULL" | "PI_SOFT" => true,
2412 
2413             // This constant changed in FreeBSD 15 (git 3458bbd397783).  It was never intended to
2414             // be stable, and probably shouldn't be bound by libc at all.
2415             "RLIM_NLIMITS" => true,
2416 
2417             // This symbol changed in FreeBSD 14 (git 051e7d78b03), but the new
2418             // version should be safe to use on older releases.
2419             "IFCAP_CANTCHANGE" => true,
2420 
2421             // These were removed in FreeBSD 14 (git c6d31b8306e)
2422             "TDF_ASTPENDING" | "TDF_NEEDSUSPCHK" | "TDF_NEEDRESCHED" | "TDF_NEEDSIGCHK"
2423             | "TDF_ALRMPEND" | "TDF_PROFPEND" | "TDF_MACPEND" => true,
2424 
2425             // This constant was removed in FreeBSD 13 (svn r363622), and never
2426             // had any legitimate use outside of the base system anyway.
2427             "CTL_P1003_1B_MAXID" => true,
2428 
2429             // This was renamed in FreeBSD 12.2 and 13 (r352486).
2430             "CTL_UNSPEC" | "CTL_SYSCTL" => true,
2431 
2432             // This was renamed in FreeBSD 12.2 and 13 (r350749).
2433             "IPPROTO_SEP" | "IPPROTO_DCCP" => true,
2434 
2435             // This was changed to 96(0x60) in FreeBSD 13:
2436             // https://github.com/freebsd/freebsd/
2437             // commit/06b00ceaa914a3907e4e27bad924f44612bae1d7
2438             "MINCORE_SUPER" if Some(13) <= freebsd_ver => true,
2439 
2440             // Added in FreeBSD 13.0 (r356667)
2441             "GRND_INSECURE" if Some(13) > freebsd_ver => true,
2442 
2443             // Added in FreeBSD 13.0 (r349609)
2444             "PROC_PROTMAX_CTL"
2445             | "PROC_PROTMAX_STATUS"
2446             | "PROC_PROTMAX_FORCE_ENABLE"
2447             | "PROC_PROTMAX_FORCE_DISABLE"
2448             | "PROC_PROTMAX_NOFORCE"
2449             | "PROC_PROTMAX_ACTIVE"
2450             | "PROC_NO_NEW_PRIVS_CTL"
2451             | "PROC_NO_NEW_PRIVS_STATUS"
2452             | "PROC_NO_NEW_PRIVS_ENABLE"
2453             | "PROC_NO_NEW_PRIVS_DISABLE"
2454             | "PROC_WXMAP_CTL"
2455             | "PROC_WXMAP_STATUS"
2456             | "PROC_WX_MAPPINGS_PERMIT"
2457             | "PROC_WX_MAPPINGS_DISALLOW_EXEC"
2458             | "PROC_WXORX_ENFORCE"
2459                 if Some(13) > freebsd_ver =>
2460             {
2461                 true
2462             }
2463 
2464             // Added in in FreeBSD 13.0 (r367776 and r367287)
2465             "SCM_CREDS2" | "LOCAL_CREDS_PERSISTENT" if Some(13) > freebsd_ver => true,
2466 
2467             // Added in FreeBSD 14
2468             "SPACECTL_DEALLOC" if Some(14) > freebsd_ver => true,
2469 
2470             // Added in FreeBSD 13.
2471             "KERN_PROC_SIGFASTBLK"
2472             | "USER_LOCALBASE"
2473             | "TDP_SIGFASTBLOCK"
2474             | "TDP_UIOHELD"
2475             | "TDP_SIGFASTPENDING"
2476             | "TDP2_COMPAT32RB"
2477             | "P2_PROTMAX_ENABLE"
2478             | "P2_PROTMAX_DISABLE"
2479             | "CTLFLAG_NEEDGIANT"
2480             | "CTL_SYSCTL_NEXTNOSKIP"
2481                 if Some(13) > freebsd_ver =>
2482             {
2483                 true
2484             }
2485 
2486             // Added in freebsd 14.
2487             "IFCAP_MEXTPG" if Some(14) > freebsd_ver => true,
2488             // Added in freebsd 13.
2489             "IFCAP_TXTLS4" | "IFCAP_TXTLS6" | "IFCAP_VXLAN_HWCSUM" | "IFCAP_VXLAN_HWTSO"
2490             | "IFCAP_TXTLS_RTLMT" | "IFCAP_TXTLS"
2491                 if Some(13) > freebsd_ver =>
2492             {
2493                 true
2494             }
2495             // Added in FreeBSD 13.
2496             "PS_FST_TYPE_EVENTFD" if Some(13) > freebsd_ver => true,
2497 
2498             // Added in FreeBSD 14.
2499             "MNT_RECURSE" | "MNT_DEFERRED" if Some(14) > freebsd_ver => true,
2500 
2501             // Added in FreeBSD 13.
2502             "MNT_EXTLS" | "MNT_EXTLSCERT" | "MNT_EXTLSCERTUSER" | "MNT_NOCOVER"
2503             | "MNT_EMPTYDIR"
2504                 if Some(13) > freebsd_ver =>
2505             {
2506                 true
2507             }
2508 
2509             // Added in FreeBSD 14.
2510             "PT_COREDUMP" | "PC_ALL" | "PC_COMPRESS" | "PT_GETREGSET" | "PT_SETREGSET"
2511             | "PT_SC_REMOTE"
2512                 if Some(14) > freebsd_ver =>
2513             {
2514                 true
2515             }
2516 
2517             // Added in FreeBSD 14.
2518             "F_KINFO" => true, // FIXME: depends how frequent freebsd 14 is updated on CI, this addition went this week only.
2519             "SHM_RENAME_NOREPLACE"
2520             | "SHM_RENAME_EXCHANGE"
2521             | "SHM_LARGEPAGE_ALLOC_DEFAULT"
2522             | "SHM_LARGEPAGE_ALLOC_NOWAIT"
2523             | "SHM_LARGEPAGE_ALLOC_HARD"
2524             | "MFD_CLOEXEC"
2525             | "MFD_ALLOW_SEALING"
2526             | "MFD_HUGETLB"
2527             | "MFD_HUGE_MASK"
2528             | "MFD_HUGE_64KB"
2529             | "MFD_HUGE_512KB"
2530             | "MFD_HUGE_1MB"
2531             | "MFD_HUGE_2MB"
2532             | "MFD_HUGE_8MB"
2533             | "MFD_HUGE_16MB"
2534             | "MFD_HUGE_32MB"
2535             | "MFD_HUGE_256MB"
2536             | "MFD_HUGE_512MB"
2537             | "MFD_HUGE_1GB"
2538             | "MFD_HUGE_2GB"
2539             | "MFD_HUGE_16GB"
2540                 if Some(13) > freebsd_ver =>
2541             {
2542                 true
2543             }
2544 
2545             // Flags introduced in FreeBSD 14.
2546             "TCP_MAXUNACKTIME"
2547             | "TCP_IDLE_REDUCE"
2548             | "TCP_REMOTE_UDP_ENCAPS_PORT"
2549             | "TCP_DELACK"
2550             | "TCP_FIN_IS_RST"
2551             | "TCP_LOG_LIMIT"
2552             | "TCP_SHARED_CWND_ALLOWED"
2553             | "TCP_PROC_ACCOUNTING"
2554             | "TCP_USE_CMP_ACKS"
2555             | "TCP_PERF_INFO"
2556             | "TCP_LRD"
2557                 if Some(14) > freebsd_ver =>
2558             {
2559                 true
2560             }
2561 
2562             // Introduced in FreeBSD 14 then removed ?
2563             "TCP_LRD" if freebsd_ver >= Some(15) => true,
2564 
2565             // Added in FreeBSD 14
2566             "LIO_READV" | "LIO_WRITEV" | "LIO_VECTORED" if Some(14) > freebsd_ver => true,
2567 
2568             // Added in FreeBSD 13
2569             "FIOSSHMLPGCNF" if Some(13) > freebsd_ver => true,
2570 
2571             // Added in FreeBSD 14
2572             "IFCAP_NV" if Some(14) > freebsd_ver => true,
2573 
2574             // FIXME: Removed in https://reviews.freebsd.org/D38574 and https://reviews.freebsd.org/D38822
2575             // We maybe should deprecate them once a stable release ships them.
2576             "IP_BINDMULTI" | "IP_RSS_LISTEN_BUCKET" => true,
2577 
2578             // FIXME: Removed in https://reviews.freebsd.org/D39127.
2579             "KERN_VNODE" => true,
2580 
2581             // Added in FreeBSD 14
2582             "EV_KEEPUDATA" if Some(14) > freebsd_ver => true,
2583 
2584             // Added in FreeBSD 13.2
2585             "AT_USRSTACKBASE" | "AT_USRSTACKLIM" if Some(13) > freebsd_ver => true,
2586 
2587             // Added in FreeBSD 14
2588             "TFD_CLOEXEC" | "TFD_NONBLOCK" | "TFD_TIMER_ABSTIME" | "TFD_TIMER_CANCEL_ON_SET"
2589                 if Some(14) > freebsd_ver =>
2590             {
2591                 true
2592             }
2593 
2594             // FIXME: Removed in FreeBSD 15:
2595             "LOCAL_CONNWAIT" if freebsd_ver >= Some(15) => true,
2596 
2597             // FIXME: The values has been changed in FreeBSD 15:
2598             "CLOCK_BOOTTIME" if Some(15) <= freebsd_ver => true,
2599 
2600             // Added in FreeBSD 14.0
2601             "TCP_FUNCTION_ALIAS" if Some(14) > freebsd_ver => true,
2602 
2603             _ => false,
2604         }
2605     });
2606 
2607     cfg.skip_type(move |ty| {
2608         match ty {
2609             // the struct "__kvm" is quite tricky to bind so since we only use a pointer to it
2610             // for now, it doesn't matter too much...
2611             "kvm_t" => true,
2612             // `eventfd(2)` and things come with it are added in FreeBSD 13
2613             "eventfd_t" if Some(13) > freebsd_ver => true,
2614 
2615             _ => false,
2616         }
2617     });
2618 
2619     cfg.skip_struct(move |ty| {
2620         if ty.starts_with("__c_anonymous_") {
2621             return true;
2622         }
2623         match ty {
2624             // `procstat` is a private struct
2625             "procstat" => true,
2626 
2627             // `spacectl_range` was introduced in FreeBSD 14
2628             "spacectl_range" if Some(14) > freebsd_ver => true,
2629 
2630             // `ptrace_coredump` introduced in FreeBSD 14.
2631             "ptrace_coredump" if Some(14) > freebsd_ver => true,
2632             // `ptrace_sc_remote` introduced in FreeBSD 14.
2633             "ptrace_sc_remote" if Some(14) > freebsd_ver => true,
2634 
2635             // `sockcred2` is not available in FreeBSD 12.
2636             "sockcred2" if Some(13) > freebsd_ver => true,
2637             // `shm_largepage_conf` was introduced in FreeBSD 13.
2638             "shm_largepage_conf" if Some(13) > freebsd_ver => true,
2639 
2640             // Those are private types
2641             "memory_type" => true,
2642             "memory_type_list" => true,
2643             "pidfh" => true,
2644             "sctp_gen_error_cause"
2645             | "sctp_error_missing_param"
2646             | "sctp_remote_error"
2647             | "sctp_assoc_change"
2648             | "sctp_send_failed_event"
2649             | "sctp_stream_reset_event" => true,
2650 
2651             // FIXME: Changed in FreeBSD 15
2652             "tcp_info" | "sockstat" if Some(15) >= freebsd_ver => true,
2653 
2654             _ => false,
2655         }
2656     });
2657 
2658     cfg.skip_fn(move |name| {
2659         // skip those that are manually verified
2660         match name {
2661             // FIXME: https://github.com/rust-lang/libc/issues/1272
2662             // Also, `execvpe` is introduced in FreeBSD 14.1
2663             "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true,
2664 
2665             // The `uname` function in the `utsname.h` FreeBSD header is a C
2666             // inline function (has no symbol) that calls the `__xuname` symbol.
2667             // Therefore the function pointer comparison does not make sense for it.
2668             "uname" => true,
2669 
2670             // FIXME: Our API is unsound. The Rust API allows aliasing
2671             // pointers, but the C API requires pointers not to alias.
2672             // We should probably be at least using `&`/`&mut` here, see:
2673             // https://github.com/gnzlbg/ctest/issues/68
2674             "lio_listio" => true,
2675 
2676             // Those are introduced in FreeBSD 12.
2677             "clock_nanosleep" | "getrandom" | "elf_aux_info" | "setproctitle_fast"
2678             | "timingsafe_bcmp" | "timingsafe_memcmp"
2679                 if Some(12) > freebsd_ver =>
2680             {
2681                 true
2682             }
2683 
2684             // Those are introduced in FreeBSD 13.
2685             "memfd_create"
2686             | "shm_create_largepage"
2687             | "shm_rename"
2688             | "getentropy"
2689             | "eventfd"
2690             | "SOCKCRED2SIZE"
2691             | "getlocalbase"
2692             | "aio_readv"
2693             | "aio_writev"
2694             | "copy_file_range"
2695             | "eventfd_read"
2696             | "eventfd_write"
2697                 if Some(13) > freebsd_ver =>
2698             {
2699                 true
2700             }
2701 
2702             // Those are introduced in FreeBSD 14.
2703             "sched_getaffinity" | "sched_setaffinity" | "sched_getcpu" | "fspacectl"
2704                 if Some(14) > freebsd_ver =>
2705             {
2706                 true
2707             }
2708 
2709             // Those are introduced in FreeBSD 14.
2710             "timerfd_create" | "timerfd_gettime" | "timerfd_settime" if Some(14) > freebsd_ver => {
2711                 true
2712             }
2713 
2714             _ => false,
2715         }
2716     });
2717 
2718     cfg.volatile_item(|i| {
2719         use ctest::VolatileItemKind::*;
2720         match i {
2721             // aio_buf is a volatile void** but since we cannot express that in
2722             // Rust types, we have to explicitly tell the checker about it here:
2723             StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => true,
2724             _ => false,
2725         }
2726     });
2727 
2728     cfg.skip_field(move |struct_, field| {
2729         match (struct_, field) {
2730             // FIXME: `sa_sigaction` has type `sighandler_t` but that type is
2731             // incorrect, see: https://github.com/rust-lang/libc/issues/1359
2732             ("sigaction", "sa_sigaction") => true,
2733 
2734             // conflicting with `p_type` macro from <resolve.h>.
2735             ("Elf32_Phdr", "p_type") => true,
2736             ("Elf64_Phdr", "p_type") => true,
2737 
2738             // not available until FreeBSD 12, and is an anonymous union there.
2739             ("xucred", "cr_pid__c_anonymous_union") => true,
2740 
2741             // m_owner field is a volatile __lwpid_t
2742             ("umutex", "m_owner") => true,
2743             // c_has_waiters field is a volatile int32_t
2744             ("ucond", "c_has_waiters") => true,
2745             // is PATH_MAX long but tests can't accept multi array as equivalent.
2746             ("kinfo_vmentry", "kve_path") => true,
2747 
2748             // a_un field is a union
2749             ("Elf32_Auxinfo", "a_un") => true,
2750             ("Elf64_Auxinfo", "a_un") => true,
2751 
2752             // union fields
2753             ("if_data", "__ifi_epoch") => true,
2754             ("if_data", "__ifi_lastchange") => true,
2755             ("ifreq", "ifr_ifru") => true,
2756             ("ifconf", "ifc_ifcu") => true,
2757 
2758             // anonymous struct
2759             ("devstat", "dev_links") => true,
2760 
2761             // FIXME: structs too complicated to bind for now...
2762             ("kinfo_proc", "ki_paddr") => true,
2763             ("kinfo_proc", "ki_addr") => true,
2764             ("kinfo_proc", "ki_tracep") => true,
2765             ("kinfo_proc", "ki_textvp") => true,
2766             ("kinfo_proc", "ki_fd") => true,
2767             ("kinfo_proc", "ki_vmspace") => true,
2768             ("kinfo_proc", "ki_pcb") => true,
2769             ("kinfo_proc", "ki_tdaddr") => true,
2770             ("kinfo_proc", "ki_pd") => true,
2771 
2772             // Anonymous type.
2773             ("filestat", "next") => true,
2774 
2775             // We ignore this field because we needed to use a hack in order to make rust 1.19
2776             // happy...
2777             ("kinfo_proc", "ki_sparestrings") => true,
2778 
2779             // `__sem_base` is a private struct field
2780             ("semid_ds", "__sem_base") => true,
2781 
2782             // `snap_time` is a `long double`, but it's a nightmare to bind correctly in rust
2783             // for the moment, so it's a best effort thing...
2784             ("statinfo", "snap_time") => true,
2785             ("sctp_sndrcvinfo", "__reserve_pad") => true,
2786             ("sctp_extrcvinfo", "__reserve_pad") => true,
2787             // `tcp_snd_wscale` and `tcp_rcv_wscale` are bitfields
2788             ("tcp_info", "tcp_snd_wscale") => true,
2789             ("tcp_info", "tcp_rcv_wscale") => true,
2790 
2791             _ => false,
2792         }
2793     });
2794     if target.contains("arm") {
2795         cfg.skip_roundtrip(move |s| match s {
2796             // Can't return an array from a C function.
2797             "__gregset_t" => true,
2798             _ => false,
2799         });
2800     }
2801 
2802     cfg.generate("../src/lib.rs", "main.rs");
2803 }
2804 
2805 fn test_emscripten(target: &str) {
2806     assert!(target.contains("emscripten"));
2807 
2808     let mut cfg = ctest_cfg();
2809     cfg.define("_GNU_SOURCE", None); // FIXME: ??
2810 
2811     headers! { cfg:
2812                "ctype.h",
2813                "dirent.h",
2814                "dlfcn.h",
2815                "errno.h",
2816                "fcntl.h",
2817                "fnmatch.h",
2818                "glob.h",
2819                "grp.h",
2820                "ifaddrs.h",
2821                "langinfo.h",
2822                "limits.h",
2823                "locale.h",
2824                "malloc.h",
2825                "mntent.h",
2826                "mqueue.h",
2827                "net/ethernet.h",
2828                "net/if.h",
2829                "net/if_arp.h",
2830                "net/route.h",
2831                "netdb.h",
2832                "netinet/in.h",
2833                "netinet/ip.h",
2834                "netinet/tcp.h",
2835                "netinet/udp.h",
2836                "netpacket/packet.h",
2837                "poll.h",
2838                "pthread.h",
2839                "pty.h",
2840                "pwd.h",
2841                "resolv.h",
2842                "sched.h",
2843                "sched.h",
2844                "semaphore.h",
2845                "shadow.h",
2846                "signal.h",
2847                "stddef.h",
2848                "stdint.h",
2849                "stdio.h",
2850                "stdlib.h",
2851                "string.h",
2852                "sys/file.h",
2853                "sys/ioctl.h",
2854                "sys/ipc.h",
2855                "sys/mman.h",
2856                "sys/mount.h",
2857                "sys/msg.h",
2858                "sys/resource.h",
2859                "sys/sem.h",
2860                "sys/shm.h",
2861                "sys/socket.h",
2862                "sys/stat.h",
2863                "sys/statvfs.h",
2864                "sys/syscall.h",
2865                "sys/sysinfo.h",
2866                "sys/time.h",
2867                "sys/times.h",
2868                "sys/types.h",
2869                "sys/uio.h",
2870                "sys/un.h",
2871                "sys/user.h",
2872                "sys/utsname.h",
2873                "sys/vfs.h",
2874                "sys/wait.h",
2875                "sys/xattr.h",
2876                "syslog.h",
2877                "termios.h",
2878                "time.h",
2879                "ucontext.h",
2880                "unistd.h",
2881                "utime.h",
2882                "utmp.h",
2883                "utmpx.h",
2884                "wchar.h",
2885     }
2886 
2887     cfg.type_name(move |ty, is_struct, is_union| {
2888         match ty {
2889             // Just pass all these through, no need for a "struct" prefix
2890             "FILE" | "fd_set" | "Dl_info" | "DIR" => ty.to_string(),
2891 
2892             // LFS64 types have been removed in Emscripten 3.1.44
2893             // https://github.com/emscripten-core/emscripten/pull/19812
2894             "off64_t" => "off_t".to_string(),
2895 
2896             // typedefs don't need any keywords
2897             t if t.ends_with("_t") => t.to_string(),
2898 
2899             // put `struct` in front of all structs:.
2900             t if is_struct => format!("struct {}", t),
2901 
2902             // put `union` in front of all unions:
2903             t if is_union => format!("union {}", t),
2904 
2905             t => t.to_string(),
2906         }
2907     });
2908 
2909     cfg.field_name(move |struct_, field| {
2910         match field {
2911             // Our stat *_nsec fields normally don't actually exist but are part
2912             // of a timeval struct
2913             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
2914                 s.replace("e_nsec", ".tv_nsec")
2915             }
2916             // Rust struct uses raw u64, rather than union
2917             "u64" if struct_ == "epoll_event" => "data.u64".to_string(),
2918             s => s.to_string(),
2919         }
2920     });
2921 
2922     cfg.skip_type(move |ty| {
2923         match ty {
2924             // sighandler_t is crazy across platforms
2925             // FIXME: is this necessary?
2926             "sighandler_t" => true,
2927 
2928             // No epoll support
2929             // https://github.com/emscripten-core/emscripten/issues/5033
2930             ty if ty.starts_with("epoll") => true,
2931 
2932             // LFS64 types have been removed in Emscripten 3.1.44
2933             // https://github.com/emscripten-core/emscripten/pull/19812
2934             t => t.ends_with("64") || t.ends_with("64_t"),
2935         }
2936     });
2937 
2938     cfg.skip_struct(move |ty| {
2939         match ty {
2940             // This is actually a union, not a struct
2941             "sigval" => true,
2942 
2943             // FIXME: Investigate why the test fails.
2944             // Skip for now to unblock CI.
2945             "pthread_condattr_t" => true,
2946             "pthread_mutexattr_t" => true,
2947 
2948             // No epoll support
2949             // https://github.com/emscripten-core/emscripten/issues/5033
2950             ty if ty.starts_with("epoll") => true,
2951             ty if ty.starts_with("signalfd") => true,
2952 
2953             // LFS64 types have been removed in Emscripten 3.1.44
2954             // https://github.com/emscripten-core/emscripten/pull/19812
2955             ty => ty.ends_with("64") || ty.ends_with("64_t"),
2956         }
2957     });
2958 
2959     cfg.skip_fn(move |name| {
2960         match name {
2961             // Emscripten does not support fork/exec/wait or any kind of multi-process support
2962             // https://github.com/emscripten-core/emscripten/blob/3.1.68/tools/system_libs.py#L1100
2963             "execv" | "execve" | "execvp" | "execvpe" | "fexecve" | "wait4" => true,
2964 
2965             _ => false,
2966         }
2967     });
2968 
2969     cfg.skip_const(move |name| {
2970         match name {
2971             // FIXME: deprecated - SIGNUNUSED was removed in glibc 2.26
2972             // users should use SIGSYS instead
2973             "SIGUNUSED" => true,
2974 
2975             // FIXME: emscripten uses different constants to constructs these
2976             n if n.contains("__SIZEOF_PTHREAD") => true,
2977 
2978             // No epoll support
2979             // https://github.com/emscripten-core/emscripten/issues/5033
2980             n if n.starts_with("EPOLL") => true,
2981 
2982             // No ptrace.h
2983             // https://github.com/emscripten-core/emscripten/pull/17704
2984             n if n.starts_with("PTRACE_") => true,
2985 
2986             // No quota.h
2987             // https://github.com/emscripten-core/emscripten/pull/17704
2988             n if n.starts_with("QIF_") => true,
2989             "USRQUOTA" | "GRPQUOTA" | "Q_GETFMT" | "Q_GETINFO" | "Q_SETINFO" | "Q_SYNC"
2990             | "Q_QUOTAON" | "Q_QUOTAOFF" | "Q_GETQUOTA" | "Q_SETQUOTA" => true,
2991 
2992             // `SYS_gettid` was removed in Emscripten v1.39.9
2993             // https://github.com/emscripten-core/emscripten/pull/10439
2994             "SYS_gettid" => true,
2995 
2996             // No personality.h
2997             // https://github.com/emscripten-core/emscripten/pull/17704
2998             "ADDR_NO_RANDOMIZE" | "MMAP_PAGE_ZERO" | "ADDR_COMPAT_LAYOUT" | "READ_IMPLIES_EXEC"
2999             | "ADDR_LIMIT_32BIT" | "SHORT_INODE" | "WHOLE_SECONDS" | "STICKY_TIMEOUTS"
3000             | "ADDR_LIMIT_3GB" => true,
3001 
3002             // `SIG_IGN` has been changed to -2 since 1 is a valid function address
3003             // https://github.com/emscripten-core/emscripten/pull/14883
3004             "SIG_IGN" => true,
3005 
3006             // LFS64 types have been removed in Emscripten 3.1.44
3007             // https://github.com/emscripten-core/emscripten/pull/19812
3008             n if n.starts_with("RLIM64") => true,
3009 
3010             _ => false,
3011         }
3012     });
3013 
3014     cfg.skip_field_type(move |struct_, field| {
3015         // This is a weird union, don't check the type.
3016         (struct_ == "ifaddrs" && field == "ifa_ifu") ||
3017         // sighandler_t type is super weird
3018         (struct_ == "sigaction" && field == "sa_sigaction") ||
3019         // sigval is actually a union, but we pretend it's a struct
3020         (struct_ == "sigevent" && field == "sigev_value")
3021     });
3022 
3023     cfg.skip_field(move |struct_, field| {
3024         // this is actually a union on linux, so we can't represent it well and
3025         // just insert some padding.
3026         (struct_ == "siginfo_t" && field == "_pad") ||
3027         // musl names this __dummy1 but it's still there
3028         (struct_ == "glob_t" && field == "gl_flags") ||
3029         // FIXME: After musl 1.1.24, it have only one field `sched_priority`,
3030         // while other fields become reserved.
3031         (struct_ == "sched_param" && [
3032             "sched_ss_low_priority",
3033             "sched_ss_repl_period",
3034             "sched_ss_init_budget",
3035             "sched_ss_max_repl",
3036         ].contains(&field))
3037     });
3038 
3039     cfg.generate("../src/lib.rs", "main.rs");
3040 }
3041 
3042 fn test_neutrino(target: &str) {
3043     assert!(target.contains("nto-qnx"));
3044 
3045     let mut cfg = ctest_cfg();
3046 
3047     headers! { cfg:
3048         "ctype.h",
3049         "dirent.h",
3050         "dlfcn.h",
3051         "sys/elf.h",
3052         "fcntl.h",
3053         "fnmatch.h",
3054         "glob.h",
3055         "grp.h",
3056         "iconv.h",
3057         "ifaddrs.h",
3058         "limits.h",
3059         "sys/link.h",
3060         "locale.h",
3061         "sys/malloc.h",
3062         "rcheck/malloc.h",
3063         "malloc.h",
3064         "mqueue.h",
3065         "net/if.h",
3066         "net/if_arp.h",
3067         "net/route.h",
3068         "netdb.h",
3069         "netinet/in.h",
3070         "netinet/ip.h",
3071         "netinet/tcp.h",
3072         "netinet/udp.h",
3073         "netinet/ip_var.h",
3074         "sys/poll.h",
3075         "pthread.h",
3076         "pwd.h",
3077         "regex.h",
3078         "resolv.h",
3079         "sys/sched.h",
3080         "sched.h",
3081         "semaphore.h",
3082         "shadow.h",
3083         "signal.h",
3084         "spawn.h",
3085         "stddef.h",
3086         "stdint.h",
3087         "stdio.h",
3088         "stdlib.h",
3089         "string.h",
3090         "sys/sysctl.h",
3091         "sys/file.h",
3092         "sys/inotify.h",
3093         "sys/ioctl.h",
3094         "sys/ipc.h",
3095         "sys/mman.h",
3096         "sys/mount.h",
3097         "sys/msg.h",
3098         "sys/resource.h",
3099         "sys/sem.h",
3100         "sys/socket.h",
3101         "sys/stat.h",
3102         "sys/statvfs.h",
3103         "sys/swap.h",
3104         "sys/termio.h",
3105         "sys/time.h",
3106         "sys/times.h",
3107         "sys/types.h",
3108         "sys/uio.h",
3109         "sys/un.h",
3110         "sys/utsname.h",
3111         "sys/wait.h",
3112         "syslog.h",
3113         "termios.h",
3114         "time.h",
3115         "sys/time.h",
3116         "ucontext.h",
3117         "unistd.h",
3118         "utime.h",
3119         "utmp.h",
3120         "wchar.h",
3121         "aio.h",
3122         "nl_types.h",
3123         "langinfo.h",
3124         "unix.h",
3125         "nbutil.h",
3126         "aio.h",
3127         "net/bpf.h",
3128         "net/if_dl.h",
3129         "sys/syspage.h",
3130 
3131         // TODO: The following header file doesn't appear as part of the default headers
3132         //       found in a standard installation of Neutrino 7.1 SDP.  The structures/
3133         //       functions dependent on it are currently commented out.
3134         //"sys/asyncmsg.h",
3135     }
3136 
3137     // Create and include a header file containing
3138     // items which are not included in any official
3139     // header file.
3140     let internal_header = "internal.h";
3141     let out_dir = env::var("OUT_DIR").unwrap();
3142     cfg.header(internal_header);
3143     cfg.include(&out_dir);
3144     std::fs::write(
3145         out_dir.to_owned() + "/" + internal_header,
3146         "#ifndef __internal_h__
3147         #define __internal_h__
3148         void __my_thread_exit(const void **);
3149         #endif",
3150     )
3151     .unwrap();
3152 
3153     cfg.type_name(move |ty, is_struct, is_union| {
3154         match ty {
3155             // Just pass all these through, no need for a "struct" prefix
3156             "FILE" | "fd_set" | "Dl_info" | "DIR" | "Elf32_Phdr" | "Elf64_Phdr" | "Elf32_Shdr"
3157             | "Elf64_Shdr" | "Elf32_Sym" | "Elf64_Sym" | "Elf32_Ehdr" | "Elf64_Ehdr"
3158             | "Elf32_Chdr" | "Elf64_Chdr" | "aarch64_qreg_t" | "syspage_entry_info"
3159             | "syspage_array_info" => ty.to_string(),
3160 
3161             "Ioctl" => "int".to_string(),
3162 
3163             t if is_union => format!("union {}", t),
3164 
3165             t if t.ends_with("_t") => t.to_string(),
3166 
3167             // put `struct` in front of all structs:.
3168             t if is_struct => format!("struct {}", t),
3169 
3170             t => t.to_string(),
3171         }
3172     });
3173 
3174     cfg.field_name(move |_struct_, field| match field {
3175         "type_" => "type".to_string(),
3176 
3177         s => s.to_string(),
3178     });
3179 
3180     cfg.volatile_item(|i| {
3181         use ctest::VolatileItemKind::*;
3182         match i {
3183             // The following fields are volatie but since we cannot express that in
3184             // Rust types, we have to explicitly tell the checker about it here:
3185             StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => true,
3186             StructField(ref n, ref f) if n == "qtime_entry" && f == "nsec_tod_adjust" => true,
3187             StructField(ref n, ref f) if n == "qtime_entry" && f == "nsec" => true,
3188             StructField(ref n, ref f) if n == "qtime_entry" && f == "nsec_stable" => true,
3189             StructField(ref n, ref f) if n == "intrspin" && f == "value" => true,
3190             _ => false,
3191         }
3192     });
3193 
3194     cfg.skip_type(move |ty| {
3195         match ty {
3196             // FIXME: `sighandler_t` type is incorrect, see:
3197             // https://github.com/rust-lang/libc/issues/1359
3198             "sighandler_t" => true,
3199 
3200             // Does not exist in Neutrino
3201             "locale_t" => true,
3202 
3203             _ => false,
3204         }
3205     });
3206 
3207     cfg.skip_struct(move |ty| {
3208         if ty.starts_with("__c_anonymous_") {
3209             return true;
3210         }
3211         match ty {
3212             "Elf64_Phdr" | "Elf32_Phdr" => true,
3213 
3214             // FIXME: This is actually a union, not a struct
3215             "sigval" => true,
3216 
3217             // union
3218             "_channel_connect_attr" => true,
3219 
3220             _ => false,
3221         }
3222     });
3223 
3224     cfg.skip_const(move |name| {
3225         match name {
3226             // These signal "functions" are actually integer values that are casted to a fn ptr
3227             // This causes the compiler to err because of "illegal cast of int to ptr".
3228             "SIG_DFL" => true,
3229             "SIG_IGN" => true,
3230             "SIG_ERR" => true,
3231 
3232             _ => false,
3233         }
3234     });
3235 
3236     cfg.skip_fn(move |name| {
3237         // skip those that are manually verified
3238         match name {
3239             // FIXME: https://github.com/rust-lang/libc/issues/1272
3240             "execv" | "execve" | "execvp" | "execvpe" => true,
3241 
3242             // wrong signature
3243             "signal" => true,
3244 
3245             // wrong signature of callback ptr
3246             "__cxa_atexit" => true,
3247 
3248             // FIXME: Our API is unsound. The Rust API allows aliasing
3249             // pointers, but the C API requires pointers not to alias.
3250             // We should probably be at least using `&`/`&mut` here, see:
3251             // https://github.com/gnzlbg/ctest/issues/68
3252             "lio_listio" => true,
3253 
3254             // 2 fields are actually unions which we're simply representing
3255             // as structures.
3256             "ChannelConnectAttr" => true,
3257 
3258             // fields contains unions
3259             "SignalKillSigval" => true,
3260             "SignalKillSigval_r" => true,
3261 
3262             // Not defined in any headers.  Defined to work around a
3263             // stack unwinding bug.
3264             "__my_thread_exit" => true,
3265 
3266             _ => false,
3267         }
3268     });
3269 
3270     cfg.skip_field_type(move |struct_, field| {
3271         // sigval is actually a union, but we pretend it's a struct
3272         struct_ == "sigevent" && field == "sigev_value" ||
3273         // Anonymous structures
3274         struct_ == "_idle_hook" && field == "time"
3275     });
3276 
3277     cfg.skip_field(move |struct_, field| {
3278         (struct_ == "__sched_param" && field == "reserved") ||
3279         (struct_ == "sched_param" && field == "reserved") ||
3280         (struct_ == "sigevent" && field == "__padding1") || // ensure alignment
3281         (struct_ == "sigevent" && field == "__padding2") || // union
3282         (struct_ == "sigevent" && field == "__sigev_un2") || // union
3283         // sighandler_t type is super weird
3284         (struct_ == "sigaction" && field == "sa_sigaction") ||
3285         // does not exist
3286         (struct_ == "syspage_entry" && field == "__reserved") ||
3287         false // keep me for smaller diffs when something is added above
3288     });
3289 
3290     cfg.skip_static(move |name| (name == "__dso_handle"));
3291 
3292     cfg.generate("../src/lib.rs", "main.rs");
3293 }
3294 
3295 fn test_vxworks(target: &str) {
3296     assert!(target.contains("vxworks"));
3297 
3298     let mut cfg = ctest::TestGenerator::new();
3299     headers! { cfg:
3300                "vxWorks.h",
3301                "yvals.h",
3302                "nfs/nfsCommon.h",
3303                "rtpLibCommon.h",
3304                "randomNumGen.h",
3305                "taskLib.h",
3306                "sysLib.h",
3307                "ioLib.h",
3308                "inetLib.h",
3309                "socket.h",
3310                "errnoLib.h",
3311                "ctype.h",
3312                "dirent.h",
3313                "dlfcn.h",
3314                "elf.h",
3315                "fcntl.h",
3316                "grp.h",
3317                "sys/poll.h",
3318                "ifaddrs.h",
3319                "langinfo.h",
3320                "limits.h",
3321                "link.h",
3322                "locale.h",
3323                "sys/stat.h",
3324                "netdb.h",
3325                "pthread.h",
3326                "pwd.h",
3327                "sched.h",
3328                "semaphore.h",
3329                "signal.h",
3330                "stddef.h",
3331                "stdint.h",
3332                "stdio.h",
3333                "stdlib.h",
3334                "string.h",
3335                "sys/file.h",
3336                "sys/ioctl.h",
3337                "sys/socket.h",
3338                "sys/time.h",
3339                "sys/times.h",
3340                "sys/types.h",
3341                "sys/uio.h",
3342                "sys/un.h",
3343                "sys/utsname.h",
3344                "sys/wait.h",
3345                "netinet/tcp.h",
3346                "syslog.h",
3347                "termios.h",
3348                "time.h",
3349                "ucontext.h",
3350                "unistd.h",
3351                "utime.h",
3352                "wchar.h",
3353                "errno.h",
3354                "sys/mman.h",
3355                "pathLib.h",
3356                "mqueue.h",
3357     }
3358     // FIXME
3359     cfg.skip_const(move |name| match name {
3360         // sighandler_t weirdness
3361         "SIG_DFL" | "SIG_ERR" | "SIG_IGN"
3362         // This is not defined in vxWorks
3363         | "RTLD_DEFAULT"   => true,
3364         _ => false,
3365     });
3366     // FIXME
3367     cfg.skip_type(move |ty| match ty {
3368         "stat64" | "sighandler_t" | "off64_t" => true,
3369         _ => false,
3370     });
3371 
3372     cfg.skip_field_type(move |struct_, field| match (struct_, field) {
3373         ("siginfo_t", "si_value") | ("stat", "st_size") | ("sigaction", "sa_u") => true,
3374         _ => false,
3375     });
3376 
3377     cfg.skip_roundtrip(move |s| match s {
3378         _ => false,
3379     });
3380 
3381     cfg.type_name(move |ty, is_struct, is_union| match ty {
3382         "DIR" | "FILE" | "Dl_info" | "RTP_DESC" => ty.to_string(),
3383         t if is_union => format!("union {}", t),
3384         t if t.ends_with("_t") => t.to_string(),
3385         t if is_struct => format!("struct {}", t),
3386         t => t.to_string(),
3387     });
3388 
3389     // FIXME
3390     cfg.skip_fn(move |name| match name {
3391         // sigval
3392         "sigqueue" | "_sigqueue"
3393         // sighandler_t
3394         | "signal"
3395         // not used in static linking by default
3396         | "dlerror" => true,
3397         _ => false,
3398     });
3399 
3400     cfg.generate("../src/lib.rs", "main.rs");
3401 }
3402 
3403 fn test_linux(target: &str) {
3404     assert!(target.contains("linux"));
3405 
3406     // target_env
3407     let gnu = target.contains("gnu");
3408     let musl = target.contains("musl") || target.contains("ohos");
3409     let uclibc = target.contains("uclibc");
3410 
3411     match (gnu, musl, uclibc) {
3412         (true, false, false) => (),
3413         (false, true, false) => (),
3414         (false, false, true) => (),
3415         (_, _, _) => panic!(
3416             "linux target lib is gnu: {}, musl: {}, uclibc: {}",
3417             gnu, musl, uclibc
3418         ),
3419     }
3420 
3421     let arm = target.contains("arm");
3422     let aarch64 = target.contains("aarch64");
3423     let i686 = target.contains("i686");
3424     let ppc = target.contains("powerpc");
3425     let ppc64 = target.contains("powerpc64");
3426     let s390x = target.contains("s390x");
3427     let sparc64 = target.contains("sparc64");
3428     let x32 = target.contains("x32");
3429     let x86_32 = target.contains("i686");
3430     let x86_64 = target.contains("x86_64");
3431     let aarch64_musl = aarch64 && musl;
3432     let gnueabihf = target.contains("gnueabihf");
3433     let x86_64_gnux32 = target.contains("gnux32") && x86_64;
3434     let riscv64 = target.contains("riscv64");
3435     let loongarch64 = target.contains("loongarch64");
3436     let uclibc = target.contains("uclibc");
3437 
3438     let mut cfg = ctest_cfg();
3439     cfg.define("_GNU_SOURCE", None);
3440     // This macro re-defines fscanf,scanf,sscanf to link to the symbols that are
3441     // deprecated since glibc >= 2.29. This allows Rust binaries to link against
3442     // glibc versions older than 2.29.
3443     cfg.define("__GLIBC_USE_DEPRECATED_SCANF", None);
3444 
3445     headers! { cfg:
3446                "ctype.h",
3447                "dirent.h",
3448                "dlfcn.h",
3449                "elf.h",
3450                "fcntl.h",
3451                "fnmatch.h",
3452                "getopt.h",
3453                "glob.h",
3454                [gnu]: "gnu/libc-version.h",
3455                "grp.h",
3456                "iconv.h",
3457                "ifaddrs.h",
3458                "langinfo.h",
3459                "libgen.h",
3460                "limits.h",
3461                "link.h",
3462                "linux/sysctl.h",
3463                "locale.h",
3464                "malloc.h",
3465                "mntent.h",
3466                "mqueue.h",
3467                "net/ethernet.h",
3468                "net/if.h",
3469                "net/if_arp.h",
3470                "net/route.h",
3471                "netdb.h",
3472                "netinet/in.h",
3473                "netinet/ip.h",
3474                "netinet/tcp.h",
3475                "netinet/udp.h",
3476                "poll.h",
3477                "pthread.h",
3478                "pty.h",
3479                "pwd.h",
3480                "regex.h",
3481                "resolv.h",
3482                "sched.h",
3483                "semaphore.h",
3484                "shadow.h",
3485                "signal.h",
3486                "spawn.h",
3487                "stddef.h",
3488                "stdint.h",
3489                "stdio.h",
3490                "stdlib.h",
3491                "string.h",
3492                "sys/epoll.h",
3493                "sys/eventfd.h",
3494                "sys/file.h",
3495                "sys/fsuid.h",
3496                "sys/klog.h",
3497                "sys/inotify.h",
3498                "sys/ioctl.h",
3499                "sys/ipc.h",
3500                "sys/mman.h",
3501                "sys/mount.h",
3502                "sys/msg.h",
3503                "sys/personality.h",
3504                "sys/prctl.h",
3505                "sys/ptrace.h",
3506                "sys/quota.h",
3507                "sys/random.h",
3508                "sys/reboot.h",
3509                "sys/resource.h",
3510                "sys/sem.h",
3511                "sys/sendfile.h",
3512                "sys/shm.h",
3513                "sys/signalfd.h",
3514                "sys/socket.h",
3515                "sys/stat.h",
3516                "sys/statvfs.h",
3517                "sys/swap.h",
3518                "sys/syscall.h",
3519                "sys/time.h",
3520                "sys/timerfd.h",
3521                "sys/times.h",
3522                "sys/timex.h",
3523                "sys/types.h",
3524                "sys/uio.h",
3525                "sys/un.h",
3526                "sys/user.h",
3527                "sys/utsname.h",
3528                "sys/vfs.h",
3529                "sys/wait.h",
3530                "syslog.h",
3531                "termios.h",
3532                "time.h",
3533                "ucontext.h",
3534                "unistd.h",
3535                "utime.h",
3536                "utmp.h",
3537                "utmpx.h",
3538                "wchar.h",
3539                "errno.h",
3540                // `sys/io.h` is only available on x86*, Alpha, IA64, and 32-bit
3541                // ARM: https://bugzilla.redhat.com/show_bug.cgi?id=1116162
3542                // Also unavailable on gnueabihf with glibc 2.30.
3543                // https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=6b33f373c7b9199e00ba5fbafd94ac9bfb4337b1
3544                [(x86_64 || x86_32 || arm) && !gnueabihf]: "sys/io.h",
3545                // `sys/reg.h` is only available on x86 and x86_64
3546                [x86_64 || x86_32]: "sys/reg.h",
3547                // sysctl system call is deprecated and not available on musl
3548                // It is also unsupported in x32, deprecated since glibc 2.30:
3549                [!(x32 || musl || gnu)]: "sys/sysctl.h",
3550                // <execinfo.h> is not supported by musl:
3551                // https://www.openwall.com/lists/musl/2015/04/09/3
3552                // <execinfo.h> is not present on uclibc.
3553                [!(musl || uclibc)]: "execinfo.h",
3554     }
3555 
3556     // Include linux headers at the end:
3557     headers! {
3558         cfg:
3559         [loongarch64]: "asm/hwcap.h",
3560         [riscv64]: "asm/hwcap.h",
3561         "asm/mman.h",
3562         [gnu]: "linux/aio_abi.h",
3563         "linux/can.h",
3564         "linux/can/raw.h",
3565         // FIXME: requires kernel headers >= 5.4.1.
3566         [!musl]: "linux/can/j1939.h",
3567         "linux/dccp.h",
3568         "linux/errqueue.h",
3569         "linux/falloc.h",
3570         "linux/filter.h",
3571         "linux/fs.h",
3572         "linux/futex.h",
3573         "linux/genetlink.h",
3574         "linux/if.h",
3575         "linux/if_addr.h",
3576         "linux/if_alg.h",
3577         "linux/if_ether.h",
3578         "linux/if_packet.h",
3579         "linux/if_tun.h",
3580         "linux/if_xdp.h",
3581         "linux/input.h",
3582         "linux/ipv6.h",
3583         "linux/kexec.h",
3584         "linux/keyctl.h",
3585         "linux/magic.h",
3586         "linux/memfd.h",
3587         "linux/membarrier.h",
3588         "linux/mempolicy.h",
3589         "linux/mman.h",
3590         "linux/module.h",
3591         // FIXME: requires kernel headers >= 5.1.
3592         [!musl]: "linux/mount.h",
3593         "linux/net_tstamp.h",
3594         "linux/netfilter/nfnetlink.h",
3595         "linux/netfilter/nfnetlink_log.h",
3596         "linux/netfilter/nfnetlink_queue.h",
3597         "linux/netfilter/nf_tables.h",
3598         "linux/netfilter_ipv4.h",
3599         "linux/netfilter_ipv6.h",
3600         "linux/netfilter_ipv6/ip6_tables.h",
3601         "linux/netlink.h",
3602         // FIXME: requires Linux >= 5.6:
3603         [!musl]: "linux/openat2.h",
3604         [!musl]: "linux/ptrace.h",
3605         "linux/quota.h",
3606         "linux/random.h",
3607         "linux/reboot.h",
3608         "linux/rtnetlink.h",
3609         "linux/sched.h",
3610         "linux/sctp.h",
3611         "linux/seccomp.h",
3612         "linux/sock_diag.h",
3613         "linux/sockios.h",
3614         "linux/tls.h",
3615         "linux/uinput.h",
3616         "linux/vm_sockets.h",
3617         "linux/wait.h",
3618         "linux/wireless.h",
3619         "sys/fanotify.h",
3620         // <sys/auxv.h> is not present on uclibc
3621         [!uclibc]: "sys/auxv.h",
3622         [gnu]: "linux/close_range.h",
3623     }
3624 
3625     // note: aio.h must be included before sys/mount.h
3626     headers! {
3627         cfg:
3628         "sys/xattr.h",
3629         "sys/sysinfo.h",
3630         // AIO is not supported by uclibc:
3631         [!uclibc]: "aio.h",
3632     }
3633 
3634     cfg.type_name(move |ty, is_struct, is_union| {
3635         match ty {
3636             // Just pass all these through, no need for a "struct" prefix
3637             "FILE" | "fd_set" | "Dl_info" | "DIR" | "Elf32_Phdr" | "Elf64_Phdr" | "Elf32_Shdr"
3638             | "Elf64_Shdr" | "Elf32_Sym" | "Elf64_Sym" | "Elf32_Ehdr" | "Elf64_Ehdr"
3639             | "Elf32_Chdr" | "Elf64_Chdr" => ty.to_string(),
3640 
3641             "Ioctl" if gnu => "unsigned long".to_string(),
3642             "Ioctl" => "int".to_string(),
3643 
3644             // LFS64 types have been removed in musl 1.2.4+
3645             "off64_t" if musl => "off_t".to_string(),
3646 
3647             // typedefs don't need any keywords
3648             t if t.ends_with("_t") => t.to_string(),
3649             // put `struct` in front of all structs:.
3650             t if is_struct => format!("struct {}", t),
3651             // put `union` in front of all unions:
3652             t if is_union => format!("union {}", t),
3653 
3654             t => t.to_string(),
3655         }
3656     });
3657 
3658     cfg.field_name(move |struct_, field| {
3659         match field {
3660             // Our stat *_nsec fields normally don't actually exist but are part
3661             // of a timeval struct
3662             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
3663                 s.replace("e_nsec", ".tv_nsec")
3664             }
3665             // FIXME: epoll_event.data is actually a union in C, but in Rust
3666             // it is only a u64 because we only expose one field
3667             // http://man7.org/linux/man-pages/man2/epoll_wait.2.html
3668             "u64" if struct_ == "epoll_event" => "data.u64".to_string(),
3669             // The following structs have a field called `type` in C,
3670             // but `type` is a Rust keyword, so these fields are translated
3671             // to `type_` in Rust.
3672             "type_"
3673                 if struct_ == "input_event"
3674                     || struct_ == "input_mask"
3675                     || struct_ == "ff_effect" =>
3676             {
3677                 "type".to_string()
3678             }
3679 
3680             s => s.to_string(),
3681         }
3682     });
3683 
3684     cfg.skip_type(move |ty| {
3685         match ty {
3686             // FIXME: `sighandler_t` type is incorrect, see:
3687             // https://github.com/rust-lang/libc/issues/1359
3688             "sighandler_t" => true,
3689 
3690             // These cannot be tested when "resolv.h" is included and are tested
3691             // in the `linux_elf.rs` file.
3692             "Elf64_Phdr" | "Elf32_Phdr" => true,
3693 
3694             // This type is private on Linux. It is implemented as a C `enum`
3695             // (`c_uint`) and this clashes with the type of the `rlimit` APIs
3696             // which expect a `c_int` even though both are ABI compatible.
3697             "__rlimit_resource_t" => true,
3698             // on Linux, this is a volatile int
3699             "pthread_spinlock_t" => true,
3700 
3701             // For internal use only, to define architecture specific ioctl constants with a libc
3702             // specific type.
3703             "Ioctl" => true,
3704 
3705             // FIXME: requires >= 5.4.1 kernel headers
3706             "pgn_t" if musl => true,
3707             "priority_t" if musl => true,
3708             "name_t" if musl => true,
3709 
3710             t => {
3711                 if musl {
3712                     // LFS64 types have been removed in musl 1.2.4+
3713                     t.ends_with("64") || t.ends_with("64_t")
3714                 } else {
3715                     false
3716                 }
3717             }
3718         }
3719     });
3720 
3721     cfg.skip_struct(move |ty| {
3722         if ty.starts_with("__c_anonymous_") {
3723             return true;
3724         }
3725         // FIXME: musl CI has old headers
3726         if musl && ty.starts_with("uinput_") {
3727             return true;
3728         }
3729         if musl && ty == "seccomp_notif" {
3730             return true;
3731         }
3732         if musl && ty == "seccomp_notif_addfd" {
3733             return true;
3734         }
3735         if musl && ty == "seccomp_notif_resp" {
3736             return true;
3737         }
3738         if musl && ty == "seccomp_notif_sizes" {
3739             return true;
3740         }
3741 
3742         // LFS64 types have been removed in musl 1.2.4+
3743         if musl && (ty.ends_with("64") || ty.ends_with("64_t")) {
3744             return true;
3745         }
3746         // FIXME: sparc64 CI has old headers
3747         if sparc64 && (ty == "uinput_ff_erase" || ty == "uinput_abs_setup") {
3748             return true;
3749         }
3750         // FIXME(https://github.com/rust-lang/libc/issues/1558): passing by
3751         // value corrupts the value for reasons not understood.
3752         if (gnu && sparc64) && (ty == "ip_mreqn" || ty == "hwtstamp_config") {
3753             return true;
3754         }
3755         // FIXME(https://github.com/rust-lang/rust/issues/43894): pass by value for structs that are not an even 32/64 bits on
3756         // big-endian systems corrupts the value for unknown reasons.
3757         if (sparc64 || ppc || ppc64 || s390x)
3758             && (ty == "sockaddr_pkt"
3759                 || ty == "tpacket_auxdata"
3760                 || ty == "tpacket_hdr_variant1"
3761                 || ty == "tpacket_req3"
3762                 || ty == "tpacket_stats_v3"
3763                 || ty == "tpacket_req_u")
3764         {
3765             return true;
3766         }
3767         // FIXME: musl doesn't compile with `struct fanout_args` for unknown reasons.
3768         if musl && ty == "fanout_args" {
3769             return true;
3770         }
3771         if sparc64 && ty == "fanotify_event_info_error" {
3772             return true;
3773         }
3774 
3775         match ty {
3776             // These cannot be tested when "resolv.h" is included and are tested
3777             // in the `linux_elf.rs` file.
3778             "Elf64_Phdr" | "Elf32_Phdr" => true,
3779 
3780             // On Linux, the type of `ut_tv` field of `struct utmpx`
3781             // can be an anonymous struct, so an extra struct,
3782             // which is absent in glibc, has to be defined.
3783             "__timeval" => true,
3784 
3785             // FIXME: This is actually a union, not a struct
3786             "sigval" => true,
3787 
3788             // This type is tested in the `linux_termios.rs` file since there
3789             // are header conflicts when including them with all the other
3790             // structs.
3791             "termios2" => true,
3792 
3793             // FIXME: remove once we set minimum supported glibc version.
3794             // ucontext_t added a new field as of glibc 2.28; our struct definition is
3795             // conservative and omits the field, but that means the size doesn't match for newer
3796             // glibcs (see https://github.com/rust-lang/libc/issues/1410)
3797             "ucontext_t" if gnu => true,
3798 
3799             // FIXME: Somehow we cannot include headers correctly in glibc 2.30.
3800             // So let's ignore for now and re-visit later.
3801             // Probably related: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91085
3802             "statx" => true,
3803             "statx_timestamp" => true,
3804 
3805             // On Linux, the type of `ut_exit` field of struct `utmpx`
3806             // can be an anonymous struct, so an extra struct,
3807             // which is absent in musl, has to be defined.
3808             "__exit_status" if musl => true,
3809 
3810             // clone_args might differ b/w libc versions
3811             "clone_args" => true,
3812 
3813             // Might differ between kernel versions
3814             "open_how" => true,
3815 
3816             // FIXME: requires >= 5.4.1 kernel headers
3817             "j1939_filter" if musl => true,
3818 
3819             // FIXME: requires >= 5.4 kernel headers
3820             "sockaddr_can" if musl => true,
3821 
3822             "sctp_initmsg" | "sctp_sndrcvinfo" | "sctp_sndinfo" | "sctp_rcvinfo"
3823             | "sctp_nxtinfo" | "sctp_prinfo" | "sctp_authinfo" => true,
3824 
3825             // FIXME: requires >= 6.1 kernel headers
3826             "canxl_frame" => true,
3827 
3828             // FIXME: The size of `iv` has been changed since Linux v6.0
3829             // https://github.com/torvalds/linux/commit/94dfc73e7cf4a31da66b8843f0b9283ddd6b8381
3830             "af_alg_iv" => true,
3831 
3832             // FIXME: Requires >= 5.1 kernel headers.
3833             // Everything that uses install-musl.sh has 4.19 kernel headers.
3834             "tls12_crypto_info_aes_gcm_256"
3835                 if (aarch64 || arm || i686 || s390x || x86_64) && musl =>
3836             {
3837                 true
3838             }
3839 
3840             // FIXME: Requires >= 5.11 kernel headers.
3841             // Everything that uses install-musl.sh has 4.19 kernel headers.
3842             "tls12_crypto_info_chacha20_poly1305"
3843                 if (aarch64 || arm || i686 || s390x || x86_64) && musl =>
3844             {
3845                 true
3846             }
3847 
3848             // FIXME: Requires >= 5.3 kernel headers.
3849             // Everything that uses install-musl.sh has 4.19 kernel headers.
3850             "xdp_options" if musl => true,
3851 
3852             // FIXME: Requires >= 5.4 kernel headers.
3853             // Everything that uses install-musl.sh has 4.19 kernel headers.
3854             "xdp_umem_reg" | "xdp_ring_offset" | "xdp_mmap_offsets" if musl => true,
3855 
3856             // FIXME: Requires >= 5.9 kernel headers.
3857             // Everything that uses install-musl.sh has 4.19 kernel headers.
3858             "xdp_statistics" if musl => true,
3859 
3860             // A new field was added in kernel 5.4, this is the old version for backwards compatibility.
3861             // https://github.com/torvalds/linux/commit/77cd0d7b3f257fd0e3096b4fdcff1a7d38e99e10
3862             "xdp_ring_offset_v1" | "xdp_mmap_offsets_v1" => true,
3863 
3864             // Multiple new fields were added in kernel 5.9, this is the old version for backwards compatibility.
3865             // https://github.com/torvalds/linux/commit/77cd0d7b3f257fd0e3096b4fdcff1a7d38e99e10
3866             "xdp_statistics_v1" => true,
3867 
3868             // A new field was added in kernel 5.4, this is the old version for backwards compatibility.
3869             // https://github.com/torvalds/linux/commit/c05cd3645814724bdeb32a2b4d953b12bdea5f8c
3870             "xdp_umem_reg_v1" => true,
3871 
3872             // Is defined in `<linux/sched/types.h>` but if this file is included at the same time
3873             // as `<sched.h>`, the `struct sched_param` is defined twice, causing the compilation to
3874             // fail. The problem doesn't seem to be present in more recent versions of the linux
3875             // kernel so we can drop this and test the type once this new version is used in CI.
3876             "sched_attr" => true,
3877 
3878             // FIXME: Requires >= 6.9 kernel headers.
3879             "epoll_params" => true,
3880 
3881             _ => false,
3882         }
3883     });
3884 
3885     cfg.skip_const(move |name| {
3886         if !gnu {
3887             // Skip definitions from the kernel on non-glibc Linux targets.
3888             // They're libc-independent, so we only need to check them on one
3889             // libc. We don't want to break CI if musl or another libc doesn't
3890             // have the definitions yet. (We do still want to check them on
3891             // every glibc target, though, as some of them can vary by
3892             // architecture.)
3893             //
3894             // This is not an exhaustive list of kernel constants, just a list
3895             // of prefixes of all those that have appeared here or that get
3896             // updated regularly and seem likely to cause breakage.
3897             if name.starts_with("AF_")
3898                 || name.starts_with("ARPHRD_")
3899                 || name.starts_with("EPOLL")
3900                 || name.starts_with("F_")
3901                 || name.starts_with("FALLOC_FL_")
3902                 || name.starts_with("IFLA_")
3903                 || name.starts_with("KEXEC_")
3904                 || name.starts_with("MS_")
3905                 || name.starts_with("MSG_")
3906                 || name.starts_with("OPEN_TREE_")
3907                 || name.starts_with("P_")
3908                 || name.starts_with("PF_")
3909                 || name.starts_with("RLIMIT_")
3910                 || name.starts_with("RTEXT_FILTER_")
3911                 || name.starts_with("SOL_")
3912                 || name.starts_with("STATX_")
3913                 || name.starts_with("SW_")
3914                 || name.starts_with("SYS_")
3915                 || name.starts_with("TCP_")
3916                 || name.starts_with("UINPUT_")
3917                 || name.starts_with("VMADDR_")
3918             {
3919                 return true;
3920             }
3921         }
3922         if musl {
3923             // FIXME: Requires >= 5.0 kernel headers
3924             if name == "SECCOMP_GET_NOTIF_SIZES"
3925                || name == "SECCOMP_FILTER_FLAG_NEW_LISTENER"
3926                || name == "SECCOMP_FILTER_FLAG_TSYNC_ESRCH"
3927                || name == "SECCOMP_USER_NOTIF_FLAG_CONTINUE"  // requires >= 5.5
3928                || name == "SECCOMP_ADDFD_FLAG_SETFD"  // requires >= 5.9
3929                || name == "SECCOMP_ADDFD_FLAG_SEND"   // requires >= 5.9
3930                || name == "SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV"  // requires >= 5.19
3931             {
3932                 return true;
3933             }
3934             // FIXME: Requires >= 5.4.1 kernel headers
3935             if name.starts_with("J1939")
3936                 || name.starts_with("RTEXT_FILTER_")
3937                 || name.starts_with("SO_J1939")
3938                 || name.starts_with("SCM_J1939")
3939             {
3940                 return true;
3941             }
3942             // FIXME: Requires >= 5.10 kernel headers
3943             if name.starts_with("MEMBARRIER_CMD_REGISTER")
3944                 || name.starts_with("MEMBARRIER_CMD_PRIVATE")
3945             {
3946                 return true;
3947             }
3948             // LFS64 types have been removed in musl 1.2.4+
3949             if name.starts_with("RLIM64") {
3950                 return true;
3951             }
3952             // CI fails because musl targets use Linux v4 kernel
3953             if name.starts_with("NI_IDN") {
3954                 return true;
3955             }
3956             // FIXME: Requires >= 6.3 kernel headers
3957             if name == "MFD_NOEXEC_SEAL" || name == "MFD_EXEC" {
3958                 return true;
3959             }
3960         }
3961         match name {
3962             // These constants are not available if gnu headers have been included
3963             // and can therefore not be tested here
3964             //
3965             // The IPV6 constants are tested in the `linux_ipv6.rs` tests:
3966             | "IPV6_FLOWINFO"
3967             | "IPV6_FLOWLABEL_MGR"
3968             | "IPV6_FLOWINFO_SEND"
3969             | "IPV6_FLOWINFO_FLOWLABEL"
3970             | "IPV6_FLOWINFO_PRIORITY"
3971             // The F_ fnctl constants are tested in the `linux_fnctl.rs` tests:
3972             | "F_CANCELLK"
3973             | "F_ADD_SEALS"
3974             | "F_GET_SEALS"
3975             | "F_SEAL_SEAL"
3976             | "F_SEAL_SHRINK"
3977             | "F_SEAL_GROW"
3978             | "F_SEAL_WRITE" => true,
3979             // The `ARPHRD_CAN` is tested in the `linux_if_arp.rs` tests
3980             // because including `linux/if_arp.h` causes some conflicts:
3981             "ARPHRD_CAN" => true,
3982 
3983             // FIXME: deprecated: not available in any header
3984             // See: https://github.com/rust-lang/libc/issues/1356
3985             "ENOATTR" => true,
3986 
3987             // FIXME: SIGUNUSED was removed in glibc 2.26
3988             // Users should use SIGSYS instead.
3989             "SIGUNUSED" => true,
3990 
3991             // FIXME: conflicts with glibc headers and is tested in
3992             // `linux_termios.rs` below:
3993             | "BOTHER"
3994             | "IBSHIFT"
3995             | "TCGETS2"
3996             | "TCSETS2"
3997             | "TCSETSW2"
3998             | "TCSETSF2" => true,
3999 
4000             // FIXME: on musl the pthread types are defined a little differently
4001             // - these constants are used by the glibc implementation.
4002             n if musl && n.contains("__SIZEOF_PTHREAD") => true,
4003 
4004             // FIXME: It was extended to 4096 since glibc 2.31 (Linux 5.4).
4005             // We should do so after a while.
4006             "SOMAXCONN" if gnu => true,
4007 
4008             // deprecated: not available from Linux kernel 5.6:
4009             "VMADDR_CID_RESERVED" => true,
4010 
4011             // IPPROTO_MAX was increased in 5.6 for IPPROTO_MPTCP:
4012             | "IPPROTO_MAX"
4013             | "IPPROTO_ETHERNET"
4014             | "IPPROTO_MPTCP" => true,
4015 
4016             // FIXME: Not yet implemented on sparc64
4017             "SYS_clone3" if sparc64 => true,
4018 
4019             // FIXME: Not defined on ARM, gnueabihf, musl, PowerPC, riscv64, s390x, and sparc64.
4020             "SYS_memfd_secret" if arm | gnueabihf | musl | ppc | riscv64 | s390x | sparc64 => true,
4021 
4022             // FIXME: Added in Linux 5.16
4023             // https://github.com/torvalds/linux/commit/039c0ec9bb77446d7ada7f55f90af9299b28ca49
4024             "SYS_futex_waitv" => true,
4025 
4026             // FIXME: Added in Linux 5.17
4027             // https://github.com/torvalds/linux/commit/c6018b4b254971863bd0ad36bb5e7d0fa0f0ddb0
4028             "SYS_set_mempolicy_home_node" => true,
4029 
4030             // FIXME: Added in Linux 5.18
4031             // https://github.com/torvalds/linux/commit/8b5413647262dda8d8d0e07e14ea1de9ac7cf0b2
4032             "NFQA_PRIORITY" => true,
4033 
4034             // FIXME: requires more recent kernel headers on CI
4035             | "UINPUT_VERSION"
4036             | "SW_MAX"
4037             | "SW_CNT"
4038                 if ppc64 || riscv64 => true,
4039 
4040             // FIXME: requires more recent kernel headers on CI
4041             | "MFD_EXEC"
4042             | "MFD_NOEXEC_SEAL"
4043             | "SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV"
4044                 if sparc64 => true,
4045 
4046             // FIXME: Not currently available in headers on ARM and musl.
4047             "NETLINK_GET_STRICT_CHK" if arm || musl => true,
4048 
4049             // kernel constants not available in uclibc 1.0.34
4050             | "EXTPROC"
4051             | "IPPROTO_BEETPH"
4052             | "IPPROTO_MPLS"
4053             | "IPV6_HDRINCL"
4054             | "IPV6_MULTICAST_ALL"
4055             | "IPV6_PMTUDISC_INTERFACE"
4056             | "IPV6_PMTUDISC_OMIT"
4057             | "IPV6_ROUTER_ALERT_ISOLATE"
4058             | "PACKET_MR_UNICAST"
4059             | "RUSAGE_THREAD"
4060             | "SHM_EXEC"
4061             | "UDP_GRO"
4062             | "UDP_SEGMENT"
4063                 if uclibc => true,
4064 
4065             // headers conflicts with linux/pidfd.h
4066             "PIDFD_NONBLOCK" => true,
4067 
4068             // is a private value for kernel usage normally
4069             "FUSE_SUPER_MAGIC" => true,
4070 
4071             // linux 5.17 min
4072             "PR_SET_VMA" | "PR_SET_VMA_ANON_NAME" => true,
4073 
4074             // present in recent kernels only
4075             "PR_SCHED_CORE" | "PR_SCHED_CORE_CREATE" | "PR_SCHED_CORE_GET" | "PR_SCHED_CORE_MAX" | "PR_SCHED_CORE_SCOPE_PROCESS_GROUP" | "PR_SCHED_CORE_SCOPE_THREAD" | "PR_SCHED_CORE_SCOPE_THREAD_GROUP" | "PR_SCHED_CORE_SHARE_FROM" | "PR_SCHED_CORE_SHARE_TO" => true,
4076 
4077             // present in recent kernels only >= 5.13
4078             "PR_PAC_SET_ENABLED_KEYS" | "PR_PAC_GET_ENABLED_KEYS" => true,
4079             // present in recent kernels only >= 5.19
4080             "PR_SME_SET_VL" | "PR_SME_GET_VL" | "PR_SME_VL_LEN_MAX" | "PR_SME_SET_VL_INHERIT" | "PR_SME_SET_VL_ONE_EXEC" => true,
4081 
4082             // Added in Linux 5.14
4083             "FUTEX_LOCK_PI2" => true,
4084 
4085             // Added in  linux 6.1
4086             "STATX_DIOALIGN"
4087             | "CAN_RAW_XL_FRAMES"
4088             | "CANXL_HDR_SIZE"
4089             | "CANXL_MAX_DLC"
4090             | "CANXL_MAX_DLC_MASK"
4091             | "CANXL_MAX_DLEN"
4092             | "CANXL_MAX_MTU"
4093             | "CANXL_MIN_DLC"
4094             | "CANXL_MIN_DLEN"
4095             | "CANXL_MIN_MTU"
4096             | "CANXL_MTU"
4097             | "CANXL_PRIO_BITS"
4098             | "CANXL_PRIO_MASK"
4099             | "CANXL_SEC"
4100             | "CANXL_XLF"
4101              => true,
4102 
4103             // FIXME: Parts of netfilter/nfnetlink*.h require more recent kernel headers:
4104             | "RTNLGRP_MCTP_IFADDR" // linux v5.17+
4105             | "RTNLGRP_TUNNEL" // linux v5.18+
4106             | "RTNLGRP_STATS" // linux v5.18+
4107                 => true,
4108 
4109             // FIXME: The below is no longer const in glibc 2.34:
4110             // https://github.com/bminor/glibc/commit/5d98a7dae955bafa6740c26eaba9c86060ae0344
4111             | "PTHREAD_STACK_MIN"
4112             | "SIGSTKSZ"
4113             | "MINSIGSTKSZ"
4114                 if gnu => true,
4115 
4116             // FIXME: Linux >= 5.16 changed its value:
4117             // https://github.com/torvalds/linux/commit/42df6e1d221dddc0f2acf2be37e68d553ad65f96
4118             "NF_NETDEV_NUMHOOKS" => true,
4119 
4120             // FIXME: requires Linux >= 5.6:
4121             | "RESOLVE_BENEATH"
4122             | "RESOLVE_CACHED"
4123             | "RESOLVE_IN_ROOT"
4124             | "RESOLVE_NO_MAGICLINKS"
4125             | "RESOLVE_NO_SYMLINKS"
4126             | "RESOLVE_NO_XDEV" if musl => true,
4127 
4128             // FIXME: requires Linux >= 5.4:
4129             | "CAN_J1939"
4130             | "CAN_NPROTO" if musl => true,
4131 
4132             // FIXME: requires Linux >= 5.6
4133             "GRND_INSECURE" if musl => true,
4134 
4135             // FIXME: requires Linux >= 5.7:
4136             "MREMAP_DONTUNMAP" if musl => true,
4137 
4138             // FIXME: requires Linux >= v5.8
4139             "IF_LINK_MODE_TESTING" if musl || sparc64 => true,
4140 
4141             // FIXME: Requires more recent kernel headers (5.9 / 5.11):
4142             | "CLOSE_RANGE_UNSHARE"
4143             | "CLOSE_RANGE_CLOEXEC" if musl => true,
4144 
4145             // FIXME: requires Linux >= 5.12:
4146             "MPOL_F_NUMA_BALANCING" if musl => true,
4147 
4148             // FIXME: Requires more recent kernel headers
4149             | "NFNL_SUBSYS_COUNT" // bumped in v5.14
4150             | "NFNL_SUBSYS_HOOK" // v5.14+
4151             | "NFULA_VLAN" // v5.4+
4152             | "NFULA_L2HDR" // v5.4+
4153             | "NFULA_VLAN_PROTO" // v5.4+
4154             | "NFULA_VLAN_TCI" // v5.4+
4155             | "NFULA_VLAN_UNSPEC" // v5.4+
4156             | "RTNLGRP_NEXTHOP" // linux v5.3+
4157             | "RTNLGRP_BRVLAN" // linux v5.6+
4158             if musl => true,
4159 
4160             | "MADV_COLD"
4161             | "MADV_PAGEOUT"
4162             | "MADV_POPULATE_READ"
4163             | "MADV_POPULATE_WRITE"
4164             if musl => true,
4165             "CLONE_CLEAR_SIGHAND" | "CLONE_INTO_CGROUP" => true,
4166 
4167             // kernel 6.1 minimum
4168             "MADV_COLLAPSE" => true,
4169 
4170             // kernel 6.2 minimum
4171             "TUN_F_USO4" | "TUN_F_USO6" | "IFF_NO_CARRIER" => true,
4172 
4173             // FIXME: Requires more recent kernel headers
4174             | "IFLA_PARENT_DEV_NAME"     // linux v5.13+
4175             | "IFLA_PARENT_DEV_BUS_NAME" // linux v5.13+
4176             | "IFLA_GRO_MAX_SIZE"        // linux v5.16+
4177             | "IFLA_TSO_MAX_SIZE"        // linux v5.18+
4178             | "IFLA_TSO_MAX_SEGS"        // linux v5.18+
4179             | "IFLA_ALLMULTI"            // linux v6.0+
4180             | "MADV_DONTNEED_LOCKED"     // linux v5.18+
4181                 => true,
4182             "SCTP_FUTURE_ASSOC" | "SCTP_CURRENT_ASSOC" | "SCTP_ALL_ASSOC" | "SCTP_PEER_ADDR_THLDS_V2" => true, // linux 5.5+
4183 
4184             // FIXME: Requires more recent kernel headers
4185             "HWTSTAMP_TX_ONESTEP_P2P" if musl => true, // linux v5.6+
4186 
4187             // kernel 6.5 minimum
4188             "MOVE_MOUNT_BENEATH" => true,
4189             // FIXME: Requires linux 6.1
4190             "ALG_SET_KEY_BY_KEY_SERIAL" | "ALG_SET_DRBG_ENTROPY" => true,
4191 
4192             // FIXME: Requires more recent kernel headers
4193             | "FAN_FS_ERROR"                      // linux v5.16+
4194             | "FAN_RENAME"                        // linux v5.17+
4195             | "FAN_REPORT_TARGET_FID"             // linux v5.17+
4196             | "FAN_REPORT_DFID_NAME_TARGET"       // linux v5.17+
4197             | "FAN_MARK_EVICTABLE"                // linux v5.19+
4198             | "FAN_MARK_IGNORE"                   // linux v6.0+
4199             | "FAN_MARK_IGNORE_SURV"              // linux v6.0+
4200             | "FAN_EVENT_INFO_TYPE_ERROR"         // linux v5.16+
4201             | "FAN_EVENT_INFO_TYPE_OLD_DFID_NAME" // linux v5.17+
4202             | "FAN_EVENT_INFO_TYPE_NEW_DFID_NAME" // linux v5.17+
4203             | "FAN_RESPONSE_INFO_NONE"            // linux v5.16+
4204             | "FAN_RESPONSE_INFO_AUDIT_RULE"      // linux v5.16+
4205             | "FAN_INFO"                          // linux v5.16+
4206                 => true,
4207 
4208             // FIXME: Requires linux 5.15+
4209             "FAN_REPORT_PIDFD" if musl => true,
4210 
4211             // FIXME: Requires linux 5.9+
4212             | "FAN_REPORT_DIR_FID"
4213             | "FAN_REPORT_NAME"
4214             | "FAN_REPORT_DFID_NAME"
4215             | "FAN_EVENT_INFO_TYPE_DFID_NAME"
4216             | "FAN_EVENT_INFO_TYPE_DFID"
4217             | "FAN_EVENT_INFO_TYPE_PIDFD"
4218             | "FAN_NOPIDFD"
4219             | "FAN_EPIDFD"
4220             if musl => true,
4221 
4222             // FIXME: Requires linux 6.5
4223             "NFT_MSG_MAX" => true,
4224 
4225             // FIXME: Requires >= 5.1 kernel headers.
4226             // Everything that uses install-musl.sh has 4.19 kernel headers.
4227             "TLS_1_3_VERSION"
4228             | "TLS_1_3_VERSION_MAJOR"
4229             | "TLS_1_3_VERSION_MINOR"
4230             | "TLS_CIPHER_AES_GCM_256"
4231             | "TLS_CIPHER_AES_GCM_256_IV_SIZE"
4232             | "TLS_CIPHER_AES_GCM_256_KEY_SIZE"
4233             | "TLS_CIPHER_AES_GCM_256_SALT_SIZE"
4234             | "TLS_CIPHER_AES_GCM_256_TAG_SIZE"
4235             | "TLS_CIPHER_AES_GCM_256_REC_SEQ_SIZE"
4236                 if (aarch64 || arm || i686 || s390x || x86_64) && musl =>
4237             {
4238                 true
4239             }
4240 
4241             // FIXME: Requires >= 5.11 kernel headers.
4242             // Everything that uses install-musl.sh has 4.19 kernel headers.
4243             "TLS_CIPHER_CHACHA20_POLY1305"
4244             | "TLS_CIPHER_CHACHA20_POLY1305_IV_SIZE"
4245             | "TLS_CIPHER_CHACHA20_POLY1305_KEY_SIZE"
4246             | "TLS_CIPHER_CHACHA20_POLY1305_SALT_SIZE"
4247             | "TLS_CIPHER_CHACHA20_POLY1305_TAG_SIZE"
4248             | "TLS_CIPHER_CHACHA20_POLY1305_REC_SEQ_SIZE"
4249                 if (aarch64 || arm || i686 || s390x || x86_64) && musl =>
4250             {
4251                 true
4252             }
4253 
4254             // FIXME: Requires >= 5.3 kernel headers.
4255             // Everything that uses install-musl.sh has 4.19 kernel headers.
4256             "XDP_OPTIONS_ZEROCOPY" | "XDP_OPTIONS"
4257                 if musl =>
4258             {
4259                 true
4260             }
4261 
4262             // FIXME: Requires >= 5.4 kernel headers.
4263             // Everything that uses install-musl.sh has 4.19 kernel headers.
4264             "XSK_UNALIGNED_BUF_OFFSET_SHIFT"
4265             | "XSK_UNALIGNED_BUF_ADDR_MASK"
4266             | "XDP_UMEM_UNALIGNED_CHUNK_FLAG"
4267             | "XDP_RING_NEED_WAKEUP"
4268             | "XDP_USE_NEED_WAKEUP"
4269                 if musl =>
4270             {
4271                 true
4272             }
4273 
4274             // FIXME: Requires >= 6.6 kernel headers.
4275             "XDP_USE_SG"
4276             | "XDP_PKT_CONTD"
4277                 =>
4278             {
4279                 true
4280             }
4281 
4282             // FIXME: Requires >= 6.6 kernel headers.
4283             "SYS_fchmodat2" => true,
4284 
4285             // FIXME: Requires >= 6.10 kernel headers.
4286             "SYS_mseal" => true,
4287 
4288             // FIXME: seems to not be available all the time (from <include/linux/sched.h>:
4289             "PF_VCPU"
4290             | "PF_IDLE"
4291             | "PF_EXITING"
4292             | "PF_POSTCOREDUMP"
4293             | "PF_IO_WORKER"
4294             | "PF_WQ_WORKER"
4295             | "PF_FORKNOEXEC"
4296             | "PF_MCE_PROCESS"
4297             | "PF_SUPERPRIV"
4298             | "PF_DUMPCORE"
4299             | "PF_SIGNALED"
4300             | "PF_MEMALLOC"
4301             | "PF_NPROC_EXCEEDED"
4302             | "PF_USED_MATH"
4303             | "PF_USER_WORKER"
4304             | "PF_NOFREEZE"
4305             | "PF_KSWAPD"
4306             | "PF_MEMALLOC_NOFS"
4307             | "PF_MEMALLOC_NOIO"
4308             | "PF_LOCAL_THROTTLE"
4309             | "PF_KTHREAD"
4310             | "PF_RANDOMIZE"
4311             | "PF_NO_SETAFFINITY"
4312             | "PF_MCE_EARLY"
4313             | "PF_MEMALLOC_PIN" => true,
4314 
4315             "SCHED_FLAG_KEEP_POLICY"
4316             | "SCHED_FLAG_KEEP_PARAMS"
4317             | "SCHED_FLAG_UTIL_CLAMP_MIN"
4318             | "SCHED_FLAG_UTIL_CLAMP_MAX"
4319             | "SCHED_FLAG_KEEP_ALL"
4320             | "SCHED_FLAG_UTIL_CLAMP"
4321             | "SCHED_FLAG_ALL" if musl => true, // Needs more recent linux headers.
4322 
4323             // FIXME: Requires >= 6.9 kernel headers.
4324             "EPIOCSPARAMS"
4325             | "EPIOCGPARAMS" => true,
4326 
4327             _ => false,
4328         }
4329     });
4330 
4331     cfg.skip_fn(move |name| {
4332         // skip those that are manually verified
4333         match name {
4334             // FIXME: https://github.com/rust-lang/libc/issues/1272
4335             "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true,
4336 
4337             // There are two versions of the sterror_r function, see
4338             //
4339             // https://linux.die.net/man/3/strerror_r
4340             //
4341             // An XSI-compliant version provided if:
4342             //
4343             // (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600)
4344             //  && ! _GNU_SOURCE
4345             //
4346             // and a GNU specific version provided if _GNU_SOURCE is defined.
4347             //
4348             // libc provides bindings for the XSI-compliant version, which is
4349             // preferred for portable applications.
4350             //
4351             // We skip the test here since here _GNU_SOURCE is defined, and
4352             // test the XSI version below.
4353             "strerror_r" => true,
4354 
4355             // FIXME: Our API is unsound. The Rust API allows aliasing
4356             // pointers, but the C API requires pointers not to alias.
4357             // We should probably be at least using `&`/`&mut` here, see:
4358             // https://github.com/gnzlbg/ctest/issues/68
4359             "lio_listio" if musl => true,
4360 
4361             // Needs glibc 2.34 or later.
4362             "posix_spawn_file_actions_addclosefrom_np" if gnu && sparc64 => true,
4363             // Needs glibc 2.35 or later.
4364             "posix_spawn_file_actions_addtcsetpgrp_np" if gnu && sparc64 => true,
4365 
4366             // FIXME: Deprecated since glibc 2.30. Remove fn once upstream does.
4367             "sysctl" if gnu => true,
4368 
4369             // FIXME: It now takes c_void instead of timezone since glibc 2.31.
4370             "gettimeofday" if gnu => true,
4371 
4372             // These are all implemented as static inline functions in uclibc, so
4373             // they cannot be linked against.
4374             // If implementations are required, they might need to be implemented
4375             // in this crate.
4376             "posix_spawnattr_init" if uclibc => true,
4377             "posix_spawnattr_destroy" if uclibc => true,
4378             "posix_spawnattr_getsigdefault" if uclibc => true,
4379             "posix_spawnattr_setsigdefault" if uclibc => true,
4380             "posix_spawnattr_getsigmask" if uclibc => true,
4381             "posix_spawnattr_setsigmask" if uclibc => true,
4382             "posix_spawnattr_getflags" if uclibc => true,
4383             "posix_spawnattr_setflags" if uclibc => true,
4384             "posix_spawnattr_getpgroup" if uclibc => true,
4385             "posix_spawnattr_setpgroup" if uclibc => true,
4386             "posix_spawnattr_getschedpolicy" if uclibc => true,
4387             "posix_spawnattr_setschedpolicy" if uclibc => true,
4388             "posix_spawnattr_getschedparam" if uclibc => true,
4389             "posix_spawnattr_setschedparam" if uclibc => true,
4390             "posix_spawn_file_actions_init" if uclibc => true,
4391             "posix_spawn_file_actions_destroy" if uclibc => true,
4392 
4393             // uclibc defines the flags type as a uint, but dependent crates
4394             // assume it's a int instead.
4395             "getnameinfo" if uclibc => true,
4396 
4397             // FIXME: This needs musl 1.2.2 or later.
4398             "gettid" if musl => true,
4399 
4400             // Needs glibc 2.33 or later.
4401             "mallinfo2" => true,
4402 
4403             "reallocarray" if musl => true,
4404 
4405             // Not defined in uclibc as of 1.0.34
4406             "gettid" if uclibc => true,
4407 
4408             // Needs musl 1.2.3 or later.
4409             "pthread_getname_np" if musl => true,
4410 
4411             // pthread_sigqueue uses sigval, which was initially declared
4412             // as a struct but should be defined as a union. However due
4413             // to the issues described here: https://github.com/rust-lang/libc/issues/2816
4414             // it can't be changed from struct.
4415             "pthread_sigqueue" => true,
4416 
4417             // There are two versions of basename(3) on Linux with glibc, see
4418             //
4419             // https://man7.org/linux/man-pages/man3/basename.3.html
4420             //
4421             // If libgen.h is included, then the POSIX version will be available;
4422             // If _GNU_SOURCE is defined and string.h is included, then the GNU one
4423             // will be used.
4424             //
4425             // libc exposes both of them, providing a prefix to differentiate between
4426             // them.
4427             //
4428             // Because the name with prefix is not a valid symbol in C, we have to
4429             // skip the tests.
4430             "posix_basename" if gnu => true,
4431             "gnu_basename" if gnu => true,
4432 
4433             // FIXME: function pointers changed since Ubuntu 23.10
4434             "strtol" | "strtoll" | "strtoul" | "strtoull" | "fscanf" | "scanf" | "sscanf" => true,
4435 
4436             // Added in musl 1.2.5
4437             "preadv2" | "pwritev2" if musl => true,
4438 
4439             _ => false,
4440         }
4441     });
4442 
4443     cfg.skip_field_type(move |struct_, field| {
4444         // This is a weird union, don't check the type.
4445         (struct_ == "ifaddrs" && field == "ifa_ifu") ||
4446         // sighandler_t type is super weird
4447         (struct_ == "sigaction" && field == "sa_sigaction") ||
4448         // __timeval type is a patch which doesn't exist in glibc
4449         (struct_ == "utmpx" && field == "ut_tv") ||
4450         // sigval is actually a union, but we pretend it's a struct
4451         (struct_ == "sigevent" && field == "sigev_value") ||
4452         // this one is an anonymous union
4453         (struct_ == "ff_effect" && field == "u") ||
4454         // `__exit_status` type is a patch which is absent in musl
4455         (struct_ == "utmpx" && field == "ut_exit" && musl) ||
4456         // `can_addr` is an anonymous union
4457         (struct_ == "sockaddr_can" && field == "can_addr")
4458     });
4459 
4460     cfg.volatile_item(|i| {
4461         use ctest::VolatileItemKind::*;
4462         match i {
4463             // aio_buf is a volatile void** but since we cannot express that in
4464             // Rust types, we have to explicitly tell the checker about it here:
4465             StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => true,
4466             _ => false,
4467         }
4468     });
4469 
4470     cfg.skip_field(move |struct_, field| {
4471         // this is actually a union on linux, so we can't represent it well and
4472         // just insert some padding.
4473         (struct_ == "siginfo_t" && field == "_pad") ||
4474         // musl names this __dummy1 but it's still there
4475         (musl && struct_ == "glob_t" && field == "gl_flags") ||
4476         // musl seems to define this as an *anonymous* bitfield
4477         (musl && struct_ == "statvfs" && field == "__f_unused") ||
4478         // sigev_notify_thread_id is actually part of a sigev_un union
4479         (struct_ == "sigevent" && field == "sigev_notify_thread_id") ||
4480         // signalfd had SIGSYS fields added in Linux 4.18, but no libc release
4481         // has them yet.
4482         (struct_ == "signalfd_siginfo" && (field == "ssi_addr_lsb" ||
4483                                            field == "_pad2" ||
4484                                            field == "ssi_syscall" ||
4485                                            field == "ssi_call_addr" ||
4486                                            field == "ssi_arch")) ||
4487         // FIXME: After musl 1.1.24, it have only one field `sched_priority`,
4488         // while other fields become reserved.
4489         (struct_ == "sched_param" && [
4490             "sched_ss_low_priority",
4491             "sched_ss_repl_period",
4492             "sched_ss_init_budget",
4493             "sched_ss_max_repl",
4494         ].contains(&field) && musl) ||
4495         // FIXME: After musl 1.1.24, the type becomes `int` instead of `unsigned short`.
4496         (struct_ == "ipc_perm" && field == "__seq" && aarch64_musl) ||
4497         // glibc uses unnamed fields here and Rust doesn't support that yet
4498         (struct_ == "timex" && field.starts_with("__unused")) ||
4499         // FIXME: It now takes mode_t since glibc 2.31 on some targets.
4500         (struct_ == "ipc_perm" && field == "mode"
4501             && ((x86_64 || i686 || arm || riscv64) && gnu || x86_64_gnux32)
4502         ) ||
4503         // the `u` field is in fact an anonymous union
4504         (gnu && struct_ == "ptrace_syscall_info" && (field == "u" || field == "pad")) ||
4505         // the vregs field is a `__uint128_t` C's type.
4506         (struct_ == "user_fpsimd_struct" && field == "vregs") ||
4507         // Linux >= 5.11 tweaked the `svm_zero` field of the `sockaddr_vm` struct.
4508         // https://github.com/torvalds/linux/commit/dc8eeef73b63ed8988224ba6b5ed19a615163a7f
4509         (struct_ == "sockaddr_vm" && field == "svm_zero") ||
4510         // the `ifr_ifru` field is an anonymous union
4511         (struct_ == "ifreq" && field == "ifr_ifru") ||
4512         // the `ifc_ifcu` field is an anonymous union
4513         (struct_ == "ifconf" && field == "ifc_ifcu") ||
4514         // glibc uses a single array `uregs` instead of individual fields.
4515         (struct_ == "user_regs" && arm) ||
4516         // the `ifr_ifrn` field is an anonymous union
4517         (struct_ == "iwreq" && field == "ifr_ifrn") ||
4518         // the `key` field is a zero-sized array
4519         (struct_ == "iw_encode_ext" && field == "key") ||
4520         // the `tcpi_snd_rcv_wscale` map two bitfield fields stored in a u8
4521         (struct_ == "tcp_info" && field == "tcpi_snd_rcv_wscale") ||
4522         // the `tcpi_delivery_rate_app_limited` field is a bitfield on musl
4523         (musl && struct_ == "tcp_info" && field == "tcpi_delivery_rate_app_limited") ||
4524         // the `tcpi_fast_open_client_fail` field is a bitfield on musl
4525         (musl && struct_ == "tcp_info" && field == "tcpi_fast_open_client_fail") ||
4526         // either fsid_t or int[2] type
4527         (struct_ == "fanotify_event_info_fid" && field == "fsid") ||
4528         // `handle` is a VLA
4529         (struct_ == "fanotify_event_info_fid" && field == "handle")
4530     });
4531 
4532     cfg.skip_roundtrip(move |s| match s {
4533         // FIXME:
4534         "mcontext_t" if s390x => true,
4535         // FIXME: This is actually a union.
4536         "fpreg_t" if s390x => true,
4537 
4538         // The test doesn't work on some env:
4539         "ipv6_mreq"
4540         | "ip_mreq_source"
4541         | "sockaddr_in6"
4542         | "sockaddr_ll"
4543         | "in_pktinfo"
4544         | "arpreq"
4545         | "arpreq_old"
4546         | "sockaddr_un"
4547         | "ff_constant_effect"
4548         | "ff_ramp_effect"
4549         | "ff_condition_effect"
4550         | "Elf32_Ehdr"
4551         | "Elf32_Chdr"
4552         | "ucred"
4553         | "in6_pktinfo"
4554         | "sockaddr_nl"
4555         | "termios"
4556         | "nlmsgerr"
4557             if sparc64 && gnu =>
4558         {
4559             true
4560         }
4561 
4562         // The `inotify_event` and `cmsghdr` types contain Flexible Array Member fields (the
4563         // `name` and `data` fields respectively) which have unspecified calling convention.
4564         // The roundtripping tests deliberately pass the structs by value to check "by value"
4565         // layout consistency, but this would be UB for the these types.
4566         "inotify_event" => true,
4567         "cmsghdr" => true,
4568 
4569         // FIXME: the call ABI of max_align_t is incorrect on these platforms:
4570         "max_align_t" if i686 || ppc64 => true,
4571 
4572         _ => false,
4573     });
4574 
4575     cfg.generate("../src/lib.rs", "main.rs");
4576 
4577     test_linux_like_apis(target);
4578 }
4579 
4580 // This function tests APIs that are incompatible to test when other APIs
4581 // are included (e.g. because including both sets of headers clashes)
4582 fn test_linux_like_apis(target: &str) {
4583     let gnu = target.contains("gnu");
4584     let musl = target.contains("musl") || target.contains("ohos");
4585     let linux = target.contains("linux");
4586     let emscripten = target.contains("emscripten");
4587     let android = target.contains("android");
4588     assert!(linux || android || emscripten);
4589 
4590     if linux || android || emscripten {
4591         // test strerror_r from the `string.h` header
4592         let mut cfg = ctest_cfg();
4593         cfg.skip_type(|_| true).skip_static(|_| true);
4594 
4595         headers! { cfg: "string.h" }
4596         cfg.skip_fn(|f| match f {
4597             "strerror_r" => false,
4598             _ => true,
4599         })
4600         .skip_const(|_| true)
4601         .skip_struct(|_| true);
4602         cfg.generate("../src/lib.rs", "linux_strerror_r.rs");
4603     }
4604 
4605     if linux || android || emscripten {
4606         // test fcntl - see:
4607         // http://man7.org/linux/man-pages/man2/fcntl.2.html
4608         let mut cfg = ctest_cfg();
4609 
4610         if musl {
4611             cfg.header("fcntl.h");
4612         } else {
4613             cfg.header("linux/fcntl.h");
4614         }
4615 
4616         cfg.skip_type(|_| true)
4617             .skip_static(|_| true)
4618             .skip_struct(|_| true)
4619             .skip_fn(|_| true)
4620             .skip_const(move |name| match name {
4621                 // test fcntl constants:
4622                 "F_CANCELLK" | "F_ADD_SEALS" | "F_GET_SEALS" | "F_SEAL_SEAL" | "F_SEAL_SHRINK"
4623                 | "F_SEAL_GROW" | "F_SEAL_WRITE" => false,
4624                 _ => true,
4625             })
4626             .type_name(move |ty, is_struct, is_union| match ty {
4627                 t if is_struct => format!("struct {}", t),
4628                 t if is_union => format!("union {}", t),
4629                 t => t.to_string(),
4630             });
4631 
4632         cfg.generate("../src/lib.rs", "linux_fcntl.rs");
4633     }
4634 
4635     if linux || android {
4636         // test termios
4637         let mut cfg = ctest_cfg();
4638         cfg.header("asm/termbits.h");
4639         cfg.header("linux/termios.h");
4640         cfg.skip_type(|_| true)
4641             .skip_static(|_| true)
4642             .skip_fn(|_| true)
4643             .skip_const(|c| match c {
4644                 "BOTHER" | "IBSHIFT" => false,
4645                 "TCGETS2" | "TCSETS2" | "TCSETSW2" | "TCSETSF2" => false,
4646                 _ => true,
4647             })
4648             .skip_struct(|s| s != "termios2")
4649             .type_name(move |ty, is_struct, is_union| match ty {
4650                 "Ioctl" if gnu => "unsigned long".to_string(),
4651                 "Ioctl" => "int".to_string(),
4652                 t if is_struct => format!("struct {}", t),
4653                 t if is_union => format!("union {}", t),
4654                 t => t.to_string(),
4655             });
4656         cfg.generate("../src/lib.rs", "linux_termios.rs");
4657     }
4658 
4659     if linux || android {
4660         // test IPV6_ constants:
4661         let mut cfg = ctest_cfg();
4662         headers! {
4663             cfg:
4664             "linux/in6.h"
4665         }
4666         cfg.skip_type(|_| true)
4667             .skip_static(|_| true)
4668             .skip_fn(|_| true)
4669             .skip_const(|_| true)
4670             .skip_struct(|_| true)
4671             .skip_const(move |name| match name {
4672                 "IPV6_FLOWINFO"
4673                 | "IPV6_FLOWLABEL_MGR"
4674                 | "IPV6_FLOWINFO_SEND"
4675                 | "IPV6_FLOWINFO_FLOWLABEL"
4676                 | "IPV6_FLOWINFO_PRIORITY" => false,
4677                 _ => true,
4678             })
4679             .type_name(move |ty, is_struct, is_union| match ty {
4680                 t if is_struct => format!("struct {}", t),
4681                 t if is_union => format!("union {}", t),
4682                 t => t.to_string(),
4683             });
4684         cfg.generate("../src/lib.rs", "linux_ipv6.rs");
4685     }
4686 
4687     if linux || android {
4688         // Test Elf64_Phdr and Elf32_Phdr
4689         // These types have a field called `p_type`, but including
4690         // "resolve.h" defines a `p_type` macro that expands to `__p_type`
4691         // making the tests for these fails when both are included.
4692         let mut cfg = ctest_cfg();
4693         cfg.header("elf.h");
4694         cfg.skip_fn(|_| true)
4695             .skip_static(|_| true)
4696             .skip_const(|_| true)
4697             .type_name(move |ty, _is_struct, _is_union| ty.to_string())
4698             .skip_struct(move |ty| match ty {
4699                 "Elf64_Phdr" | "Elf32_Phdr" => false,
4700                 _ => true,
4701             })
4702             .skip_type(move |ty| match ty {
4703                 "Elf64_Phdr" | "Elf32_Phdr" => false,
4704                 _ => true,
4705             });
4706         cfg.generate("../src/lib.rs", "linux_elf.rs");
4707     }
4708 
4709     if linux || android {
4710         // Test `ARPHRD_CAN`.
4711         let mut cfg = ctest_cfg();
4712         cfg.header("linux/if_arp.h");
4713         cfg.skip_fn(|_| true)
4714             .skip_static(|_| true)
4715             .skip_const(move |name| match name {
4716                 "ARPHRD_CAN" => false,
4717                 _ => true,
4718             })
4719             .skip_struct(|_| true)
4720             .skip_type(|_| true);
4721         cfg.generate("../src/lib.rs", "linux_if_arp.rs");
4722     }
4723 }
4724 
4725 fn which_freebsd() -> Option<i32> {
4726     let output = std::process::Command::new("freebsd-version")
4727         .output()
4728         .ok()?;
4729     if !output.status.success() {
4730         return None;
4731     }
4732 
4733     let stdout = String::from_utf8(output.stdout).ok()?;
4734 
4735     match &stdout {
4736         s if s.starts_with("10") => Some(10),
4737         s if s.starts_with("11") => Some(11),
4738         s if s.starts_with("12") => Some(12),
4739         s if s.starts_with("13") => Some(13),
4740         s if s.starts_with("14") => Some(14),
4741         s if s.starts_with("15") => Some(15),
4742         _ => None,
4743     }
4744 }
4745 
4746 fn test_haiku(target: &str) {
4747     assert!(target.contains("haiku"));
4748 
4749     let mut cfg = ctest_cfg();
4750     cfg.flag("-Wno-deprecated-declarations");
4751     cfg.define("__USE_GNU", Some("1"));
4752     cfg.define("_GNU_SOURCE", None);
4753     cfg.language(ctest::Lang::CXX);
4754 
4755     // POSIX API
4756     headers! { cfg:
4757                "alloca.h",
4758                "arpa/inet.h",
4759                "arpa/nameser.h",
4760                "arpa/nameser_compat.h",
4761                "assert.h",
4762                "complex.h",
4763                "ctype.h",
4764                "dirent.h",
4765                "div_t.h",
4766                "dlfcn.h",
4767                "endian.h",
4768                "errno.h",
4769                "fcntl.h",
4770                "fenv.h",
4771                "fnmatch.h",
4772                "fts.h",
4773                "ftw.h",
4774                "getopt.h",
4775                "glob.h",
4776                "grp.h",
4777                "inttypes.h",
4778                "iovec.h",
4779                "langinfo.h",
4780                "libgen.h",
4781                "libio.h",
4782                "limits.h",
4783                "locale.h",
4784                "malloc.h",
4785                "malloc_debug.h",
4786                "math.h",
4787                "memory.h",
4788                "monetary.h",
4789                "net/if.h",
4790                "net/if_dl.h",
4791                "net/if_media.h",
4792                "net/if_tun.h",
4793                "net/if_types.h",
4794                "net/route.h",
4795                "netdb.h",
4796                "netinet/in.h",
4797                "netinet/ip.h",
4798                "netinet/ip6.h",
4799                "netinet/ip_icmp.h",
4800                "netinet/ip_var.h",
4801                "netinet/tcp.h",
4802                "netinet/udp.h",
4803                "netinet6/in6.h",
4804                "nl_types.h",
4805                "null.h",
4806                "poll.h",
4807                "pthread.h",
4808                "pwd.h",
4809                "regex.h",
4810                "resolv.h",
4811                "sched.h",
4812                "search.h",
4813                "semaphore.h",
4814                "setjmp.h",
4815                "shadow.h",
4816                "signal.h",
4817                "size_t.h",
4818                "spawn.h",
4819                "stdint.h",
4820                "stdio.h",
4821                "stdlib.h",
4822                "string.h",
4823                "strings.h",
4824                "sys/cdefs.h",
4825                "sys/file.h",
4826                "sys/ioctl.h",
4827                "sys/ipc.h",
4828                "sys/mman.h",
4829                "sys/msg.h",
4830                "sys/param.h",
4831                "sys/poll.h",
4832                "sys/resource.h",
4833                "sys/select.h",
4834                "sys/sem.h",
4835                "sys/socket.h",
4836                "sys/sockio.h",
4837                "sys/stat.h",
4838                "sys/statvfs.h",
4839                "sys/time.h",
4840                "sys/timeb.h",
4841                "sys/times.h",
4842                "sys/types.h",
4843                "sys/uio.h",
4844                "sys/un.h",
4845                "sys/utsname.h",
4846                "sys/wait.h",
4847                "syslog.h",
4848                "tar.h",
4849                "termios.h",
4850                "time.h",
4851                "uchar.h",
4852                "unistd.h",
4853                "utime.h",
4854                "utmpx.h",
4855                "wchar.h",
4856                "wchar_t.h",
4857                "wctype.h"
4858     }
4859 
4860     // BSD Extensions
4861     headers! { cfg:
4862                "ifaddrs.h",
4863                "libutil.h",
4864                "link.h",
4865                "pty.h",
4866                "stdlib.h",
4867                "stringlist.h",
4868                "sys/link_elf.h",
4869     }
4870 
4871     // Native API
4872     headers! { cfg:
4873                "kernel/OS.h",
4874                "kernel/fs_attr.h",
4875                "kernel/fs_index.h",
4876                "kernel/fs_info.h",
4877                "kernel/fs_query.h",
4878                "kernel/fs_volume.h",
4879                "kernel/image.h",
4880                "kernel/scheduler.h",
4881                "storage/FindDirectory.h",
4882                "storage/StorageDefs.h",
4883                "support/Errors.h",
4884                "support/SupportDefs.h",
4885                "support/TypeConstants.h"
4886     }
4887 
4888     cfg.skip_struct(move |ty| {
4889         if ty.starts_with("__c_anonymous_") {
4890             return true;
4891         }
4892         match ty {
4893             // FIXME: actually a union
4894             "sigval" => true,
4895             // FIXME: locale_t does not exist on Haiku
4896             "locale_t" => true,
4897             // FIXME: rusage has a different layout on Haiku
4898             "rusage" => true,
4899             // FIXME?: complains that rust aligns on 4 byte boundary, but
4900             //         Haiku does not align it at all.
4901             "in6_addr" => true,
4902             // The d_name attribute is an array of 1 on Haiku, with the
4903             // intention that the developer allocates a larger or smaller
4904             // piece of memory depending on the expected/actual size of the name.
4905             // Other platforms have sensible defaults. In Rust, the d_name field
4906             // is sized as the _POSIX_MAX_PATH, so that path names will fit in
4907             // newly allocated dirent objects. This breaks the automated tests.
4908             "dirent" => true,
4909             // The following structs contain function pointers, which cannot be initialized
4910             // with mem::zeroed(), so skip the automated test
4911             "image_info" | "thread_info" => true,
4912 
4913             "Elf64_Phdr" => true,
4914 
4915             // is an union
4916             "cpuid_info" => true,
4917 
4918             _ => false,
4919         }
4920     });
4921 
4922     cfg.skip_type(move |ty| {
4923         match ty {
4924             // FIXME: locale_t does not exist on Haiku
4925             "locale_t" => true,
4926             // These cause errors, to be reviewed in the future
4927             "sighandler_t" => true,
4928             "pthread_t" => true,
4929             "pthread_condattr_t" => true,
4930             "pthread_mutexattr_t" => true,
4931             "pthread_rwlockattr_t" => true,
4932             _ => false,
4933         }
4934     });
4935 
4936     cfg.skip_fn(move |name| {
4937         // skip those that are manually verified
4938         match name {
4939             // FIXME: https://github.com/rust-lang/libc/issues/1272
4940             "execv" | "execve" | "execvp" | "execvpe" => true,
4941             // FIXME: does not exist on haiku
4942             "open_wmemstream" => true,
4943             "mlockall" | "munlockall" => true,
4944             "tcgetsid" => true,
4945             "cfsetspeed" => true,
4946             // ignore for now, will be part of Haiku R1 beta 3
4947             "mlock" | "munlock" => true,
4948             // returns const char * on Haiku
4949             "strsignal" => true,
4950             // uses an enum as a parameter argument, which is incorrectly
4951             // translated into a struct argument
4952             "find_path" => true,
4953 
4954             "get_cpuid" => true,
4955 
4956             // uses varargs parameter
4957             "ioctl" => true,
4958 
4959             _ => false,
4960         }
4961     });
4962 
4963     cfg.skip_const(move |name| {
4964         match name {
4965             // FIXME: these constants do not exist on Haiku
4966             "DT_UNKNOWN" | "DT_FIFO" | "DT_CHR" | "DT_DIR" | "DT_BLK" | "DT_REG" | "DT_LNK"
4967             | "DT_SOCK" => true,
4968             "USRQUOTA" | "GRPQUOTA" => true,
4969             "SIGIOT" => true,
4970             "ARPOP_REQUEST" | "ARPOP_REPLY" | "ATF_COM" | "ATF_PERM" | "ATF_PUBL"
4971             | "ATF_USETRAILERS" => true,
4972             // Haiku does not have MAP_FILE, but rustc requires it
4973             "MAP_FILE" => true,
4974             // The following does not exist on Haiku but is required by
4975             // several crates
4976             "FIOCLEX" => true,
4977             // just skip this one, it is not defined on Haiku beta 2 but
4978             // since it is meant as a mask and not a parameter it can exist
4979             // here
4980             "LOG_PRIMASK" => true,
4981             // not defined on Haiku, but [get|set]priority is, so they are
4982             // useful
4983             "PRIO_MIN" | "PRIO_MAX" => true,
4984             //
4985             _ => false,
4986         }
4987     });
4988 
4989     cfg.skip_field(move |struct_, field| {
4990         match (struct_, field) {
4991             // FIXME: the stat struct actually has timespec members, whereas
4992             //        the current representation has these unpacked.
4993             ("stat", "st_atime") => true,
4994             ("stat", "st_atime_nsec") => true,
4995             ("stat", "st_mtime") => true,
4996             ("stat", "st_mtime_nsec") => true,
4997             ("stat", "st_ctime") => true,
4998             ("stat", "st_ctime_nsec") => true,
4999             ("stat", "st_crtime") => true,
5000             ("stat", "st_crtime_nsec") => true,
5001 
5002             // these are actually unions, but we cannot represent it well
5003             ("siginfo_t", "sigval") => true,
5004             ("sem_t", "named_sem_id") => true,
5005             ("sigaction", "sa_sigaction") => true,
5006             ("sigevent", "sigev_value") => true,
5007             ("fpu_state", "_fpreg") => true,
5008             ("cpu_topology_node_info", "data") => true,
5009             // these fields have a simplified data definition in libc
5010             ("fpu_state", "_xmm") => true,
5011             ("savefpu", "_fp_ymm") => true,
5012 
5013             // skip these enum-type fields
5014             ("thread_info", "state") => true,
5015             ("image_info", "image_type") => true,
5016             _ => false,
5017         }
5018     });
5019 
5020     cfg.skip_roundtrip(move |s| match s {
5021         // FIXME: for some reason the roundtrip check fails for cpu_info
5022         "cpu_info" => true,
5023         _ => false,
5024     });
5025 
5026     cfg.type_name(move |ty, is_struct, is_union| {
5027         match ty {
5028             // Just pass all these through, no need for a "struct" prefix
5029             "area_info"
5030             | "port_info"
5031             | "port_message_info"
5032             | "team_info"
5033             | "sem_info"
5034             | "team_usage_info"
5035             | "thread_info"
5036             | "cpu_info"
5037             | "system_info"
5038             | "object_wait_info"
5039             | "image_info"
5040             | "attr_info"
5041             | "index_info"
5042             | "fs_info"
5043             | "FILE"
5044             | "DIR"
5045             | "Dl_info"
5046             | "topology_level_type"
5047             | "cpu_topology_node_info"
5048             | "cpu_topology_root_info"
5049             | "cpu_topology_package_info"
5050             | "cpu_topology_core_info" => ty.to_string(),
5051 
5052             // enums don't need a prefix
5053             "directory_which" | "path_base_directory" | "cpu_platform" | "cpu_vendor" => {
5054                 ty.to_string()
5055             }
5056 
5057             // is actually a union
5058             "sigval" => format!("union sigval"),
5059             t if is_union => format!("union {}", t),
5060             t if t.ends_with("_t") => t.to_string(),
5061             t if is_struct => format!("struct {}", t),
5062             t => t.to_string(),
5063         }
5064     });
5065 
5066     cfg.field_name(move |struct_, field| {
5067         match field {
5068             // Field is named `type` in C but that is a Rust keyword,
5069             // so these fields are translated to `type_` in the bindings.
5070             "type_" if struct_ == "object_wait_info" => "type".to_string(),
5071             "type_" if struct_ == "sem_t" => "type".to_string(),
5072             "type_" if struct_ == "attr_info" => "type".to_string(),
5073             "type_" if struct_ == "index_info" => "type".to_string(),
5074             "type_" if struct_ == "cpu_topology_node_info" => "type".to_string(),
5075             "image_type" if struct_ == "image_info" => "type".to_string(),
5076             s => s.to_string(),
5077         }
5078     });
5079     cfg.generate("../src/lib.rs", "main.rs");
5080 }
5081