Move to clang, no longer ship prebuilt binary, embed bootloader into the installer and provide a way to install the installer
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
+
+### 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.
-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:
+### 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 <path to device/image>
+limine-install <path to device/image>
```
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 <device> # Create bootloader partition using your favourite method
-./limine-install ./limine.bin <path to device/image> <start sector of boot partition>
+limine-install <path to device/image> <start sector of boot partition>
```
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 <path to file> <path in image>
...
-./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 <stddef.h>
#include <inttypes.h>
-int main(int argc, char *argv[]) {
- if (argc < 3) {
- printf("Usage: %s <path to bootloader binary> <device> [stage2 start sector]\n", argv[0]);
- return 1;
- }
+extern char _binary_src_limine_bin_start[];
- FILE *ql2_bin = fopen(argv[1], "rb");
- if (ql2_bin == NULL) {
- perror("Error: ");
+int main(int argc, char *argv[]) {
+ if (argc < 2) {
+ printf("Usage: %s <device> [stage2 start sector]\n", argv[0]);
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 <limine.h>
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 <<EOF >"$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
