diff --git a/.gitignore b/.gitignore index 59833ad8..2841a406 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ /**/*.o +/**/*.a +/**/*.bc /**/*.bin +/**/*.elf /**/*.img /bochsout.txt /bx_enh_dbg.ini diff --git a/Makefile b/Makefile index 0eb741ee..5e102cde 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,32 @@ +DESTDIR = +PREFIX = /usr/local + OS := $(shell uname) -CC = cc +CC = clang +OBJCOPY = llvm-objcopy +CFLAGS = -O2 -pipe -Wall -Wextra -.PHONY: all clean echfs-test ext2-test test.img +.PHONY: all install clean echfs-test ext2-test test.img -all: - $(MAKE) -C src all - cp src/limine.bin ./ +all: limine-install + +install: all + install -s limine-install $(DESTDIR)$(PREFIX)/bin/ clean: + rm -f limine-install $(MAKE) -C src clean -limine-install: limine-install.c - $(CC) limine-install.c -o limine-install +src/limine.bin: + $(MAKE) -C src all + +limine-install: src/limine.bin limine-install.c + $(CC) $(CFLAGS) -c limine-install.c -o limine-install.o + # FIXME: GNU objcopy supports `-O default` but for some stupid reason + # llvm-objcopy does not. This needs to be worked around. + # For now hardcode elf64-x86-64. + $(OBJCOPY) -I binary -O elf64-x86-64 src/limine.bin limine.o + $(CC) $(CFLAGS) limine.o limine-install.o -o limine-install test.img: rm -f test.img @@ -26,15 +41,15 @@ else ifeq ($(OS), FreeBSD) sudo mdconfig -d -u md9 endif -echfs-test: limine-install test.img all +echfs-test: limine-install test.img $(MAKE) -C test echfs-utils -m -p0 test.img quick-format 32768 echfs-utils -m -p0 test.img import test/test.elf boot/test.elf echfs-utils -m -p0 test.img import test/limine.cfg limine.cfg - ./limine-install src/limine.bin test.img + ./limine-install test.img qemu-system-x86_64 -hda test.img -debugcon stdio -enable-kvm -ext2-test: limine-install test.img all +ext2-test: limine-install test.img $(MAKE) -C test rm -rf test_image/ mkdir test_image @@ -49,10 +64,10 @@ ext2-test: limine-install test.img all sudo umount test_image/ sudo losetup -d `cat loopback_dev` rm -rf test_image loopback_dev - ./limine-install src/limine.bin test.img + ./limine-install test.img qemu-system-x86_64 -hda test.img -debugcon stdio -fat32-test: limine-install test.img all +fat32-test: limine-install test.img $(MAKE) -C test rm -rf test_image/ mkdir test_image @@ -77,5 +92,5 @@ else ifeq ($(OS), FreeBSD) sudo mdconfig -d -u md9 endif rm -rf test_image loopback_dev - ./limine-install src/limine.bin test.img + ./limine-install test.img qemu-system-x86_64 -hda test.img -debugcon stdio diff --git a/README.md b/README.md index 5a5d93c5..8555dbac 100644 --- a/README.md +++ b/README.md @@ -16,23 +16,35 @@ x86/x86_64 BIOS Bootloader * MBR * GPT -## How to use -This repository contains a prebuilt version of Limine so building it won't be necessary. +## Building -In order to install Limine on a MBR device (which can just be a raw image file), build the -`limine-install` program using `make limine-install`, then run the resulting executable as such: +### Dependencies +To build Limine, it is necessary to have an LLVM/Clang toolchain installed. +More specifically, the following programs need to be present: `clang`, `llvm-objcopy`, +`llvm-link`, `opt`, `ld.lld`. +Furthermore, `nasm` also needs to be installed. +`curl`, `tar`, and `zstd` need to be installed for retrieving `libgcc.a` during build. + +### Compiling +A simple `make` and `make install` will suffice. Use the PREFIX variable with +`make install` to specify where to install `limine-install`. It defaults to +`/usr/local`. + +## How to use +In order to install Limine on a MBR device (which can just be a raw image file), +run the `limine-install` as such: ```bash -./limine-install ./limine.bin +limine-install ``` If using a GPT formatted device, it will be necessary to create an extra partition (of at least 32K in size) to store stage 2 code. Then it will be necessary to tell -the install script where this partition is located by specifying the start sector. +`limine-install` where this partition is located by specifying the start sector. ```bash fdisk # Create bootloader partition using your favourite method -./limine-install ./limine.bin +limine-install ``` Then make sure the device/image contains at least 1 partition formatted in @@ -58,33 +70,12 @@ echfs-utils -m -p0 test.img import path/to/limine.cfg limine.cfg echfs-utils -m -p0 test.img import path/to/kernel.elf kernel.elf echfs-utils -m -p0 test.img import ... -./limine-install $THIS_REPO/limine.bin test.img +limine-install test.img ``` One can get `echfs-utils` by installing https://github.com/qword-os/echfs. -## Building from source -In order to hack Limine, one must build the GCC toolchain from source first. - -To do so, run the `make_toolchain.sh` script from within the `toolchain` directory; -keep in mind that the script takes `MAKEFLAGS` as an argument. - -```bash -cd toolchain -./make_toolchain.sh -j4 -``` - -After that is done, simply run `make` in the root of the repo to generate -`limine.bin`. - -### Building from source with Clang -It is also possible to build Limine with Clang, using the following make command: - -```bash -make CC="clang --target=i386-elf" -``` - ## Discord server We have a Discord server if you need support, info, or you just want to hang out: https://discord.gg/QEeZMz4 diff --git a/limine-install.c b/limine-install.c index 6afd3187..b32e895a 100644 --- a/limine-install.c +++ b/limine-install.c @@ -4,53 +4,45 @@ #include #include +extern char _binary_src_limine_bin_start[]; + int main(int argc, char *argv[]) { - if (argc < 3) { - printf("Usage: %s [stage2 start sector]\n", argv[0]); + if (argc < 2) { + printf("Usage: %s [stage2 start sector]\n", argv[0]); return 1; } - FILE *ql2_bin = fopen(argv[1], "rb"); - if (ql2_bin == NULL) { - perror("Error: "); - return 1; - } - - FILE *device = fopen(argv[2], "r+b"); + FILE *device = fopen(argv[1], "r+b"); if (device == NULL) { perror("Error: "); - fclose(ql2_bin); return 1; } uint32_t stage2_sect = 1; - if (argc >= 4) - sscanf(argv[3], "%" SCNu32, &stage2_sect); + if (argc >= 3) + sscanf(argv[2], "%" SCNu32, &stage2_sect); + // Save the original partition table of the device char orig_mbr[64]; fseek(device, 446, SEEK_SET); fread(orig_mbr, 1, 64, device); - char ql2_bootsect[512]; - fseek(ql2_bin, 0, SEEK_SET); - fread(ql2_bootsect, 1, 512, ql2_bin); + // Write the bootsector from the bootloader to the device fseek(device, 0, SEEK_SET); - fwrite(ql2_bootsect, 1, 512, device); + fwrite(&_binary_src_limine_bin_start[0], 1, 512, device); - char *ql2_stage2 = malloc(63 * 512); - fseek(ql2_bin, 512, SEEK_SET); - fread(ql2_stage2, 63, 512, ql2_bin); + // Write the rest of stage 2 to the device fseek(device, stage2_sect * 512, SEEK_SET); - fwrite(ql2_stage2, 63, 512, device); - free(ql2_stage2); + fwrite(&_binary_src_limine_bin_start[512], 63, 512, device); + // Hardcode in the bootsector the location of stage 2 fseek(device, 0x1b0, SEEK_SET); - fwrite(&stage2_sect, 1, 4, device); + fwrite(&stage2_sect, 1, sizeof(uint32_t), device); + // Write back the saved partition table to the device fseek(device, 446, SEEK_SET); fwrite(orig_mbr, 1, 64, device); - fclose(ql2_bin); fclose(device); return 0; diff --git a/limine.bin b/limine.bin deleted file mode 100644 index e5e6b961..00000000 Binary files a/limine.bin and /dev/null differ diff --git a/src/Makefile b/src/Makefile index 0679154e..5230d543 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,46 +1,50 @@ -CC = ../toolchain/bin/i386-elf-gcc -LD = ../toolchain/bin/i386-elf-gcc - -CFLAGS = -flto -Os -pipe -Wall -Wextra +OPT_LEVEL = z +CFLAGS = -pipe -Wall -Wextra INTERNAL_CFLAGS = \ + -O$(OPT_LEVEL) \ -std=gnu99 \ -ffreestanding \ - -fno-pic \ + -flto \ -mno-80387 \ -mno-mmx \ -mno-sse \ -mno-sse2 \ - -fno-stack-protector \ -I. \ -Wno-address-of-packed-member -LDFLAGS = -flto -Os - INTERNAL_LDFLAGS = \ + -static \ -nostdlib \ - -no-pie \ - -lgcc \ - -static-libgcc \ - -Tlinker.ld + -Tlinker.ld \ + -no-pie .PHONY: all clean C_FILES := $(shell find ./ -type f -name '*.c' | sort) ASM_FILES := $(shell find ./ -type f -name '*.asm' | grep -v bootsect | sort) -OBJ := $(C_FILES:.c=.o) $(ASM_FILES:.asm=.o) +ASM_OBJ := $(ASM_FILES:.asm=.o) +BC := $(C_FILES:.c=.bc) all: limine.bin -limine.bin: $(OBJ) - $(LD) $(OBJ) $(LDFLAGS) $(INTERNAL_LDFLAGS) -o stage2.bin +limine.bin: libgcc.a $(BC) $(ASM_OBJ) + llvm-link $(BC) -o bundle.bc + opt --O$(OPT_LEVEL) bundle.bc -o optimised_bundle.bc + clang --target=i386-elf -O$(OPT_LEVEL) -c optimised_bundle.bc -o optimised_bundle.o + ld.lld optimised_bundle.o $(ASM_OBJ) libgcc.a $(INTERNAL_LDFLAGS) -o stage2.elf + llvm-objcopy -O binary stage2.elf stage2.bin cd bootsect && nasm bootsect.asm -fbin -o ../limine.bin -%.o: %.c - $(CC) $(CFLAGS) $(INTERNAL_CFLAGS) -c $< -o $@ +libgcc.a: + curl https://mirror.netcologne.de/archlinux/core/os/x86_64/gcc-10.2.0-2-x86_64.pkg.tar.zst | \ + tar -I zstd -xv --strip-components=6 --occurrence=1 usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/32/libgcc.a + +%.bc: %.c + clang --target=i386-elf $(CFLAGS) $(INTERNAL_CFLAGS) -c $< -o $@ %.o: %.asm nasm $< -f elf32 -o $@ clean: - rm -f $(OBJ) + rm -f limine.bin $(ASM_OBJ) $(BC) diff --git a/src/linker.ld b/src/linker.ld index 2c5167f5..2c8efd1b 100644 --- a/src/linker.ld +++ b/src/linker.ld @@ -1,4 +1,4 @@ -OUTPUT_FORMAT(binary) +OUTPUT_FORMAT(elf32-i386) ENTRY(main) SECTIONS diff --git a/src/main.c b/src/main.c index f541b02e..08ee961f 100644 --- a/src/main.c +++ b/src/main.c @@ -3,6 +3,8 @@ ASM_BASIC( ".section .entry\n\t" + "cld\n\t" + // Zero out .bss "xor al, al\n\t" "mov edi, OFFSET bss_begin\n\t" @@ -10,7 +12,8 @@ ASM_BASIC( "sub ecx, OFFSET bss_begin\n\t" "rep stosb\n\t" - "jmp main\n\t" + "mov ebx, OFFSET main\n\t" + "jmp ebx\n\t" ); #include diff --git a/toolchain/.gitignore b/toolchain/.gitignore deleted file mode 100644 index fd038def..00000000 --- a/toolchain/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -* -!.gitignore -!make_toolchain.sh diff --git a/toolchain/make_toolchain.sh b/toolchain/make_toolchain.sh deleted file mode 100755 index 00eb5230..00000000 --- a/toolchain/make_toolchain.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env bash - -set -e -set -x - -PREFIX="$(pwd)" -TARGET=i386-elf -BINUTILSVERSION=2.35 -GCCVERSION=10.2.0 - -if [ -z "$MAKEFLAGS" ]; then - MAKEFLAGS="$1" -fi -export MAKEFLAGS - -export PATH="$PREFIX/bin:$PATH" - -if [ -x "$(command -v gmake)" ]; then - mkdir -p "$PREFIX/bin" - cat <"$PREFIX/bin/make" -#!/usr/bin/env sh -gmake "\$@" -EOF - chmod +x "$PREFIX/bin/make" -fi - -mkdir -p build -cd build - -if [ ! -f binutils-$BINUTILSVERSION.tar.gz ]; then - wget -4 https://ftp.gnu.org/gnu/binutils/binutils-$BINUTILSVERSION.tar.gz # Force IPv4 otherwise wget hangs -fi -if [ ! -f gcc-$GCCVERSION.tar.gz ]; then - wget -4 https://ftp.gnu.org/gnu/gcc/gcc-$GCCVERSION/gcc-$GCCVERSION.tar.gz # Same as above -fi - -tar -xf binutils-$BINUTILSVERSION.tar.gz -tar -xf gcc-$GCCVERSION.tar.gz - -rm -rf build-gcc build-binutils - -mkdir build-binutils -cd build-binutils -../binutils-$BINUTILSVERSION/configure --target=$TARGET --prefix="$PREFIX" --with-sysroot --disable-nls --disable-werror -make -make install -cd .. - -cd gcc-$GCCVERSION -contrib/download_prerequisites -cd .. -mkdir build-gcc -cd build-gcc -../gcc-$GCCVERSION/configure --target=$TARGET --prefix="$PREFIX" --disable-nls --enable-languages=c --without-headers -make all-gcc -make all-target-libgcc -make install-gcc -make install-target-libgcc