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