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