commit 38cc34a6e949591b8b351d3ab0a55bf8132e20e5 Author: Faynot Date: Tue Jul 7 20:35:07 2026 +0300 init commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f8a790a --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.o +*.a +obj/ +lib/ +config.mak diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d98ea08 --- /dev/null +++ b/Makefile @@ -0,0 +1,40 @@ +CC ?= gcc +AR ?= ar +RANLIB ?= ranlib +CFLAGS ?= -Os -fno-builtin -ffreestanding -nostdinc +LDFLAGS ?= -nostdlib -static + +-include config.mak +ARCH ?= x86_64 + +CFLAGS += -I. -I include + +SRC_DIRS = src/string src/stdio src/stdlib src/internal +OBJ_DIR = obj +LIB_DIR = lib + +SRCS := $(shell find $(SRC_DIRS) -name '*.c' 2>/dev/null) +OBJS := $(SRCS:%=$(OBJ_DIR)/%.o) +CRT_OBJ := crt/crt1.o + +.PHONY: all clean distclean + +all: $(LIB_DIR)/libc.a $(CRT_OBJ) + +$(OBJ_DIR)/%.c.o: %.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB_DIR)/libc.a: $(OBJS) + @mkdir -p $(LIB_DIR) + $(AR) crs $@ $^ + $(RANLIB) $@ + +crt/crt1.o: crt/crt1.c + $(CC) $(CFLAGS) -c -o $@ $< + +clean: + rm -rf $(OBJ_DIR) $(LIB_DIR) + +distclean: clean + rm -f config.mak diff --git a/README.md b/README.md new file mode 100644 index 0000000..000c96d --- /dev/null +++ b/README.md @@ -0,0 +1,56 @@ +# elibc — Standard C Library for Elyz + +Minimal C library for the Elyz OS, based on musl's organisational principles +but adapted to Elyz's capability-based, syscall-less architecture. + +## Structure + +``` +elibc/ +├── arch/ # Architecture-specific code +│ ├── generic/ # Fallback implementation & abstract interfaces +│ │ ├── kcall.h # Kernel-call abstraction (no syscalls in Elyz) +│ │ ├── startup_info.h # Process-startup description +│ │ └── bits/ # Type-size definitions +│ └── x86_64/ # x86_64 port +│ ├── kcall.h # Kernel-call placeholder (ABI TBD) +│ ├── startup_info.h # x86_64 startup layout (ABI TBD) +│ ├── atomic_arch.h # Locked cmpxchg / atomic ops +│ └── bits/ +├── include/ # Public headers (POSIX-like API) +├── src/ # Source implementations +│ ├── internal/ # Libc state, errno, bump allocator (temporary) +│ ├── string/ # Pure-C string ops (memcpy, strlen, etc.) +│ ├── stdio/ # (reserved) +│ └── stdlib/ # atoi, etc. +├── crt/ # C runtime entry point +├── Makefile +└── configure +``` + +## Key differences from a traditional libc (glibc/musl) + +| Traditional (Linux) | Elyz / elibc | +|---|---| +| `syscall`/`int 0x80` | Capability invocation → PM Actor messaging | +| `brk`/`sbrk` | `ELYZ_OP_ALLOCATE` via `elyz_kcall` | +| `_exit` syscall | `ELYZ_OP_EXIT` via kcall | +| `errno` via TLS | `__errno_location` (plain global for now) | + +## Porting to a new arch + +1. Create `arch//` with: + - `kcall.h` — kernel-call mechanism + - `startup_info.h` — process-startup layout + - `arch.mak` — arch-specific compiler/linker flags + - `bits/alltypes.h.in` — type sizes + - `atomic_arch.h` — atomic ops (optional, fallback exists) +2. Add detection in `configure` +3. Build: `./configure && make` + +## Building + +``` +./configure +make +``` diff --git a/arch/generic/arch.mak b/arch/generic/arch.mak new file mode 100644 index 0000000..251c93b --- /dev/null +++ b/arch/generic/arch.mak @@ -0,0 +1,3 @@ +# Generic architecture flags — override per port +ARCH_CFLAGS := +ARCH_LDFLAGS := diff --git a/arch/generic/bits/alltypes.h.in b/arch/generic/bits/alltypes.h.in new file mode 100644 index 0000000..342287d --- /dev/null +++ b/arch/generic/bits/alltypes.h.in @@ -0,0 +1,7 @@ +/* Generic type sizes — override per arch if different */ +TYPEDEF int ptrdiff_t; +TYPEDEF unsigned long size_t; +TYPEDEF long ssize_t; +TYPEDEF unsigned long uintptr_t; +TYPEDEF long intptr_t; +TYPEDEF int wchar_t; diff --git a/arch/generic/kcall.h b/arch/generic/kcall.h new file mode 100644 index 0000000..037d540 --- /dev/null +++ b/arch/generic/kcall.h @@ -0,0 +1,30 @@ +#ifndef _ELIBC_KCALL_H +#define _ELIBC_KCALL_H + +/* Elyz kernel-call abstraction. + * + * Elyz has NO syscall instruction. Communication with the kernel and PM + * actors happens through capability invocation — a mechanism defined by the + * kernel ABI (not finalised yet). This header describes the abstract + * interface; the arch-specific implementation (arch//kcall.h) fills in + * the concrete mechanism. + * + * Current state: STUB — returns -ENOSYS until the Elyz kernel ABI is + * stabilised. + */ + +#define ELYZ_OP_ALLOCATE 1 +#define ELYZ_OP_FREE 2 +#define ELYZ_OP_CARVE 3 +#define ELYZ_OP_EXIT 4 + +struct elyz_kcall { + unsigned long opcode; + unsigned long arg1; + unsigned long arg2; + unsigned short channel_id; +}; + +long __kcall(struct elyz_kcall *msg); + +#endif diff --git a/arch/generic/startup_info.h b/arch/generic/startup_info.h new file mode 100644 index 0000000..d01e1e8 --- /dev/null +++ b/arch/generic/startup_info.h @@ -0,0 +1,22 @@ +#ifndef _ELIBC_STARTUP_INFO_H +#define _ELIBC_STARTUP_INFO_H + +/* Process-startup information block. + * + * The Elyz loader passes an opaque structure to every new process. + * Layout and content are arch-defined (see arch//startup_info.h). + * At minimum the process receives: + * - an initial memory pool (capability / region) + * - argc / argv from the parent + * - a capability-wallet (CNode) root + */ + +struct elyz_startup { + unsigned long initial_cnode_cap; + unsigned long initial_pool_addr; + unsigned long initial_pool_size; + int argc; + char **argv; +}; + +#endif diff --git a/arch/x86_64/arch.mak b/arch/x86_64/arch.mak new file mode 100644 index 0000000..4a0344c --- /dev/null +++ b/arch/x86_64/arch.mak @@ -0,0 +1,3 @@ +# x86_64 architecture flags +ARCH_CFLAGS := -m64 +ARCH_LDFLAGS := -m elf_x86_64 diff --git a/arch/x86_64/atomic_arch.h b/arch/x86_64/atomic_arch.h new file mode 100644 index 0000000..d343d8b --- /dev/null +++ b/arch/x86_64/atomic_arch.h @@ -0,0 +1,26 @@ +#ifndef _ELIBC_ATOMIC_ARCH_H +#define _ELIBC_ATOMIC_ARCH_H + +/* x86_64: minimal atomic ops via locked cmpxchg (CPU-local, no syscall) */ + +static inline int a_cas(volatile int *p, int t, int s) +{ + int old; + __asm__ volatile ("lock; cmpxchg %3, %1" + : "=a"(old), "=m"(*p) + : "a"(t), "r"(s), "m"(*p) + : "memory"); + return old; +} + +static inline void a_inc(volatile int *p) +{ + __asm__ volatile ("lock; incl %0" : "=m"(*p) : "m"(*p) : "memory"); +} + +static inline void a_dec(volatile int *p) +{ + __asm__ volatile ("lock; decl %0" : "=m"(*p) : "m"(*p) : "memory"); +} + +#endif diff --git a/arch/x86_64/bits/alltypes.h.in b/arch/x86_64/bits/alltypes.h.in new file mode 100644 index 0000000..0eb99f2 --- /dev/null +++ b/arch/x86_64/bits/alltypes.h.in @@ -0,0 +1,6 @@ +TYPEDEF int ptrdiff_t; +TYPEDEF unsigned long size_t; +TYPEDEF long ssize_t; +TYPEDEF unsigned long uintptr_t; +TYPEDEF long intptr_t; +TYPEDEF int wchar_t; diff --git a/arch/x86_64/kcall.h b/arch/x86_64/kcall.h new file mode 100644 index 0000000..a3c8e59 --- /dev/null +++ b/arch/x86_64/kcall.h @@ -0,0 +1,21 @@ +#ifndef _ELIBC_X86_64_KCALL_H +#define _ELIBC_X86_64_KCALL_H + +#include + +/* x86_64: placeholder — Elyz kernel ABI not yet defined. + * + * When the ABI exists this will contain either: + * - a direct userspace→actor message-pipe write, or + * - a lightweight kernel entry that routes via the PMRouter. + * + * For now: return -ENOSYS and let the CPU halt. + */ +static inline long __kcall(struct elyz_kcall *msg) +{ + (void)msg; + for (;;) __asm__ volatile("hlt"); + return -38; /* -ENOSYS */ +} + +#endif diff --git a/arch/x86_64/startup_info.h b/arch/x86_64/startup_info.h new file mode 100644 index 0000000..4d20853 --- /dev/null +++ b/arch/x86_64/startup_info.h @@ -0,0 +1,16 @@ +#ifndef _ELIBC_X86_64_STARTUP_INFO_H +#define _ELIBC_X86_64_STARTUP_INFO_H + +/* x86_64 process-startup ABI. + * + * The loader places an elyz_startup block at a known location (e.g. in a + * register or on a special stack page). The exact ABI depends on the + * kernel-to-userspace handoff which is not yet implemented. + * + * Current state: STUB — the process receives argc/argv on the stack as a + * fallback, and the initial memory pool comes from a fixed test region. + */ + +#include + +#endif diff --git a/configure b/configure new file mode 100755 index 0000000..d3202ed --- /dev/null +++ b/configure @@ -0,0 +1,21 @@ +#!/bin/sh +# Minimal configure for elibc — detects target architecture + +echo "Configuring elibc..." + +# Detect architecture +case "$(uname -m)" in + x86_64|amd64) + ARCH=x86_64 + ;; + aarch64|arm64) + ARCH=aarch64 + ;; + *) + echo "Warning: unknown arch $(uname -m), falling back to x86_64" + ARCH=x86_64 + ;; +esac + +echo "ARCH := $ARCH" > config.mak +echo "Configured for $ARCH" diff --git a/crt/crt1.c b/crt/crt1.c new file mode 100644 index 0000000..6b3998b --- /dev/null +++ b/crt/crt1.c @@ -0,0 +1,32 @@ +/* Minimal C runtime — entry point before main. + * + * Elyz process startup: + * the loader transfers control here with an initial capability wallet + * (CNode) and a pool of untyped memory. No traditional syscalls exist; + * all kernel interaction goes through capability invocation. + * + * Current state: STUB — the kernel ABI is under construction. + * - argc/argv passed on stack (convention inherited from limine boot) + * - initial memory pool not yet wired; __libc_init sets up a tiny bump + * arena for development. + */ + +void _start(void) +{ + extern int main(int, char **, char **); + extern void __libc_init(void); + + register long *args __asm__("rsp"); + int argc = (int)args[0]; + char **argv = (char **)(args + 1); + char **envp = argv + argc + 1; + + __libc_init(); + + int ret = main(argc, argv, envp); + + /* No _exit syscall in Elyz — send an Exit message to the PM actor. + * For now: infinite halt. */ + for (;;) __asm__ volatile("hlt"); + (void)ret; +} diff --git a/include/errno.h b/include/errno.h new file mode 100644 index 0000000..dfb364e --- /dev/null +++ b/include/errno.h @@ -0,0 +1,42 @@ +#ifndef _ELIBC_ERRNO_H +#define _ELIBC_ERRNO_H + +#define EPERM 1 +#define ENOENT 2 +#define ESRCH 3 +#define EINTR 4 +#define EIO 5 +#define ENXIO 6 +#define E2BIG 7 +#define ENOEXEC 8 +#define EBADF 9 +#define ECHILD 10 +#define EAGAIN 11 +#define ENOMEM 12 +#define EACCES 13 +#define EFAULT 14 +#define EBUSY 16 +#define EEXIST 17 +#define EXDEV 18 +#define ENODEV 19 +#define ENOTDIR 20 +#define EISDIR 21 +#define EINVAL 22 +#define ENFILE 23 +#define EMFILE 24 +#define ENOTTY 25 +#define EFBIG 27 +#define ENOSPC 28 +#define ESPIPE 29 +#define EROFS 30 +#define EMLINK 31 +#define EPIPE 32 +#define EDOM 33 +#define ERANGE 34 +#define EDEADLK 35 +#define ENOSYS 38 + +extern int *__errno_location(void); +#define errno (*__errno_location()) + +#endif diff --git a/include/limits.h b/include/limits.h new file mode 100644 index 0000000..37af838 --- /dev/null +++ b/include/limits.h @@ -0,0 +1,26 @@ +#ifndef _ELIBC_LIMITS_H +#define _ELIBC_LIMITS_H + +#define CHAR_BIT 8 + +#define SCHAR_MIN (-128) +#define SCHAR_MAX 127 +#define UCHAR_MAX 255 + +#define SHRT_MIN (-32768) +#define SHRT_MAX 32767 +#define USHRT_MAX 65535 + +#define INT_MIN (-2147483647 - 1) +#define INT_MAX 2147483647 +#define UINT_MAX 4294967295U + +#define LONG_MIN (-9223372036854775807L - 1) +#define LONG_MAX 9223372036854775807L +#define ULONG_MAX 18446744073709551615UL + +#define LLONG_MIN (-9223372036854775807LL - 1) +#define LLONG_MAX 9223372036854775807LL +#define ULLONG_MAX 18446744073709551615ULL + +#endif diff --git a/include/stdarg.h b/include/stdarg.h new file mode 100644 index 0000000..2222814 --- /dev/null +++ b/include/stdarg.h @@ -0,0 +1,10 @@ +#ifndef _ELIBC_STDARG_H +#define _ELIBC_STDARG_H + +typedef __builtin_va_list va_list; +#define va_start(v, l) __builtin_va_start(v, l) +#define va_end(v) __builtin_va_end(v) +#define va_arg(v, t) __builtin_va_arg(v, t) +#define va_copy(d, s) __builtin_va_copy(d, s) + +#endif diff --git a/include/stdbool.h b/include/stdbool.h new file mode 100644 index 0000000..5a5c3e4 --- /dev/null +++ b/include/stdbool.h @@ -0,0 +1,9 @@ +#ifndef _ELIBC_STDBOOL_H +#define _ELIBC_STDBOOL_H + +#define bool _Bool +#define true 1 +#define false 0 +#define __bool_true_false_are_defined 1 + +#endif diff --git a/include/stddef.h b/include/stddef.h new file mode 100644 index 0000000..cd7c09a --- /dev/null +++ b/include/stddef.h @@ -0,0 +1,11 @@ +#ifndef _ELIBC_STDDEF_H +#define _ELIBC_STDDEF_H + +typedef __SIZE_TYPE__ size_t; +typedef __PTRDIFF_TYPE__ ptrdiff_t; +typedef __WCHAR_TYPE__ wchar_t; + +#define NULL ((void *)0) +#define offsetof(type, member) __builtin_offsetof(type, member) + +#endif diff --git a/include/stdint.h b/include/stdint.h new file mode 100644 index 0000000..fa7d76b --- /dev/null +++ b/include/stdint.h @@ -0,0 +1,18 @@ +#ifndef _ELIBC_STDINT_H +#define _ELIBC_STDINT_H + +typedef signed char int8_t; +typedef short int16_t; +typedef int int32_t; +typedef long long int64_t; + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +typedef __SIZE_TYPE__ size_t; +typedef __UINTPTR_TYPE__ uintptr_t; +typedef __INTPTR_TYPE__ intptr_t; + +#endif diff --git a/include/stdlib.h b/include/stdlib.h new file mode 100644 index 0000000..6647fd3 --- /dev/null +++ b/include/stdlib.h @@ -0,0 +1,18 @@ +#ifndef _ELIBC_STDLIB_H +#define _ELIBC_STDLIB_H + +#include + +void *malloc(size_t); +void *calloc(size_t, size_t); +void *realloc(void *, size_t); +void free(void *); + +void exit(int) __attribute__((noreturn)); +void abort(void) __attribute__((noreturn)); + +int atoi(const char *); +long atol(const char *); +long long atoll(const char *); + +#endif diff --git a/include/string.h b/include/string.h new file mode 100644 index 0000000..72599cd --- /dev/null +++ b/include/string.h @@ -0,0 +1,21 @@ +#ifndef _ELIBC_STRING_H +#define _ELIBC_STRING_H + +#include + +void *memcpy(void *restrict, const void *restrict, size_t); +void *memmove(void *, const void *, size_t); +void *memset(void *, int, size_t); +int memcmp(const void *, const void *, size_t); +void *memchr(const void *, int, size_t); + +size_t strlen(const char *); +int strcmp(const char *, const char *); +int strncmp(const char *, const char *, size_t); +char *strcpy(char *restrict, const char *restrict); +char *strncpy(char *restrict, const char *restrict, size_t); +char *strcat(char *restrict, const char *restrict); +char *strchr(const char *, int); +char *strrchr(const char *, int); + +#endif diff --git a/src/internal/errno.c b/src/internal/errno.c new file mode 100644 index 0000000..233e323 --- /dev/null +++ b/src/internal/errno.c @@ -0,0 +1,7 @@ +#include +#include "libc.h" + +int *__errno_location(void) +{ + return &__errno; +} diff --git a/src/internal/libc.c b/src/internal/libc.c new file mode 100644 index 0000000..9f963ae --- /dev/null +++ b/src/internal/libc.c @@ -0,0 +1,23 @@ +#include "libc.h" + +/* Initial memory pool — small bump arena for early development. + * When the PM-actor path is wired this will request memory via + * elyz_kcall with ELYZ_OP_ALLOCATE. + */ +#define EARLY_HEAP_SIZE (64UL * 1024 * 1024) +static unsigned char __early_heap[EARLY_HEAP_SIZE]; + +struct __libc __libc = { + .heap_base = (unsigned long)__early_heap, + .heap_size = EARLY_HEAP_SIZE, + .heap_used = 0, + .threaded = 0, + .progname = 0, +}; + +int __errno = 0; + +void __libc_init(void) +{ + /* Future: read startup_info from loader, wire capabilities */ +} diff --git a/src/internal/libc.h b/src/internal/libc.h new file mode 100644 index 0000000..08daf7f --- /dev/null +++ b/src/internal/libc.h @@ -0,0 +1,28 @@ +#ifndef _ELIBC_INTERNAL_LIBC_H +#define _ELIBC_INTERNAL_LIBC_H + +/* Internal libc state. + * + * This structure is the single global anchor for elibc. Fields are added + * as the kernel ABI stabilises (e.g. CNode root, PM router channel pool, + * initial capability tokens). + */ + +#include + +struct __libc { + /* Initial pool — bump-allocated until the PM-actor path is wired */ + unsigned long heap_base; + unsigned long heap_size; + unsigned long heap_used; + + int threaded; + char *progname; +}; + +extern struct __libc __libc; +extern int __errno; + +void __libc_init(void); + +#endif diff --git a/src/internal/malloc.c b/src/internal/malloc.c new file mode 100644 index 0000000..771b585 --- /dev/null +++ b/src/internal/malloc.c @@ -0,0 +1,52 @@ +/* Bump allocator on the initial memory pool. + * + * Temporary — until the PM-actor malloc path is implemented. + * Thread-safe: no — __libc.heap_used is guarded by nothing yet. + */ + +#include +#include "libc.h" + +void *malloc(size_t size) +{ + if (size == 0) size = 1; + + unsigned long heap = __libc.heap_base; + unsigned long used = __libc.heap_used; + + if (used + size > __libc.heap_size) + return 0; + + void *ptr = (void *)(heap + used); + __libc.heap_used = used + size; + return ptr; +} + +void free(void *ptr) +{ + (void)ptr; +} + +void *calloc(size_t nmemb, size_t size) +{ + size_t total = nmemb * size; + void *p = malloc(total); + if (!p) return 0; + for (size_t i = 0; i < total; i++) + ((unsigned char *)p)[i] = 0; + return p; +} + +void *realloc(void *ptr, size_t size) +{ + if (!ptr) return malloc(size); + if (size == 0) { free(ptr); return 0; } + /* Bump allocator cannot shrink/grow in place — always allocate + copy */ + void *newp = malloc(size); + if (!newp) return 0; + unsigned long old = __libc.heap_used - size; + if (old > __libc.heap_used) old = __libc.heap_used; + for (size_t i = 0; i < old; i++) + ((unsigned char *)newp)[i] = ((unsigned char *)ptr)[i]; + return newp; +} diff --git a/src/string/memcmp.c b/src/string/memcmp.c new file mode 100644 index 0000000..c17c98b --- /dev/null +++ b/src/string/memcmp.c @@ -0,0 +1,11 @@ +#include + +int memcmp(const void *vl, const void *vr, size_t n) +{ + const unsigned char *l = vl, *r = vr; + for (size_t i = 0; i < n; i++) { + if (l[i] != r[i]) + return l[i] - r[i]; + } + return 0; +} diff --git a/src/string/memcpy.c b/src/string/memcpy.c new file mode 100644 index 0000000..93fef49 --- /dev/null +++ b/src/string/memcpy.c @@ -0,0 +1,10 @@ +#include + +void *memcpy(void *restrict dest, const void *restrict src, size_t n) +{ + unsigned char *d = dest; + const unsigned char *s = src; + for (size_t i = 0; i < n; i++) + d[i] = s[i]; + return dest; +} diff --git a/src/string/memset.c b/src/string/memset.c new file mode 100644 index 0000000..7ccd827 --- /dev/null +++ b/src/string/memset.c @@ -0,0 +1,9 @@ +#include + +void *memset(void *dest, int c, size_t n) +{ + unsigned char *d = dest; + for (size_t i = 0; i < n; i++) + d[i] = (unsigned char)c; + return dest; +} diff --git a/src/string/strcmp.c b/src/string/strcmp.c new file mode 100644 index 0000000..03db269 --- /dev/null +++ b/src/string/strcmp.c @@ -0,0 +1,7 @@ +#include + +int strcmp(const char *l, const char *r) +{ + while (*l && *l == *r) l++, r++; + return *(unsigned char *)l - *(unsigned char *)r; +} diff --git a/src/string/strlen.c b/src/string/strlen.c new file mode 100644 index 0000000..450b272 --- /dev/null +++ b/src/string/strlen.c @@ -0,0 +1,8 @@ +#include + +size_t strlen(const char *s) +{ + const char *p = s; + while (*p) p++; + return p - s; +} diff --git a/tests/test_basic.c b/tests/test_basic.c new file mode 100644 index 0000000..382b452 --- /dev/null +++ b/tests/test_basic.c @@ -0,0 +1,33 @@ +#include +#include +#include +#include + +int main(void) { + /* memcpy */ + char src[] = "hello world"; + char dst[64]; + memcpy(dst, src, 12); + dst[12] = 0; + assert(strcmp(dst, "hello world") == 0); + + /* strlen */ + assert(strlen("") == 0); + assert(strlen("abc") == 3); + + /* memset */ + char buf[10]; + memset(buf, 0xAB, 10); + for (int i = 0; i < 10; i++) assert((unsigned char)buf[i] == 0xAB); + + /* memcmp */ + assert(memcmp("abc", "abc", 3) == 0); + assert(memcmp("abc", "abd", 3) < 0); + + /* strcmp */ + assert(strcmp("hello", "hello") == 0); + assert(strcmp("abc", "def") < 0); + + printf("ALL STRING TESTS PASSED\n"); + return 0; +} diff --git a/tests/test_string.c b/tests/test_string.c new file mode 100644 index 0000000..8d477b0 --- /dev/null +++ b/tests/test_string.c @@ -0,0 +1,28 @@ +typedef unsigned long size_t; +void *memcpy(void *restrict d, const void *restrict s, size_t n); +void *memset(void *d, int c, size_t n); +int memcmp(const void *l, const void *r, size_t n); +size_t strlen(const char *s); +int strcmp(const char *l, const char *r); + +void _start(void) +{ + int code = 0; + char src[] = "hello world", dst[64]; + memcpy(dst, src, 12); dst[12] = 0; + if (strcmp(dst, "hello world") != 0) code = 1; + else if (strlen("") != 0) code = 2; + else if (strlen("abc") != 3) code = 3; + else { + char buf[10]; + memset(buf, 0xAB, 10); + for (int i = 0; i < 10; i++) + if ((unsigned char)buf[i] != 0xAB) { code = 4; break; } + } + if (code == 0 && memcmp("abc", "abc", 3) != 0) code = 5; + if (code == 0 && memcmp("abc", "abd", 3) >= 0) code = 6; + if (code == 0 && strcmp("hello", "hello") != 0) code = 7; + if (code == 0 && strcmp("abc", "def") >= 0) code = 8; + + __asm__ volatile ("mov $60, %%eax\n syscall" : : "D"(code) : "rax"); +}