Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for QNX/Neutrino 7.1 #3038

Merged
merged 1 commit into from
Dec 20, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
252 changes: 252 additions & 0 deletions libc-test/build.rs
Original file line number Diff line number Diff line change
@@ -61,6 +61,7 @@ fn do_ctest() {
t if t.contains("wasi") => return test_wasi(t),
t if t.contains("windows") => return test_windows(t),
t if t.contains("vxworks") => return test_vxworks(t),
t if t.contains("nto-qnx") => return test_neutrino(t),
t => panic!("unknown target {}", t),
}
}
@@ -2654,6 +2655,257 @@ fn test_emscripten(target: &str) {
cfg.generate("../src/lib.rs", "main.rs");
}

fn test_neutrino(target: &str) {
assert!(target.contains("nto-qnx"));

let mut cfg = ctest_cfg();

headers! { cfg:
"ctype.h",
"dirent.h",
"dlfcn.h",
"sys/elf.h",
"fcntl.h",
"glob.h",
"grp.h",
"iconv.h",
"ifaddrs.h",
"limits.h",
"sys/link.h",
"locale.h",
"sys/malloc.h",
"rcheck/malloc.h",
"malloc.h",
"mqueue.h",
"net/if.h",
"net/if_arp.h",
"net/route.h",
"netdb.h",
"netinet/in.h",
"netinet/ip.h",
"netinet/tcp.h",
"netinet/udp.h",
"netinet/ip_var.h",
"sys/poll.h",
"pthread.h",
"pwd.h",
"regex.h",
"resolv.h",
"sys/sched.h",
"sched.h",
"semaphore.h",
"shadow.h",
"signal.h",
"spawn.h",
"stddef.h",
"stdint.h",
"stdio.h",
"stdlib.h",
"string.h",
"sys/sysctl.h",
"sys/file.h",
"sys/inotify.h",
"sys/ioctl.h",
"sys/ipc.h",
"sys/mman.h",
"sys/mount.h",
"sys/msg.h",
"sys/resource.h",
"sys/sem.h",
"sys/socket.h",
"sys/stat.h",
"sys/statvfs.h",
"sys/swap.h",
"sys/termio.h",
"sys/time.h",
"sys/times.h",
"sys/types.h",
"sys/uio.h",
"sys/un.h",
"sys/utsname.h",
"sys/wait.h",
"syslog.h",
"termios.h",
"time.h",
"sys/time.h",
"ucontext.h",
"unistd.h",
"utime.h",
"utmp.h",
"wchar.h",
"aio.h",
"nl_types.h",
"langinfo.h",
"unix.h",
"nbutil.h",
"aio.h",
"net/bpf.h",
"net/if_dl.h",
"sys/syspage.h",

// TODO: The following header file doesn't appear as part of the default headers
// found in a standard installation of Neutrino 7.1 SDP. The structures/
// functions dependent on it are currently commented out.
//"sys/asyncmsg.h",
}

// Create and include a header file containing
// items which are not included in any official
// header file.
let internal_header = "internal.h";
let out_dir = env::var("OUT_DIR").unwrap();
cfg.header(internal_header);
cfg.include(&out_dir);
std::fs::write(
out_dir.to_owned() + "/" + internal_header,
"#ifndef __internal_h__
#define __internal_h__
void __my_thread_exit(const void **);
#endif",
)
.unwrap();

cfg.type_name(move |ty, is_struct, is_union| {
match ty {
// Just pass all these through, no need for a "struct" prefix
"FILE" | "fd_set" | "Dl_info" | "DIR" | "Elf32_Phdr" | "Elf64_Phdr" | "Elf32_Shdr"
| "Elf64_Shdr" | "Elf32_Sym" | "Elf64_Sym" | "Elf32_Ehdr" | "Elf64_Ehdr"
| "Elf32_Chdr" | "Elf64_Chdr" | "aarch64_qreg_t" | "syspage_entry_info"
| "syspage_array_info" => ty.to_string(),

"Ioctl" => "int".to_string(),

t if is_union => format!("union {}", t),

t if t.ends_with("_t") => t.to_string(),

// put `struct` in front of all structs:.
t if is_struct => format!("struct {}", t),

t => t.to_string(),
}
});

cfg.field_name(move |_struct_, field| match field {
"type_" => "type".to_string(),

s => s.to_string(),
});

cfg.volatile_item(|i| {
use ctest::VolatileItemKind::*;
match i {
// The following fields are volatie but since we cannot express that in
// Rust types, we have to explicitly tell the checker about it here:
StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => true,
StructField(ref n, ref f) if n == "qtime_entry" && f == "nsec_tod_adjust" => true,
StructField(ref n, ref f) if n == "qtime_entry" && f == "nsec" => true,
StructField(ref n, ref f) if n == "qtime_entry" && f == "nsec_stable" => true,
StructField(ref n, ref f) if n == "intrspin" && f == "value" => true,
_ => false,
}
});

cfg.skip_type(move |ty| {
match ty {
// FIXME: `sighandler_t` type is incorrect, see:
// https://github.com/rust-lang/libc/issues/1359
"sighandler_t" => true,

// Does not exist in Neutrino
"locale_t" => true,

_ => false,
}
});

cfg.skip_struct(move |ty| {
if ty.starts_with("__c_anonymous_") {
return true;
}
match ty {
"Elf64_Phdr" | "Elf32_Phdr" => true,

// FIXME: This is actually a union, not a struct
"sigval" => true,

// union
"_channel_connect_attr" => true,

_ => false,
}
});

cfg.skip_const(move |name| {
match name {
// These signal "functions" are actually integer values that are casted to a fn ptr
// This causes the compiler to err because of "illegal cast of int to ptr".
"SIG_DFL" => true,
"SIG_IGN" => true,
"SIG_ERR" => true,

_ => false,
}
});

cfg.skip_fn(move |name| {
// skip those that are manually verified
match name {
// FIXME: https://github.com/rust-lang/libc/issues/1272
"execv" | "execve" | "execvp" | "execvpe" => true,

// wrong signature
"signal" => true,

// wrong signature of callback ptr
"__cxa_atexit" => true,

// FIXME: Our API is unsound. The Rust API allows aliasing
// pointers, but the C API requires pointers not to alias.
// We should probably be at least using `&`/`&mut` here, see:
// https://github.com/gnzlbg/ctest/issues/68
"lio_listio" => true,

// 2 fields are actually unions which we're simply representing
// as structures.
"ChannelConnectAttr" => true,

// fields contains unions
"SignalKillSigval" => true,
"SignalKillSigval_r" => true,

// Not defined in any headers. Defined to work around a
// stack unwinding bug.
"__my_thread_exit" => true,

_ => false,
}
});

cfg.skip_field_type(move |struct_, field| {
// sigval is actually a union, but we pretend it's a struct
struct_ == "sigevent" && field == "sigev_value" ||
// Anonymous structures
struct_ == "_idle_hook" && field == "time"
});

cfg.skip_field(move |struct_, field| {
(struct_ == "__sched_param" && field == "reserved") ||
(struct_ == "sched_param" && field == "reserved") ||
(struct_ == "sigevent" && field == "__sigev_un1") || // union
(struct_ == "sigevent" && field == "__sigev_un2") || // union
// sighandler_t type is super weird
(struct_ == "sigaction" && field == "sa_sigaction") ||
// does not exist
(struct_ == "syspage_entry" && field == "__reserved") ||
false // keep me for smaller diffs when something is added above
});

cfg.skip_static(move |name| (name == "__dso_handle"));

cfg.generate("../src/lib.rs", "main.rs");
}

fn test_vxworks(target: &str) {
assert!(target.contains("vxworks"));

146 changes: 97 additions & 49 deletions src/unix/mod.rs
Original file line number Diff line number Diff line change
@@ -32,6 +32,9 @@ cfg_if! {
if #[cfg(any(target_os = "espidf", target_os = "horizon"))] {
pub type uid_t = ::c_ushort;
pub type gid_t = ::c_ushort;
} else if #[cfg(target_os = "nto")] {
pub type uid_t = i32;
pub type gid_t = i32;
} else {
pub type uid_t = u32;
pub type gid_t = u32;
@@ -209,25 +212,31 @@ pub const INT_MAX: c_int = 2147483647;
pub const SIG_DFL: sighandler_t = 0 as sighandler_t;
pub const SIG_IGN: sighandler_t = 1 as sighandler_t;
pub const SIG_ERR: sighandler_t = !0 as sighandler_t;

pub const DT_UNKNOWN: u8 = 0;
pub const DT_FIFO: u8 = 1;
pub const DT_CHR: u8 = 2;
pub const DT_DIR: u8 = 4;
pub const DT_BLK: u8 = 6;
pub const DT_REG: u8 = 8;
pub const DT_LNK: u8 = 10;
pub const DT_SOCK: u8 = 12;

cfg_if! {
if #[cfg(not(target_os = "nto"))] {
pub const DT_UNKNOWN: u8 = 0;
pub const DT_FIFO: u8 = 1;
pub const DT_CHR: u8 = 2;
pub const DT_DIR: u8 = 4;
pub const DT_BLK: u8 = 6;
pub const DT_REG: u8 = 8;
pub const DT_LNK: u8 = 10;
pub const DT_SOCK: u8 = 12;
}
}
cfg_if! {
if #[cfg(not(target_os = "redox"))] {
pub const FD_CLOEXEC: ::c_int = 0x1;
}
}

pub const USRQUOTA: ::c_int = 0;
pub const GRPQUOTA: ::c_int = 1;

cfg_if! {
if #[cfg(not(target_os = "nto"))]
{
pub const USRQUOTA: ::c_int = 0;
pub const GRPQUOTA: ::c_int = 1;
}
}
pub const SIGIOT: ::c_int = 6;

pub const S_ISUID: ::mode_t = 0x800;
@@ -281,9 +290,13 @@ cfg_if! {
pub const LOG_PRIMASK: ::c_int = 7;
pub const LOG_FACMASK: ::c_int = 0x3f8;

pub const PRIO_MIN: ::c_int = -20;
pub const PRIO_MAX: ::c_int = 20;

cfg_if! {
if #[cfg(not(target_os = "nto"))]
{
pub const PRIO_MIN: ::c_int = -20;
pub const PRIO_MAX: ::c_int = 20;
}
}
pub const IPPROTO_ICMP: ::c_int = 1;
pub const IPPROTO_ICMPV6: ::c_int = 58;
pub const IPPROTO_TCP: ::c_int = 6;
@@ -361,7 +374,9 @@ cfg_if! {
target_os = "tvos",
target_os = "watchos",
target_os = "android",
target_os = "openbsd"))] {
target_os = "openbsd",
target_os = "nto",
))] {
#[link(name = "c")]
#[link(name = "m")]
extern {}
@@ -453,8 +468,6 @@ extern "C" {
link_name = "freopen$UNIX2003"
)]
pub fn freopen(filename: *const c_char, mode: *const c_char, file: *mut FILE) -> *mut FILE;
pub fn fmemopen(buf: *mut c_void, size: size_t, mode: *const c_char) -> *mut FILE;
pub fn open_memstream(ptr: *mut *mut c_char, sizeloc: *mut size_t) -> *mut FILE;

pub fn fflush(file: *mut FILE) -> c_int;
pub fn fclose(file: *mut FILE) -> c_int;
@@ -508,7 +521,6 @@ extern "C" {
pub fn abort() -> !;
pub fn exit(status: c_int) -> !;
pub fn _exit(status: c_int) -> !;
pub fn atexit(cb: extern "C" fn()) -> c_int;
#[cfg_attr(
all(target_os = "macos", target_arch = "x86"),
link_name = "system$UNIX2003"
@@ -1162,8 +1174,6 @@ extern "C" {
optlen: *mut ::socklen_t,
) -> ::c_int;
pub fn raise(signum: ::c_int) -> ::c_int;
#[cfg_attr(target_os = "netbsd", link_name = "__sigaction14")]
pub fn sigaction(signum: ::c_int, act: *const sigaction, oldact: *mut sigaction) -> ::c_int;

#[cfg_attr(target_os = "netbsd", link_name = "__utimes50")]
pub fn utimes(filename: *const ::c_char, times: *const ::timeval) -> ::c_int;
@@ -1325,8 +1335,6 @@ extern "C" {
pub fn statvfs(path: *const c_char, buf: *mut statvfs) -> ::c_int;
pub fn fstatvfs(fd: ::c_int, buf: *mut statvfs) -> ::c_int;

pub fn readlink(path: *const c_char, buf: *mut c_char, bufsz: ::size_t) -> ::ssize_t;

#[cfg_attr(target_os = "netbsd", link_name = "__sigemptyset14")]
pub fn sigemptyset(set: *mut sigset_t) -> ::c_int;
#[cfg_attr(target_os = "netbsd", link_name = "__sigaddset14")]
@@ -1347,23 +1355,6 @@ extern "C" {

pub fn mkfifo(path: *const c_char, mode: mode_t) -> ::c_int;

#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64"),
link_name = "pselect$1050"
)]
#[cfg_attr(
all(target_os = "macos", target_arch = "x86"),
link_name = "pselect$UNIX2003"
)]
#[cfg_attr(target_os = "netbsd", link_name = "__pselect50")]
pub fn pselect(
nfds: ::c_int,
readfds: *mut fd_set,
writefds: *mut fd_set,
errorfds: *mut fd_set,
timeout: *const timespec,
sigmask: *const sigset_t,
) -> ::c_int;
pub fn fseeko(stream: *mut ::FILE, offset: ::off_t, whence: ::c_int) -> ::c_int;
pub fn ftello(stream: *mut ::FILE) -> ::off_t;
#[cfg_attr(
@@ -1411,7 +1402,8 @@ extern "C" {
cfg_if! {
if #[cfg(not(any(target_os = "emscripten",
target_os = "android",
target_os = "haiku")))] {
target_os = "haiku",
target_os = "nto")))] {
extern "C" {
pub fn adjtime(delta: *const timeval, olddelta: *mut timeval) -> ::c_int;
pub fn stpncpy(dst: *mut c_char, src: *const c_char, n: size_t) -> *mut c_char;
@@ -1420,7 +1412,7 @@ cfg_if! {
}

cfg_if! {
if #[cfg(not(target_env = "uclibc"))] {
if #[cfg(not(any(target_env = "uclibc", target_os = "nto")))] {
extern "C" {
pub fn open_wmemstream(
ptr: *mut *mut wchar_t,
@@ -1439,12 +1431,8 @@ cfg_if! {
link_name = "pause$UNIX2003")]
pub fn pause() -> ::c_int;

pub fn readlinkat(dirfd: ::c_int,
pathname: *const ::c_char,
buf: *mut ::c_char,
bufsiz: ::size_t) -> ::ssize_t;
pub fn mkdirat(dirfd: ::c_int, pathname: *const ::c_char,
mode: ::mode_t) -> ::c_int;
mode: ::mode_t) -> ::c_int;
pub fn openat(dirfd: ::c_int, pathname: *const ::c_char,
flags: ::c_int, ...) -> ::c_int;

@@ -1475,7 +1463,64 @@ cfg_if! {
}

cfg_if! {
if #[cfg(not(any(target_os = "solaris", target_os = "illumos")))] {
if #[cfg(target_os = "nto")] {
extern {
pub fn readlinkat(dirfd: ::c_int,
pathname: *const ::c_char,
buf: *mut ::c_char,
bufsiz: ::size_t) -> ::c_int;
pub fn readlink(path: *const c_char, buf: *mut c_char, bufsz: ::size_t) -> ::c_int;
pub fn pselect(
nfds: ::c_int,
readfds: *mut fd_set,
writefds: *mut fd_set,
errorfds: *mut fd_set,
timeout: *mut timespec,
sigmask: *const sigset_t,
) -> ::c_int;
}
} else {
extern {
pub fn readlinkat(dirfd: ::c_int,
pathname: *const ::c_char,
buf: *mut ::c_char,
bufsiz: ::size_t) -> ::ssize_t;
pub fn fmemopen(buf: *mut c_void, size: size_t, mode: *const c_char) -> *mut FILE;
pub fn open_memstream(ptr: *mut *mut c_char, sizeloc: *mut size_t) -> *mut FILE;
pub fn atexit(cb: extern "C" fn()) -> c_int;
#[cfg_attr(target_os = "netbsd", link_name = "__sigaction14")]
pub fn sigaction(
signum: ::c_int,
act: *const sigaction,
oldact: *mut sigaction
) -> ::c_int;
pub fn readlink(path: *const c_char, buf: *mut c_char, bufsz: ::size_t) -> ::ssize_t;
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64"),
link_name = "pselect$1050"
)]
#[cfg_attr(
all(target_os = "macos", target_arch = "x86"),
link_name = "pselect$UNIX2003"
)]
#[cfg_attr(target_os = "netbsd", link_name = "__pselect50")]
pub fn pselect(
nfds: ::c_int,
readfds: *mut fd_set,
writefds: *mut fd_set,
errorfds: *mut fd_set,
timeout: *const timespec,
sigmask: *const sigset_t,
) -> ::c_int;
}
}
}

cfg_if! {
if #[cfg(not(any(target_os = "solaris",
target_os = "illumos",
target_os = "nto",
)))] {
extern {
pub fn cfmakeraw(termios: *mut ::termios);
pub fn cfsetspeed(termios: *mut ::termios,
@@ -1517,6 +1562,9 @@ cfg_if! {
} else if #[cfg(target_os = "redox")] {
mod redox;
pub use self::redox::*;
} else if #[cfg(target_os = "nto")] {
mod nto;
pub use self::nto::*;
} else {
// Unknown target_os
}
36 changes: 36 additions & 0 deletions src/unix/nto/aarch64.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
pub type c_char = u8;
pub type wchar_t = u32;
pub type c_long = i64;
pub type c_ulong = u64;
pub type time_t = i64;

s! {
pub struct aarch64_qreg_t {
pub qlo: u64,
pub qhi: u64,
}

pub struct aarch64_fpu_registers {
pub reg: [::aarch64_qreg_t; 32],
pub fpsr: u32,
pub fpcr: u32,
}

pub struct aarch64_cpu_registers {
pub gpr: [u64; 32],
pub elr: u64,
pub pstate: u64,
}

#[repr(align(16))]
pub struct mcontext_t {
pub cpu: ::aarch64_cpu_registers,
pub fpu: ::aarch64_fpu_registers,
}

pub struct stack_t {
pub ss_sp: *mut ::c_void,
pub ss_size: ::size_t,
pub ss_flags: ::c_int,
}
}
3,286 changes: 3,286 additions & 0 deletions src/unix/nto/mod.rs

Large diffs are not rendered by default.

1,288 changes: 1,288 additions & 0 deletions src/unix/nto/neutrino.rs

Large diffs are not rendered by default.

132 changes: 132 additions & 0 deletions src/unix/nto/x86_64.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
pub type c_char = i8;
pub type wchar_t = u32;
pub type c_long = i64;
pub type c_ulong = u64;
pub type time_t = i64;

s! {
#[repr(align(8))]
pub struct x86_64_cpu_registers {
pub rdi: u64,
pub rsi: u64,
pub rdx: u64,
pub r10: u64,
pub r8: u64,
pub r9: u64,
pub rax: u64,
pub rbx: u64,
pub rbp: u64,
pub rcx: u64,
pub r11: u64,
pub r12: u64,
pub r13: u64,
pub r14: u64,
pub r15: u64,
pub rip: u64,
pub cs: u32,
rsvd1: u32,
pub rflags: u64,
pub rsp: u64,
pub ss: u32,
rsvd2: u32,
}

#[repr(align(8))]
pub struct mcontext_t {
pub cpu: x86_64_cpu_registers,
#[cfg(libc_union)]
pub fpu: x86_64_fpu_registers,
#[cfg(not(libc_union))]
__reserved: [u8; 1024],
}

pub struct stack_t {
pub ss_sp: *mut ::c_void,
pub ss_size: ::size_t,
pub ss_flags: ::c_int,
}

pub struct fsave_area_64 {
pub fpu_control_word: u32,
pub fpu_status_word: u32,
pub fpu_tag_word: u32,
pub fpu_ip: u32,
pub fpu_cs: u32,
pub fpu_op: u32,
pub fpu_ds: u32,
pub st_regs: [u8; 80],
}

pub struct fxsave_area_64 {
pub fpu_control_word: u16,
pub fpu_status_word: u16,
pub fpu_tag_word: u16,
pub fpu_operand: u16,
pub fpu_rip: u64,
pub fpu_rdp: u64,
pub mxcsr: u32,
pub mxcsr_mask: u32,
pub st_regs: [u8; 128],
pub xmm_regs: [u8; 128],
reserved2: [u8; 224],
}

pub struct fpu_extention_savearea_64 {
pub other: [u8; 512],
pub xstate_bv: u64,
pub xstate_undef: [u64; 7],
pub xstate_info: [u8; 224],
}
}

s_no_extra_traits! {
#[cfg(libc_union)]
pub union x86_64_fpu_registers {
pub fsave_area: fsave_area_64,
pub fxsave_area: fxsave_area_64,
pub xsave_area: fpu_extention_savearea_64,
pub data: [u8; 1024],
}
}

cfg_if! {
if #[cfg(feature = "extra_traits")] {
#[cfg(libc_union)]
impl Eq for x86_64_fpu_registers {}

#[cfg(libc_union)]
impl PartialEq for x86_64_fpu_registers {
fn eq(&self, other: &x86_64_fpu_registers) -> bool {
unsafe {
self.fsave_area == other.fsave_area
|| self.fxsave_area == other.fxsave_area
|| self.xsave_area == other.xsave_area
}
}
}

#[cfg(libc_union)]
impl ::fmt::Debug for x86_64_fpu_registers {
fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
unsafe {
f.debug_struct("x86_64_fpu_registers")
.field("fsave_area", &self.fsave_area)
.field("fxsave_area", &self.fxsave_area)
.field("xsave_area", &self.xsave_area)
.finish()
}
}
}

#[cfg(libc_union)]
impl ::hash::Hash for x86_64_fpu_registers {
fn hash<H: ::hash::Hasher>(&self, state: &mut H) {
unsafe {
self.fsave_area.hash(state);
self.fxsave_area.hash(state);
self.xsave_area.hash(state);
}
}
}
}
}