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