LibC Internals¶
TilekarOS includes a minimal C Standard Library (libc) tailored for kernel development and future user-space applications. This document explains the implementation of core functions.
1. Formatted I/O (stdio.h)¶
Source Files: printf.c, sprintf.c, putchar.c
The implementation of printf is designed to be Freestanding.
LibK vs. User LibC:¶
__is_libk: When defined (during kernel build),printfcallsterminal_writedirectly.- User Mode: When NOT defined,
printfuses theSYS_WRITEsystem call to output data to stdout (File Descriptor 1).
Code Preview: printf.c
#include "kernel/tty.h"
#include <limits.h>
#include <stdbool.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#if !defined(__is_libk)
#include <sys/syscall.h>
#endif
static bool print(const char* data, size_t length) {
#if defined(__is_libk)
terminal_write(data, length);
#else
__syscall(SYS_WRITE, 1, (uint32_t)data, length, 0, 0);
#endif
return true;
}
static char* itoa(unsigned long long value, char* str, int base) {
char* rc;
char* ptr;
char* low;
// Check for supported base.
if (base < 2 || base > 36) {
*str = '\0';
return str;
}
rc = ptr = str;
// Set '-' for negative decimals is handled in printf wrappers,
// we treat value as unsigned long long here for generic base conversion.
// Remember where the numbers start.
low = ptr;
// The actual conversion.
do {
// Modulo is negative for negative numbers, not needed for unsigned.
*ptr++ = "0123456789abcdefghijklmnopqrstuvwxyz"[value % base];
value /= base;
} while (value);
// Terminate the string.
*ptr = '\0';
// Invert the numbers.
char* start = low;
char* end = ptr - 1;
while (start < end) {
char tmp = *start;
*start++ = *end;
*end-- = tmp;
}
return rc;
}
int printf(const char* restrict format, ...) {
va_list parameters;
va_start(parameters, format);
int written = 0;
while (*format != '\0') {
size_t maxrem = INT_MAX - written;
if (format[0] != '%' || format[1] == '%') {
if (format[0] == '%')
format++;
size_t amount = 1;
while (format[amount] && format[amount] != '%')
amount++;
if (maxrem < amount) {
// TODO: Set errno to EOVERFLOW.
return -1;
}
if (!print(format, amount))
return -1;
format += amount;
written += amount;
continue;
}
const char* format_begun_at = format++;
if (*format == 'c') {
format++;
char c = (char) va_arg(parameters, int /* char promotes to int */);
if (!maxrem) {
// TODO: Set errno to EOVERFLOW.
return -1;
}
if (!print(&c, sizeof(c)))
return -1;
written++;
} else if (*format == 's') {
format++;
const char* str = va_arg(parameters, const char*);
size_t len = strlen(str);
if (maxrem < len) {
// TODO: Set errno to EOVERFLOW.
return -1;
}
if (!print(str, len))
return -1;
written += len;
} else if (*format == 'd' || *format == 'i') {
format++;
int i = va_arg(parameters, int);
char buffer[32];
if (i < 0) {
if (!print("-", 1)) return -1;
written++;
i = -i;
}
itoa((unsigned long long)i, buffer, 10);
size_t len = strlen(buffer);
if (maxrem < len) return -1;
if (!print(buffer, len)) return -1;
written += len;
} else if (*format == 'u') {
format++;
unsigned int u = va_arg(parameters, unsigned int);
char buffer[32];
itoa(u, buffer, 10);
size_t len = strlen(buffer);
if (maxrem < len) return -1;
if (!print(buffer, len)) return -1;
written += len;
} else if (*format == 'x') {
format++;
unsigned int x = va_arg(parameters, unsigned int);
char buffer[32];
itoa(x, buffer, 16);
size_t len = strlen(buffer);
if (maxrem < len) return -1;
if (!print(buffer, len)) return -1;
written += len;
} else if (*format == 'X') {
format++;
unsigned int x = va_arg(parameters, unsigned int);
char buffer[32];
itoa(x, buffer, 16);
// Convert to uppercase
for(int k=0; buffer[k]; k++) {
if (buffer[k] >= 'a' && buffer[k] <= 'z')
buffer[k] -= 32;
}
size_t len = strlen(buffer);
if (maxrem < len) return -1;
if (!print(buffer, len)) return -1;
written += len;
} else if (*format == 'p') {
format++;
void* ptr = va_arg(parameters, void*);
char buffer[32];
if (!print("0x", 2)) return -1;
written += 2;
itoa((unsigned long long)(uintptr_t)ptr, buffer, 16);
size_t len = strlen(buffer);
if (maxrem < len) return -1;
if (!print(buffer, len)) return -1;
written += len;
} else {
format = format_begun_at;
size_t len = strlen(format);
if (maxrem < len) {
// TODO: Set errno to EOVERFLOW.
return -1;
}
if (!print(format, len))
return -1;
written += len;
format += len;
}
}
va_end(parameters);
return written;
}
Supported Specifiers:¶
%s: String%c: Character%d/%i: Signed Integer%u: Unsigned Integer%x/%X: Hexadecimal (Lowercase / Uppercase)%p: Pointer (Formatted as0x...)
2. String Manipulation (string.h)¶
Source Files: string/
TilekarOS prioritizes correctness and simplicity in its string implementation.
Key Functions:¶
memcpy: Fast byte-by-byte copying.memmove: Handles overlapping memory regions by checking if the source is before or after the destination.memset: Fills memory with a constant byte.strcmp/strncmp: Standard string comparison.
Code Preview: memmove.c
#include <string.h>
void* memmove(void* dstptr, const void* srcptr, size_t size) {
unsigned char* dst = (unsigned char*) dstptr;
const unsigned char* src = (const unsigned char*) srcptr;
if (dst < src) {
for (size_t i = 0; i < size; i++)
dst[i] = src[i];
} else {
for (size_t i = size; i != 0; i--)
dst[i-1] = src[i-1];
}
return dstptr;
}
3. Standard Library (stdlib.h)¶
Source Files: abort.c
abort(): In the kernel, this triggers a Kernel Panic message and halts the CPU. In user mode, it would eventually send a signal to terminate the process.
Code Preview: abort.c
#include <stdio.h>
#include <stdlib.h>
__attribute__((__noreturn__))
void abort(void) {
#if defined(__is_libk)
// TODO: Add proper kernel panic.
printf("kernel: panic: abort()\n");
#else
// TODO: Abnormally terminate the ktask as if by SIGABRT.
printf("abort()\n");
#endif
while (1) { }
__builtin_unreachable();
}
4. Test/Example: Complex Formatting¶
You can use sprintf to build complex strings before outputting them:
#include <stdio.h>
void log_event(const char* module, int code) {
char log_buf[128];
sprintf(log_buf, "[LOG] %s: Error Code %d", module, code);
puts(log_buf);
}