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