Parent directory
Makefile
main.c
my_libc.c
my_libc.h
my_strlen.c
print_ulong.c
sum.c
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=
main: my_libc.o main.o sum.o my_strlen.o print_ulong.o
$(LD) -o $@ $^ $(LDFLAGS)
my_libc.o: my_libc.c my_libc.h
main.o: main.c my_libc.h
sum.o: sum.c
my_strlen.o: my_strlen.c
print_ulong.o: print_ulong.c my_libc.h
.PHONY: clean
clean:
rm -f -- *.o main a.out
.PHONY: all
all: clean main
#include "my_libc.h"
long sum_array(long *p, int n);
int my_strlen(const char *s);
void print_ulong(unsigned long s);
int main() {
long a[5] = {100, 101, 102, 103, 104};
long sum = sum_array(a, 5);
char str1[] = "sum=";
char newline[] = "\n";
write(STDOUT_FILENO, str1, my_strlen(str1));
print_ulong(sum);
write(STDOUT_FILENO, newline, my_strlen(newline));
}
#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__
int my_strlen(const char *s) {
int len = 0;
while (*s++)
len++;
return len;
}
#ifndef UNIT_TEST
#include "my_libc.h"
#else
#include <unistd.h>
#include <limits.h>
#endif
int my_strlen(const char *s);
void print_ulong(unsigned long s) {
if (s == 0) {
char z = '0';
write(STDOUT_FILENO, &z, 1);
return;
}
char out[22];
out[21] = '\0';
char *p = &out[20];
while (s > 0) {
*p-- = '0' + (s % 10);
s /= 10;
}
p++;
write(STDOUT_FILENO, p, my_strlen(p));
}
#ifdef UNIT_TEST
int main() {
print_ulong(ULONG_MAX);
}
#endif
long sum(long a, long b) {
return a + b;
}
long sum_array(long *p, int n) {
long s = 0;
for (int i = 0; i < n; i++) {
s = sum(s, p[i]);
}
return s;
}