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