Repository: fwsGonzo/barebones
Branch: master
Commit: abb810ef9437
Files: 48
Total size: 78.8 KB
Directory structure:
gitextract_wcbe4rrg/
├── .gitignore
├── .gitmodules
├── README.md
├── barebones.cmake
├── build_iso.sh
├── ext/
│ └── CMakeLists.txt
├── grub/
│ └── grub.cfg
├── install_ubuntu_deps.sh
├── machines/
│ ├── 32bit/
│ │ ├── CMakeLists.txt
│ │ └── main.cpp
│ ├── default/
│ │ ├── CMakeLists.txt
│ │ └── main.cpp
│ ├── eastl/
│ │ ├── CMakeLists.txt
│ │ └── main.cpp
│ └── shared/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── library/
│ │ ├── CMakeLists.txt
│ │ ├── interface.hpp
│ │ └── library.cpp
│ └── main.cpp
├── run.sh
├── src/
│ ├── CMakeLists.txt
│ ├── crt/
│ │ ├── c_abi.c
│ │ ├── c_stubs.c
│ │ ├── cxxabi.cpp
│ │ ├── heap.c
│ │ ├── malloc.c
│ │ ├── print.c
│ │ ├── ubsan.c
│ │ └── udiv.c
│ ├── hw/
│ │ ├── serial.h
│ │ └── serial1.c
│ ├── kernel/
│ │ ├── dylib.hpp
│ │ ├── elf.cpp
│ │ ├── elf.hpp
│ │ ├── init_libc.c
│ │ ├── kernel_start.c
│ │ ├── panic.cpp
│ │ ├── start.asm
│ │ ├── start32.c
│ │ ├── start64.asm
│ │ ├── tls.cpp
│ │ └── tls.hpp
│ ├── kprint.h
│ ├── linker.ld
│ ├── main.h
│ └── multiboot.h
└── tools/
└── Makefile
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
*.o
*.d
chainloader
temp_disk
grub.iso
build/
================================================
FILE: .gitmodules
================================================
[submodule "ext/EASTL"]
path = ext/EASTL
url = https://github.com/electronicarts/EASTL.git
[submodule "ext/tinyprintf"]
path = ext/tinyprintf
url = git@github.com:fwsGonzo/tinyprintf.git
================================================
FILE: README.md
================================================
# Barebones multiboot kernel
- Intended for beginner OS development
- Run the dependency installation script (or install the equivalent packages)
- Build and boot a tiny test kernel in Qemu with `./run.sh default`.
- There is a 32-bit test kernel as well: `./run.sh 32bit`
- You can run any of the machines in the `machines` folder this way.
- 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.
- VGA textmode using ./run.sh --vga (NOTE: you must implement VGA support yourself! You will (if you're lucky) see a black screen.)
- Use ./build_iso.sh to run on real hardware or a hypervisor like VirtualBox, but keep serial port logging in mind!
## Features
- 600kb stack and working GDT (with room for IST task)
- Multiboot parameters passed to kernel start
- assert(), kprintf() and snprintf()
- Very basic heap implementation (malloc/free, new/delete)
- C and C++ global constructors, C++ RTTI and exceptions
- Stack protector support
- EASTL C++ support, which has many useful containers
- Produces tiny machine images
- Machine image is 6944 bytes with minimal build on GCC 9.1
- Machine image is 6376 bytes with minimal LTO build on Clang 8
- Remember to disable stack protector to shave a few extra bytes off!
- LTO and ThinLTO support if you are using clang
- Undefined-sanitizer support to catch some problems during runtime
- Option to unmap zero page (for the very common null-pointer access bugs)
- You have to enable this yourself, after CPU exception handling
## Running
Use `./run.sh <folder>` to build and run your kernel stored in machines folder.
- 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`.
- 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.
- Use argument --vga to get a graphical window, which starts out in VGA textmode accessible at 0xb8000.
- Use argument --sanitize to enable the undefined-sanitizer, catching problems like misaligned accesses and overflows.
## Changing build options
Go 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.
## Goal
The 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.
## Future work
- Add TLS api and support for most thread local variables
- Add support for LTO with GCC (no ideas on how to do this yet)
- Optional 512b bootloader to avoid GRUB (although not recommended)
## Validate output
./run.sh output should match:
```
------------------
* Multiboot EAX: 0x2badb002
* Multiboot EBX: 0x9500
* SSE instructions ... work!
* Global constructors ... work!
Hello OSdev world!
my_kernel (This is a test kernel!)
Press Ctrl+A -> X to close
Returned from kernel_start! Halting...
```
## Common issues
- File format not recognized
- You changed linker or ELF architecture without rebuilding all the files
- Nothing happens when I start the kernel
- 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)
- I can't use normal C/C++ library functions
- Make sure you are using something that exists in EASTL, or you will have to implement it yourself
- I can write to the zero page
- Yes you can. Welcome to (identity-mapped) kernel space. Set up your own pagetables!
- Do I really need a cross-compiler to work on this project?
- 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).
- When booting the GRUB image nothing happens, it looks to be crashing etc.
- Make sure you didn't compile with GCC and then link with LLD, and especially don't mix LTO into this.
- Minimal builds can be risky, especially -Oz on clang.
- There could be a genuine issue, so let me know.
- I'm having problems compiling or linking when using exceptions!
- 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
- I keep getting errors about not finding `bits/c++config.h` when building for 32-bit!
- You will need to install the C++ multilib package for your compiler, such as `g++-8-multilib`.
## Undefined sanitizer
- Works on GCC and Clang
- Easy to support, but has some caveats
- It's miles better if you can resolve symbol addresses to names
- Has not been extensively verified
## Link-Time Optimization
- Works on Clang out of the box, just make sure you use the correct LLD that comes with your compiler
- Both ThinLTO and full LTO works!
- 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`.
- Does not work on GCC at all due to how complicated it is to call the LTO-helpers manually
## Thread-Local Storage
- The basic foundation has been laid down
- Linker script needs to be amended to store .tbss and .tdata sections
- Functionality for creating new thread storage needs to be created
## EASTL, RTTI and exceptions
- To make use of EASTL you will need to enable the EASTL CMake option
- Tons of useful containers that work right out of the box
- To use RTTI and exceptions in C++ you will need to enable the RTTI_EXCEPTIONS option
- This option bloats the binary a great deal, due to the C++ ABI bundles, as well as the libgcc dependency
## Qemu defaults
Qemu provides you with some default devices to start with
- IDE for disk devices, but you can use PCI to get information
- e1000-compatible network card
- If you add -vga std you will get VGA graphics area at 0xb8000
- In graphics mode: PS/2 keyboard and mouse
As an example, use this function to clear the VGA textmode screen:
```
uint16_t* vga = (uint16_t*) 0xb8000;
for (int i = 0; i < 25*80; i++)
{
vga[i] = ' ' | (7 << 8);
}
```
What 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.
================================================
FILE: barebones.cmake
================================================
option(NATIVE "Compile natively for this CPU" OFF)
option(MINIMAL "Compile small executable" OFF)
option(EASTL "Compile with EASTL C++ library" OFF)
option(LTO_ENABLE "Enable LTO (Clang-only)" OFF)
option(STACK_PROTECTOR "Enable stack protector (SSP)" ON)
option(UBSAN "Enable the undefined sanitizer" OFF)
option(STRIPPED "Strip the executable" OFF)
option(DEBUG "Build and preserve debugging information" OFF)
option(RTTI_EXCEPTIONS "Enable C++ RTTI and exceptions" OFF)
option(BUILD_32 "Build a 32-bit kernel" OFF)
set(CPP_VERSION "c++17" CACHE STRING "C++ version compiler argument")
set(C_VERSION "gnu11" CACHE STRING "C version compiler argument")
set(LINKER_EXE "ld" CACHE STRING "Linker to use")
if (BUILD_32)
set(ELF_FORMAT "i386")
set(CMAKE_ASM_NASM_OBJECT_FORMAT "elf32")
set(OBJCOPY_TARGET "elf32-x86-32")
set(TARGET_TRIPLE "i686-pc-linux")
set(CAPABS "-m32")
else()
set(ELF_FORMAT "x86_64")
set(CMAKE_ASM_NASM_OBJECT_FORMAT "elf64")
set(OBJCOPY_TARGET "elf64-x86-64")
set(TARGET_TRIPLE "x86_64-pc-linux")
set(CAPABS "-m64 -fno-omit-frame-pointer -fPIE")
endif()
enable_language(ASM_NASM)
set(CAPABS "${CAPABS} -Wall -Wextra -g -ffreestanding")
# Optimization flags
set(OPTIMIZE "-mfpmath=sse -msse3")
if (NATIVE)
set(OPTIMIZE "${OPTIMIZE} -Ofast -march=native")
elseif (MINIMAL)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
set(OPTIMIZE "${OPTIMIZE} -Oz")
else()
set(OPTIMIZE "${OPTIMIZE} -Os -ffunction-sections -fdata-sections")
endif()
endif()
set(CMAKE_CXX_FLAGS "-MMD ${CAPABS} ${OPTIMIZE} -std=${CPP_VERSION}")
set(CMAKE_C_FLAGS "-MMD ${CAPABS} ${OPTIMIZE} -std=${C_VERSION}")
if (LTO_ENABLE)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto=thin")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -flto=thin")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -flto")
endif()
# BUG: workaround for LTO bug
set(KERNEL_LIBRARY --whole-archive kernel --no-whole-archive -gc-sections)
else()
set(KERNEL_LIBRARY kernel)
endif()
if (NOT RTTI_EXCEPTIONS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions -fno-rtti")
endif()
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -target ${TARGET_TRIPLE}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -target ${TARGET_TRIPLE}")
endif()
# Sanitizer options
if (UBSAN)
set(UBSAN_PARAMS "-fsanitize=undefined -fno-sanitize=vptr -DUBSAN_ENABLED")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${UBSAN_PARAMS}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${UBSAN_PARAMS}")
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-sanitize=function")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-sanitize=function")
endif()
endif()
if (STACK_PROTECTOR)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector-strong")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector-strong")
endif()
# linker stuff
set(CMAKE_LINKER ${LINKER_EXE})
set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS) # this removed -rdynamic from linker output
set(CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_LINKER> -o <TARGET> <LINK_FLAGS> <OBJECTS> <LINK_LIBRARIES>")
set(BBPATH ${CMAKE_CURRENT_LIST_DIR})
# linker arguments
string(RANDOM LENGTH 16 ALPHABET 0123456789abcdef SSP_VALUE)
set(LDSCRIPT "${BBPATH}/src/linker.ld")
set(LDFLAGS "-static -nostdlib -N -melf_${ELF_FORMAT} --script=${LDSCRIPT} --defsym __SSP__=0x${SSP_VALUE}")
if (NOT DEBUG AND STRIPPED)
set(LDFLAGS "${LDFLAGS} -s")
elseif (NOT DEBUG)
set(LDFLAGS "${LDFLAGS} -S")
endif()
if (MINIMAL)
set(LDFLAGS "${LDFLAGS} --gc-sections")
endif()
# Compiler, C and C++ libraries
include(ExternalProject)
ExternalProject_Add(exceptions
PREFIX exceptions
URL https://github.com/fwsGonzo/barebones/releases/download/exceptions/exceptions.zip
URL_HASH SHA1=8851485a7134eb8743069439235c1a2a9728ea58
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
UPDATE_COMMAND ""
INSTALL_COMMAND ""
)
# gcc-9 --print-file-name libgcc.a
if (BUILD_32)
execute_process(COMMAND ${CMAKE_C_COMPILER} -m32 --print-file-name libgcc_eh.a
OUTPUT_VARIABLE LIBGCC_ARCHIVE OUTPUT_STRIP_TRAILING_WHITESPACE)
else()
execute_process(COMMAND ${CMAKE_C_COMPILER} -m64 --print-file-name libgcc_eh.a
OUTPUT_VARIABLE LIBGCC_ARCHIVE OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
add_library(libgcc STATIC IMPORTED)
set_target_properties(libgcc PROPERTIES LINKER_LANGUAGE CXX)
if (EXISTS ${LIBGCC_ARCHIVE})
set_target_properties(libgcc PROPERTIES IMPORTED_LOCATION ${LIBGCC_ARCHIVE})
else()
# Use this if you don't have a compatible libgcc_eh.a in your system:
set_target_properties(libgcc PROPERTIES IMPORTED_LOCATION exceptions/src/exceptions/libgcc.a)
endif()
add_dependencies(libgcc exceptions)
if (RTTI_EXCEPTIONS)
add_library(cxxabi STATIC IMPORTED)
set_target_properties(cxxabi PROPERTIES LINKER_LANGUAGE CXX)
set_target_properties(cxxabi PROPERTIES IMPORTED_LOCATION exceptions/src/exceptions/libc++abi.a)
add_dependencies(cxxabi exceptions)
# Add --eh-frame-hdr for exception tables
set(LDFLAGS "${LDFLAGS} --eh-frame-hdr")
set(CXX_ABI_LIBS cxxabi)
endif()
# Machine image creation
function(add_machine_image NAME BINARY_NAME BINARY_DESC)
add_executable(${NAME} ${ARGN})
set_target_properties(${NAME} PROPERTIES OUTPUT_NAME ${BINARY_NAME})
target_include_directories(${NAME} PRIVATE src)
target_compile_definitions(${NAME} PRIVATE KERNEL_BINARY="${BINARY_NAME}")
target_compile_definitions(${NAME} PRIVATE KERNEL_DESC="${BINARY_DESC}")
add_subdirectory(${BBPATH}/ext ext)
if (EASTL)
target_link_libraries(${NAME} eastl)
endif()
add_subdirectory(${BBPATH}/src src)
target_link_libraries(${NAME}
# BUG: unfortunately, there is an LLD bug that prevents ASM objects
# from linking with outside files that are in archives, unless they are
# --whole-archived, which sort of defeats the purpose of LTO
${KERNEL_LIBRARY}
tinyprintf
${CXX_ABI_LIBS}
libgcc
kernel
)
set_target_properties(${NAME} PROPERTIES LINK_FLAGS "${LDFLAGS}")
# write out the binary name to a known file to simplify some scripts
file(WRITE ${CMAKE_BINARY_DIR}/binary.txt ${BINARY_NAME})
endfunction()
function(create_payload LIB_NAME BLOB_FILE BLOB_NAME)
# call objcopy with curated filename, and then
# create a static library LIB_NAME with payload
set(OBJECT_FILE ${BLOB_NAME}_generated.o)
add_custom_command(
OUTPUT ${OBJECT_FILE}
# temporarily name blob file as BLOB_NAME so that we can better control
# the symbols generated on the inside of the kernel
COMMAND cp ${BLOB_FILE} ${BLOB_NAME}
COMMAND ${CMAKE_OBJCOPY} -I binary -O ${OBJCOPY_TARGET} -B i386 ${BLOB_NAME} ${OBJECT_FILE}
COMMAND rm -f ${BLOB_NAME}
DEPENDS ${BLOB_FILE}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)
add_library(${LIB_NAME} STATIC ${OBJECT_FILE})
set_target_properties(${LIB_NAME} PROPERTIES LINKER_LANGUAGE C)
endfunction()
function (add_machine_payload TARGET LIB_NAME BLOB_FILE BLOB_NAME)
create_payload(${LIB_NAME} ${BLOB_FILE} ${BLOB_NAME})
target_link_libraries(${TARGET} --whole-archive mylib_payload --no-whole-archive)
endfunction()
================================================
FILE: build_iso.sh
================================================
#!/bin/bash
set -e
MACHINE=machines/${1-default}
BUILD_DIR=$MACHINE/build
mkdir -p $BUILD_DIR
pushd $BUILD_DIR
cmake ..
make -j4 $OPTION
popd
KERNEL=$BUILD_DIR/`cat $BUILD_DIR/binary.txt`
LOCAL_DISK=temp_disk
# create grub.iso
mkdir -p $LOCAL_DISK/boot/grub
cp $KERNEL $LOCAL_DISK/boot/mykernel
cp grub/grub.cfg $LOCAL_DISK/boot/grub
echo "=>"
grub-mkrescue -o grub.iso $LOCAL_DISK
echo "grub.iso constructed"
================================================
FILE: ext/CMakeLists.txt
================================================
# EASTL C++ library
if (EASTL)
add_library(eastl STATIC
EASTL/source/allocator_eastl.cpp EASTL/source/assert.cpp
EASTL/source/fixed_pool.cpp EASTL/source/hashtable.cpp
EASTL/source/intrusive_list.cpp EASTL/source/numeric_limits.cpp
EASTL/source/red_black_tree.cpp EASTL/source/string.cpp
)
target_include_directories(eastl PUBLIC
EASTL/include
EASTL/test/packages/EABase/include/Common
)
endif()
# Tiny-printf library
add_library(tinyprintf STATIC
tinyprintf/tinyprintf.c
)
target_include_directories(tinyprintf PUBLIC
tinyprintf
)
target_compile_definitions(tinyprintf PUBLIC TINYPRINTF_OVERRIDE_LIBC=0)
================================================
FILE: grub/grub.cfg
================================================
set timeout=0
set default=0 # Set the default menu entry
menuentry "My kernel" {
multiboot /boot/mykernel
boot
}
================================================
FILE: install_ubuntu_deps.sh
================================================
#!/bin/bash
git submodule update --init --recursive
sudo apt install build-essential g++-multilib nasm qemu xorriso cmake cmake-curses-gui
================================================
FILE: machines/32bit/CMakeLists.txt
================================================
cmake_minimum_required(VERSION 3.1)
project (kernel C CXX)
option(BUILD_32 "" ON)
include(../../barebones.cmake)
add_machine_image(
# name, binary and description
mykernel "my_kernel" "This is a 32-bit test kernel!"
# list of source files
main.cpp
)
target_compile_definitions(mykernel PRIVATE MYTEST="4")
================================================
FILE: machines/32bit/main.cpp
================================================
#include <main.h>
#include <assert.h>
#include <kprint.h>
#include <x86intrin.h>
static bool test_sse();
static int test_value = 0;
void kernel_main(const uint32_t eax, const uint32_t ebx)
{
kprint("------------------\n");
kprintf("* Multiboot EAX: 0x%x\n", eax);
kprintf("* Multiboot EBX: 0x%x\n", ebx);
// some helpful self-checks
kprint("* SSE instructions ... ");
kprint(test_sse() ? "work!\n" : "did NOT work!\n");
kprint("* Global constructors ... ");
kprint(test_value ? "work!\n" : "did NOT work!\n");
#ifdef UBSAN_ENABLED
// when undefined sanitizer is enabled, we can manually trigger it like this
uint64_t test = 0;
char* misaligned = (char*) &test + 2;
*(uint32_t*) misaligned = 0x12345678;
kprintf("kernel_main is at %p\n", kernel_main);
kprintf("misaligned is %p\n", (void*) test);
#endif
kprint(
"\n"
"Hello OSdev world!\n"
KERNEL_BINARY " (" KERNEL_DESC ")\n"
"\n"
"Press Ctrl+A -> X to close\n"
);
}
__attribute__((constructor))
static void my_cpp_constructor() {
test_value = 1;
}
bool test_sse()
{
typedef union
{
__m128i i128;
int32_t i32[4];
} imm;
volatile imm xmm1;
xmm1.i128 = _mm_set_epi32(1, 2, 3, 4);
return (
xmm1.i32[0] == 4 && xmm1.i32[1] == 3 &&
xmm1.i32[2] == 2 && xmm1.i32[3] == 1);
}
================================================
FILE: machines/default/CMakeLists.txt
================================================
cmake_minimum_required(VERSION 3.1)
project (kernel C CXX)
include(../../barebones.cmake)
add_machine_image(
# name, binary and description
mykernel "my_kernel" "This is a test kernel!"
# list of source files
main.cpp
)
target_compile_definitions(mykernel PRIVATE MYTEST="4")
================================================
FILE: machines/default/main.cpp
================================================
#include <main.h>
#include <assert.h>
#include <kprint.h>
#include <x86intrin.h>
static bool test_sse();
static int test_value = 0;
void kernel_main(const uint32_t eax, const uint32_t ebx)
{
kprint("------------------\n");
kprintf("* Multiboot EAX: 0x%x\n", eax);
kprintf("* Multiboot EBX: 0x%x\n", ebx);
// some helpful self-checks
kprint("* SSE instructions ... ");
kprint(test_sse() ? "work!\n" : "did NOT work!\n");
kprint("* Global constructors ... ");
kprint(test_value ? "work!\n" : "did NOT work!\n");
#ifdef UBSAN_ENABLED
// when undefined sanitizer is enabled, we can manually trigger it like this
uint64_t test = 0;
char* misaligned = (char*) &test + 2;
*(uint32_t*) misaligned = 0x12345678;
kprintf("kernel_main is at %p\n", kernel_main);
kprintf("misaligned is %p\n", (void*) test);
#endif
kprint(
"\n"
"Hello OSdev world!\n"
KERNEL_BINARY " (" KERNEL_DESC ")\n"
"\n"
"Press Ctrl+A -> X to close\n"
);
}
__attribute__((constructor))
static void my_cpp_constructor() {
test_value = 1;
}
bool test_sse()
{
typedef union
{
__m128i i128;
int32_t i32[4];
} imm;
volatile imm xmm1;
xmm1.i128 = _mm_set_epi32(1, 2, 3, 4);
return (
xmm1.i32[0] == 4 && xmm1.i32[1] == 3 &&
xmm1.i32[2] == 2 && xmm1.i32[3] == 1);
}
================================================
FILE: machines/eastl/CMakeLists.txt
================================================
cmake_minimum_required(VERSION 3.1)
project (kernel C CXX)
option(EASTL "" ON)
option(RTTI_EXCEPTIONS "" ON)
include(../../barebones.cmake)
add_machine_image(
# name, binary and description
mykernel "eastl_kernel" "This is a test for EASTL!"
# list of source files
main.cpp
)
================================================
FILE: machines/eastl/main.cpp
================================================
#include <main.h>
#include <assert.h>
#include <kprint.h>
#include <exception>
#include <EASTL/vector.h>
class IdioticException : public std::exception
{
const char* oh_god;
public:
IdioticException(const char* reason) : oh_god(reason) {}
const char* what() const noexcept override
{
return oh_god;
}
};
using callback_t = int (*) (eastl::vector<int>&);
static void test_rtti();
void kernel_main(uint32_t /*eax*/, uint32_t /*ebx*/)
{
kprintf(KERNEL_DESC "\n");
const callback_t callback = [] (auto& vec) -> int {
kprintf("EASTL vector size: %zu (last element is %d)\n",
vec.size(), vec.back());
throw IdioticException{"Test!"};
};
eastl::vector<int> test;
int caught = 0;
for (int i = 0; i < 16; i++)
{
test.push_back(i);
try {
assert(callback(test) == 0);
}
catch (const std::exception& e) {
kprintf("Exceptions caught: %d\n", ++caught);
}
}
assert(caught == 16);
test_rtti();
throw IdioticException("This is on purpose");
}
class A
{
public:
virtual void f() { kprintf("A::f() called\n"); }
};
class B : public A
{
public:
void f() { kprintf("B::f() called\n"); }
};
static void test_rtti()
{
A a;
B b;
a.f(); // A::f()
b.f(); // B::f()
A *pA = &a;
B *pB = &b;
pA->f(); // A::f()
pB->f(); // B::f()
pA = &b;
// pB = &a; // not allowed
pB = dynamic_cast<B*>(&a); // allowed but it returns NULL
assert(pB == nullptr);
}
================================================
FILE: machines/shared/CMakeLists.txt
================================================
cmake_minimum_required(VERSION 3.1)
project (kernel C CXX)
include(../../barebones.cmake)
add_machine_image(
# name, binary and description
mykernel "my_kernel" "This is a test kernel!"
# list of source files
main.cpp
)
#target_compile_definitions(mykernel PRIVATE MYTEST="4")
add_subdirectory(library)
add_machine_payload(mykernel mylib_payload
${CMAKE_BINARY_DIR}/library/libmylib.so mylib)
add_dependencies(mylib_payload mylib)
================================================
FILE: machines/shared/README.md
================================================
## Machine with shared library loader
This tiny machine allows you to call into a function in a shared library, and just enough to make it usable. No constructors or destructors.
## Questions
- Why is the init function using the C calling convention?
- It's to make it easier to look up the functions name, as it won't be mangled.
- Can you look up functions from the shared library?
- No, and that is ideally something done at loading time where functions are resolved automatically, which is something called dynamic linking.
================================================
FILE: machines/shared/library/CMakeLists.txt
================================================
cmake_minimum_required(VERSION 3.1)
project(mylib VERSION 1.0 DESCRIPTION "mylib description")
add_library(mylib SHARED
library.cpp
)
# unfortunately we have to do this to shake some of the arguments from the
# top level project
set(CMAKE_CXX_FLAGS "-Wall -Wextra -g -O2 -ffreestanding -fno-omit-frame-pointer")
target_compile_options(mylib PRIVATE "-nostdlib")
target_compile_options(mylib PRIVATE "-Os")
set_target_properties(mylib PROPERTIES VERSION ${PROJECT_VERSION})
set_target_properties(mylib PROPERTIES SOVERSION 1)
set_target_properties(mylib PROPERTIES PUBLIC_HEADER library.hpp)
target_include_directories(mylib PRIVATE .)
================================================
FILE: machines/shared/library/interface.hpp
================================================
#pragma once
#include <cstddef>
struct jumptable
{
int (*kprintf)(const char* string, ...);
void* (*malloc) (size_t);
void (*free) (void*);
};
typedef int (*init_function_t) (jumptable*);
================================================
FILE: machines/shared/library/library.cpp
================================================
#include "interface.hpp"
static int test_value = 666;
struct Test {
int a;
};
Test test;
extern "C"
__attribute__((noinline))
void test_function(jumptable* table)
{
table->kprintf("Hello from test()! jumptable = %p\n", table);
}
extern "C"
int init(jumptable* t)
{
jumptable* table = t;
table->kprintf("Hello world from a shared library! table = %p\n", table);
table->kprintf("test_value == %d\n", test_value);
char* data = (char*) table->malloc(4096);
table->kprintf("malloc() returned %p\n", data);
for (int i = 0; i < 4096; i++) {
data[i] = i & 0xFF;
}
table->free(data);
test.a = 55;
test_function(t);
return 0;
}
================================================
FILE: machines/shared/main.cpp
================================================
#include <main.h>
#include <kprint.h>
#include <cassert>
#include <cstring>
#include <cstdlib>
extern char _binary_mylib_start;
extern char _binary_mylib_end;
#include <kernel/dylib.hpp>
#include "library/interface.hpp" // a made-up interface
void kernel_main(const uint32_t, const uint32_t)
{
// load address is the start of the mylib blob
const auto* hdr = Dylib::load(&_binary_mylib_start);
assert(hdr != nullptr);
// translate init function name into symbol and create callable function
auto init = Dylib::resolve_function<init_function_t>(hdr, "init");
assert(init != nullptr);
// setup call table
static jumptable table;
table.kprintf = kprintf;
table.malloc = malloc;
table.free = free;
// call into library function
const int result = init(&table);
kprintf("init call result: %d\n", result);
}
================================================
FILE: run.sh
================================================
#!/bin/bash
set -e
GRAPHICS=-nographic
for i in "$@"
do
case $i in
--kvm)
KVM="--enable-kvm -cpu host"
shift # past argument with no value
;;
--vga)
GRAPHICS="-vga std"
shift # past argument with no value
;;
--sanitize)
OPTION="sanitize"
shift # past argument with no value
;;
--clean)
rm -rf $BUILD_DIR/
exit 0
;;
*)
# unknown option
echo "--kvm, --vga, --sanitize, --clean"
;;
esac
done
MACHINE=machines/${1-default}
BUILD_DIR=$MACHINE/build
pushd $MACHINE
mkdir -p build
pushd build
cmake ..
make -j4 $OPTION
BINARY=$BUILD_DIR/`cat binary.txt`
popd
popd
# NOTE: if building with -march=native, make sure to enable KVM,
# as emulated qemu only supports up to SSE3 instructions
CLASS=`od -An -t x1 -j 4 -N 1 $BINARY`
if [ $CLASS == "02" ]; then
echo "Starting 64-bit kernel: $BINARY"
qemu-system-x86_64 $KVM -kernel tools/chainloader -initrd $BINARY $GRAPHICS
else
echo "Starting 32-bit kernel: $BINARY"
qemu-system-x86_64 $KVM -kernel $BINARY $GRAPHICS
fi
================================================
FILE: src/CMakeLists.txt
================================================
set(KERNEL_SOURCES
# .c files
kernel/kernel_start.c
kernel/init_libc.c
hw/serial1.c
crt/c_abi.c
crt/c_stubs.c
crt/heap.c
crt/malloc.c
crt/print.c
crt/ubsan.c
# .cpp files
crt/cxxabi.cpp
kernel/elf.cpp
kernel/tls.cpp
kernel/panic.cpp
# .asm files (for NASM)
kernel/start.asm
)
if (BUILD_32)
list(APPEND KERNEL_SOURCES
kernel/start32.c
crt/udiv.c
)
else()
list(APPEND KERNEL_SOURCES
kernel/start64.asm
)
endif()
add_library(kernel STATIC ${KERNEL_SOURCES})
target_include_directories(kernel PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(kernel tinyprintf)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set_source_files_properties(
kernel/panic.cpp
PROPERTIES COMPILE_FLAGS -Wno-frame-address)
endif()
if (RTTI_EXCEPTIONS)
target_compile_definitions(kernel PRIVATE -DEH_ENABLED)
endif()
================================================
FILE: src/crt/c_abi.c
================================================
#include <kprint.h>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
__attribute__((noreturn)) void panic(const char*);
void* _impure_ptr = NULL;
void __stack_chk_fail_local()
{
panic("Stack protector: Canary modified");
__builtin_unreachable();
}
__attribute__((used))
void __stack_chk_fail()
{
panic("Stack protector: Canary modified");
__builtin_unreachable();
}
void __assert_fail(const char *assertion,
const char *file,
unsigned int line,
const char *function)
{
char buffer[4096];
snprintf(buffer, sizeof(buffer),
"Assertion failed: %s in %s:%u, function %s\n",
assertion, file, line, function);
panic(buffer);
__builtin_unreachable();
}
void __assert_func(
const char *file,
int line,
const char *func,
const char *failedexpr)
{
kprintf(
"assertion \"%s\" failed: file \"%s\", line %d%s%s\n",
failedexpr, file, line,
func ? ", function: " : "", func ? func : "");
extern void abort() __attribute__((noreturn));
abort();
}
void _exit(int status)
{
char buffer[64];
snprintf(buffer, sizeof(buffer),
"Exit called with status %d\n", status);
panic(buffer);
__builtin_unreachable();
}
__attribute__((used))
void* memset(char* dest, int ch, size_t size)
{
for (size_t i = 0; i < size; i++)
dest[i] = ch;
return dest;
}
void* memcpy(char* dest, const char* src, size_t size)
{
for (size_t i = 0; i < size; i++)
dest[i] = src[i];
return dest;
}
void* memmove(char* dest, const char* src, size_t size)
{
if (dest <= src)
{
for (size_t i = 0; i < size; i++)
dest[i] = src[i];
}
else
{
for (int i = size-1; i >= 0; i--)
dest[i] = src[i];
}
return dest;
}
int memcmp(const void* ptr1, const void* ptr2, size_t n)
{
const uint8_t* iter1 = (const uint8_t*) ptr1;
const uint8_t* iter2 = (const uint8_t*) ptr2;
while (n > 0 && *iter1 == *iter2) {
iter1++;
iter2++;
n--;
}
return n == 0 ? 0 : (*iter1 - *iter2);
}
// naive version (needed for EASTL)
float ceilf(float n)
{
long int i = (int) n;
return (n == (float) i) ? n : i + 1;
}
char* strcpy(char* dst, const char* src)
{
while ((*dst++ = *src++));
*dst = 0;
return dst;
}
size_t strlen(const char* str)
{
const char* iter;
for (iter = str; *iter; ++iter);
return iter - str;
}
int strcmp(const char* str1, const char* str2)
{
while (*str1 != 0 && *str2 != 0 && *str1 == *str2) {
str1++;
str2++;
}
return *str1 - *str2;
}
char* strcat(char* dest, const char* src)
{
strcpy(dest + strlen(dest), src);
return dest;
}
void* __memcpy_chk(void* dest, const void* src, size_t len, size_t destlen)
{
assert (len <= destlen);
return memcpy(dest, src, len);
}
void* __memset_chk(void* dest, int c, size_t len, size_t destlen)
{
assert (len <= destlen);
return memset(dest, c, len);
}
char* __strcat_chk(char* dest, const char* src, size_t destlen)
{
size_t len = strlen(dest) + strlen(src) + 1;
assert (len <= destlen);
return strcat(dest, src);
}
int isdigit(int c)
{
return (('0' <= c) && (c <= '9')) ? 1 : 0;
}
int isalpha(int c)
{
return ((c > 64 && c < 91) || (c > 96 && c <= 122)) ? 1 : 0;
}
int isxdigit(int c)
{
return (isdigit(c) || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'));
}
int isupper(int c)
{
return (c >= 'A' && c <= 'Z');
}
================================================
FILE: src/crt/c_stubs.c
================================================
#define _GNU_SOURCE
#include <stddef.h>
#include <dlfcn.h>
#include <link.h>
#include <kprint.h>
char *getenv(const char *name) {
//kprintf("getenv called for %s\n", name);
(void) name;
return NULL;
}
int setenv(const char *name, const char *value, int overwrite) {
(void) name;
(void) value;
(void) overwrite;
return -1;
}
int dl_iterate_phdr(
int (*callback) (struct dl_phdr_info *info, size_t size, void *data), void *data)
{
return 0;
}
================================================
FILE: src/crt/cxxabi.cpp
================================================
#include <kprint.h>
#include <new>
#include <cstddef>
#include <cstring>
extern "C" void* malloc(size_t);
extern "C" void free(void*);
//#define DEBUG_HEAP
#ifdef DEBUG_HEAP
#define HPRINT(fmt, ...) kprintf(fmt, __VA_ARGS__)
#else
#define HPRINT(fmt, ...) /* fmt */
#endif
void* operator new(size_t size)
{
void* res = malloc(size);
HPRINT("operator new: size %u => %p\n", size, res);
return res;
}
void* operator new[](size_t size)
{
void* res = malloc(size);
HPRINT("operator new[]: size %u => %p\n", size, res);
return res;
}
void operator delete(void* ptr)
{
HPRINT("operator delete: %p\n", ptr);
free(ptr);
}
void operator delete[](void* ptr)
{
HPRINT("operator delete[]: %p\n", ptr);
free(ptr);
}
// C++14 sized deallocation
void operator delete(void* ptr, std::size_t)
{
free(ptr);
}
void operator delete [](void* ptr, std::size_t)
{
free(ptr);
}
extern "C" void __cxa_pure_virtual()
{
kprintf("Unimplemented pure virtual function called\n");
}
/// EASTL glue ///
void* operator new[] (
size_t size, /* size */
const char*, /* pName */
int, /* flags */
unsigned, /* debugFlags */
const char*, /* file */
int) /* line */
{
void* res = malloc(size);
HPRINT("eastl new: size: %u = %p\n", size, res);
return res;
}
void* operator new[] (
size_t size, /* size */
size_t align, /* alignment */
size_t, /* alignmentOffset */
const char*, /* pName */
int, /* flags */
unsigned, /* debugFlags */
const char*, /* file */
int) /* line */
{
void* res = malloc(size);
HPRINT("eastl aligned new: size %u align %u => %p\n",
size, align, res);
(void) align;
return res;
}
================================================
FILE: src/crt/heap.c
================================================
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
static uintptr_t heap_start;
static uintptr_t heap_end;
// heap_max really should be set to max physical
// by reading the value from multiboot meminfo table
static uintptr_t heap_max = 0xC0000000;
void __init_heap(void* free_begin)
{
heap_start = (uintptr_t) free_begin;
if (heap_start & 0xF) heap_start += 0x10 - (heap_start & 0xF);
heap_end = heap_start;
assert(((heap_start & 0xF) == 0) && "Heap should be aligned");
}
// data segment size
void* sbrk(intptr_t increment)
{
uintptr_t old = heap_end;
if (heap_end + increment <= heap_max)
{
heap_end += increment;
return (void*) old;
}
return (void*) -1;
}
int posix_memalign(void **memptr, size_t alignment, size_t size)
{
uintptr_t addr = (uintptr_t) malloc(size + alignment);
const intptr_t offset = addr & (alignment-1);
if (offset) addr += (alignment-1) - offset;
*memptr = (void*) addr;
return 0;
}
================================================
FILE: src/crt/malloc.c
================================================
//
// As written by Snaipe @ https://github.com/Snaipe/malloc
//
#include <unistd.h>
#include <errno.h>
static inline size_t word_align(size_t size) {
return size + ((sizeof(size_t) - 1) & ~(sizeof(size_t) - 1));
}
struct chunk {
struct chunk *next, *prev;
size_t size;
int free;
void *data;
};
typedef struct chunk *Chunk;
static void *malloc_base() {
static Chunk b = NULL;
if (!b) {
b = sbrk(word_align(sizeof(struct chunk)));
if (b == (void*) -1) {
_exit(127);
}
b->next = NULL;
b->prev = NULL;
b->size = 0;
b->free = 0;
b->data = NULL;
}
return b;
}
Chunk malloc_chunk_find(size_t s, Chunk *heap) {
Chunk c = malloc_base();
for (; c && (!c->free || c->size < s); *heap = c, c = c->next);
return c;
}
void malloc_merge_next(Chunk c) {
c->size = c->size + c->next->size + sizeof(struct chunk);
c->next = c->next->next;
if (c->next) {
c->next->prev = c;
}
}
void malloc_split_next(Chunk c, size_t size) {
Chunk newc = (Chunk)((char*) c + size);
newc->prev = c;
newc->next = c->next;
newc->size = c->size - size;
newc->free = 1;
newc->data = newc + 1;
if (c->next) {
c->next->prev = newc;
}
c->next = newc;
c->size = size - sizeof(struct chunk);
}
void *malloc(size_t size) {
if (!size) return NULL;
size_t length = word_align(size + sizeof(struct chunk));
Chunk prev = NULL;
Chunk c = malloc_chunk_find(size, &prev);
if (!c) {
Chunk newc = sbrk(length);
if (newc == (void*) -1) {
return NULL;
}
newc->next = NULL;
newc->prev = prev;
newc->size = length - sizeof(struct chunk);
newc->data = newc + 1;
prev->next = newc;
c = newc;
} else if (length + sizeof(size_t) < c->size) {
malloc_split_next(c, length);
}
c->free = 0;
return c->data;
}
void free(void *ptr) {
if (!ptr || ptr < malloc_base() || ptr > sbrk(0)) return;
Chunk c = (Chunk) ptr - 1;
if (c->data != ptr) return;
c->free = 1;
if (c->next && c->next->free) {
malloc_merge_next(c);
}
if (c->prev->free) {
malloc_merge_next(c = c->prev);
}
if (!c->next) {
c->prev->next = NULL;
sbrk(- c->size - sizeof(struct chunk));
}
}
void *calloc(size_t nmemb, size_t size) {
size_t length = nmemb * size;
void *ptr = malloc(length);
if (ptr) {
char *dst = ptr;
for (size_t i = 0; i < length; *dst = 0, ++dst, ++i);
}
return ptr;
}
void *realloc(void *ptr, size_t size) {
void *newptr = malloc(size);
if (newptr && ptr && ptr >= malloc_base() && ptr <= sbrk(0)) {
Chunk c = (Chunk) ptr - 1;
if (c->data == ptr) {
size_t length = c->size > size ? size : c->size;
char *dst = newptr, *src = ptr;
for (size_t i = 0; i < length; *dst = *src, ++src, ++dst, ++i);
free(ptr);
}
}
return newptr;
}
================================================
FILE: src/crt/print.c
================================================
#include <assert.h>
#include <kprint.h>
#include <stdarg.h>
#include <stdio.h>
FILE* stderr = NULL;
FILE* stdout = NULL;
char* hex32_to_str(char buffer[], unsigned int val)
{
char const lut[] = "0123456789ABCDEF";
for (int i = 0; i < 4; i++)
{
buffer[i*2+0] = lut[(val >> (28-i*8)) & 0xF];
buffer[i*2+1] = lut[(val >> (24-i*8)) & 0xF];
}
buffer[8] = 0;
return buffer;
}
char* int32_to_str(char buffer[], int val)
{
char* b = buffer;
// negation
if (val < 0) { *b++ = '-'; val *= -1; }
// move to end of repr.
int tmp = val;
do { ++b; tmp /= 10; } while (tmp);
*b = 0;
// move back and insert as we go
do {
*--b = '0' + (val % 10);
val /= 10;
} while (val);
return buffer;
}
int kprintf(const char* fmt, ...)
{
char buffer[4096];
va_list va;
va_start(va, fmt);
int ret = tfp_vsnprintf(buffer, sizeof(buffer), fmt, va);
va_end(va);
kprint(buffer);
return ret;
}
#undef snprintf
__attribute__((format (printf, 3, 4)))
int snprintf(char *s, size_t maxlen, const char *format, ...)
{
va_list arg;
va_start (arg, format);
int bytes = tfp_vsnprintf(s, maxlen, format, arg);
va_end (arg);
return bytes;
}
#undef printf
#undef fprintf
#undef vfprintf
int vfprintf(FILE* fp, const char *format, va_list ap)
{
(void) fp;
char buffer[4096];
int len = tfp_vsnprintf(buffer, sizeof(buffer), format, ap);
__serial_print(buffer, len);
return len;
}
__attribute__((format (printf, 2, 3)))
int fprintf(FILE* stream, const char* fmt, ...)
{
va_list arg;
va_start (arg, fmt);
int bytes = vfprintf(stream, fmt, arg);
va_end (arg);
return bytes;
}
__attribute__((format(printf, 2, 3)))
int __printf_chk (int flag, const char *format, ...)
{
(void) flag;
va_list ap;
va_start (ap, format);
int bytes = vfprintf (stdout, format, ap);
va_end (ap);
return bytes;
}
int __fprintf_chk(FILE* fp, int flag, const char* format, ...)
{
(void) flag;
va_list arg;
va_start (arg, format);
int bytes = vfprintf(fp, format, arg);
va_end (arg);
return bytes;
}
int __vfprintf_chk(FILE* fp, int flag, const char *format, va_list ap)
{
(void) flag;
int bytes = vfprintf (fp, format, ap);
return bytes;
}
int __vsprintf_chk(char* s, int flag, size_t slen, const char* format, va_list args)
{
(void) flag;
int res = tfp_vsnprintf(s, slen, format, args);
assert ((size_t) res < slen);
return res;
}
int __vsnprintf_chk (char *s, size_t maxlen, int flags, size_t slen,
const char *format, va_list args)
{
assert (slen < maxlen);
(void) flags;
return tfp_vsnprintf(s, slen, format, args);
}
__attribute__((format(printf, 4, 5)))
int __sprintf_chk(char* s, int flags, size_t slen, const char *format, ...)
{
va_list arg;
va_start (arg, format);
int bytes = __vsprintf_chk(s, flags, slen, format, arg);
va_end (arg);
return bytes;
}
int __snprintf_chk (char *s, size_t maxlen, int flags, size_t slen,
const char *format, ...)
{
va_list arg;
int done;
va_start (arg, format);
done = __vsnprintf_chk (s, maxlen, flags, slen, format, arg);
va_end (arg);
return done;
}
================================================
FILE: src/crt/ubsan.c
================================================
#include <stdint.h>
#include <stdio.h>
#include <kprint.h>
extern void panic(const char*) __attribute__((noreturn));
extern void print_backtrace();
struct source_location {
const char *file_name;
struct {
uint32_t line;
uint32_t column;
};
};
struct type_descriptor {
uint16_t type_kind;
uint16_t type_info;
char type_name[1];
};
const char* type_kind_string[] = {
"load of", "store to", "reference binding to", "member access within",
"member call on", "constructor call on", "downcast of", "downcast of"
};
struct out_of_bounds {
struct source_location src;
struct type_descriptor* array_type;
struct type_descriptor* index_type;
};
struct overflow {
struct source_location src;
};
struct mismatch {
struct source_location src;
struct type_descriptor* type;
unsigned char log_align;
unsigned char type_kind;
};
struct function_type_mismatch {
const struct source_location src;
const struct type_descriptor* type;
};
struct nonnull_return {
struct source_location src;
struct source_location attr;
};
struct unreachable {
struct source_location src;
};
static void print_src_location(const struct source_location* src) {
kprintf("ubsan: %s at line %u col %u\n",
src->file_name, src->line, src->column);
}
static void undefined_throw(const char* error) {
kprintf("ubsan: %s", error);
print_backtrace();
kprintf("\n");
}
/// Undefined-behavior sanitizer
void __ubsan_handle_out_of_bounds(struct out_of_bounds* data)
{
print_src_location(&data->src);
undefined_throw("Out-of-bounds access");
}
void __ubsan_handle_missing_return()
{
undefined_throw("Missing return");
}
void __ubsan_handle_nonnull_return(struct nonnull_return* data)
{
print_src_location(&data->src);
undefined_throw("Non-null return");
}
void __ubsan_handle_add_overflow()
{
undefined_throw("Overflow on addition");
}
void __ubsan_handle_sub_overflow()
{
undefined_throw("Overflow on subtraction");
}
void __ubsan_handle_mul_overflow()
{
undefined_throw("Overflow on multiplication");
}
void __ubsan_handle_negate_overflow()
{
undefined_throw("Overflow on negation");
}
void __ubsan_handle_pointer_overflow()
{
undefined_throw("Pointer overflow");
}
void __ubsan_handle_divrem_overflow(struct overflow* data,
unsigned long lhs,
unsigned long rhs)
{
print_src_location(&data->src);
kprintf("ubsan: LHS %lu / RHS %lu\n", lhs, rhs);
undefined_throw("Division remainder overflow");
}
void __ubsan_handle_float_cast_overflow()
{
undefined_throw("Float-cast overflow");
}
void __ubsan_handle_shift_out_of_bounds()
{
undefined_throw("Shift out-of-bounds");
}
void __ubsan_handle_type_mismatch_v1(struct mismatch* data, uintptr_t ptr)
{
print_src_location(&data->src);
const char* reason = "Type mismatch";
const long alignment = 1 << data->log_align;
if (alignment && (ptr & (alignment-1)) != 0) {
reason = "Misaligned access";
}
else if (ptr == 0) {
reason = "Null-pointer access";
}
char buffer[2048];
// TODO: resolve symbol name
snprintf(buffer, sizeof(buffer),
"%s on ptr %p (aligned %lu)\n"
"ubsan: type name %s\n",
reason,
(void*) ptr,
alignment,
data->type->type_name);
undefined_throw(buffer);
}
void __ubsan_handle_function_type_mismatch(
struct function_type_mismatch* data,
unsigned long ptr)
{
print_src_location(&data->src);
char buffer[2048];
snprintf(buffer, sizeof(buffer),
"Function type mismatch on ptr %p\n"
"ubsan: type name %s\n"
"ubsan: function %p",
(void*) ptr,
data->type->type_name,
(void*) ptr); // TODO: resolve symbol name
undefined_throw(buffer);
}
void __ubsan_handle_nonnull_arg()
{
undefined_throw("Non-null argument violated");
}
void __ubsan_handle_invalid_builtin()
{
undefined_throw("Invalid built-in function");
}
void __ubsan_handle_load_invalid_value()
{
undefined_throw("Load of invalid value");
}
__attribute__((noreturn))
void __ubsan_handle_builtin_unreachable(struct unreachable* data)
{
print_src_location(&data->src);
panic("Unreachable code reached");
}
================================================
FILE: src/crt/udiv.c
================================================
#include <limits.h>
#include <stdint.h>
typedef unsigned su_int;
typedef long long di_int;
typedef unsigned long long du_int;
typedef union
{
du_int all;
struct
{
su_int low;
su_int high;
} s;
} udwords;
du_int
__udivmoddi4(du_int a, du_int b, du_int* rem)
{
const unsigned n_uword_bits = sizeof(su_int) * CHAR_BIT;
const unsigned n_udword_bits = sizeof(du_int) * CHAR_BIT;
udwords n;
n.all = a;
udwords d;
d.all = b;
udwords q;
udwords r;
unsigned sr;
/* special cases, X is unknown, K != 0 */
if (n.s.high == 0)
{
if (d.s.high == 0)
{
/* 0 X
* ---
* 0 X
*/
if (rem)
*rem = n.s.low % d.s.low;
return n.s.low / d.s.low;
}
/* 0 X
* ---
* K X
*/
if (rem)
*rem = n.s.low;
return 0;
}
/* n.s.high != 0 */
if (d.s.low == 0)
{
if (d.s.high == 0)
{
/* K X
* ---
* 0 0
*/
if (rem)
*rem = n.s.high % d.s.low;
return n.s.high / d.s.low;
}
/* d.s.high != 0 */
if (n.s.low == 0)
{
/* K 0
* ---
* K 0
*/
if (rem)
{
r.s.high = n.s.high % d.s.high;
r.s.low = 0;
*rem = r.all;
}
return n.s.high / d.s.high;
}
/* K K
* ---
* K 0
*/
if ((d.s.high & (d.s.high - 1)) == 0) /* if d is a power of 2 */
{
if (rem)
{
r.s.low = n.s.low;
r.s.high = n.s.high & (d.s.high - 1);
*rem = r.all;
}
return n.s.high >> __builtin_ctz(d.s.high);
}
/* K K
* ---
* K 0
*/
sr = __builtin_clz(d.s.high) - __builtin_clz(n.s.high);
/* 0 <= sr <= n_uword_bits - 2 or sr large */
if (sr > n_uword_bits - 2)
{
if (rem)
*rem = n.all;
return 0;
}
++sr;
/* 1 <= sr <= n_uword_bits - 1 */
/* q.all = n.all << (n_udword_bits - sr); */
q.s.low = 0;
q.s.high = n.s.low << (n_uword_bits - sr);
/* r.all = n.all >> sr; */
r.s.high = n.s.high >> sr;
r.s.low = (n.s.high << (n_uword_bits - sr)) | (n.s.low >> sr);
}
else /* d.s.low != 0 */
{
if (d.s.high == 0)
{
/* K X
* ---
* 0 K
*/
if ((d.s.low & (d.s.low - 1)) == 0) /* if d is a power of 2 */
{
if (rem)
*rem = n.s.low & (d.s.low - 1);
if (d.s.low == 1)
return n.all;
sr = __builtin_ctz(d.s.low);
q.s.high = n.s.high >> sr;
q.s.low = (n.s.high << (n_uword_bits - sr)) | (n.s.low >> sr);
return q.all;
}
/* K X
* ---
* 0 K
*/
sr = 1 + n_uword_bits + __builtin_clz(d.s.low) - __builtin_clz(n.s.high);
/* 2 <= sr <= n_udword_bits - 1
* q.all = n.all << (n_udword_bits - sr);
* r.all = n.all >> sr;
*/
if (sr == n_uword_bits)
{
q.s.low = 0;
q.s.high = n.s.low;
r.s.high = 0;
r.s.low = n.s.high;
}
else if (sr < n_uword_bits) // 2 <= sr <= n_uword_bits - 1
{
q.s.low = 0;
q.s.high = n.s.low << (n_uword_bits - sr);
r.s.high = n.s.high >> sr;
r.s.low = (n.s.high << (n_uword_bits - sr)) | (n.s.low >> sr);
}
else // n_uword_bits + 1 <= sr <= n_udword_bits - 1
{
q.s.low = n.s.low << (n_udword_bits - sr);
q.s.high = (n.s.high << (n_udword_bits - sr)) |
(n.s.low >> (sr - n_uword_bits));
r.s.high = 0;
r.s.low = n.s.high >> (sr - n_uword_bits);
}
}
else
{
/* K X
* ---
* K K
*/
sr = __builtin_clz(d.s.high) - __builtin_clz(n.s.high);
/* 0 <= sr <= n_uword_bits - 1 or sr large */
if (sr > n_uword_bits - 1)
{
if (rem)
*rem = n.all;
return 0;
}
++sr;
/* 1 <= sr <= n_uword_bits */
/* q.all = n.all << (n_udword_bits - sr); */
q.s.low = 0;
if (sr == n_uword_bits)
{
q.s.high = n.s.low;
r.s.high = 0;
r.s.low = n.s.high;
}
else
{
q.s.high = n.s.low << (n_uword_bits - sr);
r.s.high = n.s.high >> sr;
r.s.low = (n.s.high << (n_uword_bits - sr)) | (n.s.low >> sr);
}
}
}
/* Not a special case
* q and r are initialized with:
* q.all = n.all << (n_udword_bits - sr);
* r.all = n.all >> sr;
* 1 <= sr <= n_udword_bits - 1
*/
su_int carry = 0;
for (; sr > 0; --sr)
{
/* r:q = ((r:q) << 1) | carry */
r.s.high = (r.s.high << 1) | (r.s.low >> (n_uword_bits - 1));
r.s.low = (r.s.low << 1) | (q.s.high >> (n_uword_bits - 1));
q.s.high = (q.s.high << 1) | (q.s.low >> (n_uword_bits - 1));
q.s.low = (q.s.low << 1) | carry;
/* carry = 0;
* if (r.all >= d.all)
* {
* r.all -= d.all;
* carry = 1;
* }
*/
const di_int s = (di_int)(d.all - r.all - 1) >> (n_udword_bits - 1);
carry = s & 1;
r.all -= d.all & s;
}
q.all = (q.all << 1) | carry;
if (rem)
*rem = r.all;
return q.all;
}
du_int
__udivdi3(du_int a, du_int b)
{
return __udivmoddi4(a, b, 0);
}
du_int
__umoddi3(du_int a, du_int b)
{
du_int r;
__udivmoddi4(a, b, &r);
return r;
}
================================================
FILE: src/hw/serial.h
================================================
#pragma once
extern void __serial_print1(const char*);
extern void __serial_print(const char*, int);
extern void __serial_putchr(void*, char);
================================================
FILE: src/hw/serial1.c
================================================
#include <stdint.h>
static const uint16_t port = 0x3F8; // Serial port 1
static char initialized = 0;
static inline uint8_t inb(int port)
{
int ret;
asm ("xorl %eax,%eax");
asm ("inb %%dx,%%al":"=a" (ret):"d"(port));
return ret;
}
static inline void outb(int port, uint8_t data)
{
asm ("outb %%al,%%dx"::"a" (data), "d"(port));
}
static inline void init_serial_if_needed()
{
if (initialized) return;
initialized = 1;
// properly initialize serial port
outb(port + 1, 0x00); // Disable all interrupts
outb(port + 3, 0x80); // Enable DLAB (set baud rate divisor)
outb(port + 0, 0x03); // Set divisor to 3 (lo byte) 38400 baud
outb(port + 1, 0x00); // (hi byte)
outb(port + 3, 0x03); // 8 bits, no parity, one stop bit
outb(port + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold
}
void __serial_print1(const char* cstr)
{
init_serial_if_needed();
while (*cstr) {
while (!(inb(port + 5) & 0x20)) /* */;
outb(port, *cstr++);
}
}
void __serial_print(const char* str, int len)
{
init_serial_if_needed();
for (int i = 0; i < len; i++) {
while (!(inb(port + 5) & 0x20)) /* */;
outb(port, str[i]);
}
}
// buffered serial output
static char buffer[256];
static unsigned cnt = 0;
void fflush(void* fileno)
{
(void) fileno;
__serial_print(buffer, cnt);
cnt = 0;
}
void __serial_putchr(const void* file, char c)
{
(void) file;
buffer[cnt++] = c;
if (c == '\n' || cnt == sizeof(buffer)) {
fflush(0);
}
}
================================================
FILE: src/kernel/dylib.hpp
================================================
#pragma once
#include <kernel/elf.hpp>
struct Dylib
{
static Elf64_Ehdr* load(const void* address);
template <typename T>
static T resolve_function(const Elf64_Ehdr* hdr, const char* name);
};
inline Elf64_Ehdr* Dylib::load(const void* address)
{
auto* hdr = (Elf64_Ehdr*) address;
// validate shared library ELF header
assert(Elf::validate(hdr));
// resolve symbolic references from PLT and GOT
Elf::perform_relocations(hdr);
return hdr;
}
template <typename T>
inline T Dylib::resolve_function(const Elf64_Ehdr* hdr, const char* name)
{
const auto* sym = Elf::resolve_name(hdr, name);
if (sym == nullptr) return T();
return (T) &((char*) hdr)[sym->st_value];
}
================================================
FILE: src/kernel/elf.cpp
================================================
#include "elf.hpp"
#include <cassert>
#include <cstdint>
#include <cstring>
#include <kprint.h>
static constexpr bool DEBUG_ELF = false;
bool Elf::validate(Elf64_Ehdr* hdr)
{
return (hdr->e_ident[EI_MAG0] == ELFMAG0)
&& (hdr->e_ident[EI_MAG1] == ELFMAG1)
&& (hdr->e_ident[EI_MAG2] == ELFMAG2)
&& (hdr->e_ident[EI_MAG3] == ELFMAG3)
// we will need 64-bit addresses and such
&& (hdr->e_ident[EI_CLASS] == ELFCLASS64);
}
template <typename T>
inline T* elf_offset(const Elf64_Ehdr* hdr, intptr_t ofs) {
return (T*) &((char*) hdr)[ofs];
}
static const Elf64_Sym*
elf_sym_index(const Elf64_Ehdr* hdr, const Elf64_Shdr* shdr, uint32_t symidx)
{
assert(symidx < shdr->sh_size / sizeof(Elf64_Sym));
auto* symtab = elf_offset<Elf64_Sym>(hdr, shdr->sh_offset);
return &symtab[symidx];
}
const Elf64_Shdr* Elf::section_by_name(const Elf64_Ehdr* hdr, const char* name)
{
const auto* shdr = elf_offset<Elf64_Shdr> (hdr, hdr->e_shoff);
const auto& shstrtab = shdr[hdr->e_shnum-1];
const char* strings = elf_offset<char>(hdr, shstrtab.sh_offset);
for (auto i = 0; i < hdr->e_shnum; i++)
{
const char* shname = &strings[shdr[i].sh_name];
if (strcmp(shname, name) == 0) {
return &shdr[i];
}
}
return nullptr;
}
const Elf64_Sym* Elf::resolve_name(const Elf64_Ehdr* hdr, const char* name)
{
const auto* sym_hdr = Elf::section_by_name(hdr, ".symtab");
assert(sym_hdr != nullptr);
const auto* str_hdr = Elf::section_by_name(hdr, ".strtab");
assert(str_hdr != nullptr);
const Elf64_Sym* symtab = elf_sym_index(hdr, sym_hdr, 0);
const size_t symtab_ents = sym_hdr->sh_size / sizeof(Elf64_Sym);
const char* strtab = elf_offset<char>(hdr, str_hdr->sh_offset);
for (size_t i = 0; i < symtab_ents; i++)
{
const char* symname = &strtab[symtab[i].st_name];
//kprintf("Testing %s vs %s\n", symname, name);
if (strcmp(symname, name) == 0) {
return &symtab[i];
}
}
return nullptr;
}
static void elf_print_sym(const Elf64_Sym* sym)
{
kprintf("-> Sym is at %p with size %llu, type %u name %u\n",
(void*) sym->st_value, sym->st_size,
ELF64_ST_TYPE(sym->st_info), sym->st_name);
}
static void elf_relocate_section(Elf64_Ehdr* hdr, const char* section_name)
{
const auto* rela = Elf::section_by_name(hdr, section_name);
const auto* dyn_hdr = Elf::section_by_name(hdr, ".dynsym");
const size_t rela_ents = rela->sh_size / sizeof(Elf64_Rela);
auto* rela_addr = elf_offset<Elf64_Rela>(hdr, rela->sh_offset);
for (size_t i = 0; i < rela_ents; i++) {
const uint32_t symidx = ELF64_R_SYM(rela_addr[i].r_info);
auto* sym = elf_sym_index(hdr, dyn_hdr, symidx);
const uint8_t type = ELF64_ST_TYPE(sym->st_info);
if (type == STT_FUNC || type == STT_OBJECT)
{
uintptr_t entry = (uintptr_t) hdr + rela_addr[i].r_offset;
uintptr_t final = (uintptr_t) hdr + sym->st_value;
if constexpr (DEBUG_ELF)
{
kprintf("Relocating rela %zu with sym idx %u where %p -> %p\n",
i, symidx, (void*) entry, (void*) final);
elf_print_sym(sym);
}
*(char**) entry = (char*) final;
}
}
}
void Elf::perform_relocations(Elf64_Ehdr* hdr)
{
elf_relocate_section(hdr, ".rela.plt");
elf_relocate_section(hdr, ".rela.dyn");
}
================================================
FILE: src/kernel/elf.hpp
================================================
#pragma once
#include <elf.h>
struct Elf
{
static bool validate(Elf64_Ehdr* hdr);
static const Elf64_Shdr* section_by_name(const Elf64_Ehdr*, const char* name);
// get the symbol associated with @name
static const Elf64_Sym* resolve_name(const Elf64_Ehdr*, const char* name);
// dynamic loader
static void perform_relocations(Elf64_Ehdr* hdr);
};
================================================
FILE: src/kernel/init_libc.c
================================================
#include <assert.h>
#include <kprint.h>
#include <stddef.h>
#include <stdint.h>
#include <multiboot.h>
extern size_t strlen(const char* str);
extern char _end;
static void* multiboot_free_begin(intptr_t mb_addr)
{
const multiboot_info_t* info = (multiboot_info_t*) mb_addr;
uintptr_t end = (uintptr_t) &_end;
if (info->flags & MULTIBOOT_INFO_CMDLINE) {
const char* cmdline = (char*) (intptr_t) info->cmdline;
const uintptr_t pot_end = info->cmdline + strlen(cmdline);
if (pot_end > end) end = pot_end;
}
const multiboot_module_t* mod = (multiboot_module_t*) (intptr_t) info->mods_addr;
const multiboot_module_t* mods_end = mod + info->mods_count;
for (; mod < mods_end; mod++)
{
if (mod->mod_end > end) end = mod->mod_end;
}
return (void*) end;
}
void __init_stdlib(uint32_t mb_magic, uint32_t mb_addr)
{
assert(mb_magic == 0x2badb002);
// 1. enable printf facilities
init_printf(NULL, __serial_putchr);
// 2. find end of multiboot areas
void* free_begin = multiboot_free_begin(mb_addr);
assert(free_begin >= (void*) &_end);
// 3. initialize heap (malloc, etc.)
extern void __init_heap(void*);
__init_heap(free_begin);
#ifdef EH_ENABLED
/// 4. initialize exceptions before we run constructors
extern char __eh_frame_start[];
extern void __register_frame(void*);
__register_frame(&__eh_frame_start);
#endif
// 5. call global C/C++ constructors
extern void(*__init_array_start [])();
extern void(*__init_array_end [])();
int count = __init_array_end - __init_array_start;
for (int i = 0; i < count; i++) {
__init_array_start[i]();
}
}
================================================
FILE: src/kernel/kernel_start.c
================================================
#include <assert.h>
#include <stdint.h>
#include <kprint.h>
static void __init_paging()
{
static uintptr_t pdir0[512] __attribute__((aligned(4096)));
// unmap zero page
assert(((uintptr_t) pdir0 & 0xfff) == 0);
pdir0[0] = 0x0; // unpresent zero page
for (uintptr_t i = 1; i < 512; i++) {
pdir0[i] = (i * 0x1000) | 0x3; // RW + P
}
// install into PML2 entry 0
uintptr_t* pml2 = (uintptr_t*) 0x3000;
pml2[0] = ((uintptr_t) pdir0) | 0x3; // RW + P;
__asm__ ("invlpg 0x0");
}
void kernel_start(uint32_t eax, uint32_t ebx)
{
kprintf("kernel_start(eax: %x, ebx: %x)\n", eax, ebx);
extern void __init_stdlib(uint32_t, uint32_t);
__init_stdlib(eax, ebx);
// we have to do this after initializing .bss
// NOTE: don't enable this until you catch CPU exceptions!
//__init_paging();
extern void kernel_main(uint32_t, uint32_t);
kernel_main(eax, ebx);
}
================================================
FILE: src/kernel/panic.cpp
================================================
#include <kprint.h>
#include <cstdio>
// less risky when the stack is blown out
static char buffer[4096];
#define frp(N, ra) \
(__builtin_frame_address(N) != nullptr) && \
(ra = __builtin_return_address(N)) != nullptr && ra != (void*)-1
static void print_trace(const int N, const void* ra)
{
snprintf(buffer, sizeof(buffer),
"[%d] %p\n",
N, ra);
kprint(buffer);
}
extern "C"
void print_backtrace()
{
kprintf("\nBacktrace:\n");
void* ra;
if (frp(0, ra)) {
print_trace(0, ra);
if (frp(1, ra)) {
print_trace(1, ra);
if (frp(2, ra)) {
print_trace(2, ra);
}
}
}
}
extern "C"
__attribute__((noreturn))
void panic(const char* reason)
{
kprintf("\n\n!!! PANIC !!!\n%s\n", reason);
print_backtrace();
// the end
kprintf("\nKernel halting...\n");
while (1) asm("cli; hlt");
__builtin_unreachable();
}
extern "C"
void abort()
{
panic("Abort called");
}
extern "C"
void abort_message(const char* fmt, ...)
{
va_list arg;
va_start (arg, fmt);
int bytes = tfp_vsnprintf(buffer, sizeof(buffer), fmt, arg);
(void) bytes;
va_end (arg);
panic(buffer);
}
================================================
FILE: src/kernel/start.asm
================================================
;; stack base address at EBDA border
;; NOTE: Multiboot can use 9d400 to 9ffff
%define STACK_LOCATION 0x9D400
;; multiboot magic
%define MB_MAGIC 0x1BADB002
%define MB_FLAGS 0x3 ;; ALIGN + MEMINFO
extern _MULTIBOOT_START_
extern _LOAD_START_
extern _LOAD_END_
extern _end
extern begin_enter_longmode
extern kernel_start
ALIGN 4
section .multiboot
dd MB_MAGIC
dd MB_FLAGS
dd -(MB_MAGIC + MB_FLAGS)
dd _MULTIBOOT_START_
dd _LOAD_START_
dd _LOAD_END_
dd _end
dd _start
section .data
multiboot_data_magic: dq 0
multiboot_data_address: dq 0
global multiboot_data_magic
global multiboot_data_address
[BITS 32]
section .text
global _start ;; make _start a global symbol
_start:
cli
;; load simple GDT
lgdt [gdtr]
;; activate new GDT
jmp 0x8:rock_bottom ;; code seg
rock_bottom:
mov cx, 0x10 ;; data seg
mov ss, cx
mov ds, cx
mov es, cx
mov fs, cx
mov cx, 0x18 ;; GS seg
mov gs, cx
;; 32-bit stack ptr
mov esp, STACK_LOCATION
mov ebp, esp
;; store multiboot params
mov DWORD [multiboot_data_magic], eax
mov DWORD [multiboot_data_address], ebx
;;ASM_PRINT(strings.phase1)
call enable_cpu_feat
;; zero .bss (used to be in the C portion, but easier to do here)
extern _BSS_START_
extern _BSS_END_
mov edi, _BSS_START_
mov ecx, _BSS_END_
sub ecx, _BSS_START_
mov eax, 0
rep stosb
;; Enable stack protector:
;; GS is located at 0x1000
;; Linux uses GS:0x14 to access stack protector value
;; Copy RDTSC.EAX to this location as preliminary value
rdtsc
mov DWORD [0x1014], eax
;; for 32-bit kernels just call kernel_start here
call begin_enter_longmode
;; stop
cli
hlt
enable_cpu_feat:
;; enable SSE (pretty much always exists)
mov eax, cr0
and ax, 0xFFFB ;clear coprocessor emulation CR0.EM
or ax, 0x2 ;set coprocessor monitoring CR0.MP
mov cr0, eax
mov eax, cr4
or ax, 3 << 9 ;set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time
or ax, 0x20 ;enable native FPU exception handling
mov cr4, eax
;; read out CPU features
mov eax, 1
xor ecx, ecx
cpuid
mov edx, ecx
;; check for XSAVE support (bit 26)
and ecx, 0x04000000
jz xsave_not_supported
;; enable XSAVE
mov eax, cr4
or eax, 0x40000
mov cr4, eax
;; check for AVX support (bit 28)
and edx, 0x10000000
jz xsave_not_supported
;; enable AVX
xor ecx, ecx
xgetbv
or eax, 0x7
xsetbv
xsave_not_supported:
ret
ALIGN 32
gdtr:
dw gdt32_end - gdt32 - 1
dd gdt32
ALIGN 32
gdt32:
;; Entry 0x0: Null descriptor
dq 0x0
;; Entry 0x8: Code segment
dw 0xffff ;Limit
dw 0x0000 ;Base 15:00
db 0x00 ;Base 23:16
dw 0xcf9a ;Flags / Limit / Type [F,L,F,Type]
db 0x00 ;Base 32:24
;; Entry 0x10: Data segment
dw 0xffff ;Limit
dw 0x0000 ;Base 15:00
db 0x00 ;Base 23:16
dw 0xcf92 ;Flags / Limit / Type [F,L,F,Type]
db 0x00 ;Base 32:24
;; Entry 0x18: GS Data segment
dw 0x0100 ;Limit
dw 0x1000 ;Base 15:00
db 0x00 ;Base 23:16
dw 0x4092 ;Flags / Limit / Type [F,L,F,Type]
db 0x00 ;Base 32:24
gdt32_end:
================================================
FILE: src/kernel/start32.c
================================================
#include <stdint.h>
extern uint32_t multiboot_data_magic;
extern uint32_t multiboot_data_address;
extern void kernel_start(uint32_t eax, uint32_t ebx);
void begin_enter_longmode()
{
kernel_start(multiboot_data_magic, multiboot_data_address);
}
================================================
FILE: src/kernel/start64.asm
================================================
[BITS 32]
global begin_enter_longmode:function
extern __serial_print1
extern kernel_start
%define STACK_LOCATION 0x9D000
%define P4_TAB 0x1000 ;; one page
%define P3_TAB 0x2000 ;; one page
%define P2_TAB 0x3000 ;; many pages
%define NUM_P3_ENTRIES 4
%define NUM_P2_ENTRIES (NUM_P3_ENTRIES * 512)
%define IA32_EFER 0xC0000080
%define IA32_STAR 0xC0000081
%define IA32_LSTAR 0xc0000082
%define IA32_FMASK 0xc0000084
%define IA32_FS_BASE 0xc0000100
%define IA32_GS_BASE 0xc0000101
%define IA32_KERNEL_GS_BASE 0xc0000102
;; CR0 paging enable bit
%define PAGING_ENABLE 0x80000000
;; CR0 Supervisor write-protect enable
%define SUPER_WP_ENABLE 0x10000
;; EFER Longmode bit
%define LONGMODE_ENABLE 0x100
;; EFER Execute Disable bit
%define NX_ENABLE 0x800
;; EFER Syscall enable bit
%define SYSCALL_ENABLE 0x1
extern multiboot_data_magic
extern multiboot_data_address
SECTION .text
begin_enter_longmode:
;; disable old paging
mov eax, cr0
and eax, 0x7fffffff ;; clear PG (bit 31)
mov cr0, eax
;; address for Page Map Level 4
mov edi, P4_TAB
mov cr3, edi
;; clear out P4 and P3
mov ecx, 0x2000 / 0x4
xor eax, eax ; Nullify the A-register.
rep stosd
;; create page map entry
mov edi, P4_TAB
mov DWORD [edi], P3_TAB | 0x3 ;; present+write
;; create 1GB mappings
mov ecx, NUM_P3_ENTRIES
mov edi, P3_TAB
mov eax, P2_TAB | 0x3 ;; present + write
mov ebx, 0x0
.p3_loop:
mov DWORD [edi], eax ;; Low word
mov DWORD [edi+4], ebx ;; High word
add eax, 1 << 12 ;; page increments
adc ebx, 0 ;; increment high word when CF set
add edi, 8
loop .p3_loop
;; create 2MB mappings
mov ecx, NUM_P2_ENTRIES
mov edi, P2_TAB
mov eax, 0x0 | 0x3 | 1 << 7 ;; present + write + huge
mov ebx, 0x0
.p2_loop:
mov DWORD [edi], eax ;; Low word
mov DWORD [edi+4], ebx ;; High word
add eax, 1 << 21 ;; 2MB increments
adc ebx, 0 ;; increment high word when CF set
add edi, 8
loop .p2_loop
;; enable PAE
mov eax, cr4
or eax, 1 << 5
mov cr4, eax
;; enable long mode
mov ecx, IA32_EFER
rdmsr
or eax, (LONGMODE_ENABLE | NX_ENABLE | SYSCALL_ENABLE)
wrmsr
;; enable paging
mov eax, cr0 ; Set the A-register to control register 0.
or eax, (PAGING_ENABLE | SUPER_WP_ENABLE)
mov cr0, eax ; Set control register 0 to the A-register.
;; load 64-bit GDT
lgdt [__gdt64_base_pointer]
jmp GDT64.Code:long_mode
[BITS 64]
long_mode:
cli
;; segment regs
mov cx, GDT64.Data
mov ds, cx
mov es, cx
mov fs, cx
mov gs, cx
mov ss, cx
;; set up new stack for 64-bit
push rsp
mov rsp, STACK_LOCATION
push 0
push 0
mov rbp, rsp
mov ecx, IA32_STAR
mov edx, 0x8
mov eax, 0x0
wrmsr
;; Enable stack protector:
;; On amd64 FS should point to tls-table for cpu0
;; Linux uses FS:0x28 to access stack protector value
extern tls
mov ecx, IA32_FS_BASE
mov edx, 0x0
mov eax, tls
wrmsr
;; Set "random" stack protector value
extern __SSP__
rdtsc
mov rcx, __SSP__
xor rax, rcx
;; Install in TLS table
mov QWORD [tls+0x28], rax
;; geronimo!
mov edi, DWORD[multiboot_data_magic]
mov esi, DWORD[multiboot_data_address]
call kernel_start
;; warning that we returned from kernel_start
mov rdi, strings.panic
call __serial_print1
cli
hlt
SECTION .data
strings:
.panic: db `Returned from kernel_start! Halting...\n`,0x0
GDT64:
.Null: equ $ - GDT64 ; The null descriptor.
dq 0
.Code: equ $ - GDT64 ; The code descriptor.
dw 0 ; Limit (low).
dw 0 ; Base (low).
db 0 ; Base (middle)
db 10011010b ; Access (exec/read).
db 00100000b ; Granularity.
db 0 ; Base (high).
.Data: equ $ - GDT64 ; The data descriptor.
dw 0 ; Limit (low).
dw 0 ; Base (low).
db 0 ; Base (middle)
db 10010010b ; Access (read/write).
db 00000000b ; Granularity.
db 0 ; Base (high).
.Task: equ $ - GDT64 ; TSS descriptor.
dq 0
dq 0
dw 0x0 ;; alignment padding
__gdt64_base_pointer:
dw $ - GDT64 - 1 ; Limit.
dq GDT64 ; Base.
================================================
FILE: src/kernel/tls.cpp
================================================
#include <cstddef>
#include "tls.hpp"
#ifdef __x86_64__
static_assert(offsetof(tls_table, guard) == 0x28, "TLS is at 0x28 on amd64");
#elif __i386__
static_assert(offsetof(tls_table, guard) == 0x14, "TLS is at 0x18 on i386");
#endif
// we have to store this in .data otherwise .bss initialization
// will overwrite this in stdlib init
struct tls_table tls;
================================================
FILE: src/kernel/tls.hpp
================================================
#pragma once
#include <cstdint>
struct tls_table
{
// thread self-pointer
void* tls_data; // 0x0
// per-cpu cpuid
int cpuid;
uintptr_t pad[3];
uintptr_t guard; // _SENTINEL_VALUE_
};
extern tls_table tls;
================================================
FILE: src/kprint.h
================================================
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <hw/serial.h>
// better, more familiar way to print!
extern int kprintf(const char* fmt, ...) __attribute__((format(printf, 1, 2)));
// print text directly to serial port
static inline void kprint(const char* text)
{
__serial_print1(text);
}
#include <tinyprintf.h>
#ifdef __cplusplus
}
#endif
================================================
FILE: src/linker.ld
================================================
/**
* Inspired from the IncludeOS unikernel (https://github.com/hioa-cs/IncludeOS)
*
* This is the smallest possible linker script that supports
* normal C and C++ operation, including global constructors,
* exceptions and linker GC sections option.
**/
ENTRY(_start)
SECTIONS
{
PROVIDE(_ELF_START_ = . + 0x100000);
PROVIDE(_LOAD_START_ = _ELF_START_);
. = _ELF_START_;
.multiboot (_ELF_START_ ): {
PROVIDE(_MULTIBOOT_START_ = .);
KEEP(*(.multiboot))
}
.text ALIGN(0x10) :
{
_TEXT_START_ = .;
*(.text*)
*(.gnu.linkonce.t*)
_TEXT_END_ = .;
}
.rodata :
{
_RODATA_START_ = .;
*(.rodata*)
*(.gnu.linkonce.r*)
_RODATA_END_ = .;
}
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
KEEP (*(.init_array .ctors))
PROVIDE_HIDDEN (__init_array_end = .);
}
/*.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
PROVIDE_HIDDEN (__fini_array_end = .);
}*/
/* For stack unwinding (exception handling) */
.eh_frame_hdr ALIGN(0x8):
{
KEEP(*(.eh_frame_hdr*))
}
.eh_frame ALIGN(0x8):
{
PROVIDE (__eh_frame_start = .);
KEEP(*(.eh_frame))
LONG (0);
}
.gcc_except_table :
{
*(.gcc_except_table)
}
.data :
{
_DATA_START_ = .;
*(.data*)
*(.gnu.linkonce.d*)
_DATA_END_ = .;
}
PROVIDE(_LOAD_END_ = .);
.bss :
{
_BSS_START_ = .;
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
_BSS_END_ = .;
}
. = ALIGN(0x10);
_end = .;
}
================================================
FILE: src/main.h
================================================
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
extern void kernel_main(uint32_t eax, uint32_t ebx);
#ifdef __cplusplus
}
#endif
================================================
FILE: src/multiboot.h
================================================
/* Copyright (C) 1999,2003,2007,2008,2009,2010 Free Software Foundation, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY
* DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
* IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef MULTIBOOT_HEADER
#define MULTIBOOT_HEADER 1
/* How many bytes from the start of the file we search for the header. */
#define MULTIBOOT_SEARCH 8192
#define MULTIBOOT_HEADER_ALIGN 4
/* The magic field should contain this. */
#define MULTIBOOT_HEADER_MAGIC 0x1BADB002
/* This should be in %eax. */
#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002
/* Alignment of multiboot modules. */
#define MULTIBOOT_MOD_ALIGN 0x00001000
/* Alignment of the multiboot info structure. */
#define MULTIBOOT_INFO_ALIGN 0x00000004
/* Flags set in the 'flags' member of the multiboot header. */
/* Align all boot modules on i386 page (4KB) boundaries. */
#define MULTIBOOT_PAGE_ALIGN 0x00000001
/* Must pass memory information to OS. */
#define MULTIBOOT_MEMORY_INFO 0x00000002
/* Must pass video information to OS. */
#define MULTIBOOT_VIDEO_MODE 0x00000004
/* This flag indicates the use of the address fields in the header. */
#define MULTIBOOT_AOUT_KLUDGE 0x00010000
/* Flags to be set in the 'flags' member of the multiboot info structure. */
/* is there basic lower/upper memory information? */
#define MULTIBOOT_INFO_MEMORY 0x00000001
/* is there a boot device set? */
#define MULTIBOOT_INFO_BOOTDEV 0x00000002
/* is the command-line defined? */
#define MULTIBOOT_INFO_CMDLINE 0x00000004
/* are there modules to do something with? */
#define MULTIBOOT_INFO_MODS 0x00000008
/* These next two are mutually exclusive */
/* is there a symbol table loaded? */
#define MULTIBOOT_INFO_AOUT_SYMS 0x00000010
/* is there an ELF section header table? */
#define MULTIBOOT_INFO_ELF_SHDR 0X00000020
/* is there a full memory map? */
#define MULTIBOOT_INFO_MEM_MAP 0x00000040
/* Is there drive info? */
#define MULTIBOOT_INFO_DRIVE_INFO 0x00000080
/* Is there a config table? */
#define MULTIBOOT_INFO_CONFIG_TABLE 0x00000100
/* Is there a boot loader name? */
#define MULTIBOOT_INFO_BOOT_LOADER_NAME 0x00000200
/* Is there a APM table? */
#define MULTIBOOT_INFO_APM_TABLE 0x00000400
/* Is there video information? */
#define MULTIBOOT_INFO_VBE_INFO 0x00000800
#define MULTIBOOT_INFO_FRAMEBUFFER_INFO 0x00001000
#ifndef ASM_FILE
typedef unsigned char multiboot_uint8_t;
typedef unsigned short multiboot_uint16_t;
typedef unsigned int multiboot_uint32_t;
typedef unsigned long long multiboot_uint64_t;
struct multiboot_header
{
/* Must be MULTIBOOT_MAGIC - see above. */
multiboot_uint32_t magic;
/* Feature flags. */
multiboot_uint32_t flags;
/* The above fields plus this one must equal 0 mod 2^32. */
multiboot_uint32_t checksum;
/* These are only valid if MULTIBOOT_AOUT_KLUDGE is set. */
multiboot_uint32_t header_addr;
multiboot_uint32_t load_addr;
multiboot_uint32_t load_end_addr;
multiboot_uint32_t bss_end_addr;
multiboot_uint32_t entry_addr;
/* These are only valid if MULTIBOOT_VIDEO_MODE is set. */
multiboot_uint32_t mode_type;
multiboot_uint32_t width;
multiboot_uint32_t height;
multiboot_uint32_t depth;
};
/* The symbol table for a.out. */
struct multiboot_aout_symbol_table
{
multiboot_uint32_t tabsize;
multiboot_uint32_t strsize;
multiboot_uint32_t addr;
multiboot_uint32_t reserved;
};
typedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t;
/* The section header table for ELF. */
struct multiboot_elf_section_header_table
{
multiboot_uint32_t num;
multiboot_uint32_t size;
multiboot_uint32_t addr;
multiboot_uint32_t shndx;
};
typedef struct multiboot_elf_section_header_table multiboot_elf_section_header_table_t;
struct multiboot_info
{
/* Multiboot info version number */
multiboot_uint32_t flags;
/* Available memory from BIOS */
multiboot_uint32_t mem_lower;
multiboot_uint32_t mem_upper;
/* "root" partition */
multiboot_uint32_t boot_device;
/* Kernel command line */
multiboot_uint32_t cmdline;
/* Boot-Module list */
multiboot_uint32_t mods_count;
multiboot_uint32_t mods_addr;
union
{
multiboot_aout_symbol_table_t aout_sym;
multiboot_elf_section_header_table_t elf_sec;
} u;
/* Memory Mapping buffer */
multiboot_uint32_t mmap_length;
multiboot_uint32_t mmap_addr;
/* Drive Info buffer */
multiboot_uint32_t drives_length;
multiboot_uint32_t drives_addr;
/* ROM configuration table */
multiboot_uint32_t config_table;
/* Boot Loader Name */
multiboot_uint32_t boot_loader_name;
/* APM table */
multiboot_uint32_t apm_table;
/* Video */
multiboot_uint32_t vbe_control_info;
multiboot_uint32_t vbe_mode_info;
multiboot_uint16_t vbe_mode;
multiboot_uint16_t vbe_interface_seg;
multiboot_uint16_t vbe_interface_off;
multiboot_uint16_t vbe_interface_len;
multiboot_uint64_t framebuffer_addr;
multiboot_uint32_t framebuffer_pitch;
multiboot_uint32_t framebuffer_width;
multiboot_uint32_t framebuffer_height;
multiboot_uint8_t framebuffer_bpp;
#define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0
#define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1
#define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2
multiboot_uint8_t framebuffer_type;
union
{
struct
{
multiboot_uint32_t framebuffer_palette_addr;
multiboot_uint16_t framebuffer_palette_num_colors;
};
struct
{
multiboot_uint8_t framebuffer_red_field_position;
multiboot_uint8_t framebuffer_red_mask_size;
multiboot_uint8_t framebuffer_green_field_position;
multiboot_uint8_t framebuffer_green_mask_size;
multiboot_uint8_t framebuffer_blue_field_position;
multiboot_uint8_t framebuffer_blue_mask_size;
};
};
};
typedef struct multiboot_info multiboot_info_t;
struct multiboot_color
{
multiboot_uint8_t red;
multiboot_uint8_t green;
multiboot_uint8_t blue;
};
struct multiboot_mmap_entry
{
multiboot_uint32_t size;
multiboot_uint64_t addr;
multiboot_uint64_t len;
#define MULTIBOOT_MEMORY_AVAILABLE 1
#define MULTIBOOT_MEMORY_RESERVED 2
#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3
#define MULTIBOOT_MEMORY_NVS 4
#define MULTIBOOT_MEMORY_BADRAM 5
multiboot_uint32_t type;
} __attribute__((packed));
typedef struct multiboot_mmap_entry multiboot_memory_map_t;
struct multiboot_mod_list
{
/* the memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive */
multiboot_uint32_t mod_start;
multiboot_uint32_t mod_end;
/* Module command line */
multiboot_uint32_t cmdline;
/* padding to take it to 16 bytes (must be zero) */
multiboot_uint32_t pad;
};
typedef struct multiboot_mod_list multiboot_module_t;
/* APM BIOS info. */
struct multiboot_apm_info
{
multiboot_uint16_t version;
multiboot_uint16_t cseg;
multiboot_uint32_t offset;
multiboot_uint16_t cseg_16;
multiboot_uint16_t dseg;
multiboot_uint16_t flags;
multiboot_uint16_t cseg_len;
multiboot_uint16_t cseg_16_len;
multiboot_uint16_t dseg_len;
};
#endif /* ! ASM_FILE */
#endif /* ! MULTIBOOT_HEADER */
================================================
FILE: tools/Makefile
================================================
#
# This is the old Makefile for the project, before moving to CMake
# if CMake is not for you, then this may still be useful for you to build
# a single kernel from the source tree. Just keep in mind you need to add
# your own main file, and that the source tree is outdated. Good luck.
#
# kernel binary
OUT = mykernel
# .c files (add your own!)
C_FILES = src/kernel/kernel_start.c \
src/hw/serial1.c \
src/crt/c_abi.c src/crt/heap.c src/crt/malloc.c \
src/crt/ubsan.c \
src/prnt/print.c src/prnt/mini-printf.c
# .cpp files
CPP_FILES=src/main.cpp src/crt/cxxabi.cpp \
src/kernel/tls.cpp src/kernel/panic.cpp
# .asm files for NASM
ASM_FILES=src/kernel/start.asm src/kernel/start64.asm
# includes
INCLUDE=-Isrc
# EASTL C++ library
INCLUDE +=-Iext/EASTL/include -Iext/EASTL/test/packages/EABase/include/Common
CPP_FILES += ext/EASTL/source/allocator_eastl.cpp ext/EASTL/source/assert.cpp \
ext/EASTL/source/fixed_pool.cpp ext/EASTL/source/hashtable.cpp \
ext/EASTL/source/intrusive_list.cpp ext/EASTL/source/numeric_limits.cpp \
ext/EASTL/source/red_black_tree.cpp ext/EASTL/source/string.cpp
GDEFS =
LIBS =
OPTIMIZE = -Ofast -mfpmath=sse -msse3 #-march=native
## to enable LLVM / ThinLTO use these ##
#LD=ld.lld # path to your LLD binary
#LTO_DEFS=-flto=full # full or thin
OPTIONS=-m64 $(INCLUDE) $(GDEFS) $(OPTIMIZE) $(LTO_DEFS)
WARNS=-Wall -Wextra -pedantic
COMMON=-ffreestanding -nostdlib -MMD -fstack-protector-strong -fno-omit-frame-pointer $(OPTIONS) $(WARNS)
# inject some random numbers into a symbol so we can get some free random bits
SSP=$(shell hexdump -n 8 -e '4/4 "%08X" 1 "\n"' /dev/random)
LDFLAGS=-static -nostdlib -N -melf_x86_64 --strip-all --script=src/linker.ld --defsym __SSP__=0x$(SSP)
CFLAGS=-std=gnu11 $(COMMON)
CXXFLAGS=-std=c++14 -fno-exceptions -fno-rtti $(COMMON)
CFLAGS += $(SANITIZE)
CXXFLAGS += $(SANITIZE) #-fno-sanitize=function
COBJ=$(C_FILES:.c=.o)
CXXOBJ=$(CPP_FILES:.cpp=.o)
ASMOBJ=$(ASM_FILES:.asm=.o)
DEPS=$(CXXOBJ:.o=.d) $(COBJ:.o=.d)
.PHONY: clean all executable
%.o: %.asm
nasm -f elf64 -o $@ $<
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@
all: $(COBJ) $(CXXOBJ) $(ASMOBJ)
$(LD) $(LDFLAGS) $(COBJ) $(CXXOBJ) $(ASMOBJ) $(LIBS) -o $(OUT)
sanitize:
SANITIZE="-fsanitize=undefined -fno-sanitize=vptr" $(MAKE) all
chainloader: $(COBJ) $(CXXOBJ) $(ASMOBJ)
$(LD) $(LDFLAGS) $(COBJ) $(CXXOBJ) $(ASMOBJ) $(LIBS) -o chainloader
executable:
$(info $(OUT))
@true
clean:
$(RM) $(OUT) $(COBJ) $(CXXOBJ) $(ASMOBJ) $(DEPS)
-include $(DEPS)
gitextract_wcbe4rrg/
├── .gitignore
├── .gitmodules
├── README.md
├── barebones.cmake
├── build_iso.sh
├── ext/
│ └── CMakeLists.txt
├── grub/
│ └── grub.cfg
├── install_ubuntu_deps.sh
├── machines/
│ ├── 32bit/
│ │ ├── CMakeLists.txt
│ │ └── main.cpp
│ ├── default/
│ │ ├── CMakeLists.txt
│ │ └── main.cpp
│ ├── eastl/
│ │ ├── CMakeLists.txt
│ │ └── main.cpp
│ └── shared/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── library/
│ │ ├── CMakeLists.txt
│ │ ├── interface.hpp
│ │ └── library.cpp
│ └── main.cpp
├── run.sh
├── src/
│ ├── CMakeLists.txt
│ ├── crt/
│ │ ├── c_abi.c
│ │ ├── c_stubs.c
│ │ ├── cxxabi.cpp
│ │ ├── heap.c
│ │ ├── malloc.c
│ │ ├── print.c
│ │ ├── ubsan.c
│ │ └── udiv.c
│ ├── hw/
│ │ ├── serial.h
│ │ └── serial1.c
│ ├── kernel/
│ │ ├── dylib.hpp
│ │ ├── elf.cpp
│ │ ├── elf.hpp
│ │ ├── init_libc.c
│ │ ├── kernel_start.c
│ │ ├── panic.cpp
│ │ ├── start.asm
│ │ ├── start32.c
│ │ ├── start64.asm
│ │ ├── tls.cpp
│ │ └── tls.hpp
│ ├── kprint.h
│ ├── linker.ld
│ ├── main.h
│ └── multiboot.h
└── tools/
└── Makefile
SYMBOL INDEX (138 symbols across 26 files)
FILE: machines/32bit/main.cpp
function kernel_main (line 8) | void kernel_main(const uint32_t eax, const uint32_t ebx)
function my_cpp_constructor (line 37) | __attribute__((constructor))
function test_sse (line 42) | bool test_sse()
FILE: machines/default/main.cpp
function kernel_main (line 8) | void kernel_main(const uint32_t eax, const uint32_t ebx)
function my_cpp_constructor (line 37) | __attribute__((constructor))
function test_sse (line 42) | bool test_sse()
FILE: machines/eastl/main.cpp
class IdioticException (line 7) | class IdioticException : public std::exception
method IdioticException (line 11) | IdioticException(const char* reason) : oh_god(reason) {}
function kernel_main (line 22) | void kernel_main(uint32_t /*eax*/, uint32_t /*ebx*/)
class A (line 49) | class A
method f (line 52) | virtual void f() { kprintf("A::f() called\n"); }
class B (line 55) | class B : public A
method f (line 58) | void f() { kprintf("B::f() called\n"); }
function test_rtti (line 61) | static void test_rtti()
FILE: machines/shared/library/interface.hpp
type jumptable (line 4) | struct jumptable
FILE: machines/shared/library/library.cpp
type Test (line 4) | struct Test {
function test_function (line 10) | __attribute__((noinline))
function init (line 17) | int init(jumptable* t)
FILE: machines/shared/main.cpp
function kernel_main (line 12) | void kernel_main(const uint32_t, const uint32_t)
FILE: src/crt/c_abi.c
function __stack_chk_fail_local (line 10) | void __stack_chk_fail_local()
function __stack_chk_fail (line 15) | __attribute__((used))
function __assert_fail (line 21) | void __assert_fail(const char *assertion,
function __assert_func (line 33) | void __assert_func(
function _exit (line 46) | void _exit(int status)
function memcmp (line 82) | int memcmp(const void* ptr1, const void* ptr2, size_t n)
function ceilf (line 95) | float ceilf(float n)
function strlen (line 107) | size_t strlen(const char* str)
function strcmp (line 113) | int strcmp(const char* str1, const char* str2)
function isdigit (line 144) | int isdigit(int c)
function isalpha (line 148) | int isalpha(int c)
function isxdigit (line 152) | int isxdigit(int c)
function isupper (line 156) | int isupper(int c)
FILE: src/crt/c_stubs.c
function setenv (line 12) | int setenv(const char *name, const char *value, int overwrite) {
function dl_iterate_phdr (line 18) | int dl_iterate_phdr(
FILE: src/crt/cxxabi.cpp
function __cxa_pure_virtual (line 48) | void __cxa_pure_virtual()
FILE: src/crt/heap.c
function __init_heap (line 12) | void __init_heap(void* free_begin)
function posix_memalign (line 32) | int posix_memalign(void **memptr, size_t alignment, size_t size)
FILE: src/crt/malloc.c
function word_align (line 7) | static inline size_t word_align(size_t size) {
type chunk (line 11) | struct chunk {
type chunk (line 18) | struct chunk
type chunk (line 23) | struct chunk
function Chunk (line 36) | Chunk malloc_chunk_find(size_t s, Chunk *heap) {
function malloc_merge_next (line 42) | void malloc_merge_next(Chunk c) {
function malloc_split_next (line 50) | void malloc_split_next(Chunk c, size_t size) {
type chunk (line 66) | struct chunk
type chunk (line 76) | struct chunk
function free (line 87) | void free(void *ptr) {
FILE: src/crt/print.c
function kprintf (line 37) | int kprintf(const char* fmt, ...)
function snprintf (line 50) | __attribute__((format (printf, 3, 4)))
function vfprintf (line 63) | int vfprintf(FILE* fp, const char *format, va_list ap)
function fprintf (line 72) | __attribute__((format (printf, 2, 3)))
function __printf_chk (line 82) | __attribute__((format(printf, 2, 3)))
function __fprintf_chk (line 92) | int __fprintf_chk(FILE* fp, int flag, const char* format, ...)
function __vfprintf_chk (line 101) | int __vfprintf_chk(FILE* fp, int flag, const char *format, va_list ap)
function __vsprintf_chk (line 107) | int __vsprintf_chk(char* s, int flag, size_t slen, const char* format, v...
function __vsnprintf_chk (line 114) | int __vsnprintf_chk (char *s, size_t maxlen, int flags, size_t slen,
function __sprintf_chk (line 121) | __attribute__((format(printf, 4, 5)))
function __snprintf_chk (line 130) | int __snprintf_chk (char *s, size_t maxlen, int flags, size_t slen,
FILE: src/crt/ubsan.c
type source_location (line 7) | struct source_location {
type type_descriptor (line 14) | struct type_descriptor {
type out_of_bounds (line 24) | struct out_of_bounds {
type overflow (line 29) | struct overflow {
type mismatch (line 32) | struct mismatch {
type function_type_mismatch (line 38) | struct function_type_mismatch {
type nonnull_return (line 42) | struct nonnull_return {
type unreachable (line 46) | struct unreachable {
function print_src_location (line 50) | static void print_src_location(const struct source_location* src) {
function undefined_throw (line 55) | static void undefined_throw(const char* error) {
function __ubsan_handle_out_of_bounds (line 62) | void __ubsan_handle_out_of_bounds(struct out_of_bounds* data)
function __ubsan_handle_missing_return (line 67) | void __ubsan_handle_missing_return()
function __ubsan_handle_nonnull_return (line 71) | void __ubsan_handle_nonnull_return(struct nonnull_return* data)
function __ubsan_handle_add_overflow (line 77) | void __ubsan_handle_add_overflow()
function __ubsan_handle_sub_overflow (line 81) | void __ubsan_handle_sub_overflow()
function __ubsan_handle_mul_overflow (line 85) | void __ubsan_handle_mul_overflow()
function __ubsan_handle_negate_overflow (line 89) | void __ubsan_handle_negate_overflow()
function __ubsan_handle_pointer_overflow (line 93) | void __ubsan_handle_pointer_overflow()
function __ubsan_handle_divrem_overflow (line 97) | void __ubsan_handle_divrem_overflow(struct overflow* data,
function __ubsan_handle_float_cast_overflow (line 105) | void __ubsan_handle_float_cast_overflow()
function __ubsan_handle_shift_out_of_bounds (line 109) | void __ubsan_handle_shift_out_of_bounds()
function __ubsan_handle_type_mismatch_v1 (line 114) | void __ubsan_handle_type_mismatch_v1(struct mismatch* data, uintptr_t ptr)
function __ubsan_handle_function_type_mismatch (line 138) | void __ubsan_handle_function_type_mismatch(
function __ubsan_handle_nonnull_arg (line 154) | void __ubsan_handle_nonnull_arg()
function __ubsan_handle_invalid_builtin (line 159) | void __ubsan_handle_invalid_builtin()
function __ubsan_handle_load_invalid_value (line 163) | void __ubsan_handle_load_invalid_value()
function __ubsan_handle_builtin_unreachable (line 167) | __attribute__((noreturn))
FILE: src/crt/udiv.c
type su_int (line 3) | typedef unsigned su_int;
type di_int (line 4) | typedef long long di_int;
type du_int (line 5) | typedef unsigned long long du_int;
type udwords (line 7) | typedef union
function du_int (line 17) | du_int
function du_int (line 227) | du_int
function du_int (line 233) | du_int
FILE: src/hw/serial1.c
function inb (line 6) | static inline uint8_t inb(int port)
function outb (line 13) | static inline void outb(int port, uint8_t data)
function init_serial_if_needed (line 18) | static inline void init_serial_if_needed()
function __serial_print1 (line 31) | void __serial_print1(const char* cstr)
function __serial_print (line 39) | void __serial_print(const char* str, int len)
function fflush (line 52) | void fflush(void* fileno)
function __serial_putchr (line 59) | void __serial_putchr(const void* file, char c)
FILE: src/kernel/dylib.hpp
type Dylib (line 4) | struct Dylib
function Elf64_Ehdr (line 13) | inline Elf64_Ehdr* Dylib::load(const void* address)
function T (line 23) | inline T Dylib::resolve_function(const Elf64_Ehdr* hdr, const char* name)
FILE: src/kernel/elf.cpp
function T (line 19) | inline T* elf_offset(const Elf64_Ehdr* hdr, intptr_t ofs) {
function Elf64_Sym (line 23) | static const Elf64_Sym*
function Elf64_Shdr (line 31) | const Elf64_Shdr* Elf::section_by_name(const Elf64_Ehdr* hdr, const char...
function Elf64_Sym (line 47) | const Elf64_Sym* Elf::resolve_name(const Elf64_Ehdr* hdr, const char* name)
function elf_print_sym (line 69) | static void elf_print_sym(const Elf64_Sym* sym)
function elf_relocate_section (line 76) | static void elf_relocate_section(Elf64_Ehdr* hdr, const char* section_name)
FILE: src/kernel/elf.hpp
type Elf (line 4) | struct Elf
FILE: src/kernel/init_libc.c
function __init_stdlib (line 31) | void __init_stdlib(uint32_t mb_magic, uint32_t mb_addr)
FILE: src/kernel/kernel_start.c
function __init_paging (line 5) | static void __init_paging()
function kernel_start (line 20) | void kernel_start(uint32_t eax, uint32_t ebx)
FILE: src/kernel/panic.cpp
function print_trace (line 10) | static void print_trace(const int N, const void* ra)
function print_backtrace (line 19) | void print_backtrace()
function panic (line 35) | __attribute__((noreturn))
function abort (line 49) | void abort()
function abort_message (line 55) | void abort_message(const char* fmt, ...)
FILE: src/kernel/start32.c
function begin_enter_longmode (line 7) | void begin_enter_longmode()
FILE: src/kernel/tls.cpp
type tls_table (line 11) | struct tls_table
FILE: src/kernel/tls.hpp
type tls_table (line 4) | struct tls_table
FILE: src/kprint.h
function kprint (line 13) | static inline void kprint(const char* text)
FILE: src/multiboot.h
type multiboot_uint8_t (line 93) | typedef unsigned char multiboot_uint8_t;
type multiboot_uint16_t (line 94) | typedef unsigned short multiboot_uint16_t;
type multiboot_uint32_t (line 95) | typedef unsigned int multiboot_uint32_t;
type multiboot_uint64_t (line 96) | typedef unsigned long long multiboot_uint64_t;
type multiboot_header (line 98) | struct multiboot_header
type multiboot_aout_symbol_table (line 124) | struct multiboot_aout_symbol_table
type multiboot_aout_symbol_table_t (line 131) | typedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t;
type multiboot_elf_section_header_table (line 134) | struct multiboot_elf_section_header_table
type multiboot_elf_section_header_table_t (line 141) | typedef struct multiboot_elf_section_header_table multiboot_elf_section_...
type multiboot_info (line 143) | struct multiboot_info
type multiboot_info_t (line 220) | typedef struct multiboot_info multiboot_info_t;
type multiboot_color (line 222) | struct multiboot_color
type multiboot_mmap_entry (line 229) | struct multiboot_mmap_entry
type multiboot_memory_map_t (line 241) | typedef struct multiboot_mmap_entry multiboot_memory_map_t;
type multiboot_mod_list (line 243) | struct multiboot_mod_list
type multiboot_module_t (line 255) | typedef struct multiboot_mod_list multiboot_module_t;
type multiboot_apm_info (line 258) | struct multiboot_apm_info
Condensed preview — 48 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (87K chars).
[
{
"path": ".gitignore",
"chars": 46,
"preview": "*.o\n*.d\nchainloader\ntemp_disk\ngrub.iso\nbuild/\n"
},
{
"path": ".gitmodules",
"chars": 191,
"preview": "[submodule \"ext/EASTL\"]\n\tpath = ext/EASTL\n\turl = https://github.com/electronicarts/EASTL.git\n[submodule \"ext/tinyprintf\""
},
{
"path": "README.md",
"chars": 6935,
"preview": "# Barebones multiboot kernel\n\n- Intended for beginner OS development\n- Run the dependency installation script (or instal"
},
{
"path": "barebones.cmake",
"chars": 7184,
"preview": "option(NATIVE \"Compile natively for this CPU\" OFF)\noption(MINIMAL \"Compile small executable\" OFF)\noptio"
},
{
"path": "build_iso.sh",
"chars": 413,
"preview": "#!/bin/bash\nset -e\nMACHINE=machines/${1-default}\nBUILD_DIR=$MACHINE/build\n\nmkdir -p $BUILD_DIR\npushd $BUILD_DIR\ncmake .."
},
{
"path": "ext/CMakeLists.txt",
"chars": 629,
"preview": "\n# EASTL C++ library\nif (EASTL)\n\tadd_library(eastl STATIC\n\t\tEASTL/source/allocator_eastl.cpp EASTL/source/assert.cpp\n\t\tE"
},
{
"path": "grub/grub.cfg",
"chars": 120,
"preview": "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",
"chars": 139,
"preview": "#!/bin/bash\ngit submodule update --init --recursive\nsudo apt install build-essential g++-multilib nasm qemu xorriso cmak"
},
{
"path": "machines/32bit/CMakeLists.txt",
"chars": 313,
"preview": "cmake_minimum_required(VERSION 3.1)\nproject (kernel C CXX)\n\noption(BUILD_32 \"\" ON)\ninclude(../../barebones.cmake)\n\nadd_m"
},
{
"path": "machines/32bit/main.cpp",
"chars": 1284,
"preview": "#include <main.h>\n#include <assert.h>\n#include <kprint.h>\n#include <x86intrin.h>\nstatic bool test_sse();\nstatic int tes"
},
{
"path": "machines/default/CMakeLists.txt",
"chars": 282,
"preview": "cmake_minimum_required(VERSION 3.1)\nproject (kernel C CXX)\ninclude(../../barebones.cmake)\n\nadd_machine_image(\n\t# name, b"
},
{
"path": "machines/default/main.cpp",
"chars": 1284,
"preview": "#include <main.h>\n#include <assert.h>\n#include <kprint.h>\n#include <x86intrin.h>\nstatic bool test_sse();\nstatic int tes"
},
{
"path": "machines/eastl/CMakeLists.txt",
"chars": 282,
"preview": "cmake_minimum_required(VERSION 3.1)\nproject (kernel C CXX)\n\noption(EASTL \"\" ON)\noption(RTTI_EXCEPTIONS \"\" ON)\ninclude(.."
},
{
"path": "machines/eastl/main.cpp",
"chars": 1434,
"preview": "#include <main.h>\n#include <assert.h>\n#include <kprint.h>\n#include <exception>\n\n#include <EASTL/vector.h>\nclass IdioticE"
},
{
"path": "machines/shared/CMakeLists.txt",
"chars": 442,
"preview": "cmake_minimum_required(VERSION 3.1)\nproject (kernel C CXX)\ninclude(../../barebones.cmake)\n\nadd_machine_image(\n\t# name, b"
},
{
"path": "machines/shared/README.md",
"chars": 535,
"preview": "## Machine with shared library loader\n\nThis tiny machine allows you to call into a function in a shared library, and jus"
},
{
"path": "machines/shared/library/CMakeLists.txt",
"chars": 643,
"preview": "cmake_minimum_required(VERSION 3.1)\nproject(mylib VERSION 1.0 DESCRIPTION \"mylib description\")\n\nadd_library(mylib SHARED"
},
{
"path": "machines/shared/library/interface.hpp",
"chars": 198,
"preview": "#pragma once\n#include <cstddef>\n\nstruct jumptable\n{\n\tint (*kprintf)(const char* string, ...);\n\tvoid* (*malloc) (size_t"
},
{
"path": "machines/shared/library/library.cpp",
"chars": 639,
"preview": "#include \"interface.hpp\"\nstatic int test_value = 666;\n\nstruct Test {\n\tint a;\n};\nTest test;\n\nextern \"C\"\n__attribute__((no"
},
{
"path": "machines/shared/main.cpp",
"chars": 821,
"preview": "#include <main.h>\n#include <kprint.h>\n#include <cassert>\n#include <cstring>\n#include <cstdlib>\n\nextern char _binary_myli"
},
{
"path": "run.sh",
"chars": 1049,
"preview": "#!/bin/bash\nset -e\nGRAPHICS=-nographic\n\nfor i in \"$@\"\ndo\ncase $i in\n --kvm)\n KVM=\"--enable-kvm -cpu host\"\n shif"
},
{
"path": "src/CMakeLists.txt",
"chars": 841,
"preview": "\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\tcr"
},
{
"path": "src/crt/c_abi.c",
"chars": 3302,
"preview": "#include <kprint.h>\n#include <assert.h>\n#include <stddef.h>\n#include <stdint.h>\n#include <stdio.h>\n\n__attribute__((noret"
},
{
"path": "src/crt/c_stubs.c",
"chars": 451,
"preview": "#define _GNU_SOURCE\n#include <stddef.h>\n#include <dlfcn.h>\n#include <link.h>\n#include <kprint.h>\n\nchar *getenv(const cha"
},
{
"path": "src/crt/cxxabi.cpp",
"chars": 1781,
"preview": "#include <kprint.h>\n#include <new>\n#include <cstddef>\n#include <cstring>\nextern \"C\" void* malloc(size_t);\nextern \"C\" voi"
},
{
"path": "src/crt/heap.c",
"chars": 961,
"preview": "#include <assert.h>\n#include <stddef.h>\n#include <stdint.h>\n#include <stdlib.h>\n\nstatic uintptr_t heap_start;\nstatic uin"
},
{
"path": "src/crt/malloc.c",
"chars": 3097,
"preview": "//\n// As written by Snaipe @ https://github.com/Snaipe/malloc\n//\n#include <unistd.h>\n#include <errno.h>\n\nstatic inline s"
},
{
"path": "src/crt/print.c",
"chars": 3058,
"preview": "#include <assert.h>\n#include <kprint.h>\n#include <stdarg.h>\n#include <stdio.h>\nFILE* stderr = NULL;\nFILE* stdout = NULL;"
},
{
"path": "src/crt/ubsan.c",
"chars": 4237,
"preview": "#include <stdint.h>\n#include <stdio.h>\n#include <kprint.h>\nextern void panic(const char*) __attribute__((noreturn));\next"
},
{
"path": "src/crt/udiv.c",
"chars": 6369,
"preview": "#include <limits.h>\n#include <stdint.h>\ntypedef unsigned su_int;\ntypedef long long di_int;\ntypedef unsigned lon"
},
{
"path": "src/hw/serial.h",
"chars": 144,
"preview": "#pragma once\n\nextern void __serial_print1(const char*);\nextern void __serial_print(const char*, int);\nextern void __seri"
},
{
"path": "src/hw/serial1.c",
"chars": 1481,
"preview": "#include <stdint.h>\n\nstatic const uint16_t port = 0x3F8; // Serial port 1\nstatic char initialized = 0;\n\nstatic inline ui"
},
{
"path": "src/kernel/dylib.hpp",
"chars": 679,
"preview": "#pragma once\n#include <kernel/elf.hpp>\n\nstruct Dylib\n{\n\tstatic Elf64_Ehdr* load(const void* address);\n\n\ttemplate <typena"
},
{
"path": "src/kernel/elf.cpp",
"chars": 3172,
"preview": "#include \"elf.hpp\"\n#include <cassert>\n#include <cstdint>\n#include <cstring>\n#include <kprint.h>\nstatic constexpr bool DE"
},
{
"path": "src/kernel/elf.hpp",
"chars": 355,
"preview": "#pragma once\n#include <elf.h>\n\nstruct Elf\n{\n\tstatic bool validate(Elf64_Ehdr* hdr);\n\tstatic const Elf64_Shdr* section_by"
},
{
"path": "src/kernel/init_libc.c",
"chars": 1583,
"preview": "#include <assert.h>\n#include <kprint.h>\n#include <stddef.h>\n#include <stdint.h>\n#include <multiboot.h>\n\nextern size_t st"
},
{
"path": "src/kernel/kernel_start.c",
"chars": 867,
"preview": "#include <assert.h>\n#include <stdint.h>\n#include <kprint.h>\n\nstatic void __init_paging()\n{\n\tstatic uintptr_t pdir0[512] "
},
{
"path": "src/kernel/panic.cpp",
"chars": 1169,
"preview": "#include <kprint.h>\n#include <cstdio>\n// less risky when the stack is blown out\nstatic char buffer[4096];\n\n#define frp(N"
},
{
"path": "src/kernel/start.asm",
"chars": 3314,
"preview": ";; stack base address at EBDA border\n;; NOTE: Multiboot can use 9d400 to 9ffff\n%define STACK_LOCATION 0x9D400\n\n;; m"
},
{
"path": "src/kernel/start32.c",
"chars": 247,
"preview": "#include <stdint.h>\n\nextern uint32_t multiboot_data_magic;\nextern uint32_t multiboot_data_address;\nextern void kernel_st"
},
{
"path": "src/kernel/start64.asm",
"chars": 4759,
"preview": "[BITS 32]\nglobal begin_enter_longmode:function\nextern __serial_print1\nextern kernel_start\n\n%define STACK_LOCATION "
},
{
"path": "src/kernel/tls.cpp",
"chars": 358,
"preview": "#include <cstddef>\n#include \"tls.hpp\"\n#ifdef __x86_64__\nstatic_assert(offsetof(tls_table, guard) == 0x28, \"TLS is at 0x2"
},
{
"path": "src/kernel/tls.hpp",
"chars": 219,
"preview": "#pragma once\n#include <cstdint>\n\nstruct tls_table\n{\n // thread self-pointer\n void* tls_data; // 0x0\n // per-cpu cpuid"
},
{
"path": "src/kprint.h",
"chars": 363,
"preview": "#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <hw/serial.h>\n\n// better, more familiar way to print!\next"
},
{
"path": "src/linker.ld",
"chars": 1768,
"preview": "/**\n * Inspired from the IncludeOS unikernel (https://github.com/hioa-cs/IncludeOS)\n *\n * This is the smallest possible "
},
{
"path": "src/main.h",
"chars": 156,
"preview": "#pragma once\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nextern void kernel_main(uint32_t eax, uint32_t"
},
{
"path": "src/multiboot.h",
"chars": 8476,
"preview": "/* Copyright (C) 1999,2003,2007,2008,2009,2010 Free Software Foundation, Inc.\n *\n * Permission is hereby granted, free"
},
{
"path": "tools/Makefile",
"chars": 2589,
"preview": "#\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 u"
}
]
About this extraction
This page contains the full source code of the fwsGonzo/barebones GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 48 files (78.8 KB), approximately 24.9k tokens, and a symbol index with 138 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.