Parent directory Makefile echo.c my_libc.c my_libc.h
Download
CC=gcc LD=ld # * -nostdlib: Do not use the standard system startup files or libraries when # linking. This switch also implies -nostartfiles. This lets us provide our # own _start function. # # * -fno-asynchronous-unwind-tables: Do not generate unwind information for # exception handling. This removes the .eh_frame section, which is not # necessary for our purposes. # # * -fcf-protection=none: Disable control flow protection. This removes the # .note.gnu.property sections, which are not necessary for our purposes. # CFLAGS=-Wall -nostdlib -fno-asynchronous-unwind-tables -fcf-protection=none LDFLAGS= echo: my_libc.o echo.o $(LD) -o $@ $^ $(LDFLAGS) my_libc.o: my_libc.c my_libc.h echo.o: echo.c my_libc.h .PHONY: clean clean: rm -f -- *.o echo a.out .PHONY: all all: clean echo
#include "my_libc.h" int my_strlen(const char *s) { int len = 0; while (*s++) len++; return len; } // Print all arguments int main(int argc, char **argv) { char space[] = " "; char newline[] = "\n"; // Use write() since we don't have printf() for (int i = 1; i < argc; i++) { write(STDOUT_FILENO, argv[i], my_strlen(argv[i])); write(STDOUT_FILENO, space, 1); } write(STDOUT_FILENO, newline, 1); return 0; }
#include "my_libc.h" /* * C runtime entry point. Sets up the environment for a C program and calls * main(). */ void _start_c(long *sp) { long argc; char **argv; char **envp; int ret; // Silence warnings about different types for main() int _mylib_main(int, char **, char **) __asm__ ("main"); /* * sp : argc <-- argument count, required by main() * argv: argv[0] <-- argument vector, required by main() * argv[1] * ... * argv[argc-1] * NULL * envp: envp[0] <-- environment variables, required by main()/getenv() * envp[1] * ... * NULL * * NOT IMPLEMENTED: * _auxv: _auxv[0] <-- auxiliary vector, required by getauxval() * _auxv[1] * ... * NULL */ argc = *sp; argv = (void *)(sp + 1); envp = argv + argc + 1; ret = _mylib_main(argc, argv, envp); exit(ret); } /* * Start up code inspired by the Linux kernel's nolibc header library. * x86-64 System V ABI requires: * - %rsp must be 16-byte aligned before calling a function * - Deepest stack frame should be zero (%rbp) * * Requires -fomit-frame-pointer to work. */ void __attribute__((noreturn, optimize("omit-frame-pointer"))) _start() { __asm__ volatile ( "xor %ebp, %ebp\n" // zero the stack frame "mov %rsp, %rdi\n" // save stack pointer to %rdi, as arg1 of _start_c "and $-16, %rsp\n" // %rsp must be 16-byte aligned before call "call _start_c\n" // transfer to c runtime "hlt\n" // ensure it does not return ); __builtin_unreachable(); }
#ifndef __MY_LIBC_H__ #define __MY_LIBC_H__ /* * Header file for minimal C standard library. */ // Standard stream file descriptors #define STDIN_FILENO 0 #define STDOUT_FILENO 1 #define STDERR_FILENO 2 // System call numbers for x86_64 Linux #define __NR_write 1 #define __NR_exit 60 /* * The syscall() function is implemented as an inline assembly function. * The function uses the System V AMD64 ABI calling convention, which * specifies that the syscall number is passed in %rax, and the first six * arguments are passed in %rdi, %rsi, %rdx, %r10, %r8, and %r9. * * The syscall instruction clobbers %rcx and %r11, so we must list them as * clobbered registers. Additionally, we list memory and cc as clobbered * because the syscall instruction may write to memory and may modify the flags * register. */ static inline long syscall(long n, long a1, long a2, long a3, long a4, long a5, long a6) { long ret; register long _num asm("rax") = n; register long _arg1 asm("rdi") = a1; register long _arg2 asm("rsi") = a2; register long _arg3 asm("rdx") = a3; register long _arg4 asm("r10") = a4; register long _arg5 asm("r8") = a5; register long _arg6 asm("r9") = a6; asm volatile ( "syscall\n\t" : "=a" (ret) : "0" (_num), "r" (_arg1), "r" (_arg2), "r" (_arg3), "r" (_arg4), "r" (_arg5), "r" (_arg6) : "memory", "cc", "r11", "rcx" ); return ret; } static inline __attribute__((noreturn)) void exit(int code) { syscall(__NR_exit, code, 0, 0, 0, 0, 0); __builtin_unreachable(); } static inline void write(int fd, const char *buf, unsigned long len) { syscall(__NR_write, fd, (long)buf, len, 0, 0, 0); } #endif // __MY_LIBC_H__