 Parent directory
Parent directory
 Makefile
Makefile
 main.c
main.c
 my_libc.c
my_libc.c
 my_libc.h
my_libc.h
 my_strlen.c
my_strlen.c
 print_ulong.c
print_ulong.c
 sum.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);
}
#endiflong 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;
}