[
  {
    "path": ".gitignore",
    "content": "*.o\n*.d\nchainloader\ntemp_disk\ngrub.iso\nbuild/\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"ext/EASTL\"]\n\tpath = ext/EASTL\n\turl = https://github.com/electronicarts/EASTL.git\n[submodule \"ext/tinyprintf\"]\n\tpath = ext/tinyprintf\n\turl = git@github.com:fwsGonzo/tinyprintf.git\n"
  },
  {
    "path": "README.md",
    "content": "# Barebones multiboot kernel\n\n- Intended for beginner OS development\n- Run the dependency installation script (or install the equivalent packages)\n- Build and boot a tiny test kernel in Qemu with `./run.sh default`.\n\t- There is a 32-bit test kernel as well: `./run.sh 32bit`\n\t- You can run any of the machines in the `machines` folder this way.\n- If building with the NATIVE option (or anything that requires AVX), remember to run with KVM enabled (./run.sh --kvm) so that Qemu will present the modern instruction set features to the guest operating system.\n- VGA textmode using ./run.sh --vga (NOTE: you must implement VGA support yourself! You will (if you're lucky) see a black screen.)\n- Use ./build_iso.sh to run on real hardware or a hypervisor like VirtualBox, but keep serial port logging in mind!\n\n## Features\n\n- 600kb stack and working GDT (with room for IST task)\n- Multiboot parameters passed to kernel start\n- assert(), kprintf() and snprintf()\n- Very basic heap implementation (malloc/free, new/delete)\n- C and C++ global constructors, C++ RTTI and exceptions\n- Stack protector support\n- EASTL C++ support, which has many useful containers\n- Produces tiny machine images\n\t- Machine image is 6944 bytes with minimal build on GCC 9.1\n\t- Machine image is 6376 bytes with minimal LTO build on Clang 8\n\t- Remember to disable stack protector to shave a few extra bytes off!\n- LTO and ThinLTO support if you are using clang\n- Undefined-sanitizer support to catch some problems during runtime\n- Option to unmap zero page (for the very common null-pointer access bugs)\n    - You have to enable this yourself, after CPU exception handling\n\n## Running\n\nUse `./run.sh <folder>` to build and run your kernel stored in machines folder.\n- By default run.sh will build and run the `default` machine. The project is located in `machines/default/` and most of the output comes from `main.cpp`.\n- Use argument --kvm if you want to use hardware-accelerated virtualization, or you have built with -march=native in which you must use that. Enable virtualization in your BIOS settings if you haven't.\n- Use argument --vga to get a graphical window, which starts out in VGA textmode accessible at 0xb8000.\n- Use argument --sanitize to enable the undefined-sanitizer, catching problems like misaligned accesses and overflows.\n\n## Changing build options\n\nGo into the build folder in your machines folder and type `ccmake ..`, which opens a GUI with some adjustable settings. After changing settings press C to configure the changes, and then E to exit the GUI. Changes will be rebuilt automatically using run.sh, or you could simply use `make` like normally in the build folder itself.\n\n## Goal\n\nThe goal is to provide a barebones kernel project that implements the most basic C/C++ voodoo to let people start reading/writing to registers and devices immediately. The goal is also to provide people with a choice of using C or C++, or a mix of both.\n\n## Future work\n\n- Add TLS api and support for most thread local variables\n- Add support for LTO with GCC (no ideas on how to do this yet)\n- Optional 512b bootloader to avoid GRUB (although not recommended)\n\n## Validate output\n\n./run.sh output should match:\n```\n------------------\n* Multiboot EAX: 0x2badb002\n* Multiboot EBX: 0x9500\n* SSE instructions ... work!\n* Global constructors ... work!\n\nHello OSdev world!\nmy_kernel (This is a test kernel!)\n\nPress Ctrl+A -> X to close\nReturned from kernel_start! Halting...\n```\n\n## Common issues\n\n- File format not recognized\n    - You changed linker or ELF architecture without rebuilding all the files\n- Nothing happens when I start the kernel\n    - If you are building with -march=native, you need to enable KVM and run the kernel natively, because the machine code is full of extended instructions (like AVX)\n- I can't use normal C/C++ library functions\n    - Make sure you are using something that exists in EASTL, or you will have to implement it yourself\n- I can write to the zero page\n    - Yes you can. Welcome to (identity-mapped) kernel space. Set up your own pagetables!\n- Do I really need a cross-compiler to work on this project?\n    - No, that is a misconception that the OSdev community holds dear to its heart, but its not necessary. The Linux-native compilers will use FS/GS for TLS, and will require you to fake the table that (among other things) contain the stack sentinel value. Otherwise, you can reasonably expect normal library calls to be optimized (printf -> write).\n- When booting the GRUB image nothing happens, it looks to be crashing etc.\n\t- Make sure you didn't compile with GCC and then link with LLD, and especially don't mix LTO into this.\n\t- Minimal builds can be risky, especially -Oz on clang.\n\t- There could be a genuine issue, so let me know.\n- I'm having problems compiling or linking when using exceptions!\n\t- Make sure that you use your own exceptions, and don't rely on anything that belongs to the C++ standard library, which is not present\n- I keep getting errors about not finding `bits/c++config.h` when building for 32-bit!\n\t- You will need to install the C++ multilib package for your compiler, such as `g++-8-multilib`.\n\n## Undefined sanitizer\n\n- Works on GCC and Clang\n- Easy to support, but has some caveats\n    - It's miles better if you can resolve symbol addresses to names\n- Has not been extensively verified\n\n## Link-Time Optimization\n\n- Works on Clang out of the box, just make sure you use the correct LLD that comes with your compiler\n    - Both ThinLTO and full LTO works!\n\t- Enable LTO option with ccmake and set the LINKER_EXE option to point to a lld executable that is compatible with your Clang version. For example `ld.lld-8`.\n- Does not work on GCC at all due to how complicated it is to call the LTO-helpers manually\n\n## Thread-Local Storage\n\n- The basic foundation has been laid down\n- Linker script needs to be amended to store .tbss and .tdata sections\n- Functionality for creating new thread storage needs to be created\n\n## EASTL, RTTI and exceptions\n\n- To make use of EASTL you will need to enable the EASTL CMake option\n\t- Tons of useful containers that work right out of the box\n- To use RTTI and exceptions in C++ you will need to enable the RTTI_EXCEPTIONS option\n\t- This option bloats the binary a great deal, due to the C++ ABI bundles, as well as the libgcc dependency\n\n## Qemu defaults\n\nQemu provides you with some default devices to start with\n- IDE for disk devices, but you can use PCI to get information\n- e1000-compatible network card\n- If you add -vga std you will get VGA graphics area at 0xb8000\n- In graphics mode: PS/2 keyboard and mouse\n\nAs an example, use this function to clear the VGA textmode screen:\n```\nuint16_t* vga = (uint16_t*) 0xb8000;\nfor (int i = 0; i < 25*80; i++)\n{\n  vga[i] = ' ' | (7 << 8);\n}\n```\nWhat it's actually doing is filling the text buffer with spaces. Spaces that have a gray color. You just can't see them, because spaces are spaces.\n"
  },
  {
    "path": "barebones.cmake",
    "content": "option(NATIVE          \"Compile natively for this CPU\" OFF)\noption(MINIMAL         \"Compile small executable\" OFF)\noption(EASTL           \"Compile with EASTL C++ library\" OFF)\noption(LTO_ENABLE      \"Enable LTO (Clang-only)\" OFF)\noption(STACK_PROTECTOR \"Enable stack protector (SSP)\" ON)\noption(UBSAN           \"Enable the undefined sanitizer\" OFF)\noption(STRIPPED        \"Strip the executable\" OFF)\noption(DEBUG           \"Build and preserve debugging information\" OFF)\noption(RTTI_EXCEPTIONS \"Enable C++ RTTI and exceptions\" OFF)\noption(BUILD_32        \"Build a 32-bit kernel\" OFF)\nset(CPP_VERSION \"c++17\" CACHE STRING \"C++ version compiler argument\")\nset(C_VERSION   \"gnu11\" CACHE STRING \"C version compiler argument\")\nset(LINKER_EXE  \"ld\"    CACHE STRING \"Linker to use\")\n\nif (BUILD_32)\n\tset(ELF_FORMAT \"i386\")\n\tset(CMAKE_ASM_NASM_OBJECT_FORMAT \"elf32\")\n\tset(OBJCOPY_TARGET \"elf32-x86-32\")\n\tset(TARGET_TRIPLE  \"i686-pc-linux\")\n\tset(CAPABS \"-m32\")\nelse()\n\tset(ELF_FORMAT \"x86_64\")\n\tset(CMAKE_ASM_NASM_OBJECT_FORMAT \"elf64\")\n\tset(OBJCOPY_TARGET \"elf64-x86-64\")\n\tset(TARGET_TRIPLE  \"x86_64-pc-linux\")\n\tset(CAPABS \"-m64 -fno-omit-frame-pointer -fPIE\")\nendif()\nenable_language(ASM_NASM)\nset(CAPABS \"${CAPABS} -Wall -Wextra -g -ffreestanding\")\n\n# Optimization flags\nset(OPTIMIZE \"-mfpmath=sse -msse3\")\nif (NATIVE)\n\tset(OPTIMIZE \"${OPTIMIZE} -Ofast -march=native\")\nelseif (MINIMAL)\n\tif (\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"Clang\")\n\t\tset(OPTIMIZE \"${OPTIMIZE} -Oz\")\n\telse()\n\t\tset(OPTIMIZE \"${OPTIMIZE} -Os -ffunction-sections -fdata-sections\")\n\tendif()\nendif()\n\nset(CMAKE_CXX_FLAGS \"-MMD ${CAPABS} ${OPTIMIZE} -std=${CPP_VERSION}\")\nset(CMAKE_C_FLAGS \"-MMD ${CAPABS} ${OPTIMIZE} -std=${C_VERSION}\")\nif (LTO_ENABLE)\n\tif (\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"Clang\")\n\t\tset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -flto=thin\")\n\t\tset(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -flto=thin\")\n\telse()\n\t\tset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -flto\")\n\t\tset(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -flto\")\n\tendif()\n\t# BUG: workaround for LTO bug\n\tset(KERNEL_LIBRARY --whole-archive kernel --no-whole-archive -gc-sections)\nelse()\n\tset(KERNEL_LIBRARY kernel)\nendif()\n\nif (NOT RTTI_EXCEPTIONS)\n\tset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -fno-exceptions -fno-rtti\")\nendif()\nif (\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"Clang\")\n\tset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -target ${TARGET_TRIPLE}\")\n\tset(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -target ${TARGET_TRIPLE}\")\nendif()\n\n# Sanitizer options\nif (UBSAN)\n\tset(UBSAN_PARAMS \"-fsanitize=undefined -fno-sanitize=vptr -DUBSAN_ENABLED\")\n\tset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} ${UBSAN_PARAMS}\")\n\tset(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} ${UBSAN_PARAMS}\")\n\tif (\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"Clang\")\n\t\tset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -fno-sanitize=function\")\n\t\tset(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -fno-sanitize=function\")\n\tendif()\nendif()\nif (STACK_PROTECTOR)\n\tset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -fstack-protector-strong\")\n\tset(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -fstack-protector-strong\")\nendif()\n\n# linker stuff\nset(CMAKE_LINKER ${LINKER_EXE})\nset(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS) # this removed -rdynamic from linker output\nset(CMAKE_CXX_LINK_EXECUTABLE \"<CMAKE_LINKER> -o <TARGET> <LINK_FLAGS> <OBJECTS> <LINK_LIBRARIES>\")\n\nset(BBPATH ${CMAKE_CURRENT_LIST_DIR})\n\n# linker arguments\nstring(RANDOM LENGTH 16 ALPHABET 0123456789abcdef SSP_VALUE)\nset(LDSCRIPT \"${BBPATH}/src/linker.ld\")\nset(LDFLAGS \"-static -nostdlib -N -melf_${ELF_FORMAT} --script=${LDSCRIPT} --defsym __SSP__=0x${SSP_VALUE}\")\nif (NOT DEBUG AND STRIPPED)\n\tset(LDFLAGS \"${LDFLAGS} -s\")\nelseif (NOT DEBUG)\n\tset(LDFLAGS \"${LDFLAGS} -S\")\nendif()\nif (MINIMAL)\n\tset(LDFLAGS \"${LDFLAGS} --gc-sections\")\nendif()\n\n# Compiler, C and C++ libraries\ninclude(ExternalProject)\nExternalProject_Add(exceptions\n\t\t\tPREFIX exceptions\n\t\t\tURL https://github.com/fwsGonzo/barebones/releases/download/exceptions/exceptions.zip\n\t\t\tURL_HASH SHA1=8851485a7134eb8743069439235c1a2a9728ea58\n\t\t\tCONFIGURE_COMMAND \"\"\n\t\t\tBUILD_COMMAND \"\"\n\t\t\tUPDATE_COMMAND \"\"\n\t\t\tINSTALL_COMMAND \"\"\n\t\t)\n\n# gcc-9 --print-file-name libgcc.a\nif (BUILD_32)\nexecute_process(COMMAND ${CMAKE_C_COMPILER} -m32 --print-file-name libgcc_eh.a\n\t\t\t\tOUTPUT_VARIABLE LIBGCC_ARCHIVE OUTPUT_STRIP_TRAILING_WHITESPACE)\nelse()\nexecute_process(COMMAND ${CMAKE_C_COMPILER} -m64 --print-file-name libgcc_eh.a\n\t\t\t\tOUTPUT_VARIABLE LIBGCC_ARCHIVE OUTPUT_STRIP_TRAILING_WHITESPACE)\nendif()\n\nadd_library(libgcc STATIC IMPORTED)\nset_target_properties(libgcc PROPERTIES LINKER_LANGUAGE CXX)\nif (EXISTS ${LIBGCC_ARCHIVE})\n\tset_target_properties(libgcc PROPERTIES IMPORTED_LOCATION ${LIBGCC_ARCHIVE})\nelse()\n\t# Use this if you don't have a compatible libgcc_eh.a in your system:\n\tset_target_properties(libgcc PROPERTIES IMPORTED_LOCATION exceptions/src/exceptions/libgcc.a)\nendif()\nadd_dependencies(libgcc exceptions)\n\nif (RTTI_EXCEPTIONS)\n\tadd_library(cxxabi STATIC IMPORTED)\n\tset_target_properties(cxxabi PROPERTIES LINKER_LANGUAGE CXX)\n\tset_target_properties(cxxabi PROPERTIES IMPORTED_LOCATION exceptions/src/exceptions/libc++abi.a)\n\tadd_dependencies(cxxabi exceptions)\n\n\t# Add --eh-frame-hdr for exception tables\n\tset(LDFLAGS \"${LDFLAGS} --eh-frame-hdr\")\n\tset(CXX_ABI_LIBS cxxabi)\nendif()\n\n# Machine image creation\nfunction(add_machine_image NAME BINARY_NAME BINARY_DESC)\n\tadd_executable(${NAME} ${ARGN})\n\tset_target_properties(${NAME} PROPERTIES OUTPUT_NAME ${BINARY_NAME})\n\ttarget_include_directories(${NAME} PRIVATE src)\n\n\ttarget_compile_definitions(${NAME} PRIVATE KERNEL_BINARY=\"${BINARY_NAME}\")\n\ttarget_compile_definitions(${NAME} PRIVATE KERNEL_DESC=\"${BINARY_DESC}\")\n\n\tadd_subdirectory(${BBPATH}/ext ext)\n\tif (EASTL)\n\t\ttarget_link_libraries(${NAME} eastl)\n\tendif()\n\n\tadd_subdirectory(${BBPATH}/src src)\n\n\ttarget_link_libraries(${NAME}\n\t\t# BUG: unfortunately, there is an LLD bug that prevents ASM objects\n\t\t# from linking with outside files that are in archives, unless they are\n\t\t# --whole-archived, which sort of defeats the purpose of LTO\n\t\t${KERNEL_LIBRARY}\n\t\ttinyprintf\n\t\t${CXX_ABI_LIBS}\n\t\tlibgcc\n\t\tkernel\n\t)\n\n\tset_target_properties(${NAME} PROPERTIES LINK_FLAGS \"${LDFLAGS}\")\n\t# write out the binary name to a known file to simplify some scripts\n\tfile(WRITE ${CMAKE_BINARY_DIR}/binary.txt ${BINARY_NAME})\nendfunction()\n\nfunction(create_payload LIB_NAME BLOB_FILE BLOB_NAME)\n\t# call objcopy with curated filename, and then\n\t# create a static library LIB_NAME with payload\n\tset(OBJECT_FILE ${BLOB_NAME}_generated.o)\n\tadd_custom_command(\n\t\tOUTPUT ${OBJECT_FILE}\n\t\t# temporarily name blob file as BLOB_NAME so that we can better control\n\t\t# the symbols generated on the inside of the kernel\n\t\tCOMMAND cp ${BLOB_FILE} ${BLOB_NAME}\n\t\tCOMMAND ${CMAKE_OBJCOPY} -I binary -O ${OBJCOPY_TARGET} -B i386 ${BLOB_NAME} ${OBJECT_FILE}\n\t\tCOMMAND rm -f ${BLOB_NAME}\n\t\tDEPENDS ${BLOB_FILE}\n\t\tWORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}\n\t)\n\tadd_library(${LIB_NAME} STATIC ${OBJECT_FILE})\n\tset_target_properties(${LIB_NAME} PROPERTIES LINKER_LANGUAGE C)\nendfunction()\nfunction (add_machine_payload TARGET LIB_NAME BLOB_FILE BLOB_NAME)\n\tcreate_payload(${LIB_NAME} ${BLOB_FILE} ${BLOB_NAME})\n\ttarget_link_libraries(${TARGET} --whole-archive mylib_payload --no-whole-archive)\nendfunction()\n"
  },
  {
    "path": "build_iso.sh",
    "content": "#!/bin/bash\nset -e\nMACHINE=machines/${1-default}\nBUILD_DIR=$MACHINE/build\n\nmkdir -p $BUILD_DIR\npushd $BUILD_DIR\ncmake ..\nmake -j4 $OPTION\npopd\n\nKERNEL=$BUILD_DIR/`cat $BUILD_DIR/binary.txt`\nLOCAL_DISK=temp_disk\n\n# create grub.iso\nmkdir -p $LOCAL_DISK/boot/grub\ncp $KERNEL $LOCAL_DISK/boot/mykernel\ncp grub/grub.cfg $LOCAL_DISK/boot/grub\necho \"=>\"\ngrub-mkrescue -o grub.iso $LOCAL_DISK\necho \"grub.iso constructed\"\n"
  },
  {
    "path": "ext/CMakeLists.txt",
    "content": "\n# EASTL C++ library\nif (EASTL)\n\tadd_library(eastl STATIC\n\t\tEASTL/source/allocator_eastl.cpp EASTL/source/assert.cpp\n\t\tEASTL/source/fixed_pool.cpp EASTL/source/hashtable.cpp\n\t\tEASTL/source/intrusive_list.cpp EASTL/source/numeric_limits.cpp\n\t\tEASTL/source/red_black_tree.cpp EASTL/source/string.cpp\n\t)\n\ttarget_include_directories(eastl PUBLIC\n\t\tEASTL/include\n\t\tEASTL/test/packages/EABase/include/Common\n\t)\nendif()\n\n# Tiny-printf library\nadd_library(tinyprintf STATIC\n\t\ttinyprintf/tinyprintf.c\n\t)\ntarget_include_directories(tinyprintf PUBLIC\n\t\ttinyprintf\n\t)\ntarget_compile_definitions(tinyprintf PUBLIC TINYPRINTF_OVERRIDE_LIBC=0)\n"
  },
  {
    "path": "grub/grub.cfg",
    "content": "set timeout=0\nset default=0 # Set the default menu entry\n\nmenuentry \"My kernel\" {\n   multiboot /boot/mykernel\n   boot\n}\n"
  },
  {
    "path": "install_ubuntu_deps.sh",
    "content": "#!/bin/bash\ngit submodule update --init --recursive\nsudo apt install build-essential g++-multilib nasm qemu xorriso cmake cmake-curses-gui\n"
  },
  {
    "path": "machines/32bit/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.1)\nproject (kernel C CXX)\n\noption(BUILD_32 \"\" ON)\ninclude(../../barebones.cmake)\n\nadd_machine_image(\n\t# name, binary and description\n\tmykernel \"my_kernel\" \"This is a 32-bit test kernel!\"\n\t# list of source files\n\tmain.cpp\n)\n\ntarget_compile_definitions(mykernel PRIVATE MYTEST=\"4\")\n"
  },
  {
    "path": "machines/32bit/main.cpp",
    "content": "#include <main.h>\n#include <assert.h>\n#include <kprint.h>\n#include <x86intrin.h>\nstatic bool test_sse();\nstatic int  test_value = 0;\n\nvoid kernel_main(const uint32_t eax, const uint32_t ebx)\n{\n\tkprint(\"------------------\\n\");\n\tkprintf(\"* Multiboot EAX: 0x%x\\n\", eax);\n\tkprintf(\"* Multiboot EBX: 0x%x\\n\", ebx);\n\t// some helpful self-checks\n\tkprint(\"* SSE instructions ... \");\n\tkprint(test_sse() ? \"work!\\n\" : \"did NOT work!\\n\");\n\tkprint(\"* Global constructors ... \");\n\tkprint(test_value ? \"work!\\n\" : \"did NOT work!\\n\");\n\n#ifdef UBSAN_ENABLED\n\t// when undefined sanitizer is enabled, we can manually trigger it like this\n\tuint64_t test = 0;\n\tchar* misaligned = (char*) &test + 2;\n\t*(uint32_t*) misaligned = 0x12345678;\n\tkprintf(\"kernel_main is at %p\\n\", kernel_main);\n\tkprintf(\"misaligned is %p\\n\", (void*) test);\n#endif\n\n\tkprint(\n\t\t\"\\n\"\n\t\t\"Hello OSdev world!\\n\"\n\t\tKERNEL_BINARY \" (\" KERNEL_DESC \")\\n\"\n\t\t\"\\n\"\n\t\t\"Press Ctrl+A -> X to close\\n\"\n\t);\n}\n\n__attribute__((constructor))\nstatic void my_cpp_constructor() {\n  test_value = 1;\n}\n\nbool test_sse()\n{\n  typedef union\n  {\n    __m128i i128;\n    int32_t i32[4];\n  } imm;\n  volatile imm xmm1;\n  xmm1.i128 = _mm_set_epi32(1, 2, 3, 4);\n  return (\n      xmm1.i32[0] == 4 && xmm1.i32[1] == 3 &&\n      xmm1.i32[2] == 2 && xmm1.i32[3] == 1);\n}\n"
  },
  {
    "path": "machines/default/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.1)\nproject (kernel C CXX)\ninclude(../../barebones.cmake)\n\nadd_machine_image(\n\t# name, binary and description\n\tmykernel \"my_kernel\" \"This is a test kernel!\"\n\t# list of source files\n\tmain.cpp\n)\n\ntarget_compile_definitions(mykernel PRIVATE MYTEST=\"4\")\n"
  },
  {
    "path": "machines/default/main.cpp",
    "content": "#include <main.h>\n#include <assert.h>\n#include <kprint.h>\n#include <x86intrin.h>\nstatic bool test_sse();\nstatic int  test_value = 0;\n\nvoid kernel_main(const uint32_t eax, const uint32_t ebx)\n{\n\tkprint(\"------------------\\n\");\n\tkprintf(\"* Multiboot EAX: 0x%x\\n\", eax);\n\tkprintf(\"* Multiboot EBX: 0x%x\\n\", ebx);\n\t// some helpful self-checks\n\tkprint(\"* SSE instructions ... \");\n\tkprint(test_sse() ? \"work!\\n\" : \"did NOT work!\\n\");\n\tkprint(\"* Global constructors ... \");\n\tkprint(test_value ? \"work!\\n\" : \"did NOT work!\\n\");\n\n#ifdef UBSAN_ENABLED\n\t// when undefined sanitizer is enabled, we can manually trigger it like this\n\tuint64_t test = 0;\n\tchar* misaligned = (char*) &test + 2;\n\t*(uint32_t*) misaligned = 0x12345678;\n\tkprintf(\"kernel_main is at %p\\n\", kernel_main);\n\tkprintf(\"misaligned is %p\\n\", (void*) test);\n#endif\n\n\tkprint(\n\t\t\"\\n\"\n\t\t\"Hello OSdev world!\\n\"\n\t\tKERNEL_BINARY \" (\" KERNEL_DESC \")\\n\"\n\t\t\"\\n\"\n\t\t\"Press Ctrl+A -> X to close\\n\"\n\t);\n}\n\n__attribute__((constructor))\nstatic void my_cpp_constructor() {\n  test_value = 1;\n}\n\nbool test_sse()\n{\n  typedef union\n  {\n    __m128i i128;\n    int32_t i32[4];\n  } imm;\n  volatile imm xmm1;\n  xmm1.i128 = _mm_set_epi32(1, 2, 3, 4);\n  return (\n      xmm1.i32[0] == 4 && xmm1.i32[1] == 3 &&\n      xmm1.i32[2] == 2 && xmm1.i32[3] == 1);\n}\n"
  },
  {
    "path": "machines/eastl/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.1)\nproject (kernel C CXX)\n\noption(EASTL \"\" ON)\noption(RTTI_EXCEPTIONS \"\" ON)\ninclude(../../barebones.cmake)\n\nadd_machine_image(\n\t# name, binary and description\n\tmykernel \"eastl_kernel\" \"This is a test for EASTL!\"\n\t# list of source files\n\tmain.cpp\n)\n"
  },
  {
    "path": "machines/eastl/main.cpp",
    "content": "#include <main.h>\n#include <assert.h>\n#include <kprint.h>\n#include <exception>\n\n#include <EASTL/vector.h>\nclass IdioticException : public std::exception\n{\n    const char* oh_god;\npublic:\n\tIdioticException(const char* reason) : oh_god(reason) {}\n    const char* what() const noexcept override\n    {\n        return oh_god;\n    }\n};\n\nusing callback_t = int (*) (eastl::vector<int>&);\nstatic void test_rtti();\n\n\nvoid kernel_main(uint32_t /*eax*/, uint32_t /*ebx*/)\n{\n\tkprintf(KERNEL_DESC \"\\n\");\n\n\tconst callback_t callback = [] (auto& vec) -> int {\n\t\tkprintf(\"EASTL vector size: %zu (last element is %d)\\n\",\n\t\t\t\tvec.size(), vec.back());\n\t\tthrow IdioticException{\"Test!\"};\n\t};\n\n\teastl::vector<int> test;\n\tint caught = 0;\n\tfor (int i = 0; i < 16; i++)\n\t{\n\t\ttest.push_back(i);\n\t\ttry {\n\t\t\tassert(callback(test) == 0);\n\t\t}\n\t\tcatch (const std::exception& e) {\n\t\t\tkprintf(\"Exceptions caught: %d\\n\", ++caught);\n\t\t}\n\t}\n\tassert(caught == 16);\n\ttest_rtti();\n\tthrow IdioticException(\"This is on purpose\");\n}\n\nclass A\n{\npublic:\n\tvirtual void f() { kprintf(\"A::f() called\\n\"); }\n};\n\nclass B : public A\n{\npublic:\n\tvoid f() { kprintf(\"B::f() called\\n\"); }\n};\n\nstatic void test_rtti()\n{\n\tA a;\n\tB b;\n\ta.f();        // A::f()\n\tb.f();        // B::f()\n\n\tA *pA = &a;\n\tB *pB = &b;\n\tpA->f();      // A::f()\n\tpB->f();      // B::f()\n\n\tpA = &b;\n\t// pB = &a;      // not allowed\n\tpB = dynamic_cast<B*>(&a); // allowed but it returns NULL\n\tassert(pB == nullptr);\n}\n"
  },
  {
    "path": "machines/shared/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.1)\nproject (kernel C CXX)\ninclude(../../barebones.cmake)\n\nadd_machine_image(\n\t# name, binary and description\n\tmykernel \"my_kernel\" \"This is a test kernel!\"\n\t# list of source files\n\tmain.cpp\n)\n#target_compile_definitions(mykernel PRIVATE MYTEST=\"4\")\n\nadd_subdirectory(library)\nadd_machine_payload(mykernel mylib_payload\n\t\t\t\t\t${CMAKE_BINARY_DIR}/library/libmylib.so mylib)\nadd_dependencies(mylib_payload mylib)\n"
  },
  {
    "path": "machines/shared/README.md",
    "content": "## Machine with shared library loader\n\nThis tiny machine allows you to call into a function in a shared library, and just enough to make it usable. No constructors or destructors.\n\n## Questions\n\n- Why is the init function using the C calling convention?\n  - It's to make it easier to look up the functions name, as it won't be mangled.\n- Can you look up functions from the shared library?\n  - No, and that is ideally something done at loading time where functions are resolved automatically, which is something called dynamic linking.\n"
  },
  {
    "path": "machines/shared/library/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.1)\nproject(mylib VERSION 1.0 DESCRIPTION \"mylib description\")\n\nadd_library(mylib SHARED\n\t\tlibrary.cpp\n\t)\n\n# unfortunately we have to do this to shake some of the arguments from the\n# top level project\nset(CMAKE_CXX_FLAGS \"-Wall -Wextra -g -O2 -ffreestanding -fno-omit-frame-pointer\")\n\ntarget_compile_options(mylib PRIVATE \"-nostdlib\")\ntarget_compile_options(mylib PRIVATE \"-Os\")\n\nset_target_properties(mylib PROPERTIES VERSION ${PROJECT_VERSION})\nset_target_properties(mylib PROPERTIES SOVERSION 1)\n\nset_target_properties(mylib PROPERTIES PUBLIC_HEADER library.hpp)\ntarget_include_directories(mylib PRIVATE .)\n"
  },
  {
    "path": "machines/shared/library/interface.hpp",
    "content": "#pragma once\n#include <cstddef>\n\nstruct jumptable\n{\n\tint   (*kprintf)(const char* string, ...);\n\tvoid* (*malloc) (size_t);\n\tvoid  (*free)   (void*);\n};\n\ntypedef int (*init_function_t) (jumptable*);\n"
  },
  {
    "path": "machines/shared/library/library.cpp",
    "content": "#include \"interface.hpp\"\nstatic int test_value = 666;\n\nstruct Test {\n\tint a;\n};\nTest test;\n\nextern \"C\"\n__attribute__((noinline))\nvoid test_function(jumptable* table)\n{\n\ttable->kprintf(\"Hello from test()! jumptable = %p\\n\", table);\n}\n\nextern \"C\"\nint init(jumptable* t)\n{\n\tjumptable* table = t;\n\ttable->kprintf(\"Hello world from a shared library! table = %p\\n\", table);\n\ttable->kprintf(\"test_value == %d\\n\", test_value);\n\n\tchar* data = (char*) table->malloc(4096);\n\ttable->kprintf(\"malloc() returned %p\\n\", data);\n\n\tfor (int i = 0; i < 4096; i++) {\n\t\tdata[i] = i & 0xFF;\n\t}\n\ttable->free(data);\n\n\ttest.a = 55;\n\ttest_function(t);\n\treturn 0;\n}\n"
  },
  {
    "path": "machines/shared/main.cpp",
    "content": "#include <main.h>\n#include <kprint.h>\n#include <cassert>\n#include <cstring>\n#include <cstdlib>\n\nextern char _binary_mylib_start;\nextern char _binary_mylib_end;\n#include <kernel/dylib.hpp>\n#include \"library/interface.hpp\" // a made-up interface\n\nvoid kernel_main(const uint32_t, const uint32_t)\n{\n\t// load address is the start of the mylib blob\n\tconst auto* hdr = Dylib::load(&_binary_mylib_start);\n\tassert(hdr != nullptr);\n\n\t// translate init function name into symbol and create callable function\n\tauto init = Dylib::resolve_function<init_function_t>(hdr, \"init\");\n\tassert(init != nullptr);\n\n\t// setup call table\n\tstatic jumptable table;\n\ttable.kprintf = kprintf;\n\ttable.malloc = malloc;\n\ttable.free = free;\n\n\t// call into library function\n\tconst int result = init(&table);\n\tkprintf(\"init call result: %d\\n\", result);\n}\n"
  },
  {
    "path": "run.sh",
    "content": "#!/bin/bash\nset -e\nGRAPHICS=-nographic\n\nfor i in \"$@\"\ndo\ncase $i in\n    --kvm)\n    KVM=\"--enable-kvm -cpu host\"\n    shift # past argument with no value\n    ;;\n    --vga)\n    GRAPHICS=\"-vga std\"\n    shift # past argument with no value\n    ;;\n    --sanitize)\n    OPTION=\"sanitize\"\n    shift # past argument with no value\n    ;;\n\t--clean)\n    rm -rf $BUILD_DIR/\n    exit 0\n    ;;\n    *)\n          # unknown option\n\t\t  echo \"--kvm, --vga, --sanitize, --clean\"\n    ;;\nesac\ndone\n\nMACHINE=machines/${1-default}\nBUILD_DIR=$MACHINE/build\n\npushd $MACHINE\nmkdir -p build\npushd build\ncmake ..\nmake -j4 $OPTION\nBINARY=$BUILD_DIR/`cat binary.txt`\npopd\npopd\n\n# NOTE: if building with -march=native, make sure to enable KVM,\n# as emulated qemu only supports up to SSE3 instructions\nCLASS=`od -An -t x1 -j 4 -N 1 $BINARY`\nif [ $CLASS == \"02\" ]; then\n\techo \"Starting 64-bit kernel: $BINARY\"\n\tqemu-system-x86_64 $KVM -kernel tools/chainloader -initrd $BINARY $GRAPHICS\nelse\n\techo \"Starting 32-bit kernel: $BINARY\"\n\tqemu-system-x86_64 $KVM -kernel $BINARY $GRAPHICS\nfi\n"
  },
  {
    "path": "src/CMakeLists.txt",
    "content": "\nset(KERNEL_SOURCES\n\t# .c files\n\tkernel/kernel_start.c\n\tkernel/init_libc.c\n\thw/serial1.c\n\tcrt/c_abi.c\n\tcrt/c_stubs.c\n\tcrt/heap.c\n\tcrt/malloc.c\n\tcrt/print.c\n\tcrt/ubsan.c\n\t# .cpp files\n\tcrt/cxxabi.cpp\n\tkernel/elf.cpp\n\tkernel/tls.cpp\n\tkernel/panic.cpp\n\t# .asm files (for NASM)\n\tkernel/start.asm\n  )\nif (BUILD_32)\n\tlist(APPEND KERNEL_SOURCES\n\t  \tkernel/start32.c\n\t\tcrt/udiv.c\n\t)\nelse()\n\tlist(APPEND KERNEL_SOURCES\n  \t\tkernel/start64.asm\n\t)\nendif()\n\nadd_library(kernel STATIC ${KERNEL_SOURCES})\ntarget_include_directories(kernel PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})\ntarget_link_libraries(kernel tinyprintf)\n\nif (\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"GNU\")\n\tset_source_files_properties(\n\t\t\tkernel/panic.cpp\n\t\tPROPERTIES COMPILE_FLAGS -Wno-frame-address)\nendif()\n\nif (RTTI_EXCEPTIONS)\n\ttarget_compile_definitions(kernel PRIVATE -DEH_ENABLED)\nendif()\n"
  },
  {
    "path": "src/crt/c_abi.c",
    "content": "#include <kprint.h>\n#include <assert.h>\n#include <stddef.h>\n#include <stdint.h>\n#include <stdio.h>\n\n__attribute__((noreturn)) void panic(const char*);\nvoid* _impure_ptr = NULL;\n\nvoid __stack_chk_fail_local()\n{\n\tpanic(\"Stack protector: Canary modified\");\n\t__builtin_unreachable();\n}\n__attribute__((used))\nvoid __stack_chk_fail()\n{\n\tpanic(\"Stack protector: Canary modified\");\n\t__builtin_unreachable();\n}\nvoid __assert_fail(const char *assertion,\n                    const char *file,\n                    unsigned int line,\n                    const char *function)\n{\n\tchar buffer[4096];\n\tsnprintf(buffer, sizeof(buffer),\n\t\t\t\"Assertion failed: %s in %s:%u, function %s\\n\",\n\t\t\tassertion, file, line, function);\n\tpanic(buffer);\n\t__builtin_unreachable();\n}\nvoid __assert_func(\n\tconst char *file,\n\tint line,\n\tconst char *func,\n\tconst char *failedexpr)\n{\n\tkprintf(\n\t\t\"assertion \\\"%s\\\" failed: file \\\"%s\\\", line %d%s%s\\n\",\n\t\tfailedexpr, file, line,\n\t\tfunc ? \", function: \" : \"\", func ? func : \"\");\n\textern void abort() __attribute__((noreturn));\n\tabort();\n}\nvoid _exit(int status)\n{\n\tchar buffer[64];\n\tsnprintf(buffer, sizeof(buffer),\n\t\t\t\"Exit called with status %d\\n\", status);\n\tpanic(buffer);\n\t__builtin_unreachable();\n}\n\n__attribute__((used))\nvoid* memset(char* dest, int ch, size_t size)\n{\n\tfor (size_t i = 0; i < size; i++)\n\t\tdest[i] = ch;\n\treturn dest;\n}\nvoid* memcpy(char* dest, const char* src, size_t size)\n{\n\tfor (size_t i = 0; i < size; i++)\n\t\tdest[i] = src[i];\n\treturn dest;\n}\nvoid* memmove(char* dest, const char* src, size_t size)\n{\n\tif (dest <= src)\n\t{\n\t\tfor (size_t i = 0; i < size; i++)\n\t\t\tdest[i] = src[i];\n\t}\n\telse\n\t{\n\t\tfor (int i = size-1; i >= 0; i--)\n\t\t\tdest[i] = src[i];\n\t}\n\treturn dest;\n}\nint memcmp(const void* ptr1, const void* ptr2, size_t n)\n{\n\tconst uint8_t* iter1 = (const uint8_t*) ptr1;\n\tconst uint8_t* iter2 = (const uint8_t*) ptr2;\n\twhile (n > 0 && *iter1 == *iter2) {\n\t\titer1++;\n\t\titer2++;\n\t\tn--;\n\t}\n\treturn n == 0 ? 0 : (*iter1 - *iter2);\n}\n\n// naive version (needed for EASTL)\nfloat ceilf(float n)\n{\n\tlong int i = (int) n;\n\treturn (n == (float) i) ? n : i + 1;\n}\n\nchar* strcpy(char* dst, const char* src)\n{\n\twhile ((*dst++ = *src++));\n\t*dst = 0;\n\treturn dst;\n}\nsize_t strlen(const char* str)\n{\n\tconst char* iter;\n\tfor (iter = str; *iter; ++iter);\n\treturn iter - str;\n}\nint strcmp(const char* str1, const char* str2)\n{\n\twhile (*str1 != 0 && *str2 != 0  && *str1 == *str2) {\n      str1++;\n      str2++;\n   }\n   return *str1 - *str2;\n}\nchar* strcat(char* dest, const char* src)\n{\n\tstrcpy(dest + strlen(dest), src);\n\treturn dest;\n}\n\nvoid* __memcpy_chk(void* dest, const void* src, size_t len, size_t destlen)\n{\n  assert (len <= destlen);\n  return memcpy(dest, src, len);\n}\nvoid* __memset_chk(void* dest, int c, size_t len, size_t destlen)\n{\n  assert (len <= destlen);\n  return memset(dest, c, len);\n}\nchar* __strcat_chk(char* dest, const char* src, size_t destlen)\n{\n  size_t len = strlen(dest) + strlen(src) + 1;\n  assert (len <= destlen);\n  return strcat(dest, src);\n}\n\nint isdigit(int c)\n{\n\treturn (('0' <= c) && (c <= '9')) ? 1 : 0;\n}\nint isalpha(int c)\n{\n \treturn ((c > 64 && c < 91) || (c > 96 && c <= 122)) ? 1 : 0;\n}\nint isxdigit(int c)\n{\n\treturn (isdigit(c) || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'));\n}\nint isupper(int c)\n{\n\treturn (c >= 'A' && c <= 'Z');\n}\n"
  },
  {
    "path": "src/crt/c_stubs.c",
    "content": "#define _GNU_SOURCE\n#include <stddef.h>\n#include <dlfcn.h>\n#include <link.h>\n#include <kprint.h>\n\nchar *getenv(const char *name) {\n\t//kprintf(\"getenv called for %s\\n\", name);\n\t(void) name;\n\treturn NULL;\n}\nint setenv(const char *name, const char *value, int overwrite) {\n\t(void) name;\n\t(void) value;\n\t(void) overwrite;\n\treturn -1;\n}\nint dl_iterate_phdr(\n\tint (*callback) (struct dl_phdr_info *info, size_t size, void *data), void *data)\n{\n\treturn 0;\n}\n"
  },
  {
    "path": "src/crt/cxxabi.cpp",
    "content": "#include <kprint.h>\n#include <new>\n#include <cstddef>\n#include <cstring>\nextern \"C\" void* malloc(size_t);\nextern \"C\" void free(void*);\n\n//#define DEBUG_HEAP\n#ifdef DEBUG_HEAP\n#define HPRINT(fmt, ...) kprintf(fmt, __VA_ARGS__)\n#else\n#define HPRINT(fmt, ...) /* fmt */\n#endif\n\nvoid* operator new(size_t size)\n{\n  void* res = malloc(size);\n  HPRINT(\"operator new: size %u => %p\\n\", size, res);\n  return res;\n}\nvoid* operator new[](size_t size)\n{\n  void* res = malloc(size);\n  HPRINT(\"operator new[]: size %u => %p\\n\", size, res);\n  return res;\n}\n\nvoid operator delete(void* ptr)\n{\n  HPRINT(\"operator delete: %p\\n\", ptr);\n  free(ptr);\n}\nvoid operator delete[](void* ptr)\n{\n  HPRINT(\"operator delete[]: %p\\n\", ptr);\n  free(ptr);\n}\n// C++14 sized deallocation\nvoid operator delete(void* ptr, std::size_t)\n{\n  free(ptr);\n}\nvoid operator delete [](void* ptr, std::size_t)\n{\n  free(ptr);\n}\n\nextern \"C\" void __cxa_pure_virtual()\n{\n  kprintf(\"Unimplemented pure virtual function called\\n\");\n}\n\n/// EASTL glue ///\n\nvoid* operator new[] (\n        size_t size, /* size */\n        const char*, /* pName */\n        int,         /* flags */\n        unsigned,    /* debugFlags */\n        const char*, /* file */\n        int)         /* line */\n{\n  void* res = malloc(size);\n  HPRINT(\"eastl new: size: %u = %p\\n\", size, res);\n  return res;\n}\nvoid* operator new[] (\n        size_t size,  /* size */\n        size_t align, /* alignment */\n        size_t,       /* alignmentOffset */\n        const char*,  /* pName */\n        int,          /* flags */\n        unsigned,     /* debugFlags */\n        const char*,  /* file */\n        int)          /* line */\n{\n  void* res = malloc(size);\n  HPRINT(\"eastl aligned new: size %u align %u => %p\\n\",\n          size, align, res);\n  (void) align;\n  return res;\n}\n"
  },
  {
    "path": "src/crt/heap.c",
    "content": "#include <assert.h>\n#include <stddef.h>\n#include <stdint.h>\n#include <stdlib.h>\n\nstatic uintptr_t heap_start;\nstatic uintptr_t heap_end;\n// heap_max really should be set to max physical\n// by reading the value from multiboot meminfo table\nstatic uintptr_t heap_max = 0xC0000000;\n\nvoid __init_heap(void* free_begin)\n{\n\theap_start = (uintptr_t) free_begin;\n\tif (heap_start & 0xF) heap_start += 0x10 - (heap_start & 0xF);\n\theap_end   = heap_start;\n\tassert(((heap_start & 0xF) == 0) && \"Heap should be aligned\");\n}\n\n// data segment size\nvoid* sbrk(intptr_t increment)\n{\n\tuintptr_t old = heap_end;\n\tif (heap_end + increment <= heap_max)\n\t{\n\t\theap_end += increment;\n\t\treturn (void*) old;\n\t}\n\treturn (void*) -1;\n}\n\nint posix_memalign(void **memptr, size_t alignment, size_t size)\n{\n\tuintptr_t addr = (uintptr_t) malloc(size + alignment);\n\tconst intptr_t offset = addr & (alignment-1);\n\tif (offset) addr += (alignment-1) - offset;\n\t*memptr = (void*) addr;\n\treturn 0;\n}\n"
  },
  {
    "path": "src/crt/malloc.c",
    "content": "//\n// As written by Snaipe @ https://github.com/Snaipe/malloc\n//\n#include <unistd.h>\n#include <errno.h>\n\nstatic inline size_t word_align(size_t size) {\n    return size + ((sizeof(size_t) - 1) & ~(sizeof(size_t) - 1));\n}\n\nstruct chunk {\n    struct chunk *next, *prev;\n    size_t        size;\n    int           free;\n    void         *data;\n};\n\ntypedef struct chunk *Chunk;\n\nstatic void *malloc_base() {\n    static Chunk b = NULL;\n    if (!b) {\n        b = sbrk(word_align(sizeof(struct chunk)));\n        if (b == (void*) -1) {\n            _exit(127);\n        }\n        b->next = NULL;\n        b->prev = NULL;\n        b->size = 0;\n        b->free = 0;\n        b->data = NULL;\n    }\n    return b;\n}\n\nChunk malloc_chunk_find(size_t s, Chunk *heap) {\n    Chunk c = malloc_base();\n    for (; c && (!c->free || c->size < s); *heap = c, c = c->next);\n    return c;\n}\n\nvoid malloc_merge_next(Chunk c) {\n    c->size = c->size + c->next->size + sizeof(struct chunk);\n    c->next = c->next->next;\n    if (c->next) {\n        c->next->prev = c;\n    }\n}\n\nvoid malloc_split_next(Chunk c, size_t size) {\n    Chunk newc = (Chunk)((char*) c + size);\n    newc->prev = c;\n    newc->next = c->next;\n    newc->size = c->size - size;\n    newc->free = 1;\n    newc->data = newc + 1;\n    if (c->next) {\n        c->next->prev = newc;\n    }\n    c->next = newc;\n    c->size = size - sizeof(struct chunk);\n}\n\nvoid *malloc(size_t size) {\n    if (!size) return NULL;\n    size_t length = word_align(size + sizeof(struct chunk));\n    Chunk prev = NULL;\n    Chunk c = malloc_chunk_find(size, &prev);\n    if (!c) {\n        Chunk newc = sbrk(length);\n        if (newc == (void*) -1) {\n            return NULL;\n        }\n        newc->next = NULL;\n        newc->prev = prev;\n        newc->size = length - sizeof(struct chunk);\n        newc->data = newc + 1;\n        prev->next = newc;\n        c = newc;\n    } else if (length + sizeof(size_t) < c->size) {\n        malloc_split_next(c, length);\n    }\n    c->free = 0;\n    return c->data;\n}\n\nvoid free(void *ptr) {\n    if (!ptr || ptr < malloc_base() || ptr > sbrk(0)) return;\n    Chunk c = (Chunk) ptr - 1;\n    if (c->data != ptr) return;\n    c->free = 1;\n\n    if (c->next && c->next->free) {\n        malloc_merge_next(c);\n    }\n    if (c->prev->free) {\n        malloc_merge_next(c = c->prev);\n    }\n    if (!c->next) {\n        c->prev->next = NULL;\n        sbrk(- c->size - sizeof(struct chunk));\n    }\n}\n\nvoid *calloc(size_t nmemb, size_t size) {\n    size_t length = nmemb * size;\n    void *ptr = malloc(length);\n    if (ptr) {\n        char *dst = ptr;\n        for (size_t i = 0; i < length; *dst = 0, ++dst, ++i);\n    }\n    return ptr;\n}\n\nvoid *realloc(void *ptr, size_t size) {\n    void *newptr = malloc(size);\n    if (newptr && ptr && ptr >= malloc_base() && ptr <= sbrk(0)) {\n        Chunk c = (Chunk) ptr - 1;\n        if (c->data == ptr) {\n            size_t length = c->size > size ? size : c->size;\n            char *dst = newptr, *src = ptr;\n            for (size_t i = 0; i < length; *dst = *src, ++src, ++dst, ++i);\n            free(ptr);\n        }\n    }\n    return newptr;\n}\n"
  },
  {
    "path": "src/crt/print.c",
    "content": "#include <assert.h>\n#include <kprint.h>\n#include <stdarg.h>\n#include <stdio.h>\nFILE* stderr = NULL;\nFILE* stdout = NULL;\n\nchar* hex32_to_str(char buffer[], unsigned int val)\n{\n\tchar const lut[] = \"0123456789ABCDEF\";\n\tfor (int i = 0; i < 4; i++)\n\t{\n\t\tbuffer[i*2+0] = lut[(val >> (28-i*8)) & 0xF];\n\t\tbuffer[i*2+1] = lut[(val >> (24-i*8)) & 0xF];\n\t}\n\tbuffer[8] = 0;\n\treturn buffer;\n}\n\nchar* int32_to_str(char buffer[], int val)\n{\n\tchar* b = buffer;\n\t// negation\n\tif (val < 0) { *b++ = '-'; val *= -1; }\n\t// move to end of repr.\n\tint tmp = val;\n\tdo { ++b; tmp /= 10;  } while (tmp);\n\t*b = 0;\n\t// move back and insert as we go\n\tdo {\n\t\t*--b = '0' + (val % 10);\n\t\tval /= 10;\n\t} while (val);\n\treturn buffer;\n}\n\nint kprintf(const char* fmt, ...)\n{\n\tchar buffer[4096];\n\tva_list va;\n\tva_start(va, fmt);\n\tint ret = tfp_vsnprintf(buffer, sizeof(buffer), fmt, va);\n\tva_end(va);\n\n\tkprint(buffer);\n\treturn ret;\n}\n\n#undef snprintf\n__attribute__((format (printf, 3, 4)))\nint snprintf(char *s, size_t maxlen, const char *format, ...)\n{\n\tva_list arg;\n\tva_start (arg, format);\n\tint bytes = tfp_vsnprintf(s, maxlen, format, arg);\n\tva_end (arg);\n\treturn bytes;\n}\n\n#undef printf\n#undef fprintf\n#undef vfprintf\nint vfprintf(FILE* fp, const char *format, va_list ap)\n{\n\t(void) fp;\n\tchar buffer[4096];\n\tint len = tfp_vsnprintf(buffer, sizeof(buffer), format, ap);\n\t__serial_print(buffer, len);\n\treturn len;\n}\n\n__attribute__((format (printf, 2, 3)))\nint fprintf(FILE* stream, const char* fmt, ...)\n{\n\tva_list arg;\n    va_start (arg, fmt);\n    int bytes = vfprintf(stream, fmt, arg);\n    va_end (arg);\n\treturn bytes;\n}\n\n__attribute__((format(printf, 2, 3)))\nint __printf_chk (int flag, const char *format, ...)\n{\n\t(void) flag;\n\tva_list ap;\n\tva_start (ap, format);\n\tint bytes = vfprintf (stdout, format, ap);\n\tva_end (ap);\n\treturn bytes;\n}\nint __fprintf_chk(FILE* fp, int flag, const char* format, ...)\n{\n\t(void) flag;\n\tva_list arg;\n\tva_start (arg, format);\n\tint bytes = vfprintf(fp, format, arg);\n\tva_end (arg);\n\treturn bytes;\n}\nint __vfprintf_chk(FILE* fp, int flag, const char *format, va_list ap)\n{\n\t(void) flag;\n\tint bytes = vfprintf (fp, format, ap);\n\treturn bytes;\n}\nint __vsprintf_chk(char* s, int flag, size_t slen, const char* format, va_list args)\n{\n\t(void) flag;\n\tint res = tfp_vsnprintf(s, slen, format, args);\n\tassert ((size_t) res < slen);\n\treturn res;\n}\nint __vsnprintf_chk (char *s, size_t maxlen, int flags, size_t slen,\n\t\t                  const char *format, va_list args)\n{\n\tassert (slen < maxlen);\n\t(void) flags;\n\treturn tfp_vsnprintf(s, slen, format, args);\n}\n__attribute__((format(printf, 4, 5)))\nint __sprintf_chk(char* s, int flags, size_t slen, const char *format, ...)\n{\n\tva_list arg;\n\tva_start (arg, format);\n\tint bytes = __vsprintf_chk(s, flags, slen, format, arg);\n\tva_end (arg);\n\treturn bytes;\n}\nint __snprintf_chk (char *s, size_t maxlen, int flags, size_t slen,\n                \t\t const char *format, ...)\n{\n\tva_list arg;\n\tint done;\n\n\tva_start (arg, format);\n\tdone = __vsnprintf_chk (s, maxlen, flags, slen, format, arg);\n\tva_end (arg);\n\n\treturn done;\n}\n"
  },
  {
    "path": "src/crt/ubsan.c",
    "content": "#include <stdint.h>\n#include <stdio.h>\n#include <kprint.h>\nextern void panic(const char*) __attribute__((noreturn));\nextern void print_backtrace();\n\nstruct source_location {\n\tconst char *file_name;\n\tstruct {\n\t\tuint32_t line;\n\t\tuint32_t column;\n\t};\n};\nstruct type_descriptor {\n   uint16_t type_kind;\n   uint16_t type_info;\n   char type_name[1];\n};\nconst char* type_kind_string[] = {\n  \"load of\", \"store to\", \"reference binding to\", \"member access within\",\n  \"member call on\", \"constructor call on\", \"downcast of\", \"downcast of\"\n};\n\nstruct out_of_bounds {\n   struct source_location  src;\n   struct type_descriptor* array_type;\n   struct type_descriptor* index_type;\n};\nstruct overflow {\n  struct source_location src;\n};\nstruct mismatch {\n  struct source_location  src;\n  struct type_descriptor* type;\n  unsigned char    log_align;\n  unsigned char    type_kind;\n};\nstruct function_type_mismatch {\n  const struct source_location  src;\n  const struct type_descriptor* type;\n};\nstruct nonnull_return {\n  struct source_location src;\n  struct source_location attr;\n};\nstruct unreachable {\n  struct source_location src;\n};\n\nstatic void print_src_location(const struct source_location* src) {\n  kprintf(\"ubsan: %s at line %u col %u\\n\",\n          src->file_name, src->line, src->column);\n}\n\nstatic void undefined_throw(const char* error) {\n  kprintf(\"ubsan: %s\", error);\n  print_backtrace();\n  kprintf(\"\\n\");\n}\n\n/// Undefined-behavior sanitizer\nvoid __ubsan_handle_out_of_bounds(struct out_of_bounds* data)\n{\n  print_src_location(&data->src);\n  undefined_throw(\"Out-of-bounds access\");\n}\nvoid __ubsan_handle_missing_return()\n{\n  undefined_throw(\"Missing return\");\n}\nvoid __ubsan_handle_nonnull_return(struct nonnull_return* data)\n{\n  print_src_location(&data->src);\n  undefined_throw(\"Non-null return\");\n}\n\nvoid __ubsan_handle_add_overflow()\n{\n  undefined_throw(\"Overflow on addition\");\n}\nvoid __ubsan_handle_sub_overflow()\n{\n  undefined_throw(\"Overflow on subtraction\");\n}\nvoid __ubsan_handle_mul_overflow()\n{\n  undefined_throw(\"Overflow on multiplication\");\n}\nvoid __ubsan_handle_negate_overflow()\n{\n  undefined_throw(\"Overflow on negation\");\n}\nvoid __ubsan_handle_pointer_overflow()\n{\n  undefined_throw(\"Pointer overflow\");\n}\nvoid __ubsan_handle_divrem_overflow(struct overflow* data,\n                                    unsigned long lhs,\n                                    unsigned long rhs)\n{\n  print_src_location(&data->src);\n  kprintf(\"ubsan: LHS %lu / RHS %lu\\n\", lhs, rhs);\n  undefined_throw(\"Division remainder overflow\");\n}\nvoid __ubsan_handle_float_cast_overflow()\n{\n  undefined_throw(\"Float-cast overflow\");\n}\nvoid __ubsan_handle_shift_out_of_bounds()\n{\n  undefined_throw(\"Shift out-of-bounds\");\n}\n\nvoid __ubsan_handle_type_mismatch_v1(struct mismatch* data, uintptr_t ptr)\n{\n  print_src_location(&data->src);\n\n  const char* reason = \"Type mismatch\";\n  const long alignment = 1 << data->log_align;\n\n  if (alignment && (ptr & (alignment-1)) != 0) {\n    reason = \"Misaligned access\";\n  }\n  else if (ptr == 0) {\n    reason = \"Null-pointer access\";\n  }\n  char buffer[2048];\n  // TODO: resolve symbol name\n  snprintf(buffer, sizeof(buffer),\n          \"%s on ptr %p  (aligned %lu)\\n\"\n          \"ubsan: type name %s\\n\",\n          reason,\n          (void*) ptr,\n          alignment,\n          data->type->type_name);\n  undefined_throw(buffer);\n}\nvoid __ubsan_handle_function_type_mismatch(\n        struct function_type_mismatch* data,\n        unsigned long ptr)\n{\n  print_src_location(&data->src);\n\n  char buffer[2048];\n  snprintf(buffer, sizeof(buffer),\n          \"Function type mismatch on ptr %p\\n\"\n          \"ubsan: type name %s\\n\"\n          \"ubsan: function  %p\",\n          (void*) ptr,\n          data->type->type_name,\n          (void*) ptr); // TODO: resolve symbol name\n  undefined_throw(buffer);\n}\nvoid __ubsan_handle_nonnull_arg()\n{\n  undefined_throw(\"Non-null argument violated\");\n}\n\nvoid __ubsan_handle_invalid_builtin()\n{\n  undefined_throw(\"Invalid built-in function\");\n}\nvoid __ubsan_handle_load_invalid_value()\n{\n  undefined_throw(\"Load of invalid value\");\n}\n__attribute__((noreturn))\nvoid __ubsan_handle_builtin_unreachable(struct unreachable* data)\n{\n  print_src_location(&data->src);\n  panic(\"Unreachable code reached\");\n}\n"
  },
  {
    "path": "src/crt/udiv.c",
    "content": "#include <limits.h>\n#include <stdint.h>\ntypedef unsigned su_int;\ntypedef          long long di_int;\ntypedef unsigned long long du_int;\n\ntypedef union\n{\n    du_int all;\n    struct\n    {\n        su_int low;\n        su_int high;\n    } s;\n} udwords;\n\ndu_int\n__udivmoddi4(du_int a, du_int b, du_int* rem)\n{\n    const unsigned n_uword_bits = sizeof(su_int) * CHAR_BIT;\n    const unsigned n_udword_bits = sizeof(du_int) * CHAR_BIT;\n    udwords n;\n    n.all = a;\n    udwords d;\n    d.all = b;\n    udwords q;\n    udwords r;\n    unsigned sr;\n    /* special cases, X is unknown, K != 0 */\n    if (n.s.high == 0)\n    {\n        if (d.s.high == 0)\n        {\n            /* 0 X\n             * ---\n             * 0 X\n             */\n            if (rem)\n                *rem = n.s.low % d.s.low;\n            return n.s.low / d.s.low;\n        }\n        /* 0 X\n         * ---\n         * K X\n         */\n        if (rem)\n            *rem = n.s.low;\n        return 0;\n    }\n    /* n.s.high != 0 */\n    if (d.s.low == 0)\n    {\n        if (d.s.high == 0)\n        {\n            /* K X\n             * ---\n             * 0 0\n             */\n            if (rem)\n                *rem = n.s.high % d.s.low;\n            return n.s.high / d.s.low;\n        }\n        /* d.s.high != 0 */\n        if (n.s.low == 0)\n        {\n            /* K 0\n             * ---\n             * K 0\n             */\n            if (rem)\n            {\n                r.s.high = n.s.high % d.s.high;\n                r.s.low = 0;\n                *rem = r.all;\n            }\n            return n.s.high / d.s.high;\n        }\n        /* K K\n         * ---\n         * K 0\n         */\n        if ((d.s.high & (d.s.high - 1)) == 0)     /* if d is a power of 2 */\n        {\n            if (rem)\n            {\n                r.s.low = n.s.low;\n                r.s.high = n.s.high & (d.s.high - 1);\n                *rem = r.all;\n            }\n            return n.s.high >> __builtin_ctz(d.s.high);\n        }\n        /* K K\n         * ---\n         * K 0\n         */\n        sr = __builtin_clz(d.s.high) - __builtin_clz(n.s.high);\n        /* 0 <= sr <= n_uword_bits - 2 or sr large */\n        if (sr > n_uword_bits - 2)\n        {\n           if (rem)\n                *rem = n.all;\n            return 0;\n        }\n        ++sr;\n        /* 1 <= sr <= n_uword_bits - 1 */\n        /* q.all = n.all << (n_udword_bits - sr); */\n        q.s.low = 0;\n        q.s.high = n.s.low << (n_uword_bits - sr);\n        /* r.all = n.all >> sr; */\n        r.s.high = n.s.high >> sr;\n        r.s.low = (n.s.high << (n_uword_bits - sr)) | (n.s.low >> sr);\n    }\n    else  /* d.s.low != 0 */\n    {\n        if (d.s.high == 0)\n        {\n            /* K X\n             * ---\n             * 0 K\n             */\n            if ((d.s.low & (d.s.low - 1)) == 0)     /* if d is a power of 2 */\n            {\n                if (rem)\n                    *rem = n.s.low & (d.s.low - 1);\n                if (d.s.low == 1)\n                    return n.all;\n                sr = __builtin_ctz(d.s.low);\n                q.s.high = n.s.high >> sr;\n                q.s.low = (n.s.high << (n_uword_bits - sr)) | (n.s.low >> sr);\n                return q.all;\n            }\n            /* K X\n             * ---\n             * 0 K\n             */\n            sr = 1 + n_uword_bits + __builtin_clz(d.s.low) - __builtin_clz(n.s.high);\n            /* 2 <= sr <= n_udword_bits - 1\n             * q.all = n.all << (n_udword_bits - sr);\n             * r.all = n.all >> sr;\n             */\n            if (sr == n_uword_bits)\n            {\n                q.s.low = 0;\n                q.s.high = n.s.low;\n                r.s.high = 0;\n                r.s.low = n.s.high;\n            }\n            else if (sr < n_uword_bits)  // 2 <= sr <= n_uword_bits - 1\n            {\n                q.s.low = 0;\n                q.s.high = n.s.low << (n_uword_bits - sr);\n                r.s.high = n.s.high >> sr;\n                r.s.low = (n.s.high << (n_uword_bits - sr)) | (n.s.low >> sr);\n            }\n            else              // n_uword_bits + 1 <= sr <= n_udword_bits - 1\n            {\n                q.s.low = n.s.low << (n_udword_bits - sr);\n                q.s.high = (n.s.high << (n_udword_bits - sr)) |\n                           (n.s.low >> (sr - n_uword_bits));\n                r.s.high = 0;\n                r.s.low = n.s.high >> (sr - n_uword_bits);\n            }\n        }\n        else\n        {\n            /* K X\n             * ---\n             * K K\n             */\n            sr = __builtin_clz(d.s.high) - __builtin_clz(n.s.high);\n            /* 0 <= sr <= n_uword_bits - 1 or sr large */\n            if (sr > n_uword_bits - 1)\n            {\n                if (rem)\n                    *rem = n.all;\n                return 0;\n            }\n            ++sr;\n            /* 1 <= sr <= n_uword_bits */\n            /*  q.all = n.all << (n_udword_bits - sr); */\n            q.s.low = 0;\n            if (sr == n_uword_bits)\n            {\n                q.s.high = n.s.low;\n                r.s.high = 0;\n                r.s.low = n.s.high;\n            }\n            else\n            {\n                q.s.high = n.s.low << (n_uword_bits - sr);\n                r.s.high = n.s.high >> sr;\n                r.s.low = (n.s.high << (n_uword_bits - sr)) | (n.s.low >> sr);\n            }\n        }\n    }\n    /* Not a special case\n     * q and r are initialized with:\n     * q.all = n.all << (n_udword_bits - sr);\n     * r.all = n.all >> sr;\n     * 1 <= sr <= n_udword_bits - 1\n     */\n    su_int carry = 0;\n    for (; sr > 0; --sr)\n    {\n        /* r:q = ((r:q)  << 1) | carry */\n        r.s.high = (r.s.high << 1) | (r.s.low  >> (n_uword_bits - 1));\n        r.s.low  = (r.s.low  << 1) | (q.s.high >> (n_uword_bits - 1));\n        q.s.high = (q.s.high << 1) | (q.s.low  >> (n_uword_bits - 1));\n        q.s.low  = (q.s.low  << 1) | carry;\n        /* carry = 0;\n         * if (r.all >= d.all)\n         * {\n         *      r.all -= d.all;\n         *      carry = 1;\n         * }\n         */\n        const di_int s = (di_int)(d.all - r.all - 1) >> (n_udword_bits - 1);\n        carry = s & 1;\n        r.all -= d.all & s;\n    }\n    q.all = (q.all << 1) | carry;\n    if (rem)\n        *rem = r.all;\n    return q.all;\n}\n\ndu_int\n__udivdi3(du_int a, du_int b)\n{\n\treturn __udivmoddi4(a, b, 0);\n}\n\ndu_int\n__umoddi3(du_int a, du_int b)\n{\n\tdu_int r;\n\t__udivmoddi4(a, b, &r);\n\treturn r;\n}\n"
  },
  {
    "path": "src/hw/serial.h",
    "content": "#pragma once\n\nextern void __serial_print1(const char*);\nextern void __serial_print(const char*, int);\nextern void __serial_putchr(void*, char);\n"
  },
  {
    "path": "src/hw/serial1.c",
    "content": "#include <stdint.h>\n\nstatic const uint16_t port = 0x3F8; // Serial port 1\nstatic char initialized = 0;\n\nstatic inline uint8_t inb(int port)\n{\n\tint ret;\n\tasm (\"xorl %eax,%eax\");\n\tasm (\"inb %%dx,%%al\":\"=a\" (ret):\"d\"(port));\n\treturn ret;\n}\nstatic inline void outb(int port, uint8_t data)\n{\n\tasm (\"outb %%al,%%dx\"::\"a\" (data), \"d\"(port));\n}\n\nstatic inline void init_serial_if_needed()\n{\n\tif (initialized) return;\n\tinitialized = 1;\n\t// properly initialize serial port\n\toutb(port + 1, 0x00);    // Disable all interrupts\n\toutb(port + 3, 0x80);    // Enable DLAB (set baud rate divisor)\n\toutb(port + 0, 0x03);    // Set divisor to 3 (lo byte) 38400 baud\n\toutb(port + 1, 0x00);    //                  (hi byte)\n\toutb(port + 3, 0x03);    // 8 bits, no parity, one stop bit\n\toutb(port + 2, 0xC7);    // Enable FIFO, clear them, with 14-byte threshold\n}\n\nvoid __serial_print1(const char* cstr)\n{\n\tinit_serial_if_needed();\n\twhile (*cstr) {\n\t\twhile (!(inb(port + 5) & 0x20)) /* */;\n\t\toutb(port, *cstr++);\n\t}\n}\nvoid __serial_print(const char* str, int len)\n{\n\tinit_serial_if_needed();\n\tfor (int i = 0; i < len; i++) {\n\t\twhile (!(inb(port + 5) & 0x20)) /* */;\n\t\toutb(port, str[i]);\n\t}\n}\n\n// buffered serial output\nstatic char buffer[256];\nstatic unsigned cnt = 0;\n\nvoid fflush(void* fileno)\n{\n\t(void) fileno;\n\t__serial_print(buffer, cnt);\n\tcnt = 0;\n}\n\nvoid __serial_putchr(const void* file, char c)\n{\n\t(void) file;\n\tbuffer[cnt++] = c;\n\tif (c == '\\n' || cnt == sizeof(buffer)) {\n\t\tfflush(0);\n\t}\n}\n"
  },
  {
    "path": "src/kernel/dylib.hpp",
    "content": "#pragma once\n#include <kernel/elf.hpp>\n\nstruct Dylib\n{\n\tstatic Elf64_Ehdr* load(const void* address);\n\n\ttemplate <typename T>\n\tstatic T resolve_function(const Elf64_Ehdr* hdr, const char* name);\n};\n\n\ninline Elf64_Ehdr* Dylib::load(const void* address)\n{\n\tauto* hdr = (Elf64_Ehdr*) address;\n\t// validate shared library ELF header\n\tassert(Elf::validate(hdr));\n\t// resolve symbolic references from PLT and GOT\n\tElf::perform_relocations(hdr);\n\treturn hdr;\n}\ntemplate <typename T>\ninline T Dylib::resolve_function(const Elf64_Ehdr* hdr, const char* name)\n{\n\tconst auto* sym = Elf::resolve_name(hdr, name);\n\tif (sym == nullptr) return T();\n\treturn (T) &((char*) hdr)[sym->st_value];\n}\n"
  },
  {
    "path": "src/kernel/elf.cpp",
    "content": "#include \"elf.hpp\"\n#include <cassert>\n#include <cstdint>\n#include <cstring>\n#include <kprint.h>\nstatic constexpr bool DEBUG_ELF = false;\n\nbool Elf::validate(Elf64_Ehdr* hdr)\n{\n\treturn (hdr->e_ident[EI_MAG0] == ELFMAG0)\n\t\t&& (hdr->e_ident[EI_MAG1] == ELFMAG1)\n\t\t&& (hdr->e_ident[EI_MAG2] == ELFMAG2)\n\t\t&& (hdr->e_ident[EI_MAG3] == ELFMAG3)\n\t\t// we will need 64-bit addresses and such\n\t\t&& (hdr->e_ident[EI_CLASS] == ELFCLASS64);\n}\n\ntemplate <typename T>\ninline T* elf_offset(const Elf64_Ehdr* hdr, intptr_t ofs) {\n  return (T*) &((char*) hdr)[ofs];\n}\n\nstatic const Elf64_Sym*\nelf_sym_index(const Elf64_Ehdr* hdr, const Elf64_Shdr* shdr, uint32_t symidx)\n{\n\tassert(symidx < shdr->sh_size / sizeof(Elf64_Sym));\n\tauto* symtab = elf_offset<Elf64_Sym>(hdr, shdr->sh_offset);\n\treturn &symtab[symidx];\n}\n\nconst Elf64_Shdr* Elf::section_by_name(const Elf64_Ehdr* hdr, const char* name)\n{\n\tconst auto* shdr = elf_offset<Elf64_Shdr> (hdr, hdr->e_shoff);\n\tconst auto& shstrtab = shdr[hdr->e_shnum-1];\n\tconst char* strings = elf_offset<char>(hdr, shstrtab.sh_offset);\n\n\tfor (auto i = 0; i < hdr->e_shnum; i++)\n\t{\n\t\tconst char* shname = &strings[shdr[i].sh_name];\n\t\tif (strcmp(shname, name) == 0) {\n\t\t\treturn &shdr[i];\n\t\t}\n\t}\n\treturn nullptr;\n}\n\nconst Elf64_Sym* Elf::resolve_name(const Elf64_Ehdr* hdr, const char* name)\n{\n\tconst auto* sym_hdr = Elf::section_by_name(hdr, \".symtab\");\n\tassert(sym_hdr != nullptr);\n\tconst auto* str_hdr = Elf::section_by_name(hdr, \".strtab\");\n\tassert(str_hdr != nullptr);\n\n\tconst Elf64_Sym* symtab = elf_sym_index(hdr, sym_hdr, 0);\n\tconst size_t symtab_ents = sym_hdr->sh_size / sizeof(Elf64_Sym);\n\tconst char* strtab = elf_offset<char>(hdr, str_hdr->sh_offset);\n\n\tfor (size_t i = 0; i < symtab_ents; i++)\n\t{\n\t\tconst char* symname = &strtab[symtab[i].st_name];\n\t\t//kprintf(\"Testing %s vs %s\\n\", symname, name);\n\t\tif (strcmp(symname, name) == 0) {\n\t\t\treturn &symtab[i];\n\t\t}\n\t}\n\treturn nullptr;\n}\n\nstatic void elf_print_sym(const Elf64_Sym* sym)\n{\n\tkprintf(\"-> Sym is at %p with size %llu, type %u name %u\\n\",\n\t\t\t(void*) sym->st_value, sym->st_size,\n\t\t\tELF64_ST_TYPE(sym->st_info), sym->st_name);\n}\n\nstatic void elf_relocate_section(Elf64_Ehdr* hdr, const char* section_name)\n{\n\tconst auto* rela = Elf::section_by_name(hdr, section_name);\n\tconst auto* dyn_hdr = Elf::section_by_name(hdr, \".dynsym\");\n\tconst size_t rela_ents = rela->sh_size / sizeof(Elf64_Rela);\n\tauto* rela_addr = elf_offset<Elf64_Rela>(hdr, rela->sh_offset);\n\tfor (size_t i = 0; i < rela_ents; i++) {\n\t\tconst uint32_t symidx = ELF64_R_SYM(rela_addr[i].r_info);\n\t\tauto* sym = elf_sym_index(hdr, dyn_hdr, symidx);\n\n\t\tconst uint8_t type = ELF64_ST_TYPE(sym->st_info);\n\t\tif (type == STT_FUNC || type == STT_OBJECT)\n\t\t{\n\t\t\tuintptr_t entry = (uintptr_t) hdr + rela_addr[i].r_offset;\n\t\t\tuintptr_t final = (uintptr_t) hdr + sym->st_value;\n\t\t\tif constexpr (DEBUG_ELF)\n\t\t\t{\n\t\t\t\tkprintf(\"Relocating rela %zu with sym idx %u where %p -> %p\\n\",\n\t\t\t\t\t\ti, symidx, (void*) entry, (void*) final);\n\t\t\t\telf_print_sym(sym);\n\t\t\t}\n\t\t\t*(char**) entry = (char*) final;\n\t\t}\n\t}\n}\n\nvoid Elf::perform_relocations(Elf64_Ehdr* hdr)\n{\n\telf_relocate_section(hdr, \".rela.plt\");\n\telf_relocate_section(hdr, \".rela.dyn\");\n}\n"
  },
  {
    "path": "src/kernel/elf.hpp",
    "content": "#pragma once\n#include <elf.h>\n\nstruct Elf\n{\n\tstatic bool validate(Elf64_Ehdr* hdr);\n\tstatic const Elf64_Shdr* section_by_name(const Elf64_Ehdr*, const char* name);\n\n\t// get the symbol associated with @name\n\tstatic const Elf64_Sym* resolve_name(const Elf64_Ehdr*, const char* name);\n\t// dynamic loader\n\tstatic void perform_relocations(Elf64_Ehdr* hdr);\n};\n"
  },
  {
    "path": "src/kernel/init_libc.c",
    "content": "#include <assert.h>\n#include <kprint.h>\n#include <stddef.h>\n#include <stdint.h>\n#include <multiboot.h>\n\nextern size_t strlen(const char* str);\nextern char _end;\n\nstatic void* multiboot_free_begin(intptr_t mb_addr)\n{\n\tconst multiboot_info_t* info = (multiboot_info_t*) mb_addr;\n\tuintptr_t end = (uintptr_t) &_end;\n\n\tif (info->flags & MULTIBOOT_INFO_CMDLINE) {\n\t\tconst char* cmdline = (char*) (intptr_t) info->cmdline;\n\t\tconst uintptr_t pot_end = info->cmdline + strlen(cmdline);\n\t\tif (pot_end > end) end = pot_end;\n\t}\n\n\tconst multiboot_module_t* mod = (multiboot_module_t*) (intptr_t) info->mods_addr;\n\tconst multiboot_module_t* mods_end = mod + info->mods_count;\n\n\tfor (; mod < mods_end; mod++)\n\t{\n\t\tif (mod->mod_end > end) end = mod->mod_end;\n\t}\n\treturn (void*) end;\n}\n\nvoid __init_stdlib(uint32_t mb_magic, uint32_t mb_addr)\n{\n\tassert(mb_magic == 0x2badb002);\n\n\t// 1. enable printf facilities\n\tinit_printf(NULL, __serial_putchr);\n\n\t// 2. find end of multiboot areas\n\tvoid* free_begin = multiboot_free_begin(mb_addr);\n\tassert(free_begin >= (void*) &_end);\n\n\t// 3. initialize heap (malloc, etc.)\n\textern void __init_heap(void*);\n\t__init_heap(free_begin);\n\n#ifdef EH_ENABLED\n\t/// 4. initialize exceptions before we run constructors\n\textern char __eh_frame_start[];\n\textern void __register_frame(void*);\n\t__register_frame(&__eh_frame_start);\n#endif\n\n\t// 5. call global C/C++ constructors\n\textern void(*__init_array_start [])();\n\textern void(*__init_array_end [])();\n\tint count = __init_array_end - __init_array_start;\n\tfor (int i = 0; i < count; i++) {\n\t\t__init_array_start[i]();\n\t}\n}\n"
  },
  {
    "path": "src/kernel/kernel_start.c",
    "content": "#include <assert.h>\n#include <stdint.h>\n#include <kprint.h>\n\nstatic void __init_paging()\n{\n\tstatic uintptr_t pdir0[512] __attribute__((aligned(4096)));\n\t// unmap zero page\n\tassert(((uintptr_t) pdir0 & 0xfff) == 0);\n\tpdir0[0] = 0x0; // unpresent zero page\n\tfor (uintptr_t i = 1; i < 512; i++) {\n\t\tpdir0[i] = (i * 0x1000) | 0x3; // RW + P\n\t}\n\t// install into PML2 entry 0\n\tuintptr_t* pml2 = (uintptr_t*) 0x3000;\n\tpml2[0] = ((uintptr_t) pdir0) | 0x3; // RW + P;\n\t__asm__ (\"invlpg 0x0\");\n}\n\nvoid kernel_start(uint32_t eax, uint32_t ebx)\n{\n\tkprintf(\"kernel_start(eax: %x, ebx: %x)\\n\", eax, ebx);\n\n\textern void __init_stdlib(uint32_t, uint32_t);\n\t__init_stdlib(eax, ebx);\n\n\t// we have to do this after initializing .bss\n\t// NOTE: don't enable this until you catch CPU exceptions!\n\t//__init_paging();\n\n\textern void kernel_main(uint32_t, uint32_t);\n\tkernel_main(eax, ebx);\n}\n"
  },
  {
    "path": "src/kernel/panic.cpp",
    "content": "#include <kprint.h>\n#include <cstdio>\n// less risky when the stack is blown out\nstatic char buffer[4096];\n\n#define frp(N, ra)                                 \\\n  (__builtin_frame_address(N) != nullptr) &&       \\\n  (ra = __builtin_return_address(N)) != nullptr && ra != (void*)-1\n\nstatic void print_trace(const int N, const void* ra)\n{\n  snprintf(buffer, sizeof(buffer),\n          \"[%d] %p\\n\",\n          N, ra);\n  kprint(buffer);\n}\n\nextern \"C\"\nvoid print_backtrace()\n{\n  kprintf(\"\\nBacktrace:\\n\");\n  void* ra;\n  if (frp(0, ra)) {\n    print_trace(0, ra);\n    if (frp(1, ra)) {\n      print_trace(1, ra);\n      if (frp(2, ra)) {\n        print_trace(2, ra);\n      }\n    }\n  }\n}\n\nextern \"C\"\n__attribute__((noreturn))\nvoid panic(const char* reason)\n{\n\tkprintf(\"\\n\\n!!! PANIC !!!\\n%s\\n\", reason);\n\n\tprint_backtrace();\n\n\t// the end\n\tkprintf(\"\\nKernel halting...\\n\");\n\twhile (1) asm(\"cli; hlt\");\n\t__builtin_unreachable();\n}\n\nextern \"C\"\nvoid abort()\n{\n\tpanic(\"Abort called\");\n}\n\nextern \"C\"\nvoid abort_message(const char* fmt, ...)\n{\n\tva_list arg;\n\tva_start (arg, fmt);\n\tint bytes = tfp_vsnprintf(buffer, sizeof(buffer), fmt, arg);\n\t(void) bytes;\n\tva_end (arg);\n\tpanic(buffer);\n}\n"
  },
  {
    "path": "src/kernel/start.asm",
    "content": ";; stack base address at EBDA border\n;; NOTE: Multiboot can use 9d400 to 9ffff\n%define  STACK_LOCATION     0x9D400\n\n;; multiboot magic\n%define  MB_MAGIC   0x1BADB002\n%define  MB_FLAGS   0x3  ;; ALIGN + MEMINFO\n\nextern _MULTIBOOT_START_\nextern _LOAD_START_\nextern _LOAD_END_\nextern _end\nextern begin_enter_longmode\nextern kernel_start\n\nALIGN 4\nsection .multiboot\n  dd  MB_MAGIC\n  dd  MB_FLAGS\n  dd  -(MB_MAGIC + MB_FLAGS)\n  dd _MULTIBOOT_START_\n  dd _LOAD_START_\n  dd _LOAD_END_\n  dd _end\n  dd _start\n\nsection .data\nmultiboot_data_magic:     dq 0\nmultiboot_data_address:   dq 0\nglobal multiboot_data_magic\nglobal multiboot_data_address\n\n[BITS 32]\nsection .text\nglobal _start ;; make _start a global symbol\n_start:\n    cli\n\n    ;; load simple GDT\n    lgdt [gdtr]\n\n    ;; activate new GDT\n    jmp 0x8:rock_bottom ;; code seg\nrock_bottom:\n    mov cx, 0x10 ;; data seg\n    mov ss, cx\n    mov ds, cx\n    mov es, cx\n    mov fs, cx\n    mov cx, 0x18 ;; GS seg\n    mov gs, cx\n\n    ;; 32-bit stack ptr\n    mov esp, STACK_LOCATION\n    mov ebp, esp\n\n    ;; store multiboot params\n\tmov DWORD [multiboot_data_magic],    eax\n\tmov DWORD [multiboot_data_address],  ebx\n\n    ;;ASM_PRINT(strings.phase1)\n    call enable_cpu_feat\n\n\t;; zero .bss (used to be in the C portion, but easier to do here)\n\textern _BSS_START_\n\textern _BSS_END_\n\tmov edi, _BSS_START_\n\tmov ecx, _BSS_END_\n\tsub ecx, _BSS_START_\n\tmov eax, 0\n\trep stosb\n\n\t;; Enable stack protector:\n    ;; GS is located at 0x1000\n    ;; Linux uses GS:0x14 to access stack protector value\n    ;; Copy RDTSC.EAX to this location as preliminary value\n    rdtsc\n    mov DWORD [0x1014], eax\n\n    ;; for 32-bit kernels just call kernel_start here\n    call begin_enter_longmode\n    ;; stop\n    cli\n    hlt\n\nenable_cpu_feat:\n    ;; enable SSE (pretty much always exists)\n    mov eax, cr0\n    and ax, 0xFFFB  ;clear coprocessor emulation CR0.EM\n    or  ax, 0x2     ;set coprocessor monitoring  CR0.MP\n    mov cr0, eax\n    mov eax, cr4\n    or ax, 3 << 9   ;set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time\n    or ax, 0x20     ;enable native FPU exception handling\n    mov cr4, eax\n    ;; read out CPU features\n    mov eax, 1\n    xor ecx, ecx\n    cpuid\n    mov edx, ecx\n    ;; check for XSAVE support (bit 26)\n    and ecx, 0x04000000\n    jz xsave_not_supported\n    ;; enable XSAVE\n    mov eax, cr4\n    or  eax, 0x40000\n    mov cr4, eax\n    ;; check for AVX support (bit 28)\n    and edx, 0x10000000\n    jz xsave_not_supported\n    ;; enable AVX\n    xor ecx, ecx\n    xgetbv\n    or eax, 0x7\n    xsetbv\nxsave_not_supported:\n    ret\n\n\nALIGN 32\ngdtr:\n  dw gdt32_end - gdt32 - 1\n  dd gdt32\nALIGN 32\ngdt32:\n  ;; Entry 0x0: Null descriptor\n  dq 0x0\n  ;; Entry 0x8: Code segment\n  dw 0xffff          ;Limit\n  dw 0x0000          ;Base 15:00\n  db 0x00            ;Base 23:16\n  dw 0xcf9a          ;Flags / Limit / Type [F,L,F,Type]\n  db 0x00            ;Base 32:24\n  ;; Entry 0x10: Data segment\n  dw 0xffff          ;Limit\n  dw 0x0000          ;Base 15:00\n  db 0x00            ;Base 23:16\n  dw 0xcf92          ;Flags / Limit / Type [F,L,F,Type]\n  db 0x00            ;Base 32:24\n  ;; Entry 0x18: GS Data segment\n  dw 0x0100          ;Limit\n  dw 0x1000          ;Base 15:00\n  db 0x00            ;Base 23:16\n  dw 0x4092          ;Flags / Limit / Type [F,L,F,Type]\n  db 0x00            ;Base 32:24\ngdt32_end:\n"
  },
  {
    "path": "src/kernel/start32.c",
    "content": "#include <stdint.h>\n\nextern uint32_t multiboot_data_magic;\nextern uint32_t multiboot_data_address;\nextern void kernel_start(uint32_t eax, uint32_t ebx);\n\nvoid begin_enter_longmode()\n{\n\tkernel_start(multiboot_data_magic, multiboot_data_address);\n}\n"
  },
  {
    "path": "src/kernel/start64.asm",
    "content": "[BITS 32]\nglobal begin_enter_longmode:function\nextern __serial_print1\nextern kernel_start\n\n%define STACK_LOCATION          0x9D000\n%define P4_TAB                  0x1000 ;; one page\n%define P3_TAB                  0x2000 ;; one page\n%define P2_TAB                  0x3000 ;; many pages\n\n%define NUM_P3_ENTRIES     4\n%define NUM_P2_ENTRIES     (NUM_P3_ENTRIES * 512)\n\n%define IA32_EFER               0xC0000080\n%define IA32_STAR               0xC0000081\n%define IA32_LSTAR              0xc0000082\n%define IA32_FMASK              0xc0000084\n%define IA32_FS_BASE            0xc0000100\n%define IA32_GS_BASE            0xc0000101\n%define IA32_KERNEL_GS_BASE     0xc0000102\n\n;; CR0 paging enable bit\n%define PAGING_ENABLE 0x80000000\n;; CR0 Supervisor write-protect enable\n%define SUPER_WP_ENABLE 0x10000\n\n;; EFER Longmode bit\n%define LONGMODE_ENABLE 0x100\n;; EFER Execute Disable bit\n%define NX_ENABLE 0x800\n;; EFER Syscall enable bit\n%define SYSCALL_ENABLE 0x1\n\nextern multiboot_data_magic\nextern multiboot_data_address\n\nSECTION .text\nbegin_enter_longmode:\n    ;; disable old paging\n    mov eax, cr0\n    and eax, 0x7fffffff  ;; clear PG (bit 31)\n    mov cr0, eax\n\n    ;; address for Page Map Level 4\n    mov edi, P4_TAB\n    mov cr3, edi\n    ;; clear out P4 and P3\n    mov ecx, 0x2000 / 0x4\n    xor eax, eax       ; Nullify the A-register.\n    rep stosd\n\n    ;; create page map entry\n    mov edi, P4_TAB\n    mov DWORD [edi], P3_TAB | 0x3 ;; present+write\n\n    ;; create 1GB mappings\n    mov ecx, NUM_P3_ENTRIES\n    mov edi, P3_TAB\n    mov eax, P2_TAB | 0x3 ;; present + write\n    mov ebx, 0x0\n\n.p3_loop:\n    mov DWORD [edi],   eax   ;; Low word\n    mov DWORD [edi+4], ebx   ;; High word\n    add eax, 1 << 12         ;; page increments\n    adc ebx, 0               ;; increment high word when CF set\n    add edi, 8\n    loop .p3_loop\n\n    ;; create 2MB mappings\n    mov ecx, NUM_P2_ENTRIES\n    mov edi, P2_TAB\n    mov eax, 0x0 | 0x3 | 1 << 7 ;; present + write + huge\n    mov ebx, 0x0\n\n.p2_loop:\n    mov DWORD [edi],   eax   ;; Low word\n    mov DWORD [edi+4], ebx   ;; High word\n    add eax, 1 << 21         ;; 2MB increments\n    adc ebx, 0               ;; increment high word when CF set\n    add edi, 8\n    loop .p2_loop\n\n    ;; enable PAE\n    mov eax, cr4\n    or  eax, 1 << 5\n    mov cr4, eax\n\n    ;; enable long mode\n    mov ecx, IA32_EFER\n    rdmsr\n    or  eax, (LONGMODE_ENABLE | NX_ENABLE | SYSCALL_ENABLE)\n    wrmsr\n\n    ;; enable paging\n    mov eax, cr0                 ; Set the A-register to control register 0.\n    or  eax, (PAGING_ENABLE | SUPER_WP_ENABLE)\n    mov cr0, eax                 ; Set control register 0 to the A-register.\n\n    ;; load 64-bit GDT\n    lgdt [__gdt64_base_pointer]\n    jmp  GDT64.Code:long_mode\n\n\n[BITS 64]\nlong_mode:\n    cli\n\n    ;; segment regs\n    mov cx, GDT64.Data\n    mov ds, cx\n    mov es, cx\n    mov fs, cx\n    mov gs, cx\n    mov ss, cx\n\n    ;; set up new stack for 64-bit\n    push rsp\n    mov  rsp, STACK_LOCATION\n    push 0\n    push 0\n    mov  rbp, rsp\n\n    mov ecx, IA32_STAR\n    mov edx, 0x8\n    mov eax, 0x0\n    wrmsr\n\n    ;; Enable stack protector:\n    ;; On amd64 FS should point to tls-table for cpu0\n    ;; Linux uses FS:0x28 to access stack protector value\n    extern tls\n    mov ecx, IA32_FS_BASE\n    mov edx, 0x0\n    mov eax, tls\n    wrmsr\n    ;; Set \"random\" stack protector value\n    extern __SSP__\n    rdtsc\n    mov rcx, __SSP__\n    xor rax, rcx\n    ;; Install in TLS table\n    mov QWORD [tls+0x28], rax\n\n    ;; geronimo!\n    mov  edi, DWORD[multiboot_data_magic]\n    mov  esi, DWORD[multiboot_data_address]\n    call kernel_start\n    ;; warning that we returned from kernel_start\n    mov rdi, strings.panic\n    call __serial_print1\n    cli\n    hlt\n\nSECTION .data\nstrings:\n  .panic: db `Returned from kernel_start! Halting...\\n`,0x0\nGDT64:\n  .Null: equ $ - GDT64         ; The null descriptor.\n    dq 0\n  .Code: equ $ - GDT64         ; The code descriptor.\n    dw 0                         ; Limit (low).\n    dw 0                         ; Base (low).\n    db 0                         ; Base (middle)\n    db 10011010b                 ; Access (exec/read).\n    db 00100000b                 ; Granularity.\n    db 0                         ; Base (high).\n  .Data: equ $ - GDT64         ; The data descriptor.\n    dw 0                         ; Limit (low).\n    dw 0                         ; Base (low).\n    db 0                         ; Base (middle)\n    db 10010010b                 ; Access (read/write).\n    db 00000000b                 ; Granularity.\n    db 0                         ; Base (high).\n  .Task: equ $ - GDT64         ; TSS descriptor.\n    dq 0\n    dq 0\n\n    dw 0x0 ;; alignment padding\n__gdt64_base_pointer:\n    dw $ - GDT64 - 1             ; Limit.\n    dq GDT64                     ; Base.\n"
  },
  {
    "path": "src/kernel/tls.cpp",
    "content": "#include <cstddef>\n#include \"tls.hpp\"\n#ifdef __x86_64__\nstatic_assert(offsetof(tls_table, guard) == 0x28, \"TLS is at 0x28 on amd64\");\n#elif __i386__\nstatic_assert(offsetof(tls_table, guard) == 0x14, \"TLS is at 0x18 on i386\");\n#endif\n\n// we have to store this in .data otherwise .bss initialization\n// will overwrite this in stdlib init\nstruct tls_table tls;\n"
  },
  {
    "path": "src/kernel/tls.hpp",
    "content": "#pragma once\n#include <cstdint>\n\nstruct tls_table\n{\n  // thread self-pointer\n  void* tls_data; // 0x0\n  // per-cpu cpuid\n  int cpuid;\n\n  uintptr_t pad[3];\n  uintptr_t guard; // _SENTINEL_VALUE_\n};\nextern tls_table tls;\n"
  },
  {
    "path": "src/kprint.h",
    "content": "#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <hw/serial.h>\n\n// better, more familiar way to print!\nextern int kprintf(const char* fmt, ...) __attribute__((format(printf, 1, 2)));\n\n// print text directly to serial port\nstatic inline void kprint(const char* text)\n{\n  __serial_print1(text);\n}\n\n#include <tinyprintf.h>\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "src/linker.ld",
    "content": "/**\n * Inspired from the IncludeOS unikernel (https://github.com/hioa-cs/IncludeOS)\n *\n * This is the smallest possible linker script that supports\n * normal C and C++ operation, including global constructors,\n * exceptions and linker GC sections option.\n**/\nENTRY(_start)\n\nSECTIONS\n{\n  PROVIDE(_ELF_START_ = . + 0x100000);\n  PROVIDE(_LOAD_START_ = _ELF_START_);\n  . = _ELF_START_;\n\n  .multiboot (_ELF_START_ ): {\n      PROVIDE(_MULTIBOOT_START_ = .);\n      KEEP(*(.multiboot))\n   }\n\n  .text ALIGN(0x10) :\n  {\n    _TEXT_START_ = .;\n    *(.text*)\n    *(.gnu.linkonce.t*)\n    _TEXT_END_ = .;\n  }\n\n  .rodata :\n  {\n    _RODATA_START_ = .;\n    *(.rodata*)\n    *(.gnu.linkonce.r*)\n    _RODATA_END_ = .;\n  }\n\n  .init_array :\n  {\n    PROVIDE_HIDDEN (__init_array_start = .);\n    KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))\n    KEEP (*(.init_array .ctors))\n    PROVIDE_HIDDEN (__init_array_end = .);\n  }\n  /*.fini_array :\n  {\n    PROVIDE_HIDDEN (__fini_array_start = .);\n    KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))\n    KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))\n    PROVIDE_HIDDEN (__fini_array_end = .);\n  }*/\n\n  /* For stack unwinding (exception handling)  */\n  .eh_frame_hdr ALIGN(0x8):\n  {\n    KEEP(*(.eh_frame_hdr*))\n  }\n  .eh_frame ALIGN(0x8):\n  {\n  \tPROVIDE (__eh_frame_start = .);\n    KEEP(*(.eh_frame))\n    LONG (0);\n  }\n  .gcc_except_table :\n  {\n    *(.gcc_except_table)\n  }\n\n  .data :\n  {\n    _DATA_START_ = .;\n    *(.data*)\n    *(.gnu.linkonce.d*)\n    _DATA_END_ = .;\n  }\n\n  PROVIDE(_LOAD_END_ = .);\n\n  .bss :\n  {\n    _BSS_START_ = .;\n    *(.bss .bss.* .gnu.linkonce.b.*)\n    *(COMMON)\n    _BSS_END_ = .;\n  }\n\n  . = ALIGN(0x10);\n  _end = .;\n}\n"
  },
  {
    "path": "src/main.h",
    "content": "#pragma once\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nextern void kernel_main(uint32_t eax, uint32_t ebx);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "src/multiboot.h",
    "content": "/* Copyright (C) 1999,2003,2007,2008,2009,2010  Free Software Foundation, Inc.\n *\n *  Permission is hereby granted, free of charge, to any person obtaining a copy\n *  of this software and associated documentation files (the \"Software\"), to\n *  deal in the Software without restriction, including without limitation the\n *  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n *  sell copies of the Software, and to permit persons to whom the Software is\n *  furnished to do so, subject to the following conditions:\n *\n *  The above copyright notice and this permission notice shall be included in\n *  all copies or substantial portions of the Software.\n *\n *  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL ANY\n *  DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\n *  IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#ifndef MULTIBOOT_HEADER\n#define MULTIBOOT_HEADER 1\n\n/* How many bytes from the start of the file we search for the header. */\n#define MULTIBOOT_SEARCH                        8192\n#define MULTIBOOT_HEADER_ALIGN                  4\n\n/* The magic field should contain this. */\n#define MULTIBOOT_HEADER_MAGIC                  0x1BADB002\n\n/* This should be in %eax. */\n#define MULTIBOOT_BOOTLOADER_MAGIC              0x2BADB002\n\n/* Alignment of multiboot modules. */\n#define MULTIBOOT_MOD_ALIGN                     0x00001000\n\n/* Alignment of the multiboot info structure. */\n#define MULTIBOOT_INFO_ALIGN                    0x00000004\n\n/* Flags set in the 'flags' member of the multiboot header. */\n\n/* Align all boot modules on i386 page (4KB) boundaries. */\n#define MULTIBOOT_PAGE_ALIGN                    0x00000001\n\n/* Must pass memory information to OS. */\n#define MULTIBOOT_MEMORY_INFO                   0x00000002\n\n/* Must pass video information to OS. */\n#define MULTIBOOT_VIDEO_MODE                    0x00000004\n\n/* This flag indicates the use of the address fields in the header. */\n#define MULTIBOOT_AOUT_KLUDGE                   0x00010000\n\n/* Flags to be set in the 'flags' member of the multiboot info structure. */\n\n/* is there basic lower/upper memory information? */\n#define MULTIBOOT_INFO_MEMORY                   0x00000001\n/* is there a boot device set? */\n#define MULTIBOOT_INFO_BOOTDEV                  0x00000002\n/* is the command-line defined? */\n#define MULTIBOOT_INFO_CMDLINE                  0x00000004\n/* are there modules to do something with? */\n#define MULTIBOOT_INFO_MODS                     0x00000008\n\n/* These next two are mutually exclusive */\n\n/* is there a symbol table loaded? */\n#define MULTIBOOT_INFO_AOUT_SYMS                0x00000010\n/* is there an ELF section header table? */\n#define MULTIBOOT_INFO_ELF_SHDR                 0X00000020\n\n/* is there a full memory map? */\n#define MULTIBOOT_INFO_MEM_MAP                  0x00000040\n\n/* Is there drive info? */\n#define MULTIBOOT_INFO_DRIVE_INFO               0x00000080\n\n/* Is there a config table? */\n#define MULTIBOOT_INFO_CONFIG_TABLE             0x00000100\n\n/* Is there a boot loader name? */\n#define MULTIBOOT_INFO_BOOT_LOADER_NAME         0x00000200\n\n/* Is there a APM table? */\n#define MULTIBOOT_INFO_APM_TABLE                0x00000400\n\n/* Is there video information? */\n#define MULTIBOOT_INFO_VBE_INFO                 0x00000800\n#define MULTIBOOT_INFO_FRAMEBUFFER_INFO         0x00001000\n\n#ifndef ASM_FILE\n\ntypedef unsigned char           multiboot_uint8_t;\ntypedef unsigned short          multiboot_uint16_t;\ntypedef unsigned int            multiboot_uint32_t;\ntypedef unsigned long long      multiboot_uint64_t;\n\nstruct multiboot_header\n{\n  /* Must be MULTIBOOT_MAGIC - see above. */\n  multiboot_uint32_t magic;\n\n  /* Feature flags. */\n  multiboot_uint32_t flags;\n\n  /* The above fields plus this one must equal 0 mod 2^32. */\n  multiboot_uint32_t checksum;\n\n  /* These are only valid if MULTIBOOT_AOUT_KLUDGE is set. */\n  multiboot_uint32_t header_addr;\n  multiboot_uint32_t load_addr;\n  multiboot_uint32_t load_end_addr;\n  multiboot_uint32_t bss_end_addr;\n  multiboot_uint32_t entry_addr;\n\n  /* These are only valid if MULTIBOOT_VIDEO_MODE is set. */\n  multiboot_uint32_t mode_type;\n  multiboot_uint32_t width;\n  multiboot_uint32_t height;\n  multiboot_uint32_t depth;\n};\n\n/* The symbol table for a.out. */\nstruct multiboot_aout_symbol_table\n{\n  multiboot_uint32_t tabsize;\n  multiboot_uint32_t strsize;\n  multiboot_uint32_t addr;\n  multiboot_uint32_t reserved;\n};\ntypedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t;\n\n/* The section header table for ELF. */\nstruct multiboot_elf_section_header_table\n{\n  multiboot_uint32_t num;\n  multiboot_uint32_t size;\n  multiboot_uint32_t addr;\n  multiboot_uint32_t shndx;\n};\ntypedef struct multiboot_elf_section_header_table multiboot_elf_section_header_table_t;\n\nstruct multiboot_info\n{\n  /* Multiboot info version number */\n  multiboot_uint32_t flags;\n\n  /* Available memory from BIOS */\n  multiboot_uint32_t mem_lower;\n  multiboot_uint32_t mem_upper;\n\n  /* \"root\" partition */\n  multiboot_uint32_t boot_device;\n\n  /* Kernel command line */\n  multiboot_uint32_t cmdline;\n\n  /* Boot-Module list */\n  multiboot_uint32_t mods_count;\n  multiboot_uint32_t mods_addr;\n\n  union\n  {\n    multiboot_aout_symbol_table_t aout_sym;\n    multiboot_elf_section_header_table_t elf_sec;\n  } u;\n\n  /* Memory Mapping buffer */\n  multiboot_uint32_t mmap_length;\n  multiboot_uint32_t mmap_addr;\n\n  /* Drive Info buffer */\n  multiboot_uint32_t drives_length;\n  multiboot_uint32_t drives_addr;\n\n  /* ROM configuration table */\n  multiboot_uint32_t config_table;\n\n  /* Boot Loader Name */\n  multiboot_uint32_t boot_loader_name;\n\n  /* APM table */\n  multiboot_uint32_t apm_table;\n\n  /* Video */\n  multiboot_uint32_t vbe_control_info;\n  multiboot_uint32_t vbe_mode_info;\n  multiboot_uint16_t vbe_mode;\n  multiboot_uint16_t vbe_interface_seg;\n  multiboot_uint16_t vbe_interface_off;\n  multiboot_uint16_t vbe_interface_len;\n\n  multiboot_uint64_t framebuffer_addr;\n  multiboot_uint32_t framebuffer_pitch;\n  multiboot_uint32_t framebuffer_width;\n  multiboot_uint32_t framebuffer_height;\n  multiboot_uint8_t framebuffer_bpp;\n#define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0\n#define MULTIBOOT_FRAMEBUFFER_TYPE_RGB     1\n#define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT     2\n  multiboot_uint8_t framebuffer_type;\n  union\n  {\n    struct\n    {\n      multiboot_uint32_t framebuffer_palette_addr;\n      multiboot_uint16_t framebuffer_palette_num_colors;\n    };\n    struct\n    {\n      multiboot_uint8_t framebuffer_red_field_position;\n      multiboot_uint8_t framebuffer_red_mask_size;\n      multiboot_uint8_t framebuffer_green_field_position;\n      multiboot_uint8_t framebuffer_green_mask_size;\n      multiboot_uint8_t framebuffer_blue_field_position;\n      multiboot_uint8_t framebuffer_blue_mask_size;\n    };\n  };\n};\ntypedef struct multiboot_info multiboot_info_t;\n\nstruct multiboot_color\n{\n  multiboot_uint8_t red;\n  multiboot_uint8_t green;\n  multiboot_uint8_t blue;\n};\n\nstruct multiboot_mmap_entry\n{\n  multiboot_uint32_t size;\n  multiboot_uint64_t addr;\n  multiboot_uint64_t len;\n#define MULTIBOOT_MEMORY_AVAILABLE              1\n#define MULTIBOOT_MEMORY_RESERVED               2\n#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE       3\n#define MULTIBOOT_MEMORY_NVS                    4\n#define MULTIBOOT_MEMORY_BADRAM                 5\n  multiboot_uint32_t type;\n} __attribute__((packed));\ntypedef struct multiboot_mmap_entry multiboot_memory_map_t;\n\nstruct multiboot_mod_list\n{\n  /* the memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive */\n  multiboot_uint32_t mod_start;\n  multiboot_uint32_t mod_end;\n\n  /* Module command line */\n  multiboot_uint32_t cmdline;\n\n  /* padding to take it to 16 bytes (must be zero) */\n  multiboot_uint32_t pad;\n};\ntypedef struct multiboot_mod_list multiboot_module_t;\n\n/* APM BIOS info. */\nstruct multiboot_apm_info\n{\n  multiboot_uint16_t version;\n  multiboot_uint16_t cseg;\n  multiboot_uint32_t offset;\n  multiboot_uint16_t cseg_16;\n  multiboot_uint16_t dseg;\n  multiboot_uint16_t flags;\n  multiboot_uint16_t cseg_len;\n  multiboot_uint16_t cseg_16_len;\n  multiboot_uint16_t dseg_len;\n};\n\n#endif /* ! ASM_FILE */\n\n#endif /* ! MULTIBOOT_HEADER */\n"
  },
  {
    "path": "tools/Makefile",
    "content": "#\n# This is the old Makefile for the project, before moving to CMake\n# if CMake is not for you, then this may still be useful for you to build\n# a single kernel from the source tree. Just keep in mind you need to add\n# your own main file, and that the source tree is outdated. Good luck.\n#\n\n# kernel binary\nOUT = mykernel\n# .c files (add your own!)\nC_FILES = src/kernel/kernel_start.c \\\n\t\t\t\t\tsrc/hw/serial1.c \\\n\t\t\t\t\tsrc/crt/c_abi.c src/crt/heap.c src/crt/malloc.c \\\n\t\t\t\t\tsrc/crt/ubsan.c \\\n\t\t\t\t  src/prnt/print.c src/prnt/mini-printf.c\n# .cpp files\nCPP_FILES=src/main.cpp src/crt/cxxabi.cpp \\\n\t\t\t\t\tsrc/kernel/tls.cpp src/kernel/panic.cpp\n# .asm files for NASM\nASM_FILES=src/kernel/start.asm src/kernel/start64.asm\n# includes\nINCLUDE=-Isrc\n# EASTL C++ library\nINCLUDE +=-Iext/EASTL/include -Iext/EASTL/test/packages/EABase/include/Common\nCPP_FILES += ext/EASTL/source/allocator_eastl.cpp ext/EASTL/source/assert.cpp \\\n\t\t\t\t\t\t\text/EASTL/source/fixed_pool.cpp ext/EASTL/source/hashtable.cpp \\\n\t\t\t\t\t\t\text/EASTL/source/intrusive_list.cpp ext/EASTL/source/numeric_limits.cpp \\\n\t\t\t\t\t\t\text/EASTL/source/red_black_tree.cpp ext/EASTL/source/string.cpp\n\nGDEFS =\nLIBS  =\nOPTIMIZE = -Ofast -mfpmath=sse -msse3 #-march=native\n\n## to enable LLVM / ThinLTO use these ##\n#LD=ld.lld             # path to your LLD binary\n#LTO_DEFS=-flto=full   # full or thin\n\nOPTIONS=-m64 $(INCLUDE) $(GDEFS) $(OPTIMIZE) $(LTO_DEFS)\nWARNS=-Wall -Wextra -pedantic\nCOMMON=-ffreestanding -nostdlib -MMD -fstack-protector-strong -fno-omit-frame-pointer $(OPTIONS) $(WARNS)\n# inject some random numbers into a symbol so we can get some free random bits\nSSP=$(shell hexdump -n 8 -e '4/4 \"%08X\" 1 \"\\n\"' /dev/random)\nLDFLAGS=-static -nostdlib -N -melf_x86_64 --strip-all --script=src/linker.ld --defsym __SSP__=0x$(SSP)\nCFLAGS=-std=gnu11 $(COMMON)\nCXXFLAGS=-std=c++14 -fno-exceptions -fno-rtti $(COMMON)\nCFLAGS += $(SANITIZE)\nCXXFLAGS += $(SANITIZE) #-fno-sanitize=function\n\nCOBJ=$(C_FILES:.c=.o)\nCXXOBJ=$(CPP_FILES:.cpp=.o)\nASMOBJ=$(ASM_FILES:.asm=.o)\nDEPS=$(CXXOBJ:.o=.d) $(COBJ:.o=.d)\n\n.PHONY: clean all executable\n\n%.o: %.asm\n\tnasm -f elf64 -o $@ $<\n\n%.o: %.c\n\t$(CC) $(CFLAGS) -c $< -o $@\n\n%.o: %.cpp\n\t$(CXX) $(CXXFLAGS) -c $< -o $@\n\nall: $(COBJ) $(CXXOBJ) $(ASMOBJ)\n\t$(LD) $(LDFLAGS) $(COBJ) $(CXXOBJ) $(ASMOBJ) $(LIBS) -o $(OUT)\n\nsanitize:\n\tSANITIZE=\"-fsanitize=undefined -fno-sanitize=vptr\" $(MAKE) all\n\nchainloader: $(COBJ) $(CXXOBJ) $(ASMOBJ)\n\t$(LD) $(LDFLAGS) $(COBJ) $(CXXOBJ) $(ASMOBJ) $(LIBS) -o chainloader\n\nexecutable:\n\t$(info $(OUT))\n\t@true\n\nclean:\n\t$(RM) $(OUT) $(COBJ) $(CXXOBJ) $(ASMOBJ) $(DEPS)\n\n-include $(DEPS)\n"
  }
]