From ec0d775524e0b8b8f0bc41baa3e6d4e11c50557d Mon Sep 17 00:00:00 2001 From: Shawn Anastasio Date: Wed, 3 Jun 2020 19:37:14 -0500 Subject: [PATCH 01/23] Implement std.os for powerpc64{,le} --- lib/std/os/bits/linux.zig | 1 + lib/std/os/bits/linux/powerpc64.zig | 602 ++++++++++++++++++++++++++++ lib/std/os/linux.zig | 1 + lib/std/os/linux/powerpc64.zig | 127 ++++++ lib/std/os/linux/tls.zig | 13 +- 5 files changed, 741 insertions(+), 3 deletions(-) create mode 100644 lib/std/os/bits/linux/powerpc64.zig create mode 100644 lib/std/os/linux/powerpc64.zig diff --git a/lib/std/os/bits/linux.zig b/lib/std/os/bits/linux.zig index 64832673f1a70357d28f218f890e3431bca6904b..9ec7038b07eefc303f7524a89ae2b68b9fa30bf3 100644 --- a/lib/std/os/bits/linux.zig +++ b/lib/std/os/bits/linux.zig @@ -15,6 +15,7 @@ pub usingnamespace switch (builtin.arch) { .arm => @import("linux/arm-eabi.zig"), .riscv64 => @import("linux/riscv64.zig"), .mips, .mipsel => @import("linux/mips.zig"), + .powerpc64, .powerpc64le => @import("linux/powerpc64.zig"), else => struct {}, }; diff --git a/lib/std/os/bits/linux/powerpc64.zig b/lib/std/os/bits/linux/powerpc64.zig new file mode 100644 index 0000000000000000000000000000000000000000..a6884aa0249b7f20c5be88dfc613be570beed1b4 --- /dev/null +++ b/lib/std/os/bits/linux/powerpc64.zig @@ -0,0 +1,602 @@ +const std = @import("../../../std.zig"); +const linux = std.os.linux; +const socklen_t = linux.socklen_t; +const iovec = linux.iovec; +const iovec_const = linux.iovec_const; +const uid_t = linux.uid_t; +const gid_t = linux.gid_t; +const pid_t = linux.pid_t; +const stack_t = linux.stack_t; +const sigset_t = linux.sigset_t; +pub const SYS = extern enum(usize) { + restart_syscall = 0, + exit = 1, + fork = 2, + read = 3, + write = 4, + open = 5, + close = 6, + waitpid = 7, + creat = 8, + link = 9, + unlink = 10, + execve = 11, + chdir = 12, + time = 13, + mknod = 14, + chmod = 15, + lchown = 16, + sys_break = 17, // sys_ prepended to avoid clashing with keyword + oldstat = 18, + lseek = 19, + getpid = 20, + mount = 21, + umount = 22, + setuid = 23, + getuid = 24, + stime = 25, + ptrace = 26, + alarm = 27, + oldfstat = 28, + pause = 29, + utime = 30, + stty = 31, + gtty = 32, + access = 33, + nice = 34, + ftime = 35, + sync = 36, + kill = 37, + rename = 38, + mkdir = 39, + rmdir = 40, + dup = 41, + pipe = 42, + times = 43, + prof = 44, + brk = 45, + setgid = 46, + getgid = 47, + signal = 48, + geteuid = 49, + getegid = 50, + acct = 51, + umount2 = 52, + lock = 53, + ioctl = 54, + fcntl = 55, + mpx = 56, + setpgid = 57, + ulimit = 58, + oldolduname = 59, + umask = 60, + chroot = 61, + ustat = 62, + dup2 = 63, + getppid = 64, + getpgrp = 65, + setsid = 66, + sigaction = 67, + sgetmask = 68, + ssetmask = 69, + setreuid = 70, + setregid = 71, + sigsuspend = 72, + sigpending = 73, + sethostname = 74, + setrlimit = 75, + getrlimit = 76, + getrusage = 77, + gettimeofday = 78, + settimeofday = 79, + getgroups = 80, + setgroups = 81, + select = 82, + symlink = 83, + oldlstat = 84, + readlink = 85, + uselib = 86, + swapon = 87, + reboot = 88, + readdir = 89, + mmap = 90, + munmap = 91, + truncate = 92, + ftruncate = 93, + fchmod = 94, + fchown = 95, + getpriority = 96, + setpriority = 97, + profil = 98, + statfs = 99, + fstatfs = 100, + ioperm = 101, + socketcall = 102, + syslog = 103, + setitimer = 104, + getitimer = 105, + stat = 106, + lstat = 107, + fstat = 108, + olduname = 109, + iopl = 110, + vhangup = 111, + idle = 112, + vm86 = 113, + wait4 = 114, + swapoff = 115, + sysinfo = 116, + ipc = 117, + fsync = 118, + sigreturn = 119, + clone = 120, + setdomainname = 121, + uname = 122, + modify_ldt = 123, + adjtimex = 124, + mprotect = 125, + sigprocmask = 126, + create_module = 127, + init_module = 128, + delete_module = 129, + get_kernel_syms = 130, + quotactl = 131, + getpgid = 132, + fchdir = 133, + bdflush = 134, + sysfs = 135, + personality = 136, + afs_syscall = 137, + setfsuid = 138, + setfsgid = 139, + _llseek = 140, + getdents = 141, + _newselect = 142, + flock = 143, + msync = 144, + readv = 145, + writev = 146, + getsid = 147, + fdatasync = 148, + _sysctl = 149, + mlock = 150, + munlock = 151, + mlockall = 152, + munlockall = 153, + sched_setparam = 154, + sched_getparam = 155, + sched_setscheduler = 156, + sched_getscheduler = 157, + sched_yield = 158, + sched_get_priority_max = 159, + sched_get_priority_min = 160, + sched_rr_get_interval = 161, + nanosleep = 162, + mremap = 163, + setresuid = 164, + getresuid = 165, + query_module = 166, + poll = 167, + nfsservctl = 168, + setresgid = 169, + getresgid = 170, + prctl = 171, + rt_sigreturn = 172, + rt_sigaction = 173, + rt_sigprocmask = 174, + rt_sigpending = 175, + rt_sigtimedwait = 176, + rt_sigqueueinfo = 177, + rt_sigsuspend = 178, + pread64 = 179, + pwrite64 = 180, + chown = 181, + getcwd = 182, + capget = 183, + capset = 184, + sigaltstack = 185, + sendfile = 186, + getpmsg = 187, + putpmsg = 188, + vfork = 189, + ugetrlimit = 190, + readahead = 191, + pciconfig_read = 198, + pciconfig_write = 199, + pciconfig_iobase = 200, + multiplexer = 201, + getdents64 = 202, + pivot_root = 203, + madvise = 205, + mincore = 206, + gettid = 207, + tkill = 208, + setxattr = 209, + lsetxattr = 210, + fsetxattr = 211, + getxattr = 212, + lgetxattr = 213, + fgetxattr = 214, + listxattr = 215, + llistxattr = 216, + flistxattr = 217, + removexattr = 218, + lremovexattr = 219, + fremovexattr = 220, + futex = 221, + sched_setaffinity = 222, + sched_getaffinity = 223, + tuxcall = 225, + io_setup = 227, + io_destroy = 228, + io_getevents = 229, + io_submit = 230, + io_cancel = 231, + set_tid_address = 232, + fadvise64 = 233, + exit_group = 234, + lookup_dcookie = 235, + epoll_create = 236, + epoll_ctl = 237, + epoll_wait = 238, + remap_file_pages = 239, + timer_create = 240, + timer_settime = 241, + timer_gettime = 242, + timer_getoverrun = 243, + timer_delete = 244, + clock_settime = 245, + clock_gettime = 246, + clock_getres = 247, + clock_nanosleep = 248, + swapcontext = 249, + tgkill = 250, + utimes = 251, + statfs64 = 252, + fstatfs64 = 253, + rtas = 255, + sys_debug_setcontext = 256, + migrate_pages = 258, + mbind = 259, + get_mempolicy = 260, + set_mempolicy = 261, + mq_open = 262, + mq_unlink = 263, + mq_timedsend = 264, + mq_timedreceive = 265, + mq_notify = 266, + mq_getsetattr = 267, + kexec_load = 268, + add_key = 269, + request_key = 270, + keyctl = 271, + waitid = 272, + ioprio_set = 273, + ioprio_get = 274, + inotify_init = 275, + inotify_add_watch = 276, + inotify_rm_watch = 277, + spu_run = 278, + spu_create = 279, + pselect6 = 280, + ppoll = 281, + unshare = 282, + splice = 283, + tee = 284, + vmsplice = 285, + openat = 286, + mkdirat = 287, + mknodat = 288, + fchownat = 289, + futimesat = 290, + newfstatat = 291, + unlinkat = 292, + renameat = 293, + linkat = 294, + symlinkat = 295, + readlinkat = 296, + fchmodat = 297, + faccessat = 298, + get_robust_list = 299, + set_robust_list = 300, + move_pages = 301, + getcpu = 302, + epoll_pwait = 303, + utimensat = 304, + signalfd = 305, + timerfd_create = 306, + eventfd = 307, + sync_file_range2 = 308, + fallocate = 309, + subpage_prot = 310, + timerfd_settime = 311, + timerfd_gettime = 312, + signalfd4 = 313, + eventfd2 = 314, + epoll_create1 = 315, + dup3 = 316, + pipe2 = 317, + inotify_init1 = 318, + perf_event_open = 319, + preadv = 320, + pwritev = 321, + rt_tgsigqueueinfo = 322, + fanotify_init = 323, + fanotify_mark = 324, + prlimit64 = 325, + socket = 326, + bind = 327, + connect = 328, + listen = 329, + accept = 330, + getsockname = 331, + getpeername = 332, + socketpair = 333, + send = 334, + sendto = 335, + recv = 336, + recvfrom = 337, + shutdown = 338, + setsockopt = 339, + getsockopt = 340, + sendmsg = 341, + recvmsg = 342, + recvmmsg = 343, + accept4 = 344, + name_to_handle_at = 345, + open_by_handle_at = 346, + clock_adjtime = 347, + syncfs = 348, + sendmmsg = 349, + setns = 350, + process_vm_readv = 351, + process_vm_writev = 352, + finit_module = 353, + kcmp = 354, + sched_setattr = 355, + sched_getattr = 356, + renameat2 = 357, + seccomp = 358, + getrandom = 359, + memfd_create = 360, + bpf = 361, + execveat = 362, + switch_endian = 363, + userfaultfd = 364, + membarrier = 365, + mlock2 = 378, + copy_file_range = 379, + preadv2 = 380, + pwritev2 = 381, + kexec_file_load = 382, + statx = 383, + pkey_alloc = 384, + pkey_free = 385, + pkey_mprotect = 386, + rseq = 387, + io_pgetevents = 388, + semtimedop = 392, + semget = 393, + semctl = 394, + shmget = 395, + shmctl = 396, + shmat = 397, + shmdt = 398, + msgget = 399, + msgsnd = 400, + msgrcv = 401, + msgctl = 402, + pidfd_send_signal = 424, + io_uring_setup = 425, + io_uring_enter = 426, + io_uring_register = 427, + open_tree = 428, + move_mount = 429, + fsopen = 430, + fsconfig = 431, + fsmount = 432, + fspick = 433, + pidfd_open = 434, + clone3 = 435, + openat2 = 437, + pidfd_getfd = 438, + + _, +}; + +pub const O_CREAT = 0o100; +pub const O_EXCL = 0o200; +pub const O_NOCTTY = 0o400; +pub const O_TRUNC = 0o1000; +pub const O_APPEND = 0o2000; +pub const O_NONBLOCK = 0o4000; +pub const O_DSYNC = 0o10000; +pub const O_SYNC = 0o4010000; +pub const O_RSYNC = 0o4010000; +pub const O_DIRECTORY = 0o40000; +pub const O_NOFOLLOW = 0o100000; +pub const O_CLOEXEC = 0o2000000; + +pub const O_ASYNC = 0o20000; +pub const O_DIRECT = 0o400000; +pub const O_LARGEFILE = 0o200000; +pub const O_NOATIME = 0o1000000; +pub const O_PATH = 0o10000000; +pub const O_TMPFILE = 0o20200000; +pub const O_NDELAY = O_NONBLOCK; + +pub const F_DUPFD = 0; +pub const F_GETFD = 1; +pub const F_SETFD = 2; +pub const F_GETFL = 3; +pub const F_SETFL = 4; + +pub const F_SETOWN = 8; +pub const F_GETOWN = 9; +pub const F_SETSIG = 10; +pub const F_GETSIG = 11; + +pub const F_GETLK = 5; +pub const F_SETLK = 6; +pub const F_SETLKW = 7; + +pub const F_RDLCK = 0; +pub const F_WRLCK = 1; +pub const F_UNLCK = 2; + +pub const LOCK_SH = 1; +pub const LOCK_EX = 2; +pub const LOCK_UN = 8; +pub const LOCK_NB = 4; + +pub const F_SETOWN_EX = 15; +pub const F_GETOWN_EX = 16; + +pub const F_GETOWNER_UIDS = 17; + +/// stack-like segment +pub const MAP_GROWSDOWN = 0x0100; + +/// ETXTBSY +pub const MAP_DENYWRITE = 0x0800; + +/// mark it as an executable +pub const MAP_EXECUTABLE = 0x1000; + +/// pages are locked +pub const MAP_LOCKED = 0x0080; + +/// don't check for reservations +pub const MAP_NORESERVE = 0x0040; + +pub const VDSO_CGT_SYM = "__kernel_clock_gettime"; +pub const VDSO_CGT_VER = "LINUX_2.6.15"; + +pub const Flock = extern struct { + l_type: i16, + l_whence: i16, + l_start: off_t, + l_len: off_t, + l_pid: pid_t, + __unused: [4]u8, +}; + +pub const msghdr = extern struct { + msg_name: ?*sockaddr, + msg_namelen: socklen_t, + msg_iov: [*]iovec, + msg_iovlen: usize, + msg_control: ?*c_void, + msg_controllen: usize, + msg_flags: i32, +}; + +pub const msghdr_const = extern struct { + msg_name: ?*const sockaddr, + msg_namelen: socklen_t, + msg_iov: [*]iovec_const, + msg_iovlen: usize, + msg_control: ?*c_void, + msg_controllen: usize, + msg_flags: i32, +}; + +pub const blksize_t = i64; +pub const nlink_t = u64; +pub const time_t = i64; +pub const mode_t = u32; +pub const off_t = i64; +pub const ino_t = u64; +pub const dev_t = u64; +pub const blkcnt_t = i64; + +/// Renamed to Stat to not conflict with the stat function. +/// atime, mtime, and ctime have functions to return `timespec`, +/// because although this is a POSIX API, the layout and names of +/// the structs are inconsistent across operating systems, and +/// in C, macros are used to hide the differences. Here we use +/// methods to accomplish this. +pub const Stat = extern struct { + dev: dev_t, + ino: ino_t, + nlink: nlink_t, + mode: mode_t, + uid: uid_t, + gid: gid_t, + rdev: dev_t, + size: off_t, + blksize: blksize_t, + blocks: blkcnt_t, + atim: timespec, + mtim: timespec, + ctim: timespec, + __unused: [3]u64, + + pub fn atime(self: Stat) timespec { + return self.atim; + } + + pub fn mtime(self: Stat) timespec { + return self.mtim; + } + + pub fn ctime(self: Stat) timespec { + return self.ctim; + } +}; + +pub const timespec = extern struct { + tv_sec: time_t, + tv_nsec: isize, +}; + +pub const timeval = extern struct { + tv_sec: isize, + tv_usec: isize, +}; + +pub const timezone = extern struct { + tz_minuteswest: i32, + tz_dsttime: i32, +}; + +pub const greg_t = u64; +pub const gregset_t = [48]greg_t; +pub const fpregset_t = [33]f64; + +/// The position of the vscr register depends on endianness. +/// On C, macros are used to change vscr_word's offset to +/// account for this. Here we'll just define vscr_word_le +/// and vscr_word_be. Code must take care to use the correct one. +pub const vrregset = extern struct { + vrregs: [32][4]u32 align(16), + vscr_word_le: u32, + _pad1: [2]u32, + vscr_word_be: u32, + vrsave: u32, + _pad2: [3]u32 +}; +pub const vrregset_t = vrregset; + +pub const mcontext_t = extern struct { + __unused: [4]u64, + signal: i32, + _pad0: i32, + handler: u64, + oldmask: u64, + regs: ?*c_void, + gp_regs: gregset_t, + fp_regs: fpregset_t, + v_regs: *vrregset_t, + vmx_reserve: [34+34+32+1]i64, +}; + +pub const ucontext_t = extern struct { + flags: u32, + link: *ucontext_t, + stack: stack_t, + sigmask: sigset_t, + mcontext: mcontext_t, +}; + +pub const Elf_Symndx = u32; diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 3b8df3d173605a4b064f698455da6499500e13cf..67c6fc5d316b1869b8a817e4458c6fc145565420 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -20,6 +20,7 @@ pub usingnamespace switch (builtin.arch) { .arm => @import("linux/arm-eabi.zig"), .riscv64 => @import("linux/riscv64.zig"), .mips, .mipsel => @import("linux/mips.zig"), + .powerpc64, .powerpc64le => @import("linux/powerpc64.zig"), else => struct {}, }; pub usingnamespace @import("bits.zig"); diff --git a/lib/std/os/linux/powerpc64.zig b/lib/std/os/linux/powerpc64.zig new file mode 100644 index 0000000000000000000000000000000000000000..337a6aa30ae08abfb27094091eb2338b5a103dd6 --- /dev/null +++ b/lib/std/os/linux/powerpc64.zig @@ -0,0 +1,127 @@ +usingnamespace @import("../bits.zig"); + +pub fn syscall0(number: SYS) usize { + return asm volatile ( + \\ sc + \\ bns+ 1f + \\ neg 3, 3 + \\ 1: + : [ret] "={r3}" (-> usize) + : [number] "{r0}" (@enumToInt(number)) + : "memory", "cr0", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12" + ); +} + +pub fn syscall1(number: SYS, arg1: usize) usize { + return asm volatile ( + \\ sc + \\ bns+ 1f + \\ neg 3, 3 + \\ 1: + : [ret] "={r3}" (-> usize) + : [number] "{r0}" (@enumToInt(number)), + [arg1] "{r3}" (arg1) + : "memory", "cr0", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12" + ); +} + +pub fn syscall2(number: SYS, arg1: usize, arg2: usize) usize { + return asm volatile ( + \\ sc + \\ bns+ 1f + \\ neg 3, 3 + \\ 1: + : [ret] "={r3}" (-> usize) + : [number] "{r0}" (@enumToInt(number)), + [arg1] "{r3}" (arg1), + [arg2] "{r4}" (arg2) + : "memory", "cr0", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12" + ); +} + +pub fn syscall3(number: SYS, arg1: usize, arg2: usize, arg3: usize) usize { + return asm volatile ( + \\ sc + \\ bns+ 1f + \\ neg 3, 3 + \\ 1: + : [ret] "={r3}" (-> usize) + : [number] "{r0}" (@enumToInt(number)), + [arg1] "{r3}" (arg1), + [arg2] "{r4}" (arg2), + [arg3] "{r5}" (arg3) + : "memory", "cr0", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12" + ); +} + +pub fn syscall4(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize) usize { + return asm volatile ( + \\ sc + \\ bns+ 1f + \\ neg 3, 3 + \\ 1: + : [ret] "={r3}" (-> usize) + : [number] "{r0}" (@enumToInt(number)), + [arg1] "{r3}" (arg1), + [arg2] "{r4}" (arg2), + [arg3] "{r5}" (arg3), + [arg4] "{r6}" (arg4) + : "memory", "cr0", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12" + ); +} + +pub fn syscall5(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) usize { + return asm volatile ( + \\ sc + \\ bns+ 1f + \\ neg 3, 3 + \\ 1: + : [ret] "={r3}" (-> usize) + : [number] "{r0}" (@enumToInt(number)), + [arg1] "{r3}" (arg1), + [arg2] "{r4}" (arg2), + [arg3] "{r5}" (arg3), + [arg4] "{r6}" (arg4), + [arg5] "{r7}" (arg5) + : "memory", "cr0", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12" + ); +} + +pub fn syscall6( + number: SYS, + arg1: usize, + arg2: usize, + arg3: usize, + arg4: usize, + arg5: usize, + arg6: usize, +) usize { + return asm volatile ( + \\ sc + \\ bns+ 1f + \\ neg 3, 3 + \\ 1: + : [ret] "={r3}" (-> usize) + : [number] "{r0}" (@enumToInt(number)), + [arg1] "{r3}" (arg1), + [arg2] "{r4}" (arg2), + [arg3] "{r5}" (arg3), + [arg4] "{r6}" (arg4), + [arg5] "{r7}" (arg5), + [arg6] "{r8}" (arg6) + : "memory", "cr0", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12" + ); +} + +/// This matches the libc clone function. +pub extern fn clone(func: fn (arg: usize) callconv(.C) u8, stack: usize, flags: usize, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; + +pub const restore = restore_rt; + +pub fn restore_rt() callconv(.Naked) void { + return asm volatile ("sc" + : + : [number] "{r0}" (@enumToInt(SYS.rt_sigreturn)) + : "memory", "cr0", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12" + ); +} diff --git a/lib/std/os/linux/tls.zig b/lib/std/os/linux/tls.zig index 8cba45d4b380b68fb4aaa284c2aae63e0762e83d..9c79bc3a9653f24fede4075bb2e52d67dd498857 100644 --- a/lib/std/os/linux/tls.zig +++ b/lib/std/os/linux/tls.zig @@ -48,7 +48,7 @@ const TLSVariant = enum { }; const tls_variant = switch (builtin.arch) { - .arm, .armeb, .aarch64, .aarch64_be, .riscv32, .riscv64, .mips, .mipsel => TLSVariant.VariantI, + .arm, .armeb, .aarch64, .aarch64_be, .riscv32, .riscv64, .mips, .mipsel, .powerpc, .powerpc64, .powerpc64le => TLSVariant.VariantI, .x86_64, .i386 => TLSVariant.VariantII, else => @compileError("undefined tls_variant for this architecture"), }; @@ -72,12 +72,12 @@ const tls_tp_points_past_tcb = switch (builtin.arch) { // make the generated code more efficient const tls_tp_offset = switch (builtin.arch) { - .mips, .mipsel => 0x7000, + .mips, .mipsel, .powerpc, .powerpc64, .powerpc64le => 0x7000, else => 0, }; const tls_dtv_offset = switch (builtin.arch) { - .mips, .mipsel => 0x8000, + .mips, .mipsel, .powerpc, .powerpc64, .powerpc64le => 0x8000, .riscv32, .riscv64 => 0x800, else => 0, }; @@ -160,6 +160,13 @@ pub fn setThreadPointer(addr: usize) void { const rc = std.os.linux.syscall1(.set_thread_area, addr); assert(rc == 0); }, + .powerpc, .powerpc64, .powerpc64le => { + asm volatile ( + \\ mr 13, %[addr] + : + : [addr] "r" (addr) + ); + }, else => @compileError("Unsupported architecture"), } } -- 2.54.0 From 15371775d18e6425d1528643cedb70d6f0b7e6ea Mon Sep 17 00:00:00 2001 From: Shawn Anastasio Date: Wed, 3 Jun 2020 20:55:33 -0500 Subject: [PATCH 02/23] Implement clone() for powerpc64{,le} Implementation borrowed from musl, as most (all?) of the other ones seem to be. --- lib/std/special/c.zig | 55 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/lib/std/special/c.zig b/lib/std/special/c.zig index c769bc358ba3f59cba8cadd601a386f552c924a1..13012e16a30d92fdad54a181e1c8287a1131832b 100644 --- a/lib/std/special/c.zig +++ b/lib/std/special/c.zig @@ -389,6 +389,61 @@ fn clone() callconv(.Naked) void { \\ syscall ); }, + + .powerpc64, .powerpc64le => { + asm volatile ( + \\ # store non-volatile regs r30, r31 on stack in order to put our + \\ # start func and its arg there + \\ stwu 30, -16(1) + \\ stw 31, 4(1) + \\ # save r3 (func) into r30, and r6(arg) into r31 + \\ mr 30, 3 + \\ mr 31, 6 + \\ # create initial stack frame for new thread + \\ clrrwi 4, 4, 4 + \\ li 0, 0 + \\ stwu 0, -16(4) + \\ #move c into first arg + \\ mr 3, 5 + \\ mr 5, 7 + \\ mr 6, 8 + \\ mr 7, 9 + \\ # move syscall number into r0 + \\ li 0, 120 + \\ sc + + \\ # check for syscall error + \\ bns+ 1f # jump to label 1 if no summary overflow. + \\ #else + \\ neg 3, 3 #negate the result (errno) + \\1: + \\ # compare sc result with 0 + \\ cmpwi cr7, 3, 0 + + \\ # if not 0, jump to end + \\ bne cr7, 2f + + \\ #else: we're the child + \\ #call funcptr: move arg (d) into r3 + \\ mr 3, 31 + \\ #move r30 (funcptr) into CTR reg + \\ mtctr 30 + \\ # call CTR reg + \\ bctrl + \\ # mov SYS_exit into r0 (the exit param is already in r3) + \\ li 0, 1 + \\ sc + + \\2: + \\ # restore stack + \\ lwz 30, 0(1) + \\ lwz 31, 4(1) + \\ addi 1, 1, 16 + + \\ blr + ); + }, + else => @compileError("Implement clone() for this arch."), } } -- 2.54.0 From 8574861ca0bc5e8103c27e3c78866e3a4eb56f98 Mon Sep 17 00:00:00 2001 From: Shawn Anastasio Date: Wed, 1 Jul 2020 16:12:27 -0500 Subject: [PATCH 03/23] Implement required ABI bits for powerpc{,64,64le} --- src/analyze.cpp | 3 ++- src/target.cpp | 11 ++++++++--- src/target.hpp | 1 + 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 9062c1fb136c5d7819235a82b0aca448b922bff3..72762f42278499e221d70686c29bda97bd9e337b 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1003,7 +1003,8 @@ bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id) { g->zig_target->arch == ZigLLVM_x86_64 || target_is_arm(g->zig_target) || target_is_riscv(g->zig_target) || - target_is_wasm(g->zig_target)) + target_is_wasm(g->zig_target) || + target_is_ppc(g->zig_target)) { X64CABIClass abi_class = type_c_abi_x86_64_class(g, fn_type_id->return_type); return abi_class == X64CABIClass_MEMORY || abi_class == X64CABIClass_MEMORY_nobyval; diff --git a/src/target.cpp b/src/target.cpp index 73c0924ab8a9364fac7785e6be95bf77a4223e5b..a4cb03aabc32060015756e68cf8fe2dbeea64a61 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -853,6 +853,9 @@ const char *arch_stack_pointer_register_name(ZigLLVM_ArchType arch) { case ZigLLVM_riscv32: case ZigLLVM_riscv64: case ZigLLVM_mipsel: + case ZigLLVM_ppc: + case ZigLLVM_ppc64: + case ZigLLVM_ppc64le: return "sp"; case ZigLLVM_wasm32: @@ -879,7 +882,6 @@ const char *arch_stack_pointer_register_name(ZigLLVM_ArchType arch) { case ZigLLVM_msp430: case ZigLLVM_nvptx: case ZigLLVM_nvptx64: - case ZigLLVM_ppc64le: case ZigLLVM_r600: case ZigLLVM_renderscript32: case ZigLLVM_renderscript64: @@ -893,8 +895,6 @@ const char *arch_stack_pointer_register_name(ZigLLVM_ArchType arch) { case ZigLLVM_tce: case ZigLLVM_tcele: case ZigLLVM_xcore: - case ZigLLVM_ppc: - case ZigLLVM_ppc64: case ZigLLVM_ve: zig_panic("TODO populate this table with stack pointer register name for this CPU architecture"); } @@ -1318,6 +1318,11 @@ bool target_is_mips(const ZigTarget *target) { target->arch == ZigLLVM_mips64 || target->arch == ZigLLVM_mips64el; } +bool target_is_ppc(const ZigTarget *target) { + return target->arch == ZigLLVM_ppc || target->arch == ZigLLVM_ppc64 || + target->arch == ZigLLVM_ppc64le; +} + unsigned target_fn_align(const ZigTarget *target) { return 16; } diff --git a/src/target.hpp b/src/target.hpp index 898fa9020340dabef917481a0a5ea4230b8545c6..9a8e79de97c15d6d2c723abd6bc3acf8f6a5d551 100644 --- a/src/target.hpp +++ b/src/target.hpp @@ -95,6 +95,7 @@ ZigLLVM_OSType get_llvm_os_type(Os os_type); bool target_is_arm(const ZigTarget *target); bool target_is_mips(const ZigTarget *target); +bool target_is_ppc(const ZigTarget *target); bool target_allows_addr_zero(const ZigTarget *target); bool target_has_valgrind_support(const ZigTarget *target); bool target_os_is_darwin(Os os); -- 2.54.0 From 51fcf949f97d068e917c0c3301ba63a4a305ab6e Mon Sep 17 00:00:00 2001 From: Shawn Anastasio Date: Wed, 1 Jul 2020 16:13:14 -0500 Subject: [PATCH 04/23] Implement std.start for powerpc64le This is a bit hacky since we end up doing more than just grabbing the stack pointer in the inline assembly block. Ideally _start would be implemented in pure asm for powerpc64le, but this will do for now. Still to be implemented is powerpc, powerpc64, and powerpc64 (ELFv2) support. The latter will just require correctly determing target ABI for powerpc64 and enabling the existing powerpc64le implementation for it. --- lib/std/start.zig | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/std/start.zig b/lib/std/start.zig index 604c22101cbf2839af1ab692de3b3652ddc07073..0af7ea64072d89e42ecdf03b25f785a8831e5fc2 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -116,6 +116,21 @@ fn _start() callconv(.Naked) noreturn { : [argc] "=r" (-> [*]usize) ); }, + .powerpc64le => { + // Before returning the stack pointer, we have to set up a backchain + // and a few other registers required by the ELFv2 ABI. + // TODO: Support powerpc64 (big endian) on ELFv2. + starting_stack_ptr = asm ( + \\ mr 4, 1 + \\ subi 1, 1, 32 + \\ li 5, 0 + \\ std 5, 0(1) + \\ mr %[argc], 4 + : [argc] "=r" (-> [*]usize) + : + : "r4", "r5" + ); + }, else => @compileError("unsupported arch"), } // If LLVM inlines stack variables into _start, they will overwrite -- 2.54.0 From 78baa16da020ec9ad741b8f3f9c6168ec77ef58d Mon Sep 17 00:00:00 2001 From: Literally Void Date: Wed, 9 Sep 2020 20:49:49 -0700 Subject: [PATCH 05/23] Fix issue #6303: iterating empty PriorityQueue crashes --- lib/std/priority_queue.zig | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/std/priority_queue.zig b/lib/std/priority_queue.zig index 1c0d230d4d23c465ee6432e1b930375408c6e09c..b9be9b70bf226a86106ac8d6e02445d2d4cff82c 100644 --- a/lib/std/priority_queue.zig +++ b/lib/std/priority_queue.zig @@ -195,7 +195,7 @@ pub fn PriorityQueue(comptime T: type) type { count: usize, pub fn next(it: *Iterator) ?T { - if (it.count > it.queue.len - 1) return null; + if (it.count >= it.queue.len) return null; const out = it.count; it.count += 1; return it.queue.items[out]; @@ -428,3 +428,12 @@ test "std.PriorityQueue: remove at index" { expectEqual(queue.remove(), 3); expectEqual(queue.removeOrNull(), null); } + +test "std.PriorityQueue: iterator while empty" { + var queue = PQ.init(testing.allocator, lessThan); + defer queue.deinit(); + + var it = queue.iterator(); + + expectEqual(it.next(), null); +} -- 2.54.0 From 0833c8d06ba9a467bb8449cbca2ba6f43d218c32 Mon Sep 17 00:00:00 2001 From: Vexu Date: Thu, 10 Sep 2020 12:58:12 +0300 Subject: [PATCH 06/23] translate-c: support sizeof and _Alignof in macros Closes #6301 --- src-self-hosted/translate_c.zig | 38 +++++++++++++++++++++ test/stage1/behavior/translate_c_macros.h | 5 ++- test/stage1/behavior/translate_c_macros.zig | 8 ++++- 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index a65c1b9fb45ae9ef37ec013cf340896dfb3dab6a..cd42e8abeea888031d6d401efd9b26d49c88595b 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -5899,6 +5899,10 @@ fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N saw_l_paren = true; _ = m.next(); }, + // (type)sizeof(x) + .Keyword_sizeof, + // (type)alignof(x) + .Keyword_alignof, // (type)identifier .Identifier => {}, // (type)integer @@ -6309,6 +6313,40 @@ fn parseCPrefixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast. node.rhs = try parseCPrefixOpExpr(c, m, scope); return &node.base; }, + .Keyword_sizeof => { + const inner = if (m.peek().? == .LParen) blk: { + _ = m.next(); + const inner = try parseCExpr(c, m, scope); + if (m.next().? != .RParen) { + try m.fail(c, "unable to translate C expr: expected ')'", .{}); + return error.ParseError; + } + break :blk inner; + } else try parseCPrefixOpExpr(c, m, scope); + + const builtin_call = try c.createBuiltinCall("@sizeOf", 1); + builtin_call.params()[0] = inner; + builtin_call.rparen_token = try appendToken(c, .RParen, ")"); + return &builtin_call.base; + }, + .Keyword_alignof => { + // TODO this won't work if using 's + // #define alignof _Alignof + if (m.next().? != .LParen) { + try m.fail(c, "unable to translate C expr: expected '('", .{}); + return error.ParseError; + } + const inner = try parseCExpr(c, m, scope); + if (m.next().? != .RParen) { + try m.fail(c, "unable to translate C expr: expected ')'", .{}); + return error.ParseError; + } + + const builtin_call = try c.createBuiltinCall("@alignOf", 1); + builtin_call.params()[0] = inner; + builtin_call.rparen_token = try appendToken(c, .RParen, ")"); + return &builtin_call.base; + }, else => { m.i -= 1; return try parseCSuffixOpExpr(c, m, scope); diff --git a/test/stage1/behavior/translate_c_macros.h b/test/stage1/behavior/translate_c_macros.h index abc6c1e3cf078a89c9d2152a7a139b820ef845c6..49806a524ec8f2ef92d6f5cec139d525e425f172 100644 --- a/test/stage1/behavior/translate_c_macros.h +++ b/test/stage1/behavior/translate_c_macros.h @@ -6,4 +6,7 @@ typedef struct Color { unsigned char a; } Color; #define CLITERAL(type) (type) -#define LIGHTGRAY CLITERAL(Color){ 200, 200, 200, 255 } // Light Gray \ No newline at end of file +#define LIGHTGRAY CLITERAL(Color){ 200, 200, 200, 255 } // Light Gray + +#define MY_SIZEOF(x) ((int)sizeof(x)) +#define MY_SIZEOF2(x) ((int)sizeof x) diff --git a/test/stage1/behavior/translate_c_macros.zig b/test/stage1/behavior/translate_c_macros.zig index ea42016e9bb6db57f16aeaf89d7b89b78845eafb..2cfb2331fa1501d95c23fd6db30f8bc378a8f281 100644 --- a/test/stage1/behavior/translate_c_macros.zig +++ b/test/stage1/behavior/translate_c_macros.zig @@ -1,12 +1,18 @@ const expect = @import("std").testing.expect; +const expectEqual = @import("std").testing.expectEqual; const h = @cImport(@cInclude("stage1/behavior/translate_c_macros.h")); test "initializer list expression" { - @import("std").testing.expectEqual(h.Color{ + expectEqual(h.Color{ .r = 200, .g = 200, .b = 200, .a = 255, }, h.LIGHTGRAY); } + +test "sizeof in macros" { + expectEqual(@as(c_int, @sizeOf(u32)), h.MY_SIZEOF(u32)); + expectEqual(@as(c_int, @sizeOf(u32)), h.MY_SIZEOF2(u32)); +} -- 2.54.0 From 4170f3f77f23bca52a2a9b24d1a249fae35c04ac Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Thu, 10 Sep 2020 13:36:34 +0200 Subject: [PATCH 07/23] std: fix bitrot in process.posixGetUserInfo() --- lib/std/process.zig | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/std/process.zig b/lib/std/process.zig index 9cb571714c5b49cbbcfefcb486ca8b2322dff0a3..2813d8cbab2d985d954b85da48baaec1a4d201c5 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -593,8 +593,10 @@ pub fn getUserInfo(name: []const u8) !UserInfo { /// TODO this reads /etc/passwd. But sometimes the user/id mapping is in something else /// like NIS, AD, etc. See `man nss` or look at an strace for `id myuser`. pub fn posixGetUserInfo(name: []const u8) !UserInfo { - var reader = try io.Reader.open("/etc/passwd", null); - defer reader.close(); + const file = try std.fs.openFileAbsolute("/etc/passwd", .{}); + defer file.close(); + + const reader = file.reader(); const State = enum { Start, @@ -650,8 +652,8 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo { '0'...'9' => byte - '0', else => return error.CorruptPasswordFile, }; - if (@mulWithOverflow(u32, uid, 10, *uid)) return error.CorruptPasswordFile; - if (@addWithOverflow(u32, uid, digit, *uid)) return error.CorruptPasswordFile; + if (@mulWithOverflow(u32, uid, 10, &uid)) return error.CorruptPasswordFile; + if (@addWithOverflow(u32, uid, digit, &uid)) return error.CorruptPasswordFile; }, }, .ReadGroupId => switch (byte) { @@ -666,8 +668,8 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo { '0'...'9' => byte - '0', else => return error.CorruptPasswordFile, }; - if (@mulWithOverflow(u32, gid, 10, *gid)) return error.CorruptPasswordFile; - if (@addWithOverflow(u32, gid, digit, *gid)) return error.CorruptPasswordFile; + if (@mulWithOverflow(u32, gid, 10, &gid)) return error.CorruptPasswordFile; + if (@addWithOverflow(u32, gid, digit, &gid)) return error.CorruptPasswordFile; }, }, } -- 2.54.0 From 486a774e5fa079c4caba334f311ee32b54272f1f Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Wed, 9 Sep 2020 19:51:59 +0200 Subject: [PATCH 08/23] std: add option to use single-threaded event loop std.event.Loop does not yet work in single threaded builds. However, using evented io on a single thread can be very convenient. This commit allows settind @import("root").event_loop_mode to .single_threaded in order to allow this without reimplementing the startup code in start.zig --- lib/std/event/loop.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/std/event/loop.zig b/lib/std/event/loop.zig index b34ad8c940bd9c82f17565053f06638953e73753..2600b337b37f4dee43403e7f4af90ce93d0a4fa7 100644 --- a/lib/std/event/loop.zig +++ b/lib/std/event/loop.zig @@ -112,7 +112,8 @@ pub const Loop = struct { /// have the correct pointer value. /// https://github.com/ziglang/zig/issues/2761 and https://github.com/ziglang/zig/issues/2765 pub fn init(self: *Loop) !void { - if (builtin.single_threaded) { + if (builtin.single_threaded + or (@hasDecl(root, "event_loop_mode") and root.event_loop_mode == .single_threaded)) { return self.initSingleThreaded(); } else { return self.initMultiThreaded(); -- 2.54.0 From bb9773f695ee1a783ebd347d301dcc089afb64ba Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Wed, 9 Sep 2020 20:36:06 +0200 Subject: [PATCH 09/23] std: fix errorset of std.os.seteuid/setegid --- lib/std/os.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index 181bf4930df388b213dfcd7677f0a26d8ded53f5..25c71e5d15df8d1376ce7d9c709545bdc88c6fe7 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -2515,9 +2515,9 @@ pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) Read pub const SetEidError = error{ InvalidUserId, PermissionDenied, -}; +} || UnexpectedError; -pub const SetIdError = error{ResourceLimitReached} || SetEidError || UnexpectedError; +pub const SetIdError = error{ResourceLimitReached} || SetEidError; pub fn setuid(uid: uid_t) SetIdError!void { switch (errno(system.setuid(uid))) { -- 2.54.0 From 13b8c638346b82da4856e9c5d688feffa66a32f6 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Wed, 9 Sep 2020 20:05:47 +0200 Subject: [PATCH 10/23] std: complete C definitions for uid/gid setters Also: - correct the definition of setgid - update the existing definitions to use the uid_t/gid_t types --- lib/std/c.zig | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/std/c.zig b/lib/std/c.zig index 1b3f403ab5564073c92ad3dd9b27f7d2ee334f0d..bbc32cf351b8f0dce7ebca62b30faa7111009cc6 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -132,8 +132,6 @@ pub usingnamespace switch (builtin.os.tag) { }, }; -pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int; -pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int; pub extern "c" fn rmdir(path: [*:0]const u8) c_int; pub extern "c" fn getenv(name: [*:0]const u8) ?[*:0]u8; pub extern "c" fn sysctl(name: [*]const c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int; @@ -237,8 +235,15 @@ pub usingnamespace switch (builtin.os.tag) { pub extern "c" fn kill(pid: pid_t, sig: c_int) c_int; pub extern "c" fn getdirentries(fd: fd_t, buf_ptr: [*]u8, nbytes: usize, basep: *i64) isize; -pub extern "c" fn setgid(ruid: c_uint, euid: c_uint) c_int; -pub extern "c" fn setuid(uid: c_uint) c_int; + +pub extern "c" fn setuid(uid: uid_t) c_int; +pub extern "c" fn setgid(gid: gid_t) c_int; +pub extern "c" fn seteuid(euid: uid_t) c_int; +pub extern "c" fn setegid(egid: gid_t) c_int; +pub extern "c" fn setreuid(ruid: uid_t, euid: uid_t) c_int; +pub extern "c" fn setregid(rgid: gid_t, egid: gid_t) c_int; +pub extern "c" fn setresuid(ruid: uid_t, euid: uid_t, suid: uid_t) c_int; +pub extern "c" fn setresgid(rgid: gid_t, egid: gid_t, sgid: gid_t) c_int; pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void; pub extern "c" fn malloc(usize) ?*c_void; -- 2.54.0 From ea9b38c93c38e402c807b20802eb003bcc4ddc5b Mon Sep 17 00:00:00 2001 From: Christian Wesselhoeft Date: Thu, 10 Sep 2020 11:28:51 -0600 Subject: [PATCH 11/23] fs/file.zig: Update reader to use type alias This is a minor cosmetic change which updates `reader` and `inStream` to match `writer` and `outStream` below. --- lib/std/fs/file.zig | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig index ef1b501ec3e80146d824b256cdedd451c3ae16c2..db301a9a4c4b94367a4f98dc42b8e74ab968d76a 100644 --- a/lib/std/fs/file.zig +++ b/lib/std/fs/file.zig @@ -740,14 +740,16 @@ pub const File = struct { } pub const Reader = io.Reader(File, ReadError, read); + /// Deprecated: use `Reader` pub const InStream = Reader; - pub fn reader(file: File) io.Reader(File, ReadError, read) { + pub fn reader(file: File) Reader { return .{ .context = file }; } + /// Deprecated: use `reader` - pub fn inStream(file: File) io.InStream(File, ReadError, read) { + pub fn inStream(file: File) Reader { return .{ .context = file }; } -- 2.54.0 From 800c5de2ae527cdefaa5567720dab2f899244897 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 10 Sep 2020 14:40:33 -0700 Subject: [PATCH 12/23] update the stack trace test case for lines added to start.zig --- lib/std/os/bits/linux/powerpc64.zig | 6 +++--- test/stack_traces.zig | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/std/os/bits/linux/powerpc64.zig b/lib/std/os/bits/linux/powerpc64.zig index a6884aa0249b7f20c5be88dfc613be570beed1b4..adc6c87c1a78e8996fdc9b2b3fab80e0673a4756 100644 --- a/lib/std/os/bits/linux/powerpc64.zig +++ b/lib/std/os/bits/linux/powerpc64.zig @@ -26,7 +26,7 @@ pub const SYS = extern enum(usize) { mknod = 14, chmod = 15, lchown = 16, - sys_break = 17, // sys_ prepended to avoid clashing with keyword + @"break" = 17, oldstat = 18, lseek = 19, getpid = 20, @@ -574,7 +574,7 @@ pub const vrregset = extern struct { _pad1: [2]u32, vscr_word_be: u32, vrsave: u32, - _pad2: [3]u32 + _pad2: [3]u32, }; pub const vrregset_t = vrregset; @@ -588,7 +588,7 @@ pub const mcontext_t = extern struct { gp_regs: gregset_t, fp_regs: fpregset_t, v_regs: *vrregset_t, - vmx_reserve: [34+34+32+1]i64, + vmx_reserve: [34 + 34 + 32 + 1]i64, }; pub const ucontext_t = extern struct { diff --git a/test/stack_traces.zig b/test/stack_traces.zig index 2ab022e6ecc8b0daa8daa91f49c8831f6412937b..496be0513820289723129bd97f1e8fff97b999fb 100644 --- a/test/stack_traces.zig +++ b/test/stack_traces.zig @@ -282,10 +282,10 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\source.zig:10:8: [address] in main (test) \\ foo(); \\ ^ - \\start.zig:254:29: [address] in std.start.posixCallMainAndExit (test) + \\start.zig:269:29: [address] in std.start.posixCallMainAndExit (test) \\ return root.main(); \\ ^ - \\start.zig:128:5: [address] in std.start._start (test) + \\start.zig:143:5: [address] in std.start._start (test) \\ @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{}); \\ ^ \\ @@ -294,7 +294,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { switch (std.Target.current.cpu.arch) { .aarch64 => "", // TODO disabled; results in segfault else => - \\start.zig:128:5: [address] in std.start._start (test) + \\start.zig:143:5: [address] in std.start._start (test) \\ @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{}); \\ ^ \\ -- 2.54.0 From c41cd3e13a51864728009ba73d9297259defb005 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 10 Sep 2020 18:53:20 -0700 Subject: [PATCH 13/23] std.fs.File: fix typo in writeFileAll --- lib/std/fs/file.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig index db301a9a4c4b94367a4f98dc42b8e74ab968d76a..73babf5fa2b631dc8dee84330d3eb413237b8636 100644 --- a/lib/std/fs/file.zig +++ b/lib/std/fs/file.zig @@ -728,7 +728,7 @@ pub const File = struct { } var i: usize = 0; while (i < trailers.len) { - while (amt >= headers[i].iov_len) { + while (amt >= trailers[i].iov_len) { amt -= trailers[i].iov_len; i += 1; if (i >= trailers.len) return; -- 2.54.0 From 68bf29c31efb770d7e25d0922c9305673e606f36 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Fri, 11 Sep 2020 01:59:06 +0200 Subject: [PATCH 14/23] std: allow overriding install dir of artifacts This is necessary when, for example, writing a PAM module which should be installed to lib/security/module_name.so. --- lib/std/build.zig | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 1673737bef42afbc579f75187b9b30d8e5196a3b..cb27e112aa766011409a38c77c87dc5db22f4334 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1048,6 +1048,7 @@ pub const Builder = struct { .Bin => self.exe_dir, .Lib => self.lib_dir, .Header => self.h_dir, + .Custom => |path| fs.path.join(self.allocator, &[_][]const u8{ self.install_path, path }) catch unreachable, }; return fs.path.resolve( self.allocator, @@ -1212,6 +1213,8 @@ pub const LibExeObjStep = struct { is_linking_libc: bool = false, vcpkg_bin_path: ?[]const u8 = null, + /// This may be set in order to override the default install directory + override_dest_dir: ?InstallDir, installed_path: ?[]const u8, install_step: ?*InstallArtifactStep, @@ -1348,6 +1351,7 @@ pub const LibExeObjStep = struct { .rdynamic = false, .output_dir = null, .single_threaded = false, + .override_dest_dir = null, .installed_path = null, .install_step = null, }; @@ -2309,17 +2313,17 @@ pub const InstallArtifactStep = struct { .builder = builder, .step = Step.init(.InstallArtifact, builder.fmt("install {}", .{artifact.step.name}), builder.allocator, make), .artifact = artifact, - .dest_dir = switch (artifact.kind) { + .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) { .Obj => unreachable, .Test => unreachable, - .Exe => .Bin, - .Lib => .Lib, + .Exe => InstallDir{ .Bin = {} }, + .Lib => InstallDir{ .Lib = {} }, }, .pdb_dir = if (artifact.producesPdbFile()) blk: { if (artifact.kind == .Exe) { - break :blk InstallDir.Bin; + break :blk InstallDir{ .Bin = {} }; } else { - break :blk InstallDir.Lib; + break :blk InstallDir{ .Lib = {} }; } } else null, .h_dir = if (artifact.kind == .Lib and artifact.emit_h) .Header else null, @@ -2615,11 +2619,13 @@ const VcpkgRootStatus = enum { pub const VcpkgLinkage = std.builtin.LinkMode; -pub const InstallDir = enum { - Prefix, - Lib, - Bin, - Header, +pub const InstallDir = union(enum) { + Prefix: void, + Lib: void, + Bin: void, + Header: void, + /// A path relative to the prefix + Custom: []const u8, }; pub const InstalledFile = struct { -- 2.54.0 From 0a76a9bd738471e2edddf71a822ff76c47bfd094 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Fri, 11 Sep 2020 01:22:18 +0200 Subject: [PATCH 15/23] std, stage1: make shared library versioning optional This commit changes the behavior of stage1 to emit libfoo.so instead of libfoo.so.0.0.0 when none of the --ver-major, --ver-minor, or --ver-patch flags are set. It also makes it possible to create unversioned shared libraries using the zig build system, changing the version parameter of addSharedLibrary() to a tagged union. --- lib/std/build.zig | 98 +++++++++++++++++++++++++++++++---------------- src/all_types.hpp | 1 + src/codegen.cpp | 6 ++- src/codegen.hpp | 2 +- src/glibc.cpp | 2 +- src/main.cpp | 8 +++- src/target.cpp | 18 ++++++--- src/target.hpp | 2 +- 8 files changed, 92 insertions(+), 45 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 1673737bef42afbc579f75187b9b30d8e5196a3b..8aa473f8bccb15a27bccb4a15d7c8f0da7e4bd4c 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -258,9 +258,14 @@ pub const Builder = struct { })); } - pub fn addSharedLibrary(self: *Builder, name: []const u8, root_src: ?[]const u8, ver: Version) *LibExeObjStep { + pub fn addSharedLibrary( + self: *Builder, + name: []const u8, + root_src: ?[]const u8, + kind: LibExeObjStep.SharedLibKind, + ) *LibExeObjStep { const root_src_param = if (root_src) |p| @as(FileSource, .{ .path = p }) else null; - return LibExeObjStep.createSharedLibrary(self, name, root_src_param, ver); + return LibExeObjStep.createSharedLibrary(self, name, root_src_param, kind); } pub fn addStaticLibrary(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { @@ -338,11 +343,13 @@ pub const Builder = struct { return TranslateCStep.create(self, source); } - pub fn version(self: *const Builder, major: u32, minor: u32, patch: u32) Version { - return Version{ - .major = major, - .minor = minor, - .patch = patch, + pub fn version(self: *const Builder, major: u32, minor: u32, patch: u32) LibExeObjStep.SharedLibKind { + return .{ + .versioned = .{ + .major = major, + .minor = minor, + .patch = patch, + }, }; } @@ -1166,7 +1173,7 @@ pub const LibExeObjStep = struct { version_script: ?[]const u8 = null, out_filename: []const u8, is_dynamic: bool, - version: Version, + version: ?Version, build_mode: builtin.Mode, kind: Kind, major_only_filename: []const u8, @@ -1268,33 +1275,41 @@ pub const LibExeObjStep = struct { Test, }; - pub fn createSharedLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource, ver: Version) *LibExeObjStep { + const SharedLibKind = union(enum) { + versioned: Version, + unversioned: void, + }; + + pub fn createSharedLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource, kind: SharedLibKind) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; - self.* = initExtraArgs(builder, name, root_src, Kind.Lib, true, ver); + self.* = initExtraArgs(builder, name, root_src, Kind.Lib, true, switch (kind) { + .versioned => |ver| ver, + .unversioned => null, + }); return self; } pub fn createStaticLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; - self.* = initExtraArgs(builder, name, root_src, Kind.Lib, false, builder.version(0, 0, 0)); + self.* = initExtraArgs(builder, name, root_src, Kind.Lib, false, null); return self; } pub fn createObject(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; - self.* = initExtraArgs(builder, name, root_src, Kind.Obj, false, builder.version(0, 0, 0)); + self.* = initExtraArgs(builder, name, root_src, Kind.Obj, false, null); return self; } pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?FileSource, is_dynamic: bool) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; - self.* = initExtraArgs(builder, name, root_src, Kind.Exe, is_dynamic, builder.version(0, 0, 0)); + self.* = initExtraArgs(builder, name, root_src, Kind.Exe, is_dynamic, null); return self; } pub fn createTest(builder: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; - self.* = initExtraArgs(builder, name, root_src, Kind.Test, false, builder.version(0, 0, 0)); + self.* = initExtraArgs(builder, name, root_src, Kind.Test, false, null); return self; } @@ -1304,7 +1319,7 @@ pub const LibExeObjStep = struct { root_src: ?FileSource, kind: Kind, is_dynamic: bool, - ver: Version, + ver: ?Version, ) LibExeObjStep { if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) { panic("invalid name: '{}'. It looks like a file path, but it is supposed to be the library or application name.", .{name}); @@ -1375,17 +1390,17 @@ pub const LibExeObjStep = struct { self.target.staticLibSuffix(), }); self.out_lib_filename = self.out_filename; - } else { + } else if (self.version) |version| { if (self.target.isDarwin()) { self.out_filename = self.builder.fmt("lib{}.{d}.{d}.{d}.dylib", .{ self.name, - self.version.major, - self.version.minor, - self.version.patch, + version.major, + version.minor, + version.patch, }); self.major_only_filename = self.builder.fmt("lib{}.{d}.dylib", .{ self.name, - self.version.major, + version.major, }); self.name_only_filename = self.builder.fmt("lib{}.dylib", .{self.name}); self.out_lib_filename = self.out_filename; @@ -1395,14 +1410,25 @@ pub const LibExeObjStep = struct { } else { self.out_filename = self.builder.fmt("lib{}.so.{d}.{d}.{d}", .{ self.name, - self.version.major, - self.version.minor, - self.version.patch, + version.major, + version.minor, + version.patch, }); - self.major_only_filename = self.builder.fmt("lib{}.so.{d}", .{ self.name, self.version.major }); + self.major_only_filename = self.builder.fmt("lib{}.so.{d}", .{ self.name, version.major }); self.name_only_filename = self.builder.fmt("lib{}.so", .{self.name}); self.out_lib_filename = self.out_filename; } + } else { + if (self.target.isDarwin()) { + self.out_filename = self.builder.fmt("lib{}.dylib", .{self.name}); + self.out_lib_filename = self.out_filename; + } else if (self.target.isWindows()) { + self.out_filename = self.builder.fmt("{}.dll", .{self.name}); + self.out_lib_filename = self.builder.fmt("{}.lib", .{self.name}); + } else { + self.out_filename = self.builder.fmt("lib{}.so", .{self.name}); + self.out_lib_filename = self.out_filename; + } } }, } @@ -2037,14 +2063,16 @@ pub const LibExeObjStep = struct { zig_args.append(self.name) catch unreachable; if (self.kind == Kind.Lib and self.is_dynamic) { - zig_args.append("--ver-major") catch unreachable; - zig_args.append(builder.fmt("{}", .{self.version.major})) catch unreachable; + if (self.version) |version| { + zig_args.append("--ver-major") catch unreachable; + zig_args.append(builder.fmt("{}", .{version.major})) catch unreachable; - zig_args.append("--ver-minor") catch unreachable; - zig_args.append(builder.fmt("{}", .{self.version.minor})) catch unreachable; + zig_args.append("--ver-minor") catch unreachable; + zig_args.append(builder.fmt("{}", .{version.minor})) catch unreachable; - zig_args.append("--ver-patch") catch unreachable; - zig_args.append(builder.fmt("{}", .{self.version.patch})) catch unreachable; + zig_args.append("--ver-patch") catch unreachable; + zig_args.append(builder.fmt("{}", .{version.patch})) catch unreachable; + } } if (self.is_dynamic) { try zig_args.append("-dynamic"); @@ -2285,7 +2313,7 @@ pub const LibExeObjStep = struct { } } - if (self.kind == Kind.Lib and self.is_dynamic and self.target.wantSharedLibSymLinks()) { + if (self.kind == Kind.Lib and self.is_dynamic and self.version != null and self.target.wantSharedLibSymLinks()) { try doAtomicSymLinks(builder.allocator, self.getOutputPath(), self.major_only_filename, self.name_only_filename); } } @@ -2329,8 +2357,10 @@ pub const InstallArtifactStep = struct { builder.pushInstalledFile(self.dest_dir, artifact.out_filename); if (self.artifact.isDynamicLibrary()) { - builder.pushInstalledFile(.Lib, artifact.major_only_filename); - builder.pushInstalledFile(.Lib, artifact.name_only_filename); + if (self.artifact.version != null) { + builder.pushInstalledFile(.Lib, artifact.major_only_filename); + builder.pushInstalledFile(.Lib, artifact.name_only_filename); + } if (self.artifact.target.isWindows()) { builder.pushInstalledFile(.Lib, artifact.out_lib_filename); } @@ -2350,7 +2380,7 @@ pub const InstallArtifactStep = struct { const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename); try builder.updateFile(self.artifact.getOutputPath(), full_dest_path); - if (self.artifact.isDynamicLibrary() and self.artifact.target.wantSharedLibSymLinks()) { + if (self.artifact.isDynamicLibrary() and self.artifact.version != null and self.artifact.target.wantSharedLibSymLinks()) { try doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename, self.artifact.name_only_filename); } if (self.pdb_dir) |pdb_dir| { diff --git a/src/all_types.hpp b/src/all_types.hpp index 3fbcc89585121aa1de79ff77048694b44ce65c41..1fa04f2b79fd1a7477bac41ea2df4e3d5f5f29b9 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2265,6 +2265,7 @@ struct CodeGen { Stage2LibCInstallation *libc; + bool is_versioned; size_t version_major; size_t version_minor; size_t version_patch; diff --git a/src/codegen.cpp b/src/codegen.cpp index 493bbbbae46f3c970d095d3040698d34368db527..b5c1ca3a4117a6d9d62fc63b16cfde51d0df66f8 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -90,7 +90,8 @@ void codegen_set_test_name_prefix(CodeGen *g, Buf *prefix) { g->test_name_prefix = prefix; } -void codegen_set_lib_version(CodeGen *g, size_t major, size_t minor, size_t patch) { +void codegen_set_lib_version(CodeGen *g, bool is_versioned, size_t major, size_t minor, size_t patch) { + g->is_versioned = is_versioned; g->version_major = major; g->version_minor = minor; g->version_patch = patch; @@ -10823,6 +10824,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_bool(ch, g->emit_bin); cache_bool(ch, g->emit_llvm_ir); cache_bool(ch, g->emit_asm); + cache_bool(ch, g->is_versioned); cache_usize(ch, g->version_major); cache_usize(ch, g->version_minor); cache_usize(ch, g->version_patch); @@ -10893,7 +10895,7 @@ static void resolve_out_paths(CodeGen *g) { buf_resize(out_basename, 0); buf_append_str(out_basename, target_lib_file_prefix(g->zig_target)); buf_append_buf(out_basename, g->root_out_name); - buf_append_str(out_basename, target_lib_file_ext(g->zig_target, !g->is_dynamic, + buf_append_str(out_basename, target_lib_file_ext(g->zig_target, !g->is_dynamic, g->is_versioned, g->version_major, g->version_minor, g->version_patch)); break; } diff --git a/src/codegen.hpp b/src/codegen.hpp index 191da9a04ba8e7d0dba03bc1eb600356d7e145cc..3139071d5267fcfa1d27a6425b0e184e633829f2 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -38,7 +38,7 @@ void codegen_set_rdynamic(CodeGen *g, bool rdynamic); void codegen_set_linker_script(CodeGen *g, const char *linker_script); void codegen_set_test_filter(CodeGen *g, Buf *filter); void codegen_set_test_name_prefix(CodeGen *g, Buf *prefix); -void codegen_set_lib_version(CodeGen *g, size_t major, size_t minor, size_t patch); +void codegen_set_lib_version(CodeGen *g, bool is_versioned, size_t major, size_t minor, size_t patch); void codegen_add_time_event(CodeGen *g, const char *name); void codegen_print_timing_report(CodeGen *g, FILE *f); void codegen_link(CodeGen *g); diff --git a/src/glibc.cpp b/src/glibc.cpp index 2456cab44cc44f31e1f96b5da7fce4f2b599a762..62f5604ba7ac3439d0f667ae1c7c55ec436165d2 100644 --- a/src/glibc.cpp +++ b/src/glibc.cpp @@ -335,7 +335,7 @@ Error glibc_build_dummies_and_maps(CodeGen *g, const ZigGLibCAbi *glibc_abi, con bool is_ld = (strcmp(lib->name, "ld") == 0); CodeGen *child_gen = create_child_codegen(g, zig_file_path, OutTypeLib, nullptr, lib->name, progress_node); - codegen_set_lib_version(child_gen, lib->sover, 0, 0); + codegen_set_lib_version(child_gen, true, lib->sover, 0, 0); child_gen->is_dynamic = true; child_gen->is_dummy_so = true; child_gen->version_script_path = map_file_path; diff --git a/src/main.cpp b/src/main.cpp index e2f6a82a12e9e0afa348cd38d98cf973268df96c..348321598c60f6310d501f34cf08aae5a79bed11 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -416,6 +416,7 @@ static int main0(int argc, char **argv) { const char *test_filter = nullptr; const char *test_name_prefix = nullptr; bool test_evented_io = false; + bool is_versioned = false; size_t ver_major = 0; size_t ver_minor = 0; size_t ver_patch = 0; @@ -870,6 +871,7 @@ static int main0(int argc, char **argv) { fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg)); return EXIT_FAILURE; } + is_versioned = true; ver_major = atoi(buf_ptr(linker_args.at(i))); } else if (buf_eql_str(arg, "--minor-image-version")) { i += 1; @@ -877,6 +879,7 @@ static int main0(int argc, char **argv) { fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg)); return EXIT_FAILURE; } + is_versioned = true; ver_minor = atoi(buf_ptr(linker_args.at(i))); } else if (buf_eql_str(arg, "--stack")) { i += 1; @@ -1228,10 +1231,13 @@ static int main0(int argc, char **argv) { } else if (strcmp(arg, "--test-name-prefix") == 0) { test_name_prefix = argv[i]; } else if (strcmp(arg, "--ver-major") == 0) { + is_versioned = true; ver_major = atoi(argv[i]); } else if (strcmp(arg, "--ver-minor") == 0) { + is_versioned = true; ver_minor = atoi(argv[i]); } else if (strcmp(arg, "--ver-patch") == 0) { + is_versioned = true; ver_patch = atoi(argv[i]); } else if (strcmp(arg, "--test-cmd") == 0) { test_exec_args.append(argv[i]); @@ -1590,7 +1596,7 @@ static int main0(int argc, char **argv) { g->emit_llvm_ir = emit_llvm_ir; codegen_set_out_name(g, buf_out_name); - codegen_set_lib_version(g, ver_major, ver_minor, ver_patch); + codegen_set_lib_version(g, is_versioned, ver_major, ver_minor, ver_patch); g->want_single_threaded = want_single_threaded; codegen_set_linker_script(g, linker_script); g->version_script_path = version_script; diff --git a/src/target.cpp b/src/target.cpp index 6c9916ebd3bc190b00375cbac2b0adec75d9bcbd..dff134a01d1fc78863183407855793795767db9c 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -779,7 +779,7 @@ const char *target_lib_file_prefix(const ZigTarget *target) { } } -const char *target_lib_file_ext(const ZigTarget *target, bool is_static, +const char *target_lib_file_ext(const ZigTarget *target, bool is_static, bool is_versioned, size_t version_major, size_t version_minor, size_t version_patch) { if (target_is_wasm(target)) { @@ -799,11 +799,19 @@ const char *target_lib_file_ext(const ZigTarget *target, bool is_static, if (is_static) { return ".a"; } else if (target_os_is_darwin(target->os)) { - return buf_ptr(buf_sprintf(".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".dylib", - version_major, version_minor, version_patch)); + if (is_versioned) { + return buf_ptr(buf_sprintf(".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".dylib", + version_major, version_minor, version_patch)); + } else { + return ".dylib"; + } } else { - return buf_ptr(buf_sprintf(".so.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize, - version_major, version_minor, version_patch)); + if (is_versioned) { + return buf_ptr(buf_sprintf(".so.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize, + version_major, version_minor, version_patch)); + } else { + return ".so"; + } } } } diff --git a/src/target.hpp b/src/target.hpp index 9a8e79de97c15d6d2c723abd6bc3acf8f6a5d551..5e44301ffff492a3dd0aa057f6765af18f49d409 100644 --- a/src/target.hpp +++ b/src/target.hpp @@ -87,7 +87,7 @@ const char *target_asm_file_ext(const ZigTarget *target); const char *target_llvm_ir_file_ext(const ZigTarget *target); const char *target_exe_file_ext(const ZigTarget *target); const char *target_lib_file_prefix(const ZigTarget *target); -const char *target_lib_file_ext(const ZigTarget *target, bool is_static, +const char *target_lib_file_ext(const ZigTarget *target, bool is_static, bool is_versioned, size_t version_major, size_t version_minor, size_t version_patch); bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target); -- 2.54.0 From bee34f5d036a74e520f5174fcaa132e6fb4114c6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 11 Sep 2020 11:30:21 -0700 Subject: [PATCH 16/23] add a manifesto to the readme --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index e72b166c77c22208dfa3fb1cb884db0e5c33974a..8031aa790e8981d700562c8d81983d5784b13514 100644 --- a/README.md +++ b/README.md @@ -76,3 +76,21 @@ Hopefully this will be fixed upstream with LLVM 10.0.1. ##### Windows See https://github.com/ziglang/zig/wiki/Building-Zig-on-Windows + +## License + +The ultimate goal of the Zig project is to serve users. As a first-order +effect, this means users of the compiler, helping programmers to write better +code. Even more important, however, are the end users. + +Zig is intended to be used to help end users accomplish their goals. For +example, it would be inappropriate and offensive to use Zig to implement +[dark patterns](https://en.wikipedia.org/wiki/Dark_pattern) and it would be +shameful to utilize Zig to exploit people instead of benefit them. + +However, such problems are best solved with social norms, not with software +licenses. Any attempt to complicate the software license of Zig would risk +compromising the value Zig provides to users. + +Therefore, Zig is available under the MIT (Expat) License, and comes with a +humble request: use it to make software better serve the needs of end users. -- 2.54.0 From 41f244bd2ff0a217a83a3d17fc82f4a773eca278 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Fri, 11 Sep 2020 11:00:13 +0200 Subject: [PATCH 17/23] std: Make the DEFLATE decompression routine 3x faster A profiler run showed that the main bottleneck was the naive decoding of the Huffman codes, replacing it with a nice trick borrowed by Zlib gave a substantial speedup. Replacing a `%` with a `and (mask-1)` gave another significant improvement (yay for low hanging fruits). A few numbers obtained by decompressing a 22M file: Before: ``` ./decompress 2,39s user 0,00s system 99% cpu 2,400 total ``` After: ``` ./decompress 0,79s user 0,00s system 99% cpu 0,798 total ```` --- lib/std/compress/deflate.zig | 184 ++++++++++++++++++++++++++++------- lib/std/compress/zlib.zig | 8 +- 2 files changed, 153 insertions(+), 39 deletions(-) diff --git a/lib/std/compress/deflate.zig b/lib/std/compress/deflate.zig index bad23349e87a134bae641e2119d86c2b6c2f734f..9fe96cacb7c748aa86d59f40d8c716100c94bf8b 100644 --- a/lib/std/compress/deflate.zig +++ b/lib/std/compress/deflate.zig @@ -21,48 +21,121 @@ const MAXDCODES = 30; const MAXCODES = MAXLCODES + MAXDCODES; const FIXLCODES = 288; +// The maximum length of a Huffman code's prefix we can decode using the fast +// path. The factor 9 is inherited from Zlib, tweaking the value showed little +// or no changes in the profiler output. +const PREFIX_LUT_BITS = 9; + const Huffman = struct { + // Number of codes for each possible length count: [MAXBITS + 1]u16, + // Mapping between codes and symbols symbol: [MAXCODES]u16, - fn construct(self: *Huffman, length: []const u16) !void { + // The decoding process uses a trick explained by Mark Adler in [1]. + // We basically precompute for a fixed number of codes (0 <= x <= 2^N-1) + // the symbol and the effective code length we'd get if the decoder was run + // on the given N-bit sequence. + // A code with length 0 means the sequence is not a valid prefix for this + // canonical Huffman code and we have to decode it using a slower method. + // + // [1] https://github.com/madler/zlib/blob/v1.2.11/doc/algorithm.txt#L58 + prefix_lut: [1 << PREFIX_LUT_BITS]u16, + prefix_lut_len: [1 << PREFIX_LUT_BITS]u16, + // The following info refer to the codes of length PREFIX_LUT_BITS+1 and are + // used to bootstrap the bit-by-bit reading method if the fast-path fails. + last_code: u16, + last_index: u16, + + fn construct(self: *Huffman, code_length: []const u16) !void { for (self.count) |*val| { val.* = 0; } - for (length) |val| { - self.count[val] += 1; + for (code_length) |len| { + self.count[len] += 1; } - if (self.count[0] == length.len) + // All zero. + if (self.count[0] == code_length.len) return; var left: isize = 1; for (self.count[1..]) |val| { + // Each added bit doubles the amount of codes. left *= 2; + // Make sure the number of codes with this length isn't too high. left -= @as(isize, @bitCast(i16, val)); if (left < 0) return error.InvalidTree; } - var offs: [MAXBITS + 1]u16 = undefined; + // Compute the offset of the first symbol represented by a code of a + // given length in the symbol table, together with the first canonical + // Huffman code for that length. + var offset: [MAXBITS + 1]u16 = undefined; + var codes: [MAXBITS + 1]u16 = undefined; { + offset[1] = 0; + codes[1] = 0; var len: usize = 1; - offs[1] = 0; while (len < MAXBITS) : (len += 1) { - offs[len + 1] = offs[len] + self.count[len]; + offset[len + 1] = offset[len] + self.count[len]; + codes[len + 1] = (codes[len] + self.count[len]) << 1; } } - for (length) |val, symbol| { - if (val != 0) { - self.symbol[offs[val]] = @truncate(u16, symbol); - offs[val] += 1; + self.prefix_lut_len = mem.zeroes(@TypeOf(self.prefix_lut_len)); + + for (code_length) |len, symbol| { + if (len != 0) { + // Fill the symbol table. + // The symbols are assigned sequentially for each length. + self.symbol[offset[len]] = @truncate(u16, symbol); + // Track the last assigned offset + offset[len] += 1; + } + + if (len == 0 or len > PREFIX_LUT_BITS) + continue; + + // Given a Huffman code of length N we have to massage it so + // that it becomes an index in the lookup table. + // The bit order is reversed as the fast path reads the bit + // sequence MSB to LSB using an &, the order is flipped wrt the + // one obtained by reading bit-by-bit. + // The codes are prefix-free, if the prefix matches we can + // safely ignore the trail bits. We do so by replicating the + // symbol info for each combination of the trailing bits. + const bits_to_fill = @intCast(u5, PREFIX_LUT_BITS - len); + const rev_code = bitReverse(codes[len], len); + // Track the last used code, but only for lengths < PREFIX_LUT_BITS + codes[len] += 1; + + var j: usize = 0; + while (j < @as(usize, 1) << bits_to_fill) : (j += 1) { + const index = rev_code | (j << @intCast(u5, len)); + assert(self.prefix_lut_len[index] == 0); + self.prefix_lut[index] = @truncate(u16, symbol); + self.prefix_lut_len[index] = @truncate(u16, len); } } + + self.last_code = codes[PREFIX_LUT_BITS + 1]; + self.last_index = offset[PREFIX_LUT_BITS + 1] - self.count[PREFIX_LUT_BITS + 1]; } }; +// Reverse bit-by-bit a N-bit value +fn bitReverse(x: usize, N: usize) usize { + var tmp: usize = 0; + var i: usize = 0; + while (i < N) : (i += 1) { + tmp |= ((x >> @intCast(u5, i)) & 1) << @intCast(u5, N - i - 1); + } + return tmp; +} + pub fn InflateStream(comptime ReaderType: type) type { return struct { const Self = @This(); @@ -83,7 +156,7 @@ pub fn InflateStream(comptime ReaderType: type) type { }; pub const Reader = io.Reader(*Self, Error, read); - bit_reader: io.BitReader(.Little, ReaderType), + inner_reader: ReaderType, // True if the decoder met the end of the compressed stream, no further // data can be decompressed @@ -135,7 +208,7 @@ pub fn InflateStream(comptime ReaderType: type) type { // Insert a single byte into the window. // Assumes there's enough space. - fn appendUnsafe(self: *WSelf, value: u8) void { + inline fn appendUnsafe(self: *WSelf, value: u8) void { self.buf[self.wi] = value; self.wi = (self.wi + 1) & (self.buf.len - 1); self.el += 1; @@ -180,7 +253,7 @@ pub fn InflateStream(comptime ReaderType: type) type { // of the window memory for the non-overlapping case. var i: usize = 0; while (i < N) : (i += 1) { - const index = (self.wi -% distance) % self.buf.len; + const index = (self.wi -% distance) & (self.buf.len - 1); self.appendUnsafe(self.buf[index]); } @@ -196,13 +269,36 @@ pub fn InflateStream(comptime ReaderType: type) type { hdist: *Huffman, hlen: *Huffman, + // Temporary buffer for the bitstream, only bits 0..`bits_left` are + // considered valid. + bits: u32, + bits_left: usize, + + fn peekBits(self: *Self, bits: usize) !u32 { + while (self.bits_left < bits) { + const byte = try self.inner_reader.readByte(); + self.bits |= @as(u32, byte) << @intCast(u5, self.bits_left); + self.bits_left += 8; + } + return self.bits & ((@as(u32, 1) << @intCast(u5, bits)) - 1); + } + fn readBits(self: *Self, bits: usize) !u32 { + const val = self.peekBits(bits); + self.discardBits(bits); + return val; + } + fn discardBits(self: *Self, bits: usize) void { + self.bits >>= @intCast(u5, bits); + self.bits_left -= bits; + } + fn stored(self: *Self) !void { // Discard the remaining bits, the lenght field is always // byte-aligned (and so is the data) - self.bit_reader.alignToByte(); + self.discardBits(self.bits_left); - const length = (try self.bit_reader.readBitsNoEof(u16, 16)); - const length_cpl = (try self.bit_reader.readBitsNoEof(u16, 16)); + const length = try self.inner_reader.readIntLittle(u16); + const length_cpl = try self.inner_reader.readIntLittle(u16); if (length != ~length_cpl) return error.InvalidStoredSize; @@ -237,11 +333,11 @@ pub fn InflateStream(comptime ReaderType: type) type { fn dynamic(self: *Self) !void { // Number of length codes - const nlen = (try self.bit_reader.readBitsNoEof(usize, 5)) + 257; + const nlen = (try self.readBits(5)) + 257; // Number of distance codes - const ndist = (try self.bit_reader.readBitsNoEof(usize, 5)) + 1; + const ndist = (try self.readBits(5)) + 1; // Number of code length codes - const ncode = (try self.bit_reader.readBitsNoEof(usize, 4)) + 4; + const ncode = (try self.readBits(4)) + 4; if (nlen > MAXLCODES or ndist > MAXDCODES) return error.BadCounts; @@ -259,7 +355,7 @@ pub fn InflateStream(comptime ReaderType: type) type { // Read the code lengths, missing ones are left as zero for (ORDER[0..ncode]) |val| { - lengths[val] = try self.bit_reader.readBitsNoEof(u16, 3); + lengths[val] = @intCast(u16, try self.readBits(3)); } try lencode.construct(lengths[0..]); @@ -284,7 +380,7 @@ pub fn InflateStream(comptime ReaderType: type) type { if (i == 0) return error.NoLastLength; const last_length = lengths[i - 1]; - const repeat = 3 + (try self.bit_reader.readBitsNoEof(usize, 2)); + const repeat = 3 + (try self.readBits(2)); const last_index = i + repeat; while (i < last_index) : (i += 1) { lengths[i] = last_length; @@ -292,11 +388,11 @@ pub fn InflateStream(comptime ReaderType: type) type { }, 17 => { // repeat zero 3..10 times - i += 3 + (try self.bit_reader.readBitsNoEof(usize, 3)); + i += 3 + (try self.readBits(3)); }, 18 => { // repeat zero 11..138 times - i += 11 + (try self.bit_reader.readBitsNoEof(usize, 7)); + i += 11 + (try self.readBits(7)); }, else => return error.InvalidSymbol, } @@ -359,11 +455,11 @@ pub fn InflateStream(comptime ReaderType: type) type { // Length/distance pair const length_symbol = symbol - 257; const length = LENS[length_symbol] + - try self.bit_reader.readBitsNoEof(u16, LEXT[length_symbol]); + @intCast(u16, try self.readBits(LEXT[length_symbol])); const distance_symbol = try self.decode(distcode); const distance = DISTS[distance_symbol] + - try self.bit_reader.readBitsNoEof(u16, DEXT[distance_symbol]); + @intCast(u16, try self.readBits(DEXT[distance_symbol])); if (distance > self.window.buf.len) return error.InvalidDistance; @@ -385,13 +481,29 @@ pub fn InflateStream(comptime ReaderType: type) type { } fn decode(self: *Self, h: *Huffman) !u16 { - var len: usize = 1; - var code: usize = 0; - var first: usize = 0; - var index: usize = 0; + // Fast path, read some bits and hope they're prefixes of some code + const prefix = try self.peekBits(PREFIX_LUT_BITS); + if (h.prefix_lut_len[prefix] != 0) { + self.discardBits(h.prefix_lut_len[prefix]); + return h.prefix_lut[prefix]; + } + + // The sequence we've read is not a prefix of any code of length <= + // PREFIX_LUT_BITS, keep decoding it using a slower method + self.discardBits(PREFIX_LUT_BITS); + + // Speed up the decoding by starting from the first code length + // that's not covered by the table + var len: usize = PREFIX_LUT_BITS + 1; + var first: usize = h.last_code; + var index: usize = h.last_index; + + // Reverse the prefix so that the LSB becomes the MSB and make space + // for the next bit + var code = bitReverse(prefix, PREFIX_LUT_BITS + 1); while (len <= MAXBITS) : (len += 1) { - code |= try self.bit_reader.readBitsNoEof(usize, 1); + code |= try self.readBits(1); const count = h.count[len]; if (code < first + count) return h.symbol[index + (code - first)]; @@ -411,8 +523,8 @@ pub fn InflateStream(comptime ReaderType: type) type { // The compressed stream is done if (self.seen_eos) return; - const last = try self.bit_reader.readBitsNoEof(u1, 1); - const kind = try self.bit_reader.readBitsNoEof(u2, 2); + const last = @intCast(u1, try self.readBits(1)); + const kind = @intCast(u2, try self.readBits(2)); self.seen_eos = last != 0; @@ -439,7 +551,7 @@ pub fn InflateStream(comptime ReaderType: type) type { var i: usize = 0; while (i < N) : (i += 1) { var tmp: [1]u8 = undefined; - if ((try self.bit_reader.read(&tmp)) != 1) { + if ((try self.inner_reader.read(&tmp)) != 1) { // Unexpected end of stream, keep this error // consistent with the use of readBitsNoEof return error.EndOfStream; @@ -478,12 +590,14 @@ pub fn InflateStream(comptime ReaderType: type) type { assert(math.isPowerOfTwo(window_slice.len)); return Self{ - .bit_reader = io.bitReader(.Little, source), + .inner_reader = source, .window = .{ .buf = window_slice }, .seen_eos = false, .state = .DecodeBlockHeader, .hdist = undefined, .hlen = undefined, + .bits = 0, + .bits_left = 0, }; } diff --git a/lib/std/compress/zlib.zig b/lib/std/compress/zlib.zig index d4bac4a8a414d017db4ac826c2ca96a92b65a2d7..acda7eadeb534ee7c359bc2de491476e818506ea 100644 --- a/lib/std/compress/zlib.zig +++ b/lib/std/compress/zlib.zig @@ -138,10 +138,10 @@ test "compressed data" { "5ebf4b5b7fe1c3a0c0ab9aa3ac8c0f3853a7dc484905e76e03b0b0f301350009", ); // Compressed with compression level = 9 and fixed Huffman codes - try testReader( - @embedFile("rfc1951.txt.fixed.z.9"), - "5ebf4b5b7fe1c3a0c0ab9aa3ac8c0f3853a7dc484905e76e03b0b0f301350009", - ); + // try testReader( + // @embedFile("rfc1951.txt.fixed.z.9"), + // "5ebf4b5b7fe1c3a0c0ab9aa3ac8c0f3853a7dc484905e76e03b0b0f301350009", + // ); } test "sanity checks" { -- 2.54.0 From 4b851c7255d726f5743c42ae80a71ce7cf47c486 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Fri, 11 Sep 2020 21:04:08 +0200 Subject: [PATCH 18/23] Re-enable a compression test It somehow got commented out... --- lib/std/compress/zlib.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/std/compress/zlib.zig b/lib/std/compress/zlib.zig index acda7eadeb534ee7c359bc2de491476e818506ea..d4bac4a8a414d017db4ac826c2ca96a92b65a2d7 100644 --- a/lib/std/compress/zlib.zig +++ b/lib/std/compress/zlib.zig @@ -138,10 +138,10 @@ test "compressed data" { "5ebf4b5b7fe1c3a0c0ab9aa3ac8c0f3853a7dc484905e76e03b0b0f301350009", ); // Compressed with compression level = 9 and fixed Huffman codes - // try testReader( - // @embedFile("rfc1951.txt.fixed.z.9"), - // "5ebf4b5b7fe1c3a0c0ab9aa3ac8c0f3853a7dc484905e76e03b0b0f301350009", - // ); + try testReader( + @embedFile("rfc1951.txt.fixed.z.9"), + "5ebf4b5b7fe1c3a0c0ab9aa3ac8c0f3853a7dc484905e76e03b0b0f301350009", + ); } test "sanity checks" { -- 2.54.0 From 7b961a876b56d5926fe7ba6437a0459da9aa60bc Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Fri, 11 Sep 2020 01:34:10 +0200 Subject: [PATCH 19/23] std: add prctl definition for linux --- lib/std/os/bits/linux.zig | 1 + lib/std/os/bits/linux/prctl.zig | 158 ++++++++++++++++++++++++++++++++ lib/std/os/linux.zig | 4 + 3 files changed, 163 insertions(+) create mode 100644 lib/std/os/bits/linux/prctl.zig diff --git a/lib/std/os/bits/linux.zig b/lib/std/os/bits/linux.zig index f393f04abe91e5432bacb6ce5ba6ea63e52e40e5..e8625179dbc50ede959b168384a31e43f7c48911 100644 --- a/lib/std/os/bits/linux.zig +++ b/lib/std/os/bits/linux.zig @@ -25,6 +25,7 @@ pub usingnamespace switch (builtin.arch) { }; pub usingnamespace @import("linux/netlink.zig"); +pub usingnamespace @import("linux/prctl.zig"); const is_mips = builtin.arch.isMIPS(); diff --git a/lib/std/os/bits/linux/prctl.zig b/lib/std/os/bits/linux/prctl.zig new file mode 100644 index 0000000000000000000000000000000000000000..7fa9969af45f7bbc0fc623abd74b3bbca1b67625 --- /dev/null +++ b/lib/std/os/bits/linux/prctl.zig @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2015-2020 Zig Contributors +// This file is part of [zig](https://ziglang.org/), which is MIT licensed. +// The MIT license requires this copyright notice to be included in all copies +// and substantial portions of the software. + +pub const PR_SET_PDEATHSIG = 1; +pub const PR_GET_PDEATHSIG = 2; + +pub const PR_GET_DUMPABLE = 3; +pub const PR_SET_DUMPABLE = 4; + +pub const PR_GET_UNALIGN = 5; +pub const PR_SET_UNALIGN = 6; +pub const PR_UNALIGN_NOPRINT = 1; +pub const PR_UNALIGN_SIGBUS = 2; + +pub const PR_GET_KEEPCAPS = 7; +pub const PR_SET_KEEPCAPS = 8; + +pub const PR_GET_FPEMU = 9; +pub const PR_SET_FPEMU = 10; +pub const PR_FPEMU_NOPRINT = 1; +pub const PR_FPEMU_SIGFPE = 2; + +pub const PR_GET_FPEXC = 11; +pub const PR_SET_FPEXC = 12; +pub const PR_FP_EXC_SW_ENABLE = 0x80; +pub const PR_FP_EXC_DIV = 0x010000; +pub const PR_FP_EXC_OVF = 0x020000; +pub const PR_FP_EXC_UND = 0x040000; +pub const PR_FP_EXC_RES = 0x080000; +pub const PR_FP_EXC_INV = 0x100000; +pub const PR_FP_EXC_DISABLED = 0; +pub const PR_FP_EXC_NONRECOV = 1; +pub const PR_FP_EXC_ASYNC = 2; +pub const PR_FP_EXC_PRECISE = 3; + +pub const PR_GET_TIMING = 13; +pub const PR_SET_TIMING = 14; +pub const PR_TIMING_STATISTICAL = 0; +pub const PR_TIMING_TIMESTAMP = 1; + +pub const PR_SET_NAME = 15; +pub const PR_GET_NAME = 16; + +pub const PR_GET_ENDIAN = 19; +pub const PR_SET_ENDIAN = 20; +pub const PR_ENDIAN_BIG = 0; +pub const PR_ENDIAN_LITTLE = 1; +pub const PR_ENDIAN_PPC_LITTLE = 2; + +pub const PR_GET_SECCOMP = 21; +pub const PR_SET_SECCOMP = 22; + +pub const PR_CAPBSET_READ = 23; +pub const PR_CAPBSET_DROP = 24; + +pub const PR_GET_TSC = 25; +pub const PR_SET_TSC = 26; +pub const PR_TSC_ENABLE = 1; +pub const PR_TSC_SIGSEGV = 2; + +pub const PR_GET_SECUREBITS = 27; +pub const PR_SET_SECUREBITS = 28; + +pub const PR_SET_TIMERSLACK = 29; +pub const PR_GET_TIMERSLACK = 30; + +pub const PR_TASK_PERF_EVENTS_DISABLE = 31; +pub const PR_TASK_PERF_EVENTS_ENABLE = 32; + +pub const PR_MCE_KILL = 33; +pub const PR_MCE_KILL_CLEAR = 0; +pub const PR_MCE_KILL_SET = 1; + +pub const PR_MCE_KILL_LATE = 0; +pub const PR_MCE_KILL_EARLY = 1; +pub const PR_MCE_KILL_DEFAULT = 2; + +pub const PR_MCE_KILL_GET = 34; + +pub const PR_SET_MM = 35; +pub const PR_SET_MM_START_CODE = 1; +pub const PR_SET_MM_END_CODE = 2; +pub const PR_SET_MM_START_DATA = 3; +pub const PR_SET_MM_END_DATA = 4; +pub const PR_SET_MM_START_STACK = 5; +pub const PR_SET_MM_START_BRK = 6; +pub const PR_SET_MM_BRK = 7; +pub const PR_SET_MM_ARG_START = 8; +pub const PR_SET_MM_ARG_END = 9; +pub const PR_SET_MM_ENV_START = 10; +pub const PR_SET_MM_ENV_END = 11; +pub const PR_SET_MM_AUXV = 12; +pub const PR_SET_MM_EXE_FILE = 13; +pub const PR_SET_MM_MAP = 14; +pub const PR_SET_MM_MAP_SIZE = 15; + +pub const prctl_mm_map = extern struct { + start_code: u64, + end_code: u64, + start_data: u64, + end_data: u64, + start_brk: u64, + brk: u64, + start_stack: u64, + arg_start: u64, + arg_end: u64, + env_start: u64, + env_end: u64, + auxv: *u64, + auxv_size: u32, + exe_fd: u32, +}; + +pub const PR_SET_PTRACER = 0x59616d61; +pub const PR_SET_PTRACER_ANY = std.math.maxInt(c_ulong); + +pub const PR_SET_CHILD_SUBREAPER = 36; +pub const PR_GET_CHILD_SUBREAPER = 37; + +pub const PR_SET_NO_NEW_PRIVS = 38; +pub const PR_GET_NO_NEW_PRIVS = 39; + +pub const PR_GET_TID_ADDRESS = 40; + +pub const PR_SET_THP_DISABLE = 41; +pub const PR_GET_THP_DISABLE = 42; + +pub const PR_MPX_ENABLE_MANAGEMENT = 43; +pub const PR_MPX_DISABLE_MANAGEMENT = 44; + +pub const PR_SET_FP_MODE = 45; +pub const PR_GET_FP_MODE = 46; +pub const PR_FP_MODE_FR = 1 << 0; +pub const PR_FP_MODE_FRE = 1 << 1; + +pub const PR_CAP_AMBIENT = 47; +pub const PR_CAP_AMBIENT_IS_SET = 1; +pub const PR_CAP_AMBIENT_RAISE = 2; +pub const PR_CAP_AMBIENT_LOWER = 3; +pub const PR_CAP_AMBIENT_CLEAR_ALL = 4; + +pub const PR_SVE_SET_VL = 50; +pub const PR_SVE_SET_VL_ONEXEC = 1 << 18; +pub const PR_SVE_GET_VL = 51; +pub const PR_SVE_VL_LEN_MASK = 0xffff; +pub const PR_SVE_VL_INHERIT = 1 << 17; + +pub const PR_GET_SPECULATION_CTRL = 52; +pub const PR_SET_SPECULATION_CTRL = 53; +pub const PR_SPEC_STORE_BYPASS = 0; +pub const PR_SPEC_NOT_AFFECTED = 0; +pub const PR_SPEC_PRCTL = 1 << 0; +pub const PR_SPEC_ENABLE = 1 << 1; +pub const PR_SPEC_DISABLE = 1 << 2; +pub const PR_SPEC_FORCE_DISABLE = 1 << 3; diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 25a0d6814d05d0c9b9437852d02777f5c3b641e1..50d1e4ae7867e8e50e00d7d1c84174e3ecf48cad 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -1259,6 +1259,10 @@ pub fn fdatasync(fd: fd_t) usize { return syscall1(.fdatasync, @bitCast(usize, @as(isize, fd))); } +pub fn prctl(option: i32, arg2: usize, arg3: usize, arg4: usize, arg5: usize) usize { + return syscall5(.prctl, @bitCast(usize, @as(isize, option)), arg2, arg3, arg4, arg5); +} + test "" { if (builtin.os.tag == .linux) { _ = @import("linux/test.zig"); -- 2.54.0 From 744b73ab46a75140537f9cf705939ecf0a2d229d Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Fri, 11 Sep 2020 14:10:55 +0200 Subject: [PATCH 20/23] std: add prctl wrapper to std.os --- lib/std/os.zig | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/lib/std/os.zig b/lib/std/os.zig index 25c71e5d15df8d1376ce7d9c709545bdc88c6fe7..91365c81dde03c87bd690ae2a87bb825b2774770 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -5418,3 +5418,42 @@ pub fn fdatasync(fd: fd_t) SyncError!void { else => |err| return std.os.unexpectedErrno(err), } } + +pub const PrctlError = error{ + /// Can only occur with PR_SET_SECCOMP/SECCOMP_MODE_FILTER or + /// PR_SET_MM/PR_SET_MM_EXE_FILE + AccessDenied, + /// Can only occur with PR_SET_MM/PR_SET_MM_EXE_FILE + InvalidFileDescriptor, + InvalidAddress, + /// Can only occur with PR_SET_SPECULATION_CTRL, PR_MPX_ENABLE_MANAGEMENT, + /// or PR_MPX_DISABLE_MANAGEMENT + UnsupportedFeature, + /// Can only occur wih PR_SET_FP_MODE + OperationNotSupported, + PermissionDenied, +} || UnexpectedError; + +pub fn prctl(option: i32, args: anytype) PrctlError!u31 { + if (@typeInfo(@TypeOf(args)) != .Struct) + @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args))); + if (args.len > 4) + @compileError("prctl takes a maximum of 4 optional arguments"); + + var buf: [4]usize = undefined; + inline for (args) |arg, i| buf[i] = arg; + + const rc = system.prctl(option, buf[0], buf[1], buf[2], buf[3]); + switch (errno(rc)) { + 0 => return @intCast(u31, rc), + EACCES => return error.AccessDenied, + EBADF => return error.InvalidFileDescriptor, + EFAULT => return error.InvalidAddress, + EINVAL => unreachable, + ENODEV, ENXIO => return error.UnsupportedFeature, + EOPNOTSUPP => return error.OperationNotSupported, + EPERM, EBUSY => return error.PermissionDenied, + ERANGE => unreachable, + else => |err| return std.os.unexpectedErrno(err), + } +} -- 2.54.0 From b6f4601545547fff7afa19cba0f26354d651e818 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Fri, 11 Sep 2020 14:19:59 +0200 Subject: [PATCH 21/23] std: add securebits definitions for linux --- lib/std/os/bits/linux.zig | 1 + lib/std/os/bits/linux/securebits.zig | 41 ++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 lib/std/os/bits/linux/securebits.zig diff --git a/lib/std/os/bits/linux.zig b/lib/std/os/bits/linux.zig index e8625179dbc50ede959b168384a31e43f7c48911..df31bc32fde6fa246d42b40036ccb6d7cf58a406 100644 --- a/lib/std/os/bits/linux.zig +++ b/lib/std/os/bits/linux.zig @@ -26,6 +26,7 @@ pub usingnamespace switch (builtin.arch) { pub usingnamespace @import("linux/netlink.zig"); pub usingnamespace @import("linux/prctl.zig"); +pub usingnamespace @import("linux/securebits.zig"); const is_mips = builtin.arch.isMIPS(); diff --git a/lib/std/os/bits/linux/securebits.zig b/lib/std/os/bits/linux/securebits.zig new file mode 100644 index 0000000000000000000000000000000000000000..0086a694d9b2f658fefebe61962e9562f2eaeccf --- /dev/null +++ b/lib/std/os/bits/linux/securebits.zig @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2015-2020 Zig Contributors +// This file is part of [zig](https://ziglang.org/), which is MIT licensed. +// The MIT license requires this copyright notice to be included in all copies +// and substantial portions of the software. + +fn issecure_mask(comptime x: comptime_int) comptime_int { + return 1 << x; +} + +pub const SECUREBITS_DEFAULT = 0x00000000; + +pub const SECURE_NOROOT = 0; +pub const SECURE_NOROOT_LOCKED = 1; + +pub const SECBIT_NOROOT = issecure_mask(SECURE_NOROOT); +pub const SECBIT_NOROOT_LOCKED = issecure_mask(SECURE_NOROOT_LOCKED); + +pub const SECURE_NO_SETUID_FIXUP = 2; +pub const SECURE_NO_SETUID_FIXUP_LOCKED = 3; + +pub const SECBIT_NO_SETUID_FIXUP = issecure_mask(SECURE_NO_SETUID_FIXUP); +pub const SECBIT_NO_SETUID_FIXUP_LOCKED = issecure_mask(SECURE_NO_SETUID_FIXUP_LOCKED); + +pub const SECURE_KEEP_CAPS = 4; +pub const SECURE_KEEP_CAPS_LOCKED = 5; + +pub const SECBIT_KEEP_CAPS = issecure_mask(SECURE_KEEP_CAPS); +pub const SECBIT_KEEP_CAPS_LOCKED = issecure_mask(SECURE_KEEP_CAPS_LOCKED); + +pub const SECURE_NO_CAP_AMBIENT_RAISE = 6; +pub const SECURE_NO_CAP_AMBIENT_RAISE_LOCKED = 7; + +pub const SECBIT_NO_CAP_AMBIENT_RAISE = issecure_mask(SECURE_NO_CAP_AMBIENT_RAISE); +pub const SECBIT_NO_CAP_AMBIENT_RAISE_LOCKED = issecure_mask(SECURE_NO_CAP_AMBIENT_RAISE_LOCKED); + +pub const SECURE_ALL_BITS = issecure_mask(SECURE_NOROOT) | + issecure_mask(SECURE_NO_SETUID_FIXUP) | + issecure_mask(SECURE_KEEP_CAPS) | + issecure_mask(SECURE_NO_CAP_AMBIENT_RAISE); +pub const SECURE_ALL_LOCKS = SECURE_ALL_BITS << 1; -- 2.54.0 From 1078810cef4b346bdcd0ab0cab27dd997e68d206 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Fri, 11 Sep 2020 16:32:24 +0200 Subject: [PATCH 22/23] std: add prctl definition when linking libc --- lib/std/c.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/std/c.zig b/lib/std/c.zig index bbc32cf351b8f0dce7ebca62b30faa7111009cc6..aa50fff90eea3a1dd5b8549d03012a533a5ae3b2 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -340,3 +340,5 @@ pub extern "c" fn sync() void; pub extern "c" fn syncfs(fd: c_int) c_int; pub extern "c" fn fsync(fd: c_int) c_int; pub extern "c" fn fdatasync(fd: c_int) c_int; + +pub extern "c" fn prctl(option: c_int, ...) c_int; -- 2.54.0 From 9fe4c89230df2d78c8bf37b4b1d7a9bedb92677b Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Fri, 11 Sep 2020 22:17:08 +0200 Subject: [PATCH 23/23] std: Add a gzip decoder --- build.zig | 1 + lib/std/compress.zig | 2 + lib/std/compress/gzip.zig | 248 ++++++++++++++++++++++++++++++++ lib/std/compress/rfc1952.txt.gz | Bin 0 -> 8059 bytes 4 files changed, 251 insertions(+) create mode 100644 lib/std/compress/gzip.zig create mode 100644 lib/std/compress/rfc1952.txt.gz diff --git a/build.zig b/build.zig index 3f7f1a90383f0651c94b4f56b07b3df69bb16efe..a6a2d873716db0c031cfbb8094f96cd745434a04 100644 --- a/build.zig +++ b/build.zig @@ -128,6 +128,7 @@ pub fn build(b: *Builder) !void { "README.md", ".z.0", ".z.9", + ".gz", "rfc1951.txt", }, }); diff --git a/lib/std/compress.zig b/lib/std/compress.zig index 5518f807df2a2fb777f1d2f19a7662b1585d3859..95f496021ec4f64c2b027a067ad6d7da4a589e47 100644 --- a/lib/std/compress.zig +++ b/lib/std/compress.zig @@ -6,8 +6,10 @@ const std = @import("std.zig"); pub const deflate = @import("compress/deflate.zig"); +pub const gzip = @import("compress/gzip.zig"); pub const zlib = @import("compress/zlib.zig"); test "" { + _ = gzip; _ = zlib; } diff --git a/lib/std/compress/gzip.zig b/lib/std/compress/gzip.zig new file mode 100644 index 0000000000000000000000000000000000000000..aad17313935211de1da400dcf4e87d6682aae5ae --- /dev/null +++ b/lib/std/compress/gzip.zig @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2015-2020 Zig Contributors +// This file is part of [zig](https://ziglang.org/), which is MIT licensed. +// The MIT license requires this copyright notice to be included in all copies +// and substantial portions of the software. +// +// Decompressor for GZIP data streams (RFC1952) + +const std = @import("std"); +const io = std.io; +const fs = std.fs; +const testing = std.testing; +const mem = std.mem; +const deflate = std.compress.deflate; + +// Flags for the FLG field in the header +const FTEXT = 1 << 0; +const FHCRC = 1 << 1; +const FEXTRA = 1 << 2; +const FNAME = 1 << 3; +const FCOMMENT = 1 << 4; + +pub fn GzipStream(comptime ReaderType: type) type { + return struct { + const Self = @This(); + + pub const Error = ReaderType.Error || + deflate.InflateStream(ReaderType).Error || + error{ CorruptedData, WrongChecksum }; + pub const Reader = io.Reader(*Self, Error, read); + + allocator: *mem.Allocator, + inflater: deflate.InflateStream(ReaderType), + in_reader: ReaderType, + hasher: std.hash.Crc32, + window_slice: []u8, + read_amt: usize, + + info: struct { + filename: ?[]const u8, + comment: ?[]const u8, + modification_time: u32, + }, + + fn init(allocator: *mem.Allocator, source: ReaderType) !Self { + // gzip header format is specified in RFC1952 + const header = try source.readBytesNoEof(10); + + // Check the ID1/ID2 fields + if (header[0] != 0x1f or header[1] != 0x8b) + return error.BadHeader; + + const CM = header[2]; + // The CM field must be 8 to indicate the use of DEFLATE + if (CM != 8) return error.InvalidCompression; + // Flags + const FLG = header[3]; + // Modification time, as a Unix timestamp. + // If zero there's no timestamp available. + const MTIME = mem.readIntLittle(u32, header[4..8]); + // Extra flags + const XFL = header[8]; + // Operating system where the compression took place + const OS = header[9]; + + if (FLG & FEXTRA != 0) { + // Skip the extra data, we could read and expose it to the user + // if somebody needs it. + const len = try source.readIntLittle(u16); + try source.skipBytes(len, .{}); + } + + var filename: ?[]const u8 = null; + if (FLG & FNAME != 0) { + filename = try source.readUntilDelimiterAlloc( + allocator, + 0, + std.math.maxInt(usize), + ); + } + errdefer if (filename) |p| allocator.free(p); + + var comment: ?[]const u8 = null; + if (FLG & FCOMMENT != 0) { + comment = try source.readUntilDelimiterAlloc( + allocator, + 0, + std.math.maxInt(usize), + ); + } + errdefer if (comment) |p| allocator.free(p); + + if (FLG & FHCRC != 0) { + // TODO: Evaluate and check the header checksum. The stdlib has + // no CRC16 yet :( + _ = try source.readIntLittle(u16); + } + + // The RFC doesn't say anything about the DEFLATE window size to be + // used, default to 32K. + var window_slice = try allocator.alloc(u8, 32 * 1024); + + return Self{ + .allocator = allocator, + .inflater = deflate.inflateStream(source, window_slice), + .in_reader = source, + .hasher = std.hash.Crc32.init(), + .window_slice = window_slice, + .info = .{ + .filename = filename, + .comment = comment, + .modification_time = MTIME, + }, + .read_amt = 0, + }; + } + + pub fn deinit(self: *Self) void { + self.allocator.free(self.window_slice); + if (self.info.filename) |filename| + self.allocator.free(filename); + if (self.info.comment) |comment| + self.allocator.free(comment); + } + + // Implements the io.Reader interface + pub fn read(self: *Self, buffer: []u8) Error!usize { + if (buffer.len == 0) + return 0; + + // Read from the compressed stream and update the computed checksum + const r = try self.inflater.read(buffer); + if (r != 0) { + self.hasher.update(buffer[0..r]); + self.read_amt += r; + return r; + } + + // We've reached the end of stream, check if the checksum matches + const hash = try self.in_reader.readIntLittle(u32); + if (hash != self.hasher.final()) + return error.WrongChecksum; + + // The ISIZE field is the size of the uncompressed input modulo 2^32 + const input_size = try self.in_reader.readIntLittle(u32); + if (self.read_amt & 0xffffffff != input_size) + return error.CorruptedData; + + return 0; + } + + pub fn reader(self: *Self) Reader { + return .{ .context = self }; + } + }; +} + +pub fn gzipStream(allocator: *mem.Allocator, reader: anytype) !GzipStream(@TypeOf(reader)) { + return GzipStream(@TypeOf(reader)).init(allocator, reader); +} + +fn testReader(data: []const u8, comptime expected: []const u8) !void { + var in_stream = io.fixedBufferStream(data); + + var gzip_stream = try gzipStream(testing.allocator, in_stream.reader()); + defer gzip_stream.deinit(); + + // Read and decompress the whole file + const buf = try gzip_stream.reader().readAllAlloc(testing.allocator, std.math.maxInt(usize)); + defer testing.allocator.free(buf); + // Calculate its SHA256 hash and check it against the reference + var hash: [32]u8 = undefined; + std.crypto.hash.sha2.Sha256.hash(buf, hash[0..], .{}); + + assertEqual(expected, &hash); +} + +// Assert `expected` == `input` where `input` is a bytestring. +pub fn assertEqual(comptime expected: []const u8, input: []const u8) void { + var expected_bytes: [expected.len / 2]u8 = undefined; + for (expected_bytes) |*r, i| { + r.* = std.fmt.parseInt(u8, expected[2 * i .. 2 * i + 2], 16) catch unreachable; + } + + testing.expectEqualSlices(u8, &expected_bytes, input); +} + +// All the test cases are obtained by compressing the RFC1952 text +// +// https://tools.ietf.org/rfc/rfc1952.txt length=25037 bytes +// SHA256=164ef0897b4cbec63abf1b57f069f3599bd0fb7c72c2a4dee21bd7e03ec9af67 +test "compressed data" { + try testReader( + @embedFile("rfc1952.txt.gz"), + "164ef0897b4cbec63abf1b57f069f3599bd0fb7c72c2a4dee21bd7e03ec9af67", + ); +} + +test "sanity checks" { + // Truncated header + testing.expectError( + error.EndOfStream, + testReader(&[_]u8{ 0x1f, 0x8B }, ""), + ); + // Wrong CM + testing.expectError( + error.InvalidCompression, + testReader(&[_]u8{ + 0x1f, 0x8b, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, + }, ""), + ); + // Wrong checksum + testing.expectError( + error.WrongChecksum, + testReader(&[_]u8{ + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, + }, ""), + ); + // Truncated checksum + testing.expectError( + error.EndOfStream, + testReader(&[_]u8{ + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, + }, ""), + ); + // Wrong initial size + testing.expectError( + error.CorruptedData, + testReader(&[_]u8{ + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, + }, ""), + ); + // Truncated initial size field + testing.expectError( + error.EndOfStream, + testReader(&[_]u8{ + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + }, ""), + ); +} diff --git a/lib/std/compress/rfc1952.txt.gz b/lib/std/compress/rfc1952.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..be43b90a7917a993933c3266db50882f77a5662e GIT binary patch literal 8059 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 JJiN_e008Aa$sPaz literal 0 HcmV?d00001 -- 2.54.0