~sircmpwn/hare-dev

harec: Add OpenBSD port v1 PROPOSED

Brian Callahan: 1
 Add OpenBSD port

 19 files changed, 734 insertions(+), 46 deletions(-)
#745283 alpine.yml failed
#745284 freebsd.yml failed
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~sircmpwn/hare-dev/patches/31638/mbox | git am -3
Learn more about email & git

[PATCH harec] Add OpenBSD port Export this patch

Hi all --

Here is a port of harec to OpenBSD. All tests pass on amd64:
https://bsd.network/@bcallah/108201642210354959

Testing on arm64 and riscv64 forthcoming in the next few days.

The standard library port will come soon.

There are some things I'd like feedback on:
1. harec uses a mktemp function which conflicts with the libc function
2. There are printf format mismatches. Maybe it is OpenBSD-specific.
3. OpenBSD requires a .note.openbsd.ident section. I put the code in
   rt/+openbsd/start+x86_64.s but maybe there is a better place for it
Thanks.

~Brian

Signed-off-by: Brian Callahan <bcallah@posteo.net>
---
 config.sh                     |   5 +
 include/gen.h                 |   2 +-
 rt/+openbsd/errno.ha          |  97 +++++++++++++++
 rt/+openbsd/segmalloc.ha      |  10 ++
 rt/+openbsd/start+aarch64.s   |   8 ++
 rt/+openbsd/start+riscv64.s   |   6 +
 rt/+openbsd/start+x86_64.s    |  15 +++
 rt/+openbsd/syscall+aarch64.s |  69 +++++++++++
 rt/+openbsd/syscall+riscv64.s |  69 +++++++++++
 rt/+openbsd/syscall+x86_64.s  |  69 +++++++++++
 rt/+openbsd/syscallno.ha      | 222 ++++++++++++++++++++++++++++++++++
 rt/+openbsd/syscalls.ha       |  85 +++++++++++++
 rt/configure                  |  27 +++++
 rt/hare.sc                    |   6 +
 src/emit.c                    |   2 +-
 src/gen.c                     |  70 +++++------
 src/genutil.c                 |   2 +-
 src/qtype.c                   |   8 +-
 src/typedef.c                 |   8 +-
 19 files changed, 734 insertions(+), 46 deletions(-)
 create mode 100644 rt/+openbsd/errno.ha
 create mode 100644 rt/+openbsd/segmalloc.ha
 create mode 100644 rt/+openbsd/start+aarch64.s
 create mode 100644 rt/+openbsd/start+riscv64.s
 create mode 100644 rt/+openbsd/start+x86_64.s
 create mode 100644 rt/+openbsd/syscall+aarch64.s
 create mode 100644 rt/+openbsd/syscall+riscv64.s
 create mode 100644 rt/+openbsd/syscall+x86_64.s
 create mode 100644 rt/+openbsd/syscallno.ha
 create mode 100644 rt/+openbsd/syscalls.ha

diff --git a/config.sh b/config.sh
index b004fa1..cf6dfe1 100644
--- a/config.sh
+++ b/config.sh
@@ -9,6 +9,11 @@ LD=${LD:-ld}
QBE=${QBE:-qbe}
SCDOC=${SCDOC:-scdoc}

if [ `uname -s` = "OpenBSD" ]
then
	LD="${LD:-ld} -nopie"
fi

for arg
do
	case "$arg" in
diff --git a/include/gen.h b/include/gen.h
index 40379d5..2b01279 100644
--- a/include/gen.h
+++ b/include/gen.h
@@ -83,7 +83,7 @@ void gen(const struct unit *unit,

// genutil.c
char *gen_name(struct gen_context *ctx, const char *fmt);
struct gen_value mktemp(struct gen_context *ctx,
struct gen_value emktemp(struct gen_context *ctx,
	const struct type *type, const char *fmt);
struct qbe_value mkqval(struct gen_context *ctx, struct gen_value *value);
struct qbe_value mklval(struct gen_context *ctx, struct gen_value *value);
diff --git a/rt/+openbsd/errno.ha b/rt/+openbsd/errno.ha
new file mode 100644
index 0000000..5a1f86d
--- /dev/null
+++ b/rt/+openbsd/errno.ha
@@ -0,0 +1,97 @@
export def EPERM: int = 1;
export def ENOENT: int = 2;
export def ESRCH: int = 3;
export def EINTR: int = 4;
export def EIO: int = 5;
export def ENXIO: int = 6;
export def E2BIG: int = 7;
export def ENOEXEC: int = 8;
export def EBADF: int = 9;
export def ECHILD: int = 10;
export def EDEADLK: int = 11;
export def ENOMEM: int = 12;
export def EACCES: int = 13;
export def EFAULT: int = 14;
export def ENOTBLK: int = 15;
export def EBUSY: int = 16;
export def EEXIST: int = 17;
export def EXDEV: int = 18;
export def ENODEV: int = 19;
export def ENOTDIR: int = 20;
export def EISDIR: int = 21;
export def EINVAL: int = 22;
export def ENFILE: int = 23;
export def EMFILE: int = 24;
export def ENOTTY: int = 25;
export def ETXTBSY: int = 26;
export def EFBIG: int = 27;
export def ENOSPC: int = 28;
export def ESPIPE: int = 29;
export def EROFS: int = 30;
export def EMLINK: int = 31;
export def EPIPE: int = 32;
export def EDOM: int = 33;
export def ERANGE: int = 34;
export def EAGAIN: int = 35;
export def EWOULDBLOCK: int = EAGAIN;
export def EINPROGRESS: int = 36;
export def EALREADY: int = 37;
export def ENOTSOCK: int = 38;
export def EDESTADDRREQ: int = 39;
export def EMSGSIZE: int = 40;
export def EPROTOTYPE: int = 41;
export def ENOPROTOOPT: int = 42;
export def EPROTONOSUPPORT: int = 43;
export def ESOCKTNOSUPPORT: int = 44;
export def EOPNOTSUPP: int = 45;
export def EPFNOSUPPORT: int = 46;
export def EAFNOSUPPORT: int = 47;
export def EADDRINUSE: int = 48;
export def EADDRNOTAVAIL: int = 49;
export def ENETDOWN: int = 50;
export def ENETUNREACH: int = 51;
export def ENETRESET: int = 52;
export def ECONNABORTED: int = 53;
export def ECONNRESET: int = 54;
export def ENOBUFS: int = 55;
export def EISCONN: int = 56;
export def ENOTCONN: int = 57;
export def ESHUTDOWN: int = 58;
export def ETOOMANYREFS: int = 59;
export def ETIMEDOUT: int = 60;
export def ECONNREFUSED: int = 61;
export def ELOOP: int = 62;
export def ENAMETOOLONG: int = 63;
export def EHOSTDOWN: int = 64;
export def EHOSTUNREACH: int = 65;
export def ENOTEMPTY: int = 66;
export def EPROCLIM: int = 67;
export def EUSERS: int = 68;
export def EDQUOT: int = 69;
export def ESTALE: int = 70;
export def EREMOTE: int = 71;
export def EBADRPC: int = 72;
export def ERPCMISMATCH: int = 73;
export def EPROGUNAVAIL: int = 74;
export def EPROGMISMATCH: int = 75;
export def EPROCUNAVAIL: int = 76;
export def ENOLCK: int = 77;
export def ENOSYS: int = 78;
export def EFTYPE: int = 79;
export def EAUTH: int = 80;
export def ENEEDAUTH: int = 81;
export def EIPSEC: int = 82;
export def ENOATTR: int = 83;
export def EILSEQ: int = 84;
export def ENOMEDIUM: int = 85;
export def EMEDIUMTYPE: int = 86;
export def EOVERFLOW: int = 87;
export def ECANCELED: int = 88;
export def EIDRM: int = 89;
export def ENOMSG: int = 90;
export def ENOTSUP: int = 91;
export def EBADMSG: int = 92;
export def ENOTRECOVERABLE: int = 93;
export def EOWNERDEAD: int = 94;
export def EPROTO: int = 95;
export def ELAST: int = 95;
diff --git a/rt/+openbsd/segmalloc.ha b/rt/+openbsd/segmalloc.ha
new file mode 100644
index 0000000..e3844b4
--- /dev/null
+++ b/rt/+openbsd/segmalloc.ha
@@ -0,0 +1,10 @@
// Allocates a segment.
fn segmalloc(n: size) nullable *void = {
	let p: *void = mmap(null, n,
		PROT_READ | PROT_WRITE,
		MAP_PRIVATE | MAP_ANON, -1, 0);
	return if (p: uintptr: int == -ENOMEM) null else p;
};

// Frees a segment allocated with segmalloc.
fn segfree(p: *void, s: size) int = munmap(p, s);
diff --git a/rt/+openbsd/start+aarch64.s b/rt/+openbsd/start+aarch64.s
new file mode 100644
index 0000000..edbde9d
--- /dev/null
+++ b/rt/+openbsd/start+aarch64.s
@@ -0,0 +1,8 @@
.text
.global _start
_start:
	mov x29, #0
	mov x30, #0
	mov x0, sp
	add sp, x0, #-16
	b rt.start_ha
diff --git a/rt/+openbsd/start+riscv64.s b/rt/+openbsd/start+riscv64.s
new file mode 100644
index 0000000..d1c9f61
--- /dev/null
+++ b/rt/+openbsd/start+riscv64.s
@@ -0,0 +1,6 @@
.text
.global _start
_start:
	mv a0, sp
	andi sp, sp, -16
	tail rt.start_ha
diff --git a/rt/+openbsd/start+x86_64.s b/rt/+openbsd/start+x86_64.s
new file mode 100644
index 0000000..0ac2066
--- /dev/null
+++ b/rt/+openbsd/start+x86_64.s
@@ -0,0 +1,15 @@
	.section ".note.openbsd.ident", "a"
	.p2align 2
	.long	0x8
	.long	0x4
	.long	0x1
	.asciz	"OpenBSD"
	.long	0x0
	.previous

.text
.global _start
_start:
	xor %rbp, %rbp
	movq %rsp, %rdi
	call rt.start_ha
diff --git a/rt/+openbsd/syscall+aarch64.s b/rt/+openbsd/syscall+aarch64.s
new file mode 100644
index 0000000..97a807f
--- /dev/null
+++ b/rt/+openbsd/syscall+aarch64.s
@@ -0,0 +1,69 @@
.section .text.rt.syscall0
.global rt.syscall0
rt.syscall0:
	mov x8, x0
	svc 0
	ret

.section .text.rt.syscall1
.global rt.syscall1
rt.syscall1:
	mov x8, x0
	mov x0, x1
	svc 0
	ret

.section .text.rt.syscall2
.global rt.syscall2
rt.syscall2:
	mov x8, x0
	mov x0, x1
	mov x1, x2
	svc 0
	ret

.section .text.rt.syscall3
.global rt.syscall3
rt.syscall3:
	mov x8, x0
	mov x0, x1
	mov x1, x2
	mov x2, x3
	svc 0
	ret

.section .text.rt.syscall4
.global rt.syscall4
rt.syscall4:
	mov x8, x0
	mov x0, x1
	mov x1, x2
	mov x2, x3
	mov x3, x4
	svc 0
	ret

.section .text.rt.syscall5
.global rt.syscall5
rt.syscall5:
	mov x8, x0
	mov x0, x1
	mov x1, x2
	mov x2, x3
	mov x3, x4
	mov x4, x5
	svc 0
	ret

.section .text.rt.syscall6
.global rt.syscall6
rt.syscall6:
	mov x8, x0
	mov x0, x1
	mov x1, x2
	mov x2, x3
	mov x3, x4
	mov x4, x5
	mov x5, x6
	svc 0
	ret
diff --git a/rt/+openbsd/syscall+riscv64.s b/rt/+openbsd/syscall+riscv64.s
new file mode 100644
index 0000000..6645eda
--- /dev/null
+++ b/rt/+openbsd/syscall+riscv64.s
@@ -0,0 +1,69 @@
.section .text.rt.syscall0
.global rt.syscall0
rt.syscall0:
	mv a7, a0
	ecall
	ret

.section .text.rt.syscall1
.global rt.syscall1
rt.syscall1:
	mv a7, a0
	mv a0, a1
	ecall
	ret

.section .text.rt.syscall2
.global rt.syscall2
rt.syscall2:
	mv a7, a0
	mv a0, a1
	mv a1, a2
	ecall
	ret

.section .text.rt.syscall3
.global rt.syscall3
rt.syscall3:
	mv a7, a0
	mv a0, a1
	mv a1, a2
	mv a2, a3
	ecall
	ret

.section .text.rt.syscall4
.global rt.syscall4
rt.syscall4:
	mv a7, a0
	mv a0, a1
	mv a1, a2
	mv a2, a3
	mv a3, a4
	ecall
	ret

.section .text.rt.syscall5
.global rt.syscall5
rt.syscall5:
	mv a7, a0
	mv a0, a1
	mv a1, a2
	mv a2, a3
	mv a3, a4
	mv a4, a5
	ecall
	ret

.section .text.rt.syscall6
.global rt.syscall6
rt.syscall6:
	mv a7, a0
	mv a0, a1
	mv a1, a2
	mv a2, a3
	mv a3, a4
	mv a4, a5
	mv a5, a6
	ecall
	ret
diff --git a/rt/+openbsd/syscall+x86_64.s b/rt/+openbsd/syscall+x86_64.s
new file mode 100644
index 0000000..786f272
--- /dev/null
+++ b/rt/+openbsd/syscall+x86_64.s
@@ -0,0 +1,69 @@
.section .text.rt.syscall0
.global rt.syscall0
rt.syscall0:
	movq %rdi, %rax
	syscall
	ret

.section .text.rt.syscall1
.global rt.syscall1
rt.syscall1:
	movq %rdi, %rax
	movq %rsi, %rdi
	syscall
	ret

.section .text.rt.syscall2
.global rt.syscall2
rt.syscall2:
	movq %rdi, %rax
	movq %rsi, %rdi
	movq %rdx, %rsi
	syscall
	ret

.section .text.rt.syscall3
.global rt.syscall3
rt.syscall3:
	movq %rdi, %rax
	movq %rsi, %rdi
	movq %rdx, %rsi
	movq %rcx, %rdx
	syscall
	ret

.section .text.rt.syscall4
.global rt.syscall4
rt.syscall4:
	movq %rdi, %rax
	movq %r8, %r10
	movq %rsi, %rdi
	movq %rdx, %rsi
	movq %rcx, %rdx
	syscall
	ret

.section .text.rt.syscall5
.global rt.syscall5
rt.syscall5:
	movq %rdi, %rax
	movq %r8, %r10
	movq %rsi, %rdi
	movq %r9, %r8
	movq %rdx, %rsi
	movq %rcx, %rdx
	syscall
	ret

.section .text.rt.syscall6
.global rt.syscall6
rt.syscall6:
	movq %rdi, %rax
	movq %r8, %r10
	movq %rsi, %rdi
	movq %r9, %r8
	movq %rdx, %rsi
	movq 8(%rsp), %r9
	movq %rcx, %rdx
	syscall
	ret
diff --git a/rt/+openbsd/syscallno.ha b/rt/+openbsd/syscallno.ha
new file mode 100644
index 0000000..cccf753
--- /dev/null
+++ b/rt/+openbsd/syscallno.ha
@@ -0,0 +1,222 @@
export def SYS_syscall: u64 = 0;
export def SYS_exit: u64 = 1;
export def SYS_fork: u64 = 2;
export def SYS_read: u64 = 3;
export def SYS_write: u64 = 4;
export def SYS_open: u64 = 5;
export def SYS_close: u64 = 6;
export def SYS_getentropy: u64 = 7;
export def SYS___tfork: u64 = 8;
export def SYS_link: u64 = 9;
export def SYS_unlink: u64 = 10;
export def SYS_wait4: u64 = 11;
export def SYS_chdir: u64 = 12;
export def SYS_fchdir: u64 = 13;
export def SYS_mknod: u64 = 14;
export def SYS_chmod: u64 = 15;
export def SYS_chown: u64 = 16;
export def SYS_break: u64 = 17;
export def SYS_getdtablecount: u64 = 18;
export def SYS_getrusage: u64 = 19;
export def SYS_getpid: u64 = 20;
export def SYS_mount: u64 = 21;
export def SYS_unmount: u64 = 22;
export def SYS_setuid: u64 = 23;
export def SYS_getuid: u64 = 24;
export def SYS_geteuid: u64 = 25;
export def SYS_ptrace: u64 = 26;
export def SYS_recvmsg: u64 = 27;
export def SYS_sendmsg: u64 = 28;
export def SYS_recvfrom: u64 = 29;
export def SYS_accept: u64 = 30;
export def SYS_getpeername: u64 = 31;
export def SYS_getsockname: u64 = 32;
export def SYS_access: u64 = 33;
export def SYS_chflags: u64 = 34;
export def SYS_fchflags: u64 = 35;
export def SYS_sync: u64 = 36;
export def SYS_msyscall: u64 = 37;
export def SYS_stat: u64 = 38;
export def SYS_getppid: u64 = 39;
export def SYS_lstat: u64 = 40;
export def SYS_dup: u64 = 41;
export def SYS_fstatat: u64 = 42;
export def SYS_getegid: u64 = 43;
export def SYS_profil: u64 = 44;
export def SYS_ktrace: u64 = 45;
export def SYS_sigaction: u64 = 46;
export def SYS_getgid: u64 = 47;
export def SYS_sigprocmask: u64 = 48;
export def SYS_mmap: u64 = 49;
export def SYS_setlogin: u64 = 50;
export def SYS_acct: u64 = 51;
export def SYS_sigpending: u64 = 52;
export def SYS_fstat: u64 = 53;
export def SYS_ioctl: u64 = 54;
export def SYS_reboot: u64 = 55;
export def SYS_revoke: u64 = 56;
export def SYS_symlink: u64 = 57;
export def SYS_readlink: u64 = 58;
export def SYS_execve: u64 = 59;
export def SYS_umask: u64 = 60;
export def SYS_chroot: u64 = 61;
export def SYS_getfsstat: u64 = 62;
export def SYS_statfs: u64 = 63;
export def SYS_fstatfs: u64 = 64;
export def SYS_fhstatfs: u64 = 65;
export def SYS_vfork: u64 = 66;
export def SYS_gettimeofday: u64 = 67;
export def SYS_settimeofday: u64 = 68;
export def SYS_setitimer: u64 = 69;
export def SYS_getitimer: u64 = 70;
export def SYS_select: u64 = 71;
export def SYS_kevent: u64 = 72;
export def SYS_munmap: u64 = 73;
export def SYS_mprotect: u64 = 74;
export def SYS_madvise: u64 = 75;
export def SYS_utimes: u64 = 76;
export def SYS_futimes: u64 = 77;
export def SYS_mquery: u64 = 78;
export def SYS_getgroups: u64 = 79;
export def SYS_setgroups: u64 = 80;
export def SYS_getpgrp: u64 = 81;
export def SYS_setpgid: u64 = 82;
export def SYS_futex: u64 = 83;
export def SYS_utimensat: u64 = 84;
export def SYS_futimens: u64 = 85;
export def SYS_kbind: u64 = 86;
export def SYS_clock_gettime: u64 = 87;
export def SYS_clock_settime: u64 = 88;
export def SYS_clock_getres: u64 = 89;
export def SYS_dup2: u64 = 90;
export def SYS_nanosleep: u64 = 91;
export def SYS_fcntl: u64 = 92;
export def SYS_accept4: u64 = 93;
export def SYS___thrsleep: u64 = 94;
export def SYS_fsync: u64 = 95;
export def SYS_setpriority: u64 = 96;
export def SYS_socket: u64 = 97;
export def SYS_connect: u64 = 98;
export def SYS_getdents: u64 = 99;
export def SYS_getpriority: u64 = 100;
export def SYS_pipe2: u64 = 101;
export def SYS_dup3: u64 = 102;
export def SYS_sigreturn: u64 = 103;
export def SYS_bind: u64 = 104;
export def SYS_setsockopt: u64 = 105;
export def SYS_listen: u64 = 106;
export def SYS_chflagsat: u64 = 107;
export def SYS_pledge: u64 = 108;
export def SYS_ppoll: u64 = 109;
export def SYS_pselect: u64 = 110;
export def SYS_sigsuspend: u64 = 111;
export def SYS_sendsyslog: u64 = 112;
export def SYS_unveil: u64 = 114;
export def SYS___realpath: u64 = 115;
export def SYS_getsockopt: u64 = 118;
export def SYS_thrkill: u64 = 119;
export def SYS_readv: u64 = 120;
export def SYS_writev: u64 = 121;
export def SYS_kill: u64 = 122;
export def SYS_fchown: u64 = 123;
export def SYS_fchmod: u64 = 124;
export def SYS_setreuid: u64 = 126;
export def SYS_setregid: u64 = 127;
export def SYS_rename: u64 = 128;
export def SYS_flock: u64 = 131;
export def SYS_mkfifo: u64 = 132;
export def SYS_sendto: u64 = 133;
export def SYS_shutdown: u64 = 134;
export def SYS_socketpair: u64 = 135;
export def SYS_mkdir: u64 = 136;
export def SYS_rmdir: u64 = 137;
export def SYS_adjtime: u64 = 140;
export def SYS_getlogin_r: u64 = 141;
export def SYS_setsid: u64 = 147;
export def SYS_quotactl: u64 = 148;
export def SYS_nfssvc: u64 = 155;
export def SYS_getfh: u64 = 161;
export def SYS___tmpfd: u64 = 164;
export def SYS_sysarch: u64 = 165;
export def SYS_lseek: u64 = 166;
export def SYS_truncate: u64 = 167;
export def SYS_ftruncate: u64 = 168;
export def SYS_pread: u64 = 169;
export def SYS_pwrite: u64 = 170;
export def SYS_preadv: u64 = 171;
export def SYS_pwritev: u64 = 172;
export def SYS_pad_pread: u64 = 173;
export def SYS_pad_pwrite: u64 = 174;
export def SYS_setgid: u64 = 181;
export def SYS_setegid: u64 = 182;
export def SYS_seteuid: u64 = 183;
export def SYS_pathconf: u64 = 191;
export def SYS_fpathconf: u64 = 192;
export def SYS_swapctl: u64 = 193;
export def SYS_getrlimit: u64 = 194;
export def SYS_setrlimit: u64 = 195;
export def SYS_pad_mmap: u64 = 197;
export def SYS___syscall: u64 = 198;
export def SYS_pad_lseek: u64 = 199;
export def SYS_pad_truncate: u64 = 200;
export def SYS_pad_ftruncate: u64 = 201;
export def SYS___sysctl: u64 = 202;
export def SYS_mlock: u64 = 203;
export def SYS_munlock: u64 = 204;
export def SYS_getpgid: u64 = 207;
export def SYS_utrace: u64 = 209;
export def SYS_semget: u64 = 221;
export def SYS_msgget: u64 = 225;
export def SYS_msgsnd: u64 = 226;
export def SYS_msgrcv: u64 = 227;
export def SYS_shmat: u64 = 228;
export def SYS_shmdt: u64 = 230;
export def SYS_minherit: u64 = 250;
export def SYS_poll: u64 = 252;
export def SYS_issetugid: u64 = 253;
export def SYS_lchown: u64 = 254;
export def SYS_getsid: u64 = 255;
export def SYS_msync: u64 = 256;
export def SYS_pipe: u64 = 263;
export def SYS_fhopen: u64 = 264;
export def SYS_pad_preadv: u64 = 267;
export def SYS_pad_pwritev: u64 = 268;
export def SYS_pad_kqueue: u64 = 269;
export def SYS_mlockall: u64 = 271;
export def SYS_munlockall: u64 = 272;
export def SYS_getresuid: u64 = 281;
export def SYS_setresuid: u64 = 282;
export def SYS_getresgid: u64 = 283;
export def SYS_setresgid: u64 = 284;
export def SYS_pad_mquery: u64 = 286;
export def SYS_closefrom: u64 = 287;
export def SYS_sigaltstack: u64 = 288;
export def SYS_shmget: u64 = 289;
export def SYS_semop: u64 = 290;
export def SYS_fhstat: u64 = 294;
export def SYS___semctl: u64 = 295;
export def SYS_shmctl: u64 = 296;
export def SYS_msgctl: u64 = 297;
export def SYS_sched_yield: u64 = 298;
export def SYS_getthrid: u64 = 299;
export def SYS___thrwakeup: u64 = 301;
export def SYS___threxit: u64 = 302;
export def SYS___thrsigdivert: u64 = 303;
export def SYS___getcwd: u64 = 304;
export def SYS_adjfreq: u64 = 305;
export def SYS_setrtable: u64 = 310;
export def SYS_getrtable: u64 = 311;
export def SYS_faccessat: u64 = 313;
export def SYS_fchmodat: u64 = 314;
export def SYS_fchownat: u64 = 315;
export def SYS_linkat: u64 = 317;
export def SYS_mkdirat: u64 = 318;
export def SYS_mkfifoat: u64 = 319;
export def SYS_mknodat: u64 = 320;
export def SYS_openat: u64 = 321;
export def SYS_readlinkat: u64 = 322;
export def SYS_renameat: u64 = 323;
export def SYS_symlinkat: u64 = 324;
export def SYS_unlinkat: u64 = 325;
export def SYS___set_tcb: u64 = 329;
export def SYS___get_tcb: u64 = 330;
diff --git a/rt/+openbsd/syscalls.ha b/rt/+openbsd/syscalls.ha
new file mode 100644
index 0000000..690e5f3
--- /dev/null
+++ b/rt/+openbsd/syscalls.ha
@@ -0,0 +1,85 @@
fn syscall0(_: u64) u64;
fn syscall1(_: u64, _: u64) u64;
fn syscall2(_: u64, _: u64, _: u64) u64;
fn syscall3(_: u64, _: u64, _: u64, _: u64) u64;
fn syscall4(_: u64, _: u64, _: u64, _: u64, _: u64) u64;
fn syscall5(_: u64, _: u64, _: u64, _: u64, _: u64, _: u64) u64;
fn syscall6(_: u64, _: u64, _: u64, _: u64, _: u64, _: u64, _: u64) u64;

export fn write(fd: int, buf: *const void, count: size) size =
	syscall3(SYS_write, fd: u64, buf: uintptr: u64, count: u64): size;

export fn close(fd: int) int = syscall1(SYS_close, fd: u64): int;

export fn dup2(old: int, new: int) int =
	syscall2(SYS_dup2, old: u64, new: u64): int;

export fn getpid() int = syscall0(SYS_getpid): int;

export @noreturn fn exit(status: int) void = { syscall1(SYS_exit, status: u64); };

export fn fork() int = syscall0(SYS_fork): int;

export fn execve(
	path: *const char,
	argv: *[*]nullable *const char,
	envp: *[*]nullable *const char,
) int = syscall3(SYS_execve,
	path: uintptr: u64,
	argv: uintptr: u64,
	envp: uintptr: u64): int;

export fn wait4(pid: int, status: *int, options: int, rusage: nullable *void) void = {
	syscall4(SYS_wait4, pid: u64, status: uintptr: u64,
		options: u64, rusage: uintptr: u64);
};

export fn wifexited(status: int) bool = wtermsig(status) == 0;
export fn wexitstatus(status: int) int = (status & 0xff00) >> 8;

export fn wtermsig(status: int) int = status & 0x7f;
export fn wifsignaled(status: int) bool =
	wtermsig(status) != 0o177 && wtermsig(status) != 0 && status != 0x13;

export fn kill(pid: int, signal: int) int =
	syscall2(SYS_kill, pid: u64, signal: u64): int;

export fn pipe2(pipefd: *[2]int, flags: int) int =
	syscall2(SYS_pipe2, pipefd: uintptr: u64, flags: u64): int;

export def MAP_SHARED: uint = 0x0001;
export def MAP_PRIVATE: uint = 0x0002;
export def MAP_FIXED: uint = 0x0010;
export def MAP_HASSEMAPHORE: uint = 0x0200;
export def MAP_STACK: uint = 0x0400;
export def MAP_NOSYNC: uint = 0x0800;
export def MAP_FILE: uint = 0x0000;
export def MAP_ANON: uint = 0x1000;
export def MAP_GUARD: uint = 0x00002000;
export def MAP_EXCL: uint = 0x00004000;
export def MAP_NOCORE: uint = 0x00020000;
export def MAP_PREFAULT_READ: uint = 0x00040000;
export def MAP_32BIT: uint = 0x00080000;

def PROT_NONE: uint = 0x00;
def PROT_READ: uint = 0x01;
def PROT_WRITE: uint = 0x02;
def PROT_EXEC: uint = 0x04;

export fn mmap(
	addr: nullable *void,
	length: size,
	prot: uint,
	flags: uint,
	fd: int,
	offs: size
) *void = {
	return syscall6(SYS_mmap, addr: uintptr: u64, length: u64, prot: u64,
		flags: u64, fd: u64, offs: u64): uintptr: *void;
};

export fn munmap(addr: *void, length: size) int =
	syscall2(SYS_munmap, addr: uintptr: u64, length: u64): int;

export def SIGABRT: int	= 6;
export def SIGCHLD: int	= 20;
diff --git a/rt/configure b/rt/configure
index 1717684..672a5cf 100644
--- a/rt/configure
+++ b/rt/configure
@@ -48,6 +48,33 @@ rt() {
			rt: libhart.a rt/+freebsd/start+$arch.o
			EOF
			;;
		OpenBSD)
			case $arch in
				amd64)
					arch=x86_64
					;;
				aarch64|riscv64)
					;;
				*)
					printf "unsupported OpenBSD arch %s\n" "$(uname)" >&2
					;;
			esac
			rtstart=rt/+openbsd/start+$arch.o
			cat <<-EOF
			rtstart=rt/+openbsd/start+$arch.o

			libhart_srcs=\
				rt/+openbsd/errno.ha \
				rt/+openbsd/segmalloc.ha \
				rt/+openbsd/syscallno.ha \
				rt/+openbsd/syscalls.ha \

			libhart_objs=\
				rt/+openbsd/syscall+$arch.o

			rt: libhart.a rt/+openbsd/start+$arch.o
			EOF
			;;
		*)
			printf "rt not supported for %s\n" "$(uname)" >&2
			exit 1
diff --git a/rt/hare.sc b/rt/hare.sc
index f93ed9d..71702ab 100644
--- a/rt/hare.sc
+++ b/rt/hare.sc
@@ -2,6 +2,7 @@ PHDRS {
	headers PT_PHDR PHDRS;
	text PT_LOAD FILEHDR PHDRS;
	data PT_LOAD;
	note PT_NOTE;
}
ENTRY(_start);
SECTIONS {
@@ -34,6 +35,11 @@ SECTIONS {
		PROVIDE_HIDDEN (__test_array_end = .);
	} :data

	.note.openbsd.ident : {
		KEEP (*(.note.openbsd.ident))
		*(.note.openbsd.*)
	} :data :note

	.bss : {
		KEEP (*(.bss))
		*(.bss.*)
diff --git a/src/emit.c b/src/emit.c
index 6213bcd..4ec622d 100644
--- a/src/emit.c
+++ b/src/emit.c
@@ -96,7 +96,7 @@ emit_const(struct qbe_value *val, FILE *out)
		fprintf(out, "%u", val->wval);
		break;
	case Q_LONG:
		fprintf(out, "%lu", val->lval);
		fprintf(out, "%llu", val->lval);
		break;
	case Q_SINGLE:
		fprintf(out, "s_%.*g", DECIMAL_DIG, val->sval);
diff --git a/src/gen.c b/src/gen.c
index a10f937..50819d2 100644
--- a/src/gen.c
+++ b/src/gen.c
@@ -501,7 +501,7 @@ gen_expr_alloc_init_with(struct gen_context *ctx,
	objtype = objtype->pointer.referent;

	struct qbe_value sz = constl(objtype->size);
	struct gen_value result = mktemp(ctx, expr->result, ".%d");
	struct gen_value result = emktemp(ctx, expr->result, ".%d");
	struct qbe_value qresult = mkqval(ctx, &result);
	struct qbe_value rtfunc = mkrtfunc(ctx, "rt.malloc");
	pushi(ctx->current, &qresult, Q_CALL, &rtfunc, &sz, NULL);
@@ -539,7 +539,7 @@ gen_expr_alloc_slice_with(struct gen_context *ctx,
		gen_alloc_slice_at(ctx, expr, *out, expand);
		return gv_void;
	}
	struct gen_value temp = mktemp(ctx, expr->result, "object.%d");
	struct gen_value temp = emktemp(ctx, expr->result, "object.%d");
	struct qbe_value base = mkqval(ctx, &temp);
	struct qbe_value sz = constl(expr->result->size);
	enum qbe_instr alloc = alloc_for_align(expr->result->align);
@@ -555,7 +555,7 @@ gen_expr_alloc_copy_with(struct gen_context *ctx,
	// alloc(init...) case
	struct gen_value ret = gv_void;
	if (out == NULL) {
		ret = mktemp(ctx, expr->result, "object.%d");
		ret = emktemp(ctx, expr->result, "object.%d");
		out = &ret;

		struct qbe_value base = mkqval(ctx, out);
@@ -906,7 +906,7 @@ gen_expr_binarithm(struct gen_context *ctx, const struct expression *expr)
{
	const struct type *ltype = type_dealias(expr->binarithm.lvalue->result);
	const struct type *rtype = type_dealias(expr->binarithm.rvalue->result);
	struct gen_value result = mktemp(ctx, expr->result, ".%d");
	struct gen_value result = emktemp(ctx, expr->result, ".%d");
	struct qbe_value qresult = mkqval(ctx, &result);

	if (expr->binarithm.op == BIN_LAND || expr->binarithm.op == BIN_LOR) {
@@ -1070,7 +1070,7 @@ gen_expr_call(struct gen_context *ctx, const struct expression *expr)
	};
	struct gen_value rval = gv_void;
	if (type_dealias(rtype->func.result)->storage != STORAGE_VOID) {
		rval = mktemp(ctx, rtype->func.result, "returns.%d");
		rval = emktemp(ctx, rtype->func.result, "returns.%d");
		call.out = xcalloc(1, sizeof(struct qbe_value));
		*call.out = mkqval(ctx, &rval);
		call.out->type = qtype_lookup(ctx, rtype->func.result, false);
@@ -1238,8 +1238,8 @@ gen_expr_cast_tagged_at(struct gen_context *ctx,
		subtype = tagged_subset_compat(to, from) ? from : to;
		const struct type *innertype = type_store_tagged_to_union(
				ctx->store, type_dealias(subtype));
		struct gen_value iout = mktemp(ctx, innertype, ".%d");
		struct gen_value ival = mktemp(ctx, innertype, ".%d");
		struct gen_value iout = emktemp(ctx, innertype, ".%d");
		struct gen_value ival = emktemp(ctx, innertype, ".%d");
		struct qbe_value qiout = mkqval(ctx, &iout);
		struct qbe_value qival = mkqval(ctx, &ival);
		struct qbe_value offs = constl(to->align);
@@ -1259,7 +1259,7 @@ gen_expr_cast_tagged_at(struct gen_context *ctx,
			return;
		}

		struct gen_value storage = mktemp(ctx, subtype, ".%d");
		struct gen_value storage = emktemp(ctx, subtype, ".%d");
		struct qbe_value qstor = mklval(ctx, &storage);
		struct qbe_value offs = constl(to->align);
		pushi(ctx->current, &qstor, Q_ADD, &qout, &offs, NULL);
@@ -1323,9 +1323,9 @@ gen_expr_cast_array_at(struct gen_context *ctx,

	struct qbe_value base = mkqval(ctx, &out);
	struct qbe_value offs = constl((typein->array.length - 1) * membtype->size);
	struct gen_value next = mktemp(ctx, membtype, ".%d");
	struct gen_value next = emktemp(ctx, membtype, ".%d");
	struct qbe_value ptr = mklval(ctx, &next);
	struct gen_value item = mktemp(ctx, membtype, "item.%d");
	struct gen_value item = emktemp(ctx, membtype, "item.%d");
	struct qbe_value qitem = mklval(ctx, &item);
	pushi(ctx->current, &qitem, Q_ADD, &base, &offs, NULL);

@@ -1404,7 +1404,7 @@ gen_expr_cast(struct gen_context *ctx, const struct expression *expr)
	}

	if (cast_prefers_at(expr)) {
		struct gen_value out = mktemp(ctx, expr->result, "object.%d");
		struct gen_value out = emktemp(ctx, expr->result, "object.%d");
		struct qbe_value base = mkqval(ctx, &out);
		struct qbe_value sz = constl(expr->result->size);
		enum qbe_instr alloc = alloc_for_align(expr->result->align);
@@ -1437,7 +1437,7 @@ gen_expr_cast(struct gen_context *ctx, const struct expression *expr)
		struct qbe_value qval = mkqval(ctx, &val);
		if (expr->cast.kind == C_TEST) {
			struct gen_value out =
				mktemp(ctx, &builtin_type_bool, ".%d");
				emktemp(ctx, &builtin_type_bool, ".%d");
			struct qbe_value qout = mkqval(ctx, &out);
			struct qbe_value zero = constl(0);

@@ -1507,7 +1507,7 @@ gen_expr_cast(struct gen_context *ctx, const struct expression *expr)

	struct gen_value value = gen_expr(ctx, expr->cast.value);
	struct qbe_value qvalue = mkqval(ctx, &value);
	struct gen_value result = mktemp(ctx, expr->result, "cast.%d");
	struct gen_value result = emktemp(ctx, expr->result, "cast.%d");
	struct qbe_value qresult = mkqval(ctx, &result);
	struct gen_value intermediate;
	struct qbe_value qintermediate;
@@ -1580,7 +1580,7 @@ gen_expr_cast(struct gen_context *ctx, const struct expression *expr)
				switch (from->size) {
				case 1:
				case 2:
					intermediate = mktemp(ctx,
					intermediate = emktemp(ctx,
						&builtin_type_i32, "cast.%d");
					qintermediate = mkqval(ctx, &intermediate);
					pushi(ctx->current, &qintermediate,
@@ -1600,7 +1600,7 @@ gen_expr_cast(struct gen_context *ctx, const struct expression *expr)
				switch (from->size) {
				case 1:
				case 2:
					intermediate = mktemp(ctx,
					intermediate = emktemp(ctx,
						&builtin_type_i32, "cast.%d");
					qintermediate = mkqval(ctx, &intermediate);
					pushi(ctx->current, &qintermediate,
@@ -1665,7 +1665,7 @@ gen_expr_compound_with(struct gen_context *ctx,

	struct gen_value gvout = gv_void;
	if (!out) {
		gvout = mktemp(ctx, expr->result, ".%d");
		gvout = emktemp(ctx, expr->result, ".%d");
	}
	scope->out = out;
	scope->result = gvout;
@@ -1692,7 +1692,7 @@ gen_const_array_at(struct gen_context *ctx,

	size_t n = 0;
	const struct type *atype = type_dealias(expr->result);
	struct gen_value item = mktemp(ctx, atype->array.members, "item.%d");
	struct gen_value item = emktemp(ctx, atype->array.members, "item.%d");
	for (const struct array_constant *ac = aexpr; ac; ac = ac->next) {
		struct qbe_value offs = constl(n * atype->array.members->size);
		struct qbe_value ptr = mklval(ctx, &item);
@@ -1768,7 +1768,7 @@ static struct gen_value
gen_expr_const(struct gen_context *ctx, const struct expression *expr)
{
	if (type_is_aggregate(type_dealias(expr->result))) {
		struct gen_value out = mktemp(ctx, expr->result, "object.%d");
		struct gen_value out = emktemp(ctx, expr->result, "object.%d");
		struct qbe_value base = mkqval(ctx, &out);
		struct qbe_value sz = constl(expr->result->size);
		enum qbe_instr alloc = alloc_for_align(expr->result->align);
@@ -1987,7 +1987,7 @@ gen_expr_if_with(struct gen_context *ctx,
{
	struct gen_value gvout = gv_void;
	if (!out) {
		gvout = mktemp(ctx, expr->result, ".%d");
		gvout = emktemp(ctx, expr->result, ".%d");
	}

	struct qbe_statement ltrue, lfalse, lend;
@@ -2194,7 +2194,7 @@ gen_nested_match_tests(struct gen_context *ctx, struct gen_value object,
	// tag of the foo object for int.
	struct qbe_value *subtag = &tag;
	struct qbe_value subval = mkcopy(ctx, &object, "subval.%d");
	struct gen_value match = mktemp(ctx, &builtin_type_bool, ".%d");
	struct gen_value match = emktemp(ctx, &builtin_type_bool, ".%d");
	struct qbe_value qmatch = mkqval(ctx, &match);
	struct qbe_value temp = mkqtmp(ctx, &qbe_word, ".%d");
	const struct type *subtype = object.type;
@@ -2244,7 +2244,7 @@ gen_subset_match_tests(struct gen_context *ctx,
	//
	// In this situation, we test the match object's tag against each type
	// ID of the case type.
	struct gen_value match = mktemp(ctx, &builtin_type_bool, ".%d");
	struct gen_value match = emktemp(ctx, &builtin_type_bool, ".%d");
	for (const struct type_tagged_union *tu = &type->tagged; tu; tu = tu->next) {
		struct qbe_statement lnexttag;
		struct qbe_value bnexttag = mklabel(ctx, &lnexttag, ".%d");
@@ -2265,7 +2265,7 @@ gen_match_with_tagged(struct gen_context *ctx,
{
	struct gen_value gvout = gv_void;
	if (!out) {
		gvout = mktemp(ctx, expr->result, ".%d");
		gvout = emktemp(ctx, expr->result, ".%d");
	}

	const struct type *objtype = expr->match.value->result;
@@ -2397,7 +2397,7 @@ gen_match_with_nullable(struct gen_context *ctx,
{
	struct gen_value gvout = gv_void;
	if (!out) {
		gvout = mktemp(ctx, expr->result, ".%d");
		gvout = emktemp(ctx, expr->result, ".%d");
	}

	struct qbe_statement lout;
@@ -2433,7 +2433,7 @@ gen_match_with_nullable(struct gen_context *ctx,
		}

		struct gen_binding *gb = xcalloc(1, sizeof(struct gen_binding));
		gb->value = mktemp(ctx, _case->type, "binding.%d");
		gb->value = emktemp(ctx, _case->type, "binding.%d");
		gb->object = _case->object;
		gb->next = ctx->bindings;
		ctx->bindings = gb;
@@ -2509,7 +2509,7 @@ gen_expr_measure(struct gen_context *ctx, const struct expression *expr)
		case STORAGE_STRING:
			gv = gen_expr(ctx, value);
			gv = gen_autoderef(ctx, gv);
			temp = mktemp(ctx, &builtin_type_size, ".%d");
			temp = emktemp(ctx, &builtin_type_size, ".%d");
			struct qbe_value qv = mkqval(ctx, &gv),
				qtemp = mkqval(ctx, &temp),
				offs = constl(builtin_type_size.size);
@@ -2580,7 +2580,7 @@ gen_expr_struct_at(struct gen_context *ctx,
			&base, &zero, &size, NULL);
	}

	struct gen_value ftemp = mktemp(ctx, &builtin_type_void, "field.%d");
	struct gen_value ftemp = emktemp(ctx, &builtin_type_void, "field.%d");
	for (const struct expr_struct_field *field = expr->_struct.fields;
			field; field = field->next) {
		if (!field->value) {
@@ -2603,7 +2603,7 @@ gen_expr_switch_with(struct gen_context *ctx,
{
	struct gen_value gvout = gv_void;
	if (!out) {
		gvout = mktemp(ctx, expr->result, ".%d");
		gvout = emktemp(ctx, expr->result, ".%d");
	}

	struct qbe_statement lout;
@@ -2714,7 +2714,7 @@ gen_expr_slice_at(struct gen_context *ctx,
		qbase = mkqtmp(ctx, ctx->arch.sz, "base.%d");
		enum qbe_instr load = load_for_type(ctx, &builtin_type_size);
		pushi(ctx->current, &qbase, load, &qobject, NULL);
		length = mktemp(ctx, &builtin_type_size, "len.%d");
		length = emktemp(ctx, &builtin_type_size, "len.%d");
		qlength = mkqval(ctx, &length);
		pushi(ctx->current, &qptr, Q_ADD, &qobject, &offset, NULL);
		pushi(ctx->current, &qlength, load, &qptr, NULL);
@@ -2795,7 +2795,7 @@ gen_expr_tuple_at(struct gen_context *ctx,
	struct qbe_value base = mkqval(ctx, &out);

	const struct type *type = type_dealias(expr->result);
	struct gen_value vtemp = mktemp(ctx, &builtin_type_void, "value.%d");
	struct gen_value vtemp = emktemp(ctx, &builtin_type_void, "value.%d");
	const struct expression_tuple *value = &expr->tuple;
	for (const struct type_tuple *tuple = &type->tuple;
			tuple; tuple = tuple->next) {
@@ -2822,7 +2822,7 @@ gen_expr_unarithm(struct gen_context *ctx,
			val.type = expr->result;
			return val;
		}
		struct gen_value val = mktemp(ctx, operand->result, ".%d");
		struct gen_value val = emktemp(ctx, operand->result, ".%d");
		struct qbe_value qv = mklval(ctx, &val);
		struct qbe_value sz = constl(val.type->size);
		enum qbe_instr alloc = alloc_for_align(val.type->align);
@@ -2837,21 +2837,21 @@ gen_expr_unarithm(struct gen_context *ctx,
		return gen_load(ctx, val);
	case UN_BNOT:
		val = gen_expr(ctx, operand);
		temp = mktemp(ctx, operand->result, ".%d");
		temp = emktemp(ctx, operand->result, ".%d");
		qval = mkqval(ctx, &val), qtmp = mkqval(ctx, &temp);
		struct qbe_value ones = constl((uint64_t)-1);
		pushi(ctx->current, &qtmp, Q_XOR, &qval, &ones, NULL);
		return temp;
	case UN_LNOT:
		val = gen_expr(ctx, operand);
		temp = mktemp(ctx, operand->result, ".%d");
		temp = emktemp(ctx, operand->result, ".%d");
		qval = mkqval(ctx, &val), qtmp = mkqval(ctx, &temp);
		struct qbe_value zerow = constw(0);
		pushi(ctx->current, &qtmp, Q_CEQW, &qval, &zerow, NULL);
		return temp;
	case UN_MINUS:
		val = gen_expr(ctx, operand);
		temp = mktemp(ctx, operand->result, ".%d");
		temp = emktemp(ctx, operand->result, ".%d");
		qval = mkqval(ctx, &val), qtmp = mkqval(ctx, &temp);
		pushi(ctx->current, &qtmp, Q_NEG, &qval, NULL);
		return temp;
@@ -2866,7 +2866,7 @@ gen_expr_vaarg(struct gen_context *ctx,
	const struct expression *expr)
{
	// XXX: qbe only supports variadic base types, should check for this
	struct gen_value result = mktemp(ctx, expr->result, ".%d");
	struct gen_value result = emktemp(ctx, expr->result, ".%d");
	struct qbe_value qresult = mkqval(ctx, &result);
	struct gen_value ap = gen_expr(ctx, expr->vaarg.ap);
	struct qbe_value qap = mkqval(ctx, &ap);
@@ -2951,7 +2951,7 @@ gen_expr(struct gen_context *ctx, const struct expression *expr)
		return *(struct gen_value *)expr->user;
	}

	struct gen_value out = mktemp(ctx, expr->result, "object.%d");
	struct gen_value out = emktemp(ctx, expr->result, "object.%d");
	struct qbe_value base = mkqval(ctx, &out);
	struct qbe_value sz = constl(expr->result->size);
	enum qbe_instr alloc = alloc_for_align(expr->result->align);
diff --git a/src/genutil.c b/src/genutil.c
index 9ae16e3..46eb693 100644
--- a/src/genutil.c
+++ b/src/genutil.c
@@ -66,7 +66,7 @@ mkqtmp(struct gen_context *ctx, const struct qbe_type *qtype, const char *fmt)
}

struct gen_value
mktemp(struct gen_context *ctx, const struct type *type, const char *fmt)
emktemp(struct gen_context *ctx, const struct type *type, const char *fmt)
{
	return (struct gen_value){
		.kind = GV_TEMP,
diff --git a/src/qtype.c b/src/qtype.c
index c25d948..aed53de 100644
--- a/src/qtype.c
+++ b/src/qtype.c
@@ -18,9 +18,9 @@ sf_compar(const void *_a, const void *_b)
static const struct qbe_type *
tagged_qtype(struct gen_context *ctx, const struct type *type)
{
	int n = snprintf(NULL, 0, "tags.%zd", ctx->id);
	int n = snprintf(NULL, 0, "tags.%llu", ctx->id);
	char *name = xcalloc(1, n + 1);
	snprintf(name, n + 1, "tags.%zd", ctx->id);
	snprintf(name, n + 1, "tags.%llu", ctx->id);
	++ctx->id;

	struct qbe_def *def = xcalloc(1, sizeof(struct qbe_def));
@@ -65,9 +65,9 @@ aggregate_lookup(struct gen_context *ctx, const struct type *type)
		}
	}

	int n = snprintf(NULL, 0, "type.%zd", ctx->id);
	int n = snprintf(NULL, 0, "type.%llu", ctx->id);
	char *name = xcalloc(1, n + 1);
	snprintf(name, n + 1, "type.%zd", ctx->id);
	snprintf(name, n + 1, "type.%llu", ctx->id);
	++ctx->id;

	struct qbe_def *def = xcalloc(1, sizeof(struct qbe_def));
diff --git a/src/typedef.c b/src/typedef.c
index 80d6e64..9419276 100644
--- a/src/typedef.c
+++ b/src/typedef.c
@@ -86,7 +86,7 @@ emit_const(const struct expression *expr, FILE *out)
	case STORAGE_I8:
	case STORAGE_ICONST:
	case STORAGE_INT:
		fprintf(out, "%ld%s", val->ival,
		fprintf(out, "%jd%s", val->ival,
			storage_to_suffix(type_dealias(expr->result)->storage));
		break;
	case STORAGE_NULL:
@@ -99,7 +99,7 @@ emit_const(const struct expression *expr, FILE *out)
	case STORAGE_U8:
	case STORAGE_UINT:
	case STORAGE_UINTPTR:
		fprintf(out, "%lu%s", val->uval,
		fprintf(out, "%ju%s", val->uval,
			storage_to_suffix(type_dealias(expr->result)->storage));
		break;
	case STORAGE_VOID:
@@ -301,10 +301,10 @@ emit_type(const struct type *type, FILE *out)
				ev; ev = ev->next) {
			fprintf(out, "%s = ", ev->name);
			if (type_is_signed(type)) {
				fprintf(out, "%zd%s", ev->ival,
				fprintf(out, "%lld%s", ev->ival,
					storage_to_suffix(type->_enum.storage));
			} else {
				fprintf(out, "%zu%s", ev->uval,
				fprintf(out, "%llu%s", ev->uval,
					storage_to_suffix(type->_enum.storage));
			}
			if (ev->next) {
-- 
2.35.1
harec/patches: FAILED in 49s

[Add OpenBSD port][0] from [Brian Callahan][1]

[0]: https://lists.sr.ht/~sircmpwn/hare-dev/patches/31638
[1]: mailto:bcallah@posteo.net

✗ #745283 FAILED harec/patches/alpine.yml  https://builds.sr.ht/~sircmpwn/job/745283
✗ #745284 FAILED harec/patches/freebsd.yml https://builds.sr.ht/~sircmpwn/job/745284
Nice work!

On Wed Apr 27, 2022 at 3:32 AM UTC, Brian Callahan wrote:
Good work Brian!

Just offering a tiny bit of feedback, as yesterday I tried to do some of 
the porting myself too. The mmap flags in syscalls.ha should be:

export def MAP_SHARED: uint = 0x0001;
export def MAP_PRIVATE: uint = 0x0002;
export def MAP_FIXED: uint = 0x0010;
export def __MAP_NOREPLACE: uint = 0x0800;
export def MAP_ANON: uint = 0x1000;
export def MAP_ANONYMOUS: uint = MAP_ANON;
export def __MAP_NOFAULT: uint = 0x2000;
export def MAP_STACK: uint = 0x4000;
export def MAP_CONCEAL: uint = 0x8000;

In particular, the MAP_FIXED has a different value than FreeBSD's.

Probably not a big deal in harec's rt, but I hope it is useful when 
porting the stdlib.

-- 
GR