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 64832673f1..9ec7038b07 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 0000000000..a6884aa024 --- /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 3b8df3d173..67c6fc5d31 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 0000000000..337a6aa30a --- /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 8cba45d4b3..9c79bc3a96 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"), } } 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 c769bc358b..13012e16a3 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."), } } 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 9062c1fb13..72762f4227 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 73c0924ab8..a4cb03aabc 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 898fa90203..9a8e79de97 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); 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 604c22101c..0af7ea6407 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 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 1c0d230d4d..b9be9b70bf 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); +} 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 a65c1b9fb4..cd42e8abee 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 abc6c1e3cf..49806a524e 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 ea42016e9b..2cfb2331fa 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)); +} 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 9cb571714c..2813d8cbab 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; }, }, } 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 b34ad8c940..2600b337b3 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(); 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 181bf4930d..25c71e5d15 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))) { 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 1b3f403ab5..bbc32cf351 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; 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 ef1b501ec3..db301a9a4c 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 }; } 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 a6884aa024..adc6c87c1a 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 2ab022e6ec..496be05138 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, .{}); \\ ^ \\ 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 db301a9a4c..73babf5fa2 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; 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 1673737bef..cb27e112aa 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 { 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 1673737bef..8aa473f8bc 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 3fbcc89585..1fa04f2b79 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 493bbbbae4..b5c1ca3a41 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 191da9a04b..3139071d52 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 2456cab44c..62f5604ba7 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 e2f6a82a12..348321598c 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 6c9916ebd3..dff134a01d 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 9a8e79de97..5e44301fff 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); 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 e72b166c77..8031aa790e 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. 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 bad23349e8..9fe96cacb7 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 d4bac4a8a4..acda7eadeb 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" { 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 acda7eadeb..d4bac4a8a4 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" { 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 f393f04abe..e8625179db 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 0000000000..7fa9969af4 --- /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 25a0d6814d..50d1e4ae78 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"); 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 25c71e5d15..91365c81dd 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), + } +} 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 e8625179db..df31bc32fd 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 0000000000..0086a694d9 --- /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; 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 bbc32cf351..aa50fff90e 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; 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 3f7f1a9038..a6a2d87371 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 5518f807df..95f496021e 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 0000000000..aad1731393 --- /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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 oc!TCNeMp0OdZ(9smFU literal 0 HcmV?d00001