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