// SPDX-License-Identifier: Apache-2.0 // SchOS — first-party userland runtime: _start entry and exit. #include "types.h" #include "user.h" // The raw syscall. It takes the status in %rdi, and both callers below must // pass one: this declaration used to be `void _xv6_exit(void)`, so the status // was whatever the caller happened to leave in that register. Every program // that ended with a bare exit() reported a random number, which is why // `if socktest; then` could not work for any of them. extern void _xv6_exit(int status); // exit(void) in this library means exit(0). A program that wants to report // something returns it from main -- lib/start.S passes that through now -- or // calls exit_group(n). void exit(void) { _xv6_exit(0); for (;;); } /* _start lives in lib/start.S — it must read incoming registers, which * cannot be expressed portably in C. See that file. */ // The four the compiler is allowed to call without being asked. // // A freestanding build gets no libc, but it does not get a compiler that // promises never to *call* one: the C standard lets an implementation emit // calls to memcpy, memmove, memset and memcmp for things that look nothing // like a function call. A struct assignment is the common one -- // // *out = in; /* struct pciuser_info, 96 bytes */ // // -- which gcc unrolled into stores and clang turned into `call memcpy`, so // ldlsd linked for months and then did not, on a machine where nothing about // it had changed except which compiler ran. The fix is not to avoid struct // assignment; it is to provide the four functions, because the compiler is // within its rights either way. // // Deliberately simple. These are not the hot path of anything here, and a // clever one risks being turned back into a call to itself. void * memcpy(void *dst, const void *src, unsigned long n) { unsigned char *d = dst; const unsigned char *s = src; while (n--) *d++ = *s++; return dst; } void * memmove(void *dst, const void *src, unsigned long n) { unsigned char *d = dst; const unsigned char *s = src; if (d == s || n == 0) return dst; // Backwards when they overlap the wrong way, which is the whole reason // this is not memcpy. if (d < s) { while (n--) *d++ = *s++; } else { d += n; s += n; while (n--) *--d = *--s; } return dst; } void * memset(void *dst, int c, unsigned long n) { unsigned char *d = dst; while (n--) *d++ = (unsigned char)c; return dst; } int memcmp(const void *a, const void *b, unsigned long n) { const unsigned char *p = a, *q = b; while (n--) { if (*p != *q) return *p - *q; p++; q++; } return 0; } void __stack_chk_fail(void) { // A smashed stack is not a successful run. 134 is what a shell reports for // SIGABRT, which is the nearest true thing this can say. _xv6_exit(134); for (;;); } // exec() without an environment. // // The syscall is execve, three arguments, because that is what Linux numbers // 59 and what musl calls. The two-argument spelling stays because most of // cmd/ has no environment to pass and should not have to say so -- but it // says so *here*, explicitly, rather than leaving %rdx to chance. int exec(char *path, char **argv) { return execve(path, argv, 0); }