Parent directory
Makefile
echo.c
my_libc.h
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: echo.o
$(LD) -o $@ $^ $(LDFLAGS)
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;
}
#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);
}
/*
* 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();
}
#endif // __MY_LIBC_H__