zig/lib/std/c/openbsd.zig
mlugg a18fd41064
std: rework/remove ucontext_t
Our usage of `ucontext_t` in the standard library was kind of
problematic. We unnecessarily mimiced libc-specific structures, and our
`getcontext` implementation was overkill for our use case of stack
tracing.

This commit introduces a new namespace, `std.debug.cpu_context`, which
contains "context" types for various architectures (currently x86,
x86_64, ARM, and AARCH64) containing the general-purpose CPU registers;
the ones needed in practice for stack unwinding. Each implementation has
a function `current` which populates the structure using inline
assembly. The structure is user-overrideable, though that should only be
necessary if the standard library does not have an implementation for
the *architecture*: that is to say, none of this is OS-dependent.

Of course, in POSIX signal handlers, we get a `ucontext_t` from the
kernel. The function `std.debug.cpu_context.fromPosixSignalContext`
converts this to a `std.debug.cpu_context.Native` with a big ol' target
switch.

This functionality is not exposed from `std.c` or `std.posix`, and
neither are `ucontext_t`, `mcontext_t`, or `getcontext`. The rationale
is that these types and functions do not conform to a specific ABI, and
in fact tend to get updated over time based on CPU features and
extensions; in addition, different libcs use different structures which
are "partially compatible" with the kernel structure. Overall, it's a
mess, but all we need is the kernel context, so we can just define a
kernel-compatible structure as long as we don't claim C compatibility by
putting it in `std.c` or `std.posix`.

This change resulted in a few nice `std.debug` simplifications, but
nothing too noteworthy. However, the main benefit of this change is that
DWARF unwinding---sometimes necessary for collecting stack traces
reliably---now requires far less target-specific integration.

Also fix a bug I noticed in `PageAllocator` (I found this due to a bug
in my distro's QEMU distribution; thanks, broken QEMU patch!) and I
think a couple of minor bugs in `std.debug`.

Resolves: #23801
Resolves: #23802
2025-09-30 13:44:54 +01:00

445 lines
17 KiB
Zig

const std = @import("../std.zig");
const assert = std.debug.assert;
const maxInt = std.math.maxInt;
const builtin = @import("builtin");
const caddr_t = std.c.caddr_t;
const iovec = std.posix.iovec;
const iovec_const = std.posix.iovec_const;
const passwd = std.c.passwd;
const timespec = std.c.timespec;
const uid_t = std.c.uid_t;
const pid_t = std.c.pid_t;
comptime {
assert(builtin.os.tag == .openbsd); // Prevent access of std.c symbols on wrong OS.
}
pub extern "c" fn ptrace(request: c_int, pid: pid_t, addr: caddr_t, data: c_int) c_int;
pub const pthread_spinlock_t = extern struct {
inner: ?*anyopaque = null,
};
pub extern "c" fn pledge(promises: ?[*:0]const u8, execpromises: ?[*:0]const u8) c_int;
pub extern "c" fn unveil(path: ?[*:0]const u8, permissions: ?[*:0]const u8) c_int;
pub extern "c" fn getthrid() pid_t;
pub const FUTEX = struct {
pub const WAIT = 1;
pub const WAKE = 2;
pub const REQUEUE = 3;
pub const PRIVATE_FLAG = 128;
};
pub extern "c" fn futex(uaddr: ?*const volatile u32, op: c_int, val: c_int, timeout: ?*const timespec, uaddr2: ?*const volatile u32) c_int;
pub const login_cap_t = extern struct {
class: ?[*:0]const u8,
cap: ?[*:0]const u8,
style: ?[*:0]const u8,
};
pub extern "c" fn login_getclass(class: ?[*:0]const u8) ?*login_cap_t;
pub extern "c" fn login_getstyle(lc: *login_cap_t, style: ?[*:0]const u8, atype: ?[*:0]const u8) ?[*:0]const u8;
pub extern "c" fn login_getcapbool(lc: *login_cap_t, cap: [*:0]const u8, def: c_int) c_int;
pub extern "c" fn login_getcapnum(lc: *login_cap_t, cap: [*:0]const u8, def: i64, err: i64) i64;
pub extern "c" fn login_getcapsize(lc: *login_cap_t, cap: [*:0]const u8, def: i64, err: i64) i64;
pub extern "c" fn login_getcapstr(lc: *login_cap_t, cap: [*:0]const u8, def: [*:0]const u8, err: [*:0]const u8) [*:0]const u8;
pub extern "c" fn login_getcaptime(lc: *login_cap_t, cap: [*:0]const u8, def: i64, err: i64) i64;
pub extern "c" fn login_close(lc: *login_cap_t) void;
pub extern "c" fn setclasscontext(class: [*:0]const u8, flags: c_uint) c_int;
pub extern "c" fn setusercontext(lc: *login_cap_t, pwd: *passwd, uid: uid_t, flags: c_uint) c_int;
pub const auth_session_t = opaque {};
pub extern "c" fn auth_userokay(name: [*:0]const u8, style: ?[*:0]const u8, arg_type: ?[*:0]const u8, password: ?[*:0]const u8) c_int;
pub extern "c" fn auth_approval(as: ?*auth_session_t, ?*login_cap_t, name: ?[*:0]const u8, type: ?[*:0]const u8) c_int;
pub extern "c" fn auth_userchallenge(name: [*:0]const u8, style: ?[*:0]const u8, arg_type: ?[*:0]const u8, chappengep: *?[*:0]const u8) ?*auth_session_t;
pub extern "c" fn auth_userresponse(as: *auth_session_t, response: [*:0]const u8, more: c_int) c_int;
pub extern "c" fn auth_usercheck(name: [*:0]const u8, style: ?[*:0]const u8, arg_type: ?[*:0]const u8, password: ?[*:0]const u8) ?*auth_session_t;
pub extern "c" fn auth_open() ?*auth_session_t;
pub extern "c" fn auth_close(as: *auth_session_t) c_int;
pub extern "c" fn auth_setdata(as: *auth_session_t, ptr: *anyopaque, len: usize) c_int;
pub extern "c" fn auth_setitem(as: *auth_session_t, item: auth_item_t, value: [*:0]const u8) c_int;
pub extern "c" fn auth_getitem(as: *auth_session_t, item: auth_item_t) ?[*:0]const u8;
pub extern "c" fn auth_setoption(as: *auth_session_t, n: [*:0]const u8, v: [*:0]const u8) c_int;
pub extern "c" fn auth_setstate(as: *auth_session_t, s: c_int) void;
pub extern "c" fn auth_getstate(as: *auth_session_t) c_int;
pub extern "c" fn auth_clean(as: *auth_session_t) void;
pub extern "c" fn auth_clrenv(as: *auth_session_t) void;
pub extern "c" fn auth_clroption(as: *auth_session_t, option: [*:0]const u8) void;
pub extern "c" fn auth_clroptions(as: *auth_session_t) void;
pub extern "c" fn auth_setenv(as: *auth_session_t) void;
pub extern "c" fn auth_getvalue(as: *auth_session_t, what: [*:0]const u8) ?[*:0]const u8;
pub extern "c" fn auth_verify(as: ?*auth_session_t, style: ?[*:0]const u8, name: ?[*:0]const u8, ...) ?*auth_session_t;
pub extern "c" fn auth_call(as: *auth_session_t, path: [*:0]const u8, ...) c_int;
pub extern "c" fn auth_challenge(as: *auth_session_t) [*:0]const u8;
pub extern "c" fn auth_check_expire(as: *auth_session_t) i64;
pub extern "c" fn auth_check_change(as: *auth_session_t) i64;
pub extern "c" fn auth_getpwd(as: *auth_session_t) ?*passwd;
pub extern "c" fn auth_setpwd(as: *auth_session_t, pwd: *passwd) c_int;
pub extern "c" fn auth_mkvalue(value: [*:0]const u8) ?[*:0]const u8;
pub extern "c" fn auth_cat(file: [*:0]const u8) c_int;
pub extern "c" fn auth_checknologin(lc: *login_cap_t) void;
// TODO: auth_set_va_list requires zig support for va_list type (#515)
pub extern "c" fn getpwuid_shadow(uid: uid_t) ?*passwd;
pub extern "c" fn getpwnam_shadow(name: [*:0]const u8) ?*passwd;
pub extern "c" fn setpassent(stayopen: c_int) c_int;
pub extern "c" fn uid_from_user(name: [*:0]const u8, uid: *uid_t) c_int;
pub extern "c" fn user_from_uid(uid: uid_t, noname: c_int) ?[*:0]const u8;
pub extern "c" fn bcrypt_gensalt(log_rounds: u8) [*:0]const u8;
pub extern "c" fn bcrypt(pass: [*:0]const u8, salt: [*:0]const u8) ?[*:0]const u8;
pub extern "c" fn bcrypt_newhash(pass: [*:0]const u8, log_rounds: c_int, hash: [*]u8, hashlen: usize) c_int;
pub extern "c" fn bcrypt_checkpass(pass: [*:0]const u8, goodhash: [*:0]const u8) c_int;
pub extern "c" fn pw_dup(pw: *const passwd) ?*passwd;
pub const auth_item_t = enum(c_int) {
ALL = 0,
CHALLENGE = 1,
CLASS = 2,
NAME = 3,
SERVICE = 4,
STYLE = 5,
INTERACTIVE = 6,
};
pub const BI = struct {
pub const AUTH = "authorize"; // Accepted authentication
pub const REJECT = "reject"; // Rejected authentication
pub const CHALLENGE = "reject challenge"; // Reject with a challenge
pub const SILENT = "reject silent"; // Reject silently
pub const REMOVE = "remove"; // remove file on error
pub const ROOTOKAY = "authorize root"; // root authenticated
pub const SECURE = "authorize secure"; // okay on non-secure line
pub const SETENV = "setenv"; // set environment variable
pub const UNSETENV = "unsetenv"; // unset environment variable
pub const VALUE = "value"; // set local variable
pub const EXPIRED = "reject expired"; // account expired
pub const PWEXPIRED = "reject pwexpired"; // password expired
pub const FDPASS = "fd"; // child is passing an fd
};
pub const AUTH = struct {
pub const OKAY: c_int = 0x01; // user authenticated
pub const ROOTOKAY: c_int = 0x02; // authenticated as root
pub const SECURE: c_int = 0x04; // secure login
pub const SILENT: c_int = 0x08; // silent rejection
pub const CHALLENGE: c_int = 0x10; // a challenge was given
pub const EXPIRED: c_int = 0x20; // account expired
pub const PWEXPIRED: c_int = 0x40; // password expired
pub const ALLOW: c_int = (OKAY | ROOTOKAY | SECURE);
};
pub const TCFLUSH = enum(u32) {
none = 0,
I = 1,
O = 2,
IO = 3,
};
pub const TCIO = enum(u32) {
OOFF = 1,
OON = 2,
IOFF = 3,
ION = 4,
};
pub const E = enum(u16) {
/// No error occurred.
SUCCESS = 0,
PERM = 1, // Operation not permitted
NOENT = 2, // No such file or directory
SRCH = 3, // No such process
INTR = 4, // Interrupted system call
IO = 5, // Input/output error
NXIO = 6, // Device not configured
@"2BIG" = 7, // Argument list too long
NOEXEC = 8, // Exec format error
BADF = 9, // Bad file descriptor
CHILD = 10, // No child processes
DEADLK = 11, // Resource deadlock avoided
// 11 was AGAIN
NOMEM = 12, // Cannot allocate memory
ACCES = 13, // Permission denied
FAULT = 14, // Bad address
NOTBLK = 15, // Block device required
BUSY = 16, // Device busy
EXIST = 17, // File exists
XDEV = 18, // Cross-device link
NODEV = 19, // Operation not supported by device
NOTDIR = 20, // Not a directory
ISDIR = 21, // Is a directory
INVAL = 22, // Invalid argument
NFILE = 23, // Too many open files in system
MFILE = 24, // Too many open files
NOTTY = 25, // Inappropriate ioctl for device
TXTBSY = 26, // Text file busy
FBIG = 27, // File too large
NOSPC = 28, // No space left on device
SPIPE = 29, // Illegal seek
ROFS = 30, // Read-only file system
MLINK = 31, // Too many links
PIPE = 32, // Broken pipe
// math software
DOM = 33, // Numerical argument out of domain
RANGE = 34, // Result too large or too small
// non-blocking and interrupt i/o
// also: WOULDBLOCK: operation would block
AGAIN = 35, // Resource temporarily unavailable
INPROGRESS = 36, // Operation now in progress
ALREADY = 37, // Operation already in progress
// ipc/network software -- argument errors
NOTSOCK = 38, // Socket operation on non-socket
DESTADDRREQ = 39, // Destination address required
MSGSIZE = 40, // Message too long
PROTOTYPE = 41, // Protocol wrong type for socket
NOPROTOOPT = 42, // Protocol option not available
PROTONOSUPPORT = 43, // Protocol not supported
SOCKTNOSUPPORT = 44, // Socket type not supported
OPNOTSUPP = 45, // Operation not supported
PFNOSUPPORT = 46, // Protocol family not supported
AFNOSUPPORT = 47, // Address family not supported by protocol family
ADDRINUSE = 48, // Address already in use
ADDRNOTAVAIL = 49, // Can't assign requested address
// ipc/network software -- operational errors
NETDOWN = 50, // Network is down
NETUNREACH = 51, // Network is unreachable
NETRESET = 52, // Network dropped connection on reset
CONNABORTED = 53, // Software caused connection abort
CONNRESET = 54, // Connection reset by peer
NOBUFS = 55, // No buffer space available
ISCONN = 56, // Socket is already connected
NOTCONN = 57, // Socket is not connected
SHUTDOWN = 58, // Can't send after socket shutdown
TOOMANYREFS = 59, // Too many references: can't splice
TIMEDOUT = 60, // Operation timed out
CONNREFUSED = 61, // Connection refused
LOOP = 62, // Too many levels of symbolic links
NAMETOOLONG = 63, // File name too long
// should be rearranged
HOSTDOWN = 64, // Host is down
HOSTUNREACH = 65, // No route to host
NOTEMPTY = 66, // Directory not empty
// quotas & mush
PROCLIM = 67, // Too many processes
USERS = 68, // Too many users
DQUOT = 69, // Disc quota exceeded
// Network File System
STALE = 70, // Stale NFS file handle
REMOTE = 71, // Too many levels of remote in path
BADRPC = 72, // RPC struct is bad
RPCMISMATCH = 73, // RPC version wrong
PROGUNAVAIL = 74, // RPC prog. not avail
PROGMISMATCH = 75, // Program version wrong
PROCUNAVAIL = 76, // Bad procedure for program
NOLCK = 77, // No locks available
NOSYS = 78, // Function not implemented
FTYPE = 79, // Inappropriate file type or format
AUTH = 80, // Authentication error
NEEDAUTH = 81, // Need authenticator
IPSEC = 82, // IPsec processing failure
NOATTR = 83, // Attribute not found
// Wide/multibyte-character handling, ISO/IEC 9899/AMD1:1995
ILSEQ = 84, // Illegal byte sequence
NOMEDIUM = 85, // No medium found
MEDIUMTYPE = 86, // Wrong medium type
OVERFLOW = 87, // Value too large to be stored in data type
CANCELED = 88, // Operation canceled
IDRM = 89, // Identifier removed
NOMSG = 90, // No message of desired type
NOTSUP = 91, // Not supported
BADMSG = 92, // Bad or Corrupt message
NOTRECOVERABLE = 93, // State not recoverable
OWNERDEAD = 94, // Previous owner died
PROTO = 95, // Protocol error
_,
};
pub const MAX_PAGE_SHIFT = switch (builtin.cpu.arch) {
.x86 => 12,
.sparc64 => 13,
};
pub const HW = struct {
pub const MACHINE = 1;
pub const MODEL = 2;
pub const NCPU = 3;
pub const BYTEORDER = 4;
pub const PHYSMEM = 5;
pub const USERMEM = 6;
pub const PAGESIZE = 7;
pub const DISKNAMES = 8;
pub const DISKSTATS = 9;
pub const DISKCOUNT = 10;
pub const SENSORS = 11;
pub const CPUSPEED = 12;
pub const SETPERF = 13;
pub const VENDOR = 14;
pub const PRODUCT = 15;
pub const VERSION = 16;
pub const SERIALNO = 17;
pub const UUID = 18;
pub const PHYSMEM64 = 19;
pub const USERMEM64 = 20;
pub const NCPUFOUND = 21;
pub const ALLOWPOWERDOWN = 22;
pub const PERFPOLICY = 23;
pub const SMT = 24;
pub const NCPUONLINE = 25;
pub const POWER = 26;
};
pub const PTHREAD_STACK_MIN = switch (builtin.cpu.arch) {
.sparc64 => 1 << 13,
.mips64 => 1 << 14,
else => 1 << 12,
};
// https://github.com/openbsd/src/blob/718a31b40d39fc6064de6355eb144e74633133fc/sys/netinet/in.h#L283
pub const IP = struct {
pub const OPTIONS = 1;
pub const HDRINCL = 2;
pub const TOS = 3;
pub const TTL = 4;
pub const RECVOPTS = 5;
pub const RECVRETOPTS = 6;
pub const RECVDSTADDR = 7;
pub const RETOPTS = 8;
pub const MULTICAST_IF = 9;
pub const MULTICAST_TTL = 10;
pub const MULTICAST_LOOP = 11;
pub const ADD_MEMBERSHIP = 12;
pub const DROP_MEMBERSHIP = 13;
pub const PORTRANGE = 19;
pub const AUTH_LEVEL = 20;
pub const ESP_TRANS_LEVEL = 21;
pub const ESP_NETWORK_LEVEL = 22;
pub const IPSEC_LOCAL_ID = 23;
pub const IPSEC_REMOTE_ID = 24;
pub const IPSEC_LOCAL_CRED = 25;
pub const IPSEC_REMOTE_CRED = 26;
pub const IPSEC_LOCAL_AUTH = 27;
pub const IPSEC_REMOTE_AUTH = 28;
pub const IPCOMP_LEVEL = 29;
pub const RECVIF = 30;
pub const RECVTTL = 31;
pub const MINTTL = 32;
pub const RECVDSTPORT = 33;
pub const PIPEX = 34;
pub const RECVRTABLE = 35;
pub const IPSECFLOWINFO = 36;
pub const IPDEFTTL = 37;
pub const SENDSRCADDR = RECVDSTADDR;
pub const RTABLE = 0x1021;
pub const DEFAULT_MULTICAST_TTL = 1;
pub const DEFAULT_MULTICAST_LOOP = 1;
pub const MIN_MEMBERSHIPS = 15;
pub const MAX_MEMBERSHIPS = 4095;
pub const PORTRANGE_DEFAULT = 0;
pub const PORTRANGE_HIGH = 1;
pub const PORTRANGE_LOW = 2;
};
// https://github.com/openbsd/src/blob/718a31b40d39fc6064de6355eb144e74633133fc/sys/netinet6/in6.h#L284
pub const IPV6 = struct {
pub const UNICAST_HOPS = 4;
pub const MULTICAST_IF = 9;
pub const MULTICAST_HOPS = 10;
pub const MULTICAST_LOOP = 11;
pub const JOIN_GROUP = 12;
pub const LEAVE_GROUP = 13;
pub const PORTRANGE = 14;
pub const CHECKSUM = 26;
pub const V6ONLY = 27;
pub const RTHDRDSTOPTS = 35;
pub const RECVPKTINFO = 36;
pub const RECVHOPLIMIT = 37;
pub const RECVRTHDR = 38;
pub const RECVHOPOPTS = 39;
pub const RECVDSTOPTS = 40;
pub const USE_MIN_MTU = 42;
pub const RECVPATHMTU = 43;
pub const PATHMTU = 44;
pub const PKTINFO = 46;
pub const HOPLIMIT = 47;
pub const NEXTHOP = 48;
pub const HOPOPTS = 49;
pub const DSTOPTS = 50;
pub const RTHDR = 51;
pub const AUTH_LEVEL = 53;
pub const ESP_TRANS_LEVEL = 54;
pub const ESP_NETWORK_LEVEL = 55;
pub const RECVTCLASS = 57;
pub const AUTOFLOWLABEL = 59;
pub const IPCOMP_LEVEL = 60;
pub const TCLASS = 61;
pub const DONTFRAG = 62;
pub const PIPEX = 63;
pub const RECVDSTPORT = 64;
pub const MINHOPCOUNT = 65;
pub const RTABLE = 0x1021;
pub const RTHDR_LOOSE = 0;
pub const RTHDR_TYPE_0 = 0;
pub const DEFAULT_MULTICAST_HOPS = 1;
pub const DEFAULT_MULTICAST_LOOP = 1;
pub const PORTRANGE_DEFAULT = 0;
pub const PORTRANGE_HIGH = 1;
pub const PORTRANGE_LOW = 2;
};
// https://github.com/openbsd/src/blob/718a31b40d39fc6064de6355eb144e74633133fc/sys/netinet/ip.h#L73
pub const IPTOS = struct {
pub const LOWDELAY = 0x10;
pub const THROUGHPUT = 0x08;
pub const RELIABILITY = 0x04;
pub const CE = 0x01;
pub const ECT = 0x02;
pub const PREC_NETCONTROL = 0xe0;
pub const PREC_INTERNETCONTROL = 0xc0;
pub const PREC_CRITIC_ECP = 0xa0;
pub const PREC_FLASHOVERRIDE = 0x80;
pub const PREC_FLASH = 0x60;
pub const PREC_IMMEDIATE = 0x40;
pub const PREC_PRIORITY = 0x20;
pub const PREC_ROUTINE = 0x00;
pub const DSCP_CS0 = 0x00;
pub const DSCP_LE = 0x04;
pub const DSCP_CS1 = 0x20;
pub const DSCP_AF11 = 0x28;
pub const DSCP_AF12 = 0x30;
pub const DSCP_AF13 = 0x38;
pub const DSCP_CS2 = 0x40;
pub const DSCP_AF21 = 0x48;
pub const DSCP_AF22 = 0x50;
pub const DSCP_AF23 = 0x58;
pub const DSCP_CS3 = 0x60;
pub const DSCP_AF31 = 0x68;
pub const DSCP_AF32 = 0x70;
pub const DSCP_AF33 = 0x78;
pub const DSCP_CS4 = 0x80;
pub const DSCP_AF41 = 0x88;
pub const DSCP_AF42 = 0x90;
pub const DSCP_AF43 = 0x98;
pub const DSCP_CS5 = 0xa0;
pub const DSCP_EF = 0xb8;
pub const DSCP_CS6 = 0xc0;
pub const DSCP_CS7 = 0xe0;
pub const ECN_NOTECT = 0x00;
pub const ECN_ECT1 = 0x01;
pub const ECN_ECT0 = 0x02;
pub const ECN_CE = 0x03;
pub const ECN_MASK = 0x03;
};