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