| 1 |
/* start.c - Minimal libc for Pactor64 userspace |
| 2 |
* |
| 3 |
* Provides _start entry point, syscall wrappers, string functions, |
| 4 |
* printf, and bump allocator. |
| 5 |
*/ |
| 6 |
#include "libc.h" |
| 7 |
|
| 8 |
/* Syscall numbers (Linux x86-64 compatible) */ |
| 9 |
#define SYS_READ 0 |
| 10 |
#define SYS_WRITE 1 |
| 11 |
#define SYS_BRK 12 |
| 12 |
#define SYS_EXIT 60 |
| 13 |
|
| 14 |
/* Inline syscall instruction */ |
| 15 |
static inline long __syscall1(long num, long a1) { |
| 16 |
long ret; |
| 17 |
__asm__ volatile ( |
| 18 |
"syscall" |
| 19 |
: "=a"(ret) |
| 20 |
: "a"(num), "D"(a1) |
| 21 |
: "rcx", "r11", "memory" |
| 22 |
); |
| 23 |
return ret; |
| 24 |
} |
| 25 |
|
| 26 |
static inline long __syscall3(long num, long a1, long a2, long a3) { |
| 27 |
long ret; |
| 28 |
__asm__ volatile ( |
| 29 |
"syscall" |
| 30 |
: "=a"(ret) |
| 31 |
: "a"(num), "D"(a1), "S"(a2), "d"(a3) |
| 32 |
: "rcx", "r11", "memory" |
| 33 |
); |
| 34 |
return ret; |
| 35 |
} |
| 36 |
|
| 37 |
/* === System call wrappers === */ |
| 38 |
|
| 39 |
long write(int fd, const void *buf, unsigned long count) { |
| 40 |
return __syscall3(SYS_WRITE, fd, (long)buf, (long)count); |
| 41 |
} |
| 42 |
|
| 43 |
long read(int fd, void *buf, unsigned long count) { |
| 44 |
return __syscall3(SYS_READ, fd, (long)buf, (long)count); |
| 45 |
} |
| 46 |
|
| 47 |
void exit(int status) { |
| 48 |
__syscall1(SYS_EXIT, status); |
| 49 |
__builtin_unreachable(); |
| 50 |
} |
| 51 |
|
| 52 |
long brk(void *addr) { |
| 53 |
return __syscall1(SYS_BRK, (long)addr); |
| 54 |
} |
| 55 |
|
| 56 |
/* === String functions === */ |
| 57 |
|
| 58 |
unsigned long strlen(const char *s) { |
| 59 |
unsigned long len = 0; |
| 60 |
while (s[len]) len++; |
| 61 |
return len; |
| 62 |
} |
| 63 |
|
| 64 |
int strcmp(const char *s1, const char *s2) { |
| 65 |
while (*s1 && *s1 == *s2) { s1++; s2++; } |
| 66 |
return (unsigned char)*s1 - (unsigned char)*s2; |
| 67 |
} |
| 68 |
|
| 69 |
int strncmp(const char *s1, const char *s2, unsigned long n) { |
| 70 |
while (n && *s1 && *s1 == *s2) { s1++; s2++; n--; } |
| 71 |
return n ? (unsigned char)*s1 - (unsigned char)*s2 : 0; |
| 72 |
} |
| 73 |
|
| 74 |
char *strcpy(char *dst, const char *src) { |
| 75 |
char *ret = dst; |
| 76 |
while ((*dst++ = *src++)); |
| 77 |
return ret; |
| 78 |
} |
| 79 |
|
| 80 |
char *strncpy(char *dst, const char *src, unsigned long n) { |
| 81 |
char *ret = dst; |
| 82 |
while (n && (*dst++ = *src++)) n--; |
| 83 |
while (n--) *dst++ = '\0'; |
| 84 |
return ret; |
| 85 |
} |
| 86 |
|
| 87 |
void *memset(void *s, int c, unsigned long n) { |
| 88 |
unsigned char *p = (unsigned char *)s; |
| 89 |
while (n--) *p++ = (unsigned char)c; |
| 90 |
return s; |
| 91 |
} |
| 92 |
|
| 93 |
void *memcpy(void *dst, const void *src, unsigned long n) { |
| 94 |
unsigned char *d = (unsigned char *)dst; |
| 95 |
const unsigned char *s = (const unsigned char *)src; |
| 96 |
while (n--) *d++ = *s++; |
| 97 |
return dst; |
| 98 |
} |
| 99 |
|
| 100 |
void *memmove(void *dst, const void *src, unsigned long n) { |
| 101 |
unsigned char *d = (unsigned char *)dst; |
| 102 |
const unsigned char *s = (const unsigned char *)src; |
| 103 |
if (d < s) { |
| 104 |
while (n--) *d++ = *s++; |
| 105 |
} else { |
| 106 |
d += n; s += n; |
| 107 |
while (n--) *--d = *--s; |
| 108 |
} |
| 109 |
return dst; |
| 110 |
} |
| 111 |
|
| 112 |
/* === Bump allocator === */ |
| 113 |
|
| 114 |
static void *heap_start = 0; |
| 115 |
static void *heap_end = 0; |
| 116 |
static void *heap_current = 0; |
| 117 |
|
| 118 |
static void heap_init(void) { |
| 119 |
heap_start = (void *)0x400000; /* Start at 4MB */ |
| 120 |
heap_end = (void *)0x10000000; /* Up to 256MB */ |
| 121 |
heap_current = heap_start; |
| 122 |
brk(heap_end); |
| 123 |
} |
| 124 |
|
| 125 |
void *malloc(unsigned long size) { |
| 126 |
if (heap_start == 0) heap_init(); |
| 127 |
|
| 128 |
/* Align to 16 bytes */ |
| 129 |
size = (size + 15) & ~15UL; |
| 130 |
|
| 131 |
void *ptr = heap_current; |
| 132 |
heap_current = (void *)((unsigned long)heap_current + size); |
| 133 |
|
| 134 |
if (heap_current > heap_end) { |
| 135 |
/* Out of memory */ |
| 136 |
return 0; |
| 137 |
} |
| 138 |
|
| 139 |
return ptr; |
| 140 |
} |
| 141 |
|
| 142 |
void free(void *ptr) { |
| 143 |
/* Bump allocator - no-op for simplicity */ |
| 144 |
(void)ptr; |
| 145 |
} |
| 146 |
|
| 147 |
/* === I/O === */ |
| 148 |
|
| 149 |
int putchar(int c) { |
| 150 |
char ch = (char)c; |
| 151 |
write(1, &ch, 1); |
| 152 |
return c; |
| 153 |
} |
| 154 |
|
| 155 |
int puts(const char *s) { |
| 156 |
unsigned long len = strlen(s); |
| 157 |
write(1, s, len); |
| 158 |
putchar('\n'); |
| 159 |
return (int)(len + 1); |
| 160 |
} |
| 161 |
|
| 162 |
/* === printf === */ |
| 163 |
|
| 164 |
static void print_string(const char *s) { |
| 165 |
if (!s) s = "(null)"; |
| 166 |
write(1, s, strlen(s)); |
| 167 |
} |
| 168 |
|
| 169 |
static void print_dec(unsigned long val) { |
| 170 |
char buf[21]; |
| 171 |
int i = 0; |
| 172 |
if (val == 0) { putchar('0'); return; } |
| 173 |
while (val > 0) { |
| 174 |
buf[i++] = '0' + (val % 10); |
| 175 |
val /= 10; |
| 176 |
} |
| 177 |
while (i > 0) putchar(buf[--i]); |
| 178 |
} |
| 179 |
|
| 180 |
static void print_signed_dec(long val) { |
| 181 |
if (val < 0) { |
| 182 |
putchar('-'); |
| 183 |
print_dec((unsigned long)-val); |
| 184 |
} else { |
| 185 |
print_dec((unsigned long)val); |
| 186 |
} |
| 187 |
} |
| 188 |
|
| 189 |
static void print_hex(unsigned long val) { |
| 190 |
char buf[17]; |
| 191 |
int i = 0; |
| 192 |
if (val == 0) { putchar('0'); return; } |
| 193 |
while (val > 0) { |
| 194 |
int digit = val & 0xF; |
| 195 |
buf[i++] = digit < 10 ? '0' + digit : 'a' + digit - 10; |
| 196 |
val >>= 4; |
| 197 |
} |
| 198 |
while (i > 0) putchar(buf[--i]); |
| 199 |
} |
| 200 |
|
| 201 |
int printf(const char *fmt, ...) { |
| 202 |
va_list ap; |
| 203 |
va_start(ap, fmt); |
| 204 |
int count = 0; |
| 205 |
|
| 206 |
for (int i = 0; fmt[i]; i++) { |
| 207 |
if (fmt[i] == '%' && fmt[i + 1]) { |
| 208 |
i++; |
| 209 |
switch (fmt[i]) { |
| 210 |
case 'd': |
| 211 |
print_signed_dec(va_arg(ap, long)); |
| 212 |
break; |
| 213 |
case 'u': |
| 214 |
print_dec(va_arg(ap, unsigned long)); |
| 215 |
break; |
| 216 |
case 'x': |
| 217 |
print_hex(va_arg(ap, unsigned long)); |
| 218 |
break; |
| 219 |
case 's': |
| 220 |
print_string(va_arg(ap, const char *)); |
| 221 |
break; |
| 222 |
case 'c': |
| 223 |
putchar((char)va_arg(ap, int)); |
| 224 |
break; |
| 225 |
case '%': |
| 226 |
putchar('%'); |
| 227 |
break; |
| 228 |
default: |
| 229 |
putchar('%'); |
| 230 |
putchar(fmt[i]); |
| 231 |
break; |
| 232 |
} |
| 233 |
} else { |
| 234 |
putchar(fmt[i]); |
| 235 |
count++; |
| 236 |
} |
| 237 |
} |
| 238 |
va_end(ap); |
| 239 |
return count; |
| 240 |
} |
| 241 |
|
| 242 |
/* === Entry point === */ |
| 243 |
|
| 244 |
void _start(void) { |
| 245 |
int ret = main(0, 0); |
| 246 |
exit(ret); |
| 247 |
} |
| 248 |
|