Compare commits

..

3 Commits

Author SHA1 Message Date
73ad441585 Add waitpid and execve 2026-01-17 15:19:52 -05:00
ea0ec6a818 Fix fork on aarch64 2026-01-17 15:07:16 -05:00
ed7a811736 Clean up printing code in buildtest 2026-01-17 14:06:47 -05:00
5 changed files with 272 additions and 168 deletions

View File

@@ -57,9 +57,10 @@ intptr_t read(int32_t fd, const void* buf, intptr_t size){
#define CLONE_CHILD_SETTID 0x1000000
#define CLONE_CHILD_CLEARTID 0x200000
#define SIGCHLD 0x11
uint32_t fork(){
long int rtn;
long int flags = CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID;
long int flags = CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID | SIGCHLD;
asm volatile (
// Assembly Instructions
"mov x8, #220\n" // Syscall number for 'clone' is 220 in AArch64
@@ -80,6 +81,50 @@ uint32_t fork(){
return (uint32_t)rtn;
}
// SYS_EXECVE 221
int32_t execve(const char* filename, const char* argv[], const char* envp[]){
long int rtn;
asm volatile(
// Assembly Instructions
"mov x0, %0\n" // Set x0 to value of filename
"mov x1, %1\n" // Set x1 to value of argv
"mov x2, %2\n" // Set x2 to value of envp
"mov x8, #221\n" // Syscall number
"svc #0\n" // Make syscall
// Output operands
: "=r"(rtn)
// Input operands
: "r"(filename), "r" (argv), "r"(envp)
// Clobbered registers
: "x0", "x1", "x2", "x8", "memory"
);
return (int32_t)rtn;
}
uint32_t wait4(uint32_t pid, int* wstatus, int options, void* rusage) {
uint64_t rtn;
asm volatile (
// Assembly Instructions
"mov x8, #260\n" // Syscall number for 'listen' is 201 in AArch64
"mov x0, %1\n" // pid
"mov x1, %2\n" // wstatus
"mov x2, %3\n" // options
"mov x3, %4\n" // rusage
"svc #0\n" // Make the syscall
"mov %0, x0\n" // save return value
// Output operands
: "=r" (rtn)
// Input operand
: "r" ((uint64_t)pid), "r"(wstatus), "r"((int64_t)options), "r"(rusage)
// Clobbered registers
: "x0", "x1", "x8", "memory"
);
return (uint32_t)rtn;
}
uint32_t waitpid(uint32_t pid, int* wstatus, int options) {
return wait4(pid, wstatus, options, 0);
}
int32_t openat(int32_t fd, const char* filename, uint32_t flags, uint32_t mode) {
long int rtn;
asm volatile (

View File

@@ -1,5 +1,19 @@
#if defined(__mips__)
#include "../int.h"
#include "../sys.h"
/*
#ifndef _START_DEFINED
#define _START_DEFINED
extern int main(int c, char** v);
void __start() {
void* fp = __builtin_frame_address(0);
register int argc asm("a0") = *(uintptr_t*)(fp+32);
register char** argv asm("a1") = fp+36;
exit(main(argc, argv));
}
#endif */ /* ifndef _START_DEFINED */
#include "generic.h"
@@ -59,6 +73,37 @@ uint32_t fork(){
return (uint32_t)rtn;
}
uint32_t waitpid(uint32_t pid, int* wstatus, int options) {
int child_pid = 0;
asm volatile (
"move $a0, %1\n"
"move $a1, %2\n"
"move $a2, %3\n"
"li $v0, 4007\n"
"syscall\n"
"move %0, $v0\n"
: "=r"(child_pid)
: "r"(pid), "r"(wstatus), "r"(options)
: "a0", "a1", "a2", "v0", "memory"
);
return child_pid;
}
int32_t execve(const char* filename, const char* argv[], const char* envp[]){
int rtn;
asm volatile (
"move $a0, %0\n"
"move $a1, %1\n"
"move $a2, %2\n"
"li $v0, 4011\n"
"syscall\n"
: "=r"(rtn)
: "r"(filename), "r"(argv), "r"(envp)
: "a0", "a1", "a2", "a3", "v0"
);
return (int32_t)rtn;
}
int32_t openat(int32_t fd, const char* filename, uint32_t flags, uint32_t mode) {
int32_t file = 0;
asm volatile (

View File

@@ -5,16 +5,16 @@
// Start code
#ifndef _START_DEFINED
#define _START_DEFINED
extern int main(int argc, char** argv);
extern int main(int argc, char **argv);
void _start() {
void* fp = __builtin_frame_address(0);
register int argc asm("rdi") = *(uint64_t*)(fp+8);
register char** argv asm("rsi") = fp+16;
void *fp = __builtin_frame_address(0);
register int argc asm("rdi") = *(uint64_t *)(fp + 8);
register char **argv asm("rsi") = fp + 16;
exit(main(argc, argv));
}
#endif /* ifndef _START_DEFINED */
//include other generic functions
// include other generic functions
#include "generic.h"
//
@@ -22,254 +22,245 @@ void _start() {
#define SYS_EXIT 60
void exit(int8_t status) {
asm volatile(
"syscall"
:
: "a"(SYS_EXIT), "D"(status)
:
);
for(;;);
asm volatile("syscall" : : "a"(SYS_EXIT), "D"(status) :);
for (;;)
;
}
#define SYS_WRITE 1
intptr_t write(int32_t fd, const void* buf, intptr_t size) {
intptr_t write(int32_t fd, const void *buf, intptr_t size) {
intptr_t n_written = 0;
asm volatile (
"syscall\n"
"movq %0, %%rax\n"
: "=r"(n_written)
: "a"(SYS_WRITE), "D"(fd), "S"(buf), "d"(size) // RDI, RSI, RDX are used in x86-64 for write args
: "rcx", "r11"
);
asm volatile("syscall\n"
"movq %0, %%rax\n"
: "=r"(n_written)
: "a"(SYS_WRITE), "D"(fd), "S"(buf),
"d"(size) // RDI, RSI, RDX are used in x86-64 for write args
: "rcx", "r11");
return n_written;
}
#define SYS_READ 0
intptr_t read(int32_t fd, const void* buf, intptr_t size) {
intptr_t read(int32_t fd, const void *buf, intptr_t size) {
intptr_t n_read = 0;
asm volatile (
"syscall\n"
"movq %0, %%rax\n"
: "=r"(n_read)
: "a"(SYS_READ), "D"(fd), "S"(buf), "d"(size) // RDI, RSI, RDX are used in x86-64 for write args
: "rcx", "r11", "memory"
);
asm volatile("syscall\n"
"movq %0, %%rax\n"
: "=r"(n_read)
: "a"(SYS_READ), "D"(fd), "S"(buf),
"d"(size) // RDI, RSI, RDX are used in x86-64 for write args
: "rcx", "r11", "memory");
return n_read;
}
#define SYS_FORK 57
uint32_t fork() {
uint64_t rtn;
asm volatile(
"syscall\n" // syscall
"movq %0, %%rax\n" // save return value
: "=r"(rtn)
: "a"(SYS_FORK)
:
);
asm volatile("syscall\n" // syscall
"movq %0, %%rax\n" // save return value
: "=r"(rtn)
: "a"(SYS_FORK)
:);
return (uint32_t)rtn;
}
#define SYS_EXECVE 59
int32_t execve(const char *filename, const char *argv[], const char *envp[]) {
int64_t rtn;
asm volatile("syscall\n"
: "=r"(rtn)
: "a"(SYS_EXECVE), "D"(filename), "S"(argv), "d"(envp)
: "rcx", "r11");
return (int32_t)rtn;
}
#define SYS_WAIT4 61
uint32_t wait4(uint32_t pid, int *wstatus, int options, void *rusage) {
uint32_t child_pid;
asm volatile("mov %5, %%r10\n"
"syscall\n"
: "=a"(child_pid)
: "a"(SYS_WAIT4), "D"(pid), "S"(wstatus), "d"(options),
"r"(rusage)
: "rcx", "r10", "r11", "memory");
return child_pid;
}
uint32_t waitpid(uint32_t pid, int *wstatus, int options) {
return wait4(pid, wstatus, options, 0);
}
#define SYS_OPENAT 257
int32_t openat(int32_t fd, const char* filename, uint32_t flags, uint32_t mode) {
int32_t openat(int32_t fd, const char *filename, uint32_t flags,
uint32_t mode) {
int32_t file;
asm volatile (
asm volatile(
"mov %5, %%r10\n"
"syscall\n"
: "=a"(file)
: "a"(SYS_OPENAT), "D"(fd), "S"(filename), "d"(flags), "r"((uint64_t)mode) // RDI, RSI, RDX are used in x86-64 for write args
: "rcx", "r10", "r11"
);
: "a"(SYS_OPENAT), "D"(fd), "S"(filename), "d"(flags),
"r"((uint64_t)mode) // RDI, RSI, RDX are used in x86-64 for write args
: "rcx", "r10", "r11");
return file;
}
#define SYS_CLOSE 3
int close(unsigned int fd) {
int rtn;
asm volatile(
"syscall"
: "=a"(rtn)
: "a"(SYS_CLOSE), "D"(fd)
: "rcx", "r11"
);
asm volatile("syscall" : "=a"(rtn) : "a"(SYS_CLOSE), "D"(fd) : "rcx", "r11");
return rtn;
}
#define SYS_FSYNC 74
int fsync(unsigned int fd) {
int rtn;
asm volatile(
"syscall"
: "=a"(rtn)
: "a"(SYS_FSYNC), "D"(fd)
: "rcx", "r11"
);
asm volatile("syscall" : "=a"(rtn) : "a"(SYS_FSYNC), "D"(fd) : "rcx", "r11");
return rtn;
}
#define SYS_SOCKET 41
int socket(int domain, int type, int protocol) {
int32_t fd;
asm volatile (
"syscall\n"
: "=a"(fd)
: "a"(SYS_SOCKET), "D"(domain), "S"(type), "d"(protocol)
: "rcx", "r11"
);
asm volatile("syscall\n"
: "=a"(fd)
: "a"(SYS_SOCKET), "D"(domain), "S"(type), "d"(protocol)
: "rcx", "r11");
return fd;
}
#define SYS_SETSOCKOPT 54
int setsockopt(int fd, int level, int optname, const void* optval, socklen_t optlen) {
int setsockopt(int fd, int level, int optname, const void *optval,
socklen_t optlen) {
int rtn;
asm volatile(
"mov %5, %%r10\n"
"mov %6, %%r8\n"
"syscall\n"
: "=a"(rtn)
: "a"(SYS_SETSOCKOPT), "D"(fd), "S"(level), "d"(optname), "r"(optval), "r"((uint64_t)optlen)
: "rcx", "r11"
);
asm volatile("mov %5, %%r10\n"
"mov %6, %%r8\n"
"syscall\n"
: "=a"(rtn)
: "a"(SYS_SETSOCKOPT), "D"(fd), "S"(level), "d"(optname),
"r"(optval), "r"((uint64_t)optlen)
: "rcx", "r11");
return rtn;
}
#define SYS_BIND 49
int bind(int fd, const void* sockaddr, socklen_t addrlen) {
int bind(int fd, const void *sockaddr, socklen_t addrlen) {
int rtn;
asm volatile(
"syscall\n"
: "=a"(rtn)
: "a"(SYS_BIND), "D"(fd), "S"(sockaddr), "d"(addrlen)
: "rcx", "r11"
);
asm volatile("syscall\n"
: "=a"(rtn)
: "a"(SYS_BIND), "D"(fd), "S"(sockaddr), "d"(addrlen)
: "rcx", "r11");
return rtn;
}
#define SYS_LISTEN 50
int listen(int fd, int backlog) {
int rtn;
asm volatile(
"syscall\n"
: "=a"(rtn)
: "a"(SYS_LISTEN), "D"(fd), "S"(backlog)
: "rcx", "r11"
);
asm volatile("syscall\n"
: "=a"(rtn)
: "a"(SYS_LISTEN), "D"(fd), "S"(backlog)
: "rcx", "r11");
return rtn;
}
#define SYS_ACCEPT 43
int accept(int fd, void* addr, socklen_t* addrlen) {
int accept(int fd, void *addr, socklen_t *addrlen) {
int rtn;
asm volatile(
"syscall\n"
: "=a"(rtn)
: "a"(SYS_ACCEPT), "D"(fd), "S"(addr), "d"(addrlen)
: "rcx", "r11", "memory"
);
asm volatile("syscall\n"
: "=a"(rtn)
: "a"(SYS_ACCEPT), "D"(fd), "S"(addr), "d"(addrlen)
: "rcx", "r11", "memory");
return rtn;
}
#define SYS_CONNECT 42
int connect(int fd, void* addr, socklen_t addrlen) {
int connect(int fd, void *addr, socklen_t addrlen) {
int rtn;
asm volatile(
"syscall\n"
: "=a"(rtn)
: "a"(SYS_CONNECT), "D"(fd), "S"(addr), "d"(addrlen)
: "rcx", "r11"
);
asm volatile("syscall\n"
: "=a"(rtn)
: "a"(SYS_CONNECT), "D"(fd), "S"(addr), "d"(addrlen)
: "rcx", "r11");
return rtn;
}
#define SYS_SENDTO 44
ssize_t sendto(int fd, const void* buf, size_t size, int flags, const void* sockaddr, socklen_t addrlen) {
ssize_t sendto(int fd, const void *buf, size_t size, int flags,
const void *sockaddr, socklen_t addrlen) {
ssize_t nsent;
asm volatile(
"mov %5, %%r10\n"
"mov %6, %%r8\n"
"mov %7, %%r9\n"
"syscall\n"
: "=a"(nsent)
: "a"(SYS_SENDTO), "D"(fd), "S"(buf), "d"(size), "r"((int64_t)flags), "r"(sockaddr), "r"((uint64_t)addrlen)
: "rcx", "r11"
);
asm volatile("mov %5, %%r10\n"
"mov %6, %%r8\n"
"mov %7, %%r9\n"
"syscall\n"
: "=a"(nsent)
: "a"(SYS_SENDTO), "D"(fd), "S"(buf), "d"(size),
"r"((int64_t)flags), "r"(sockaddr), "r"((uint64_t)addrlen)
: "rcx", "r11");
return nsent;
}
#define SYS_RECVFROM 45
ssize_t recvfrom(int fd, const void* buf, size_t size, int flags, const void* sockaddr, socklen_t addrlen) {
ssize_t recvfrom(int fd, const void *buf, size_t size, int flags,
const void *sockaddr, socklen_t addrlen) {
ssize_t nrecv;
asm volatile(
"mov %5, %%r10\n"
"mov %6, %%r8\n"
"mov %7, %%r9\n"
"syscall\n"
: "=a"(nrecv)
: "a"(SYS_RECVFROM), "D"(fd), "S"(buf), "d"(size), "r"((int64_t)flags), "r"(sockaddr), "r"((int64_t)addrlen)
: "rcx", "r11", "memory"
);
asm volatile("mov %5, %%r10\n"
"mov %6, %%r8\n"
"mov %7, %%r9\n"
"syscall\n"
: "=a"(nrecv)
: "a"(SYS_RECVFROM), "D"(fd), "S"(buf), "d"(size),
"r"((int64_t)flags), "r"(sockaddr), "r"((int64_t)addrlen)
: "rcx", "r11", "memory");
return nrecv;
}
#define SYS_CLOCK_SETTIME 227
int clock_settime(clockid_t clockid, const struct timespec *tp) {
int rtn;
asm volatile(
"syscall\n"
: "=a"(rtn)
: "a"(SYS_CLOCK_SETTIME), "D"(clockid), "S"(tp)
: "rcx", "r11"
);
asm volatile("syscall\n"
: "=a"(rtn)
: "a"(SYS_CLOCK_SETTIME), "D"(clockid), "S"(tp)
: "rcx", "r11");
return rtn;
}
#define SYS_CLOCK_GETTIME 228
int clock_gettime(clockid_t clockid, struct timespec *tp) {
int rtn;
asm volatile(
"syscall\n"
: "=a"(rtn)
: "a"(SYS_CLOCK_GETTIME), "D"(clockid), "S"(tp)
: "rcx", "r11", "memory"
);
asm volatile("syscall\n"
: "=a"(rtn)
: "a"(SYS_CLOCK_GETTIME), "D"(clockid), "S"(tp)
: "rcx", "r11", "memory");
return rtn;
}
#define SYS_CLOCK_GETRES 229
int clock_getres(clockid_t clockid, struct timespec *res){
int clock_getres(clockid_t clockid, struct timespec *res) {
int rtn;
asm volatile(
"syscall\n"
: "=a"(rtn)
: "a"(SYS_CLOCK_GETRES), "D"(clockid), "S"(res)
: "rcx", "r11", "memory"
);
asm volatile("syscall\n"
: "=a"(rtn)
: "a"(SYS_CLOCK_GETRES), "D"(clockid), "S"(res)
: "rcx", "r11", "memory");
return rtn;
}
#define SYS_CLOCK_NANOSLEEP 230
int clock_nanosleep(clockid_t clockid, int flags, const struct timespec *t, struct timespec* remain){
int clock_nanosleep(clockid_t clockid, int flags, const struct timespec *t,
struct timespec *remain) {
int rtn;
asm volatile(
"mov %5, %%r10\n"
"syscall\n"
: "=a"(rtn)
: "a"(SYS_CLOCK_NANOSLEEP), "D"(clockid), "S"(flags), "d"(t), "r"(remain)
: "rcx", "r11", "memory"
);
asm volatile("mov %5, %%r10\n"
"syscall\n"
: "=a"(rtn)
: "a"(SYS_CLOCK_NANOSLEEP), "D"(clockid), "S"(flags), "d"(t),
"r"(remain)
: "rcx", "r11", "memory");
return rtn;
}
#define SYS_RT_SIGACTION 13
int rt_sigaction(int signum, const void* act, void* oldact, size_t sigset_len) {
int rt_sigaction(int signum, const void *act, void *oldact, size_t sigset_len) {
int rtn;
asm volatile(
"mov %5, %%r10\n"
"syscall\n"
: "=a"(rtn)
: "a"(SYS_RT_SIGACTION), "D"(signum), "S"(act), "d"(oldact), "r"(sigset_len)
: "rcx", "r11", "memory"
);
asm volatile("mov %5, %%r10\n"
"syscall\n"
: "=a"(rtn)
: "a"(SYS_RT_SIGACTION), "D"(signum), "S"(act), "d"(oldact),
"r"(sigset_len)
: "rcx", "r11", "memory");
return rtn;
}

View File

@@ -1,6 +1,22 @@
#include "int.h"
#include "sys.h"
#define print(str) write(STDOUT, str, (sizeof str) - 1)
void printhex(uint64_t num) {
char msg[17] = {' '};
msg[16] = '\n';
for (int i = 0; i < 16; ++i) {
char nibble = (num >> ((15 - i) * 4)) & 0xf;
char c;
if (nibble > 9) {
c = nibble + '7';
} else {
c = nibble + '0';
}
msg[i] = c;
}
write(STDOUT, msg, 17);
}
int main(int argc, char* argv[]) {
// Test the write syscall
@@ -17,27 +33,32 @@ int main(int argc, char* argv[]) {
// Test the fork syscall
uint64_t pid = fork();
// Print the pid in hex
char msg[17] = {' '};
msg[16] = '\n';
for(int i = 0; i < 16; ++i) {
char nibble = (pid >> ((15 - i) * 4)) & 0xf;
char c;
if (nibble > 9) {c = nibble + '7';}
else {c = nibble + '0';}
msg[i] = c;
}
write(STDOUT, msg, 17);
// Child process exits
if(pid == 0) return 0;
//TODO: wait on child to remove zombie process
printhex(pid);
// Child process
if (pid == 0) {
const char *progname = "/bin/sh";
const char *argv[4] = {
progname,
"-c",
"echo hi from sh",
0,
};
const char **envp = {0};
int32_t err = execve(progname, argv, envp);
// return with error code if execve fails
return err;
}
int status; // Note: this is not JUST the exit code of the child
waitpid(pid, &status, 0);
print("Child wstatus: ");
printhex((uint64_t)status);
// Test the read syscall
#define INPUT_BUFFER_LEN 4096
char input_buffer[INPUT_BUFFER_LEN] = {0};
int32_t fout = openc("outfile", O_RDWR | O_CREAT | O_TRUNC, 0664);
write(STDOUT, "Enter some text:", 16);
print("Enter some text:");
intptr_t n_read = read(STDIN, input_buffer, INPUT_BUFFER_LEN);
write(STDOUT, input_buffer, n_read);
if(fout > 0) {
@@ -55,15 +76,15 @@ int main(int argc, char* argv[]) {
i = read(file, input_buffer, INPUT_BUFFER_LEN);
}
} else {
write(STDOUT, "Could not open /proc/version\n", 29);
print("Could not open /proc/version\n");
}
// Test sleep
write(STDOUT, "Sleeping for 5 secs\n", 20);
print("Sleeping for 5 secs\n");
struct timespec sleeptime;
sleeptime.tv_sec = 5;
while (nanosleep(&sleeptime, &sleeptime) != 0) {}
write(STDOUT, "DONE\n", 5);
print("DONE\n");
return 69;
}

2
sys.h
View File

@@ -141,6 +141,8 @@ int clock_nanosleep(clockid_t clockid, int flags, const struct timespec *t, stru
#define nanosleep(t, remain) clock_nanosleep(CLOCK_MONOTONIC, 0, t, remain)
uint32_t fork();
int32_t execve(const char* filename, const char* argv[], const char* envp[]);
uint32_t waitpid(uint32_t pid, int* wstatus, int options);
void *memset(void* s, int c, unsigned long n);
int strlen(char* s);