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