feat: musl boot

This commit is contained in:
Jake Hamilton 2024-06-06 22:49:52 -07:00
parent b99c63a123
commit 8e27d6d87e
Signed by: jakehamilton
GPG key ID: 9762169A1B35EA68
18 changed files with 826 additions and 27 deletions

View file

@ -13,8 +13,9 @@
modules = import ./src;
forEachSystem = lib.attrs.generate [
"x86_64-linux"
"aarch64-linux"
# "x86_64-linux"
# "aarch64-linux"
"i686-linux"
# "x86_64-darwin"
# "aarch64-darwin"
];

View file

@ -0,0 +1,77 @@
{
lib,
config,
}: let
system = config.aux.system;
builders = config.aux.foundation.builders;
stage0 = config.aux.foundation.stages.stage0;
stage1 = config.aux.foundation.stages.stage1;
in {
options.aux.foundation.builders.bash.boot = {
build = lib.options.create {
type = lib.types.function lib.types.package;
description = "Builds a package using the kaem builder.";
};
};
config = {
aux.foundation.builders.bash.boot = {
build = lib.modules.overrides.default (settings @ {
name,
script,
meta ? {},
extras ? {},
env ? {},
deps ? {},
...
}: let
package = builtins.derivation (
(builtins.removeAttrs settings ["meta" "extras" "executable" "env" "deps" "script"])
// env
// {
inherit name system script;
passAsFile = ["script"];
builder = "${stage1.bash.boot.package}/bin/bash";
args = [
"-e"
(builtins.toFile "bash-builder.sh" ''
export CONFIG_SHELL=$SHELL
# Normalize the NIX_BUILD_CORES variable. The value might be 0, which
# means that we're supposed to try and auto-detect the number of
# available CPU cores at run-time. We don't have nproc to detect the
# number of available CPU cores so default to 1 if not set.
NIX_BUILD_CORES="''${NIX_BUILD_CORES:-1}"
if [ $NIX_BUILD_CORES -le 0 ]; then
NIX_BUILD_CORES=1
fi
export NIX_BUILD_CORES
bash -eux $scriptPath
'')
];
SHELL = "${stage1.bash.boot.package}/bin/bash";
PATH = lib.paths.bin (
(deps.build.host or [])
++ [
stage1.bash.boot.package
stage1.coreutils.boot.package
stage0.mescc-tools-extra.package
]
);
}
);
in
package
// {
inherit meta extras;
});
};
};
}

View file

@ -0,0 +1,22 @@
{
lib,
config,
}: let
system = config.aux.system;
builders = config.aux.foundation.builders;
stage0 = config.aux.foundation.stages.stage0;
in {
includes = [
./boot.nix
];
options.aux.foundation.builders.bash = {
# TODO: Bash builder that isn't boot.
};
config = {
aux.foundation.builders.bash = {
};
};
}

View file

@ -1,5 +1,6 @@
let
modules = {
builderBash = builders/bash;
builderFileText = ./builders/file/text;
builderKaem = ./builders/kaem;
builderRaw = ./builders/raw;

View file

@ -287,6 +287,11 @@
};
in {
options.aux.platform = {
name = lib.options.create {
type = lib.types.string;
description = "Name of the platform";
};
family = lib.options.create {
type = lib.types.string;
description = "Family of the platform";
@ -314,11 +319,31 @@ in {
default.value = null;
description = "Version of the platform";
};
build = lib.options.create {
type = lib.types.string;
description = "The build entry, such as x86-unknown-linux-gnu.";
};
host = lib.options.create {
type = lib.types.string;
description = "The host entry, such as x86-unknown-linux-gnu.";
};
};
config = {
aux.platform =
(
platforms.${platform}
or (builtins.throw "Unsupported platform: ${system}");
or (builtins.throw "Unsupported platform: ${system}")
)
// {
name = platform;
# These will only ever have `linux` as the target since we
# do not support darwin bootstrapping.
build = "${platform}-unknown-${target}-gnu";
host = "${platform}-unknown-${target}-gnu";
};
};
}

View file

@ -114,8 +114,9 @@ in {
executable = blood-elf.package;
args = [
args =
(lib.lists.when (config.aux.platform.bits == 64) "--64")
++ [
"-f"
hex2_linker_M1
(

View file

@ -116,8 +116,9 @@ in {
executable = blood-elf.package;
args = [
args =
(lib.lists.when (config.aux.platform.bits == 64) "--64")
++ [
"-f"
kaem_M1
(

View file

@ -7,6 +7,7 @@
stage1 = config.aux.foundation.stages.stage1;
in {
includes = [
# These packages are built using Kaem.
./nyacc
./mes
./ln-boot
@ -15,6 +16,13 @@ in {
./gnumake
./coreutils
./bash
# These packages are built using Bash v2.
./gnused
./gnugrep
./gnutar
./gzip
./musl
];
config = {
@ -33,6 +41,12 @@ in {
stage1-gnumake = stage1.gnumake.package;
stage1-coreutils-boot = stage1.coreutils.boot.package;
stage1-bash-boot = stage1.bash.boot.package;
stage1-gnused-boot = stage1.gnused.boot.package;
stage1-gnugrep = stage1.gnugrep.package;
stage1-gnutar-boot = stage1.gnutar.boot.package;
stage1-gzip = stage1.gzip.package;
stage1-musl-boot = stage1.musl.boot.package;
};
extras = {

View file

@ -0,0 +1,106 @@
{
lib,
config,
}: let
cfg = config.aux.foundation.stages.stage1.gnugrep;
builders = config.aux.foundation.builders;
stage1 = config.aux.foundation.stages.stage1;
in {
options.aux.foundation.stages.stage1.gnugrep = {
package = lib.options.create {
type = lib.types.package;
description = "The package to use for gnugrep.";
};
version = lib.options.create {
type = lib.types.string;
description = "Version of the package.";
};
src = lib.options.create {
type = lib.types.package;
description = "Source for the package.";
};
meta = {
description = lib.options.create {
type = lib.types.string;
description = "Description for the package.";
default.value = "GNU implementation of the Unix grep command";
};
homepage = lib.options.create {
type = lib.types.string;
description = "Homepage for the package.";
default.value = "https://www.gnu.org/software/grep";
};
license = lib.options.create {
# TODO: Add a proper type for licenses.
type = lib.types.attrs.any;
description = "License for the package.";
default.value = lib.licenses.gpl3Plus;
};
platforms = lib.options.create {
type = lib.types.list.of lib.types.string;
description = "Platforms the package supports.";
default.value = ["x86_64-linux" "aarch64-linux" "i686-linux"];
};
mainProgram = lib.options.create {
type = lib.types.string;
description = "The main program of the package.";
default.value = "grep";
};
};
};
config = {
aux.foundation.stages.stage1.gnugrep = {
version = "2.4";
src = builtins.fetchurl {
url = "https://ftpmirror.gnu.org/grep/grep-${cfg.version}.tar.gz";
sha256 = "05iayw5sfclc476vpviz67hdy03na0pz2kb5csa50232nfx34853";
};
package = let
# Thanks to the live-bootstrap project!
# See https://github.com/fosslinux/live-bootstrap/blob/1bc4296091c51f53a5598050c8956d16e945b0f5/sysa/grep-2.4
makefile = builtins.fetchurl {
url = "https://github.com/fosslinux/live-bootstrap/raw/1bc4296091c51f53a5598050c8956d16e945b0f5/sysa/grep-2.4/mk/main.mk";
sha256 = "08an9ljlqry3p15w28hahm6swnd3jxizsd2188przvvsj093j91k";
};
in
builders.bash.boot.build {
name = "gnused-boot-${cfg.version}";
meta = cfg.meta;
deps.build.host = [
stage1.tinycc.mes.compiler.package
stage1.gnumake.package
];
script = ''
# Unpack
ungz --file ${cfg.src} --output grep.tar
untar --file grep.tar
rm grep.tar
cd grep-${cfg.version}
# Configure
cp ${makefile} Makefile
# Build
make CC="tcc -B ${stage1.tinycc.mes.libs.package}/lib"
# Install
make install PREFIX=$out
'';
};
};
};
}

View file

@ -0,0 +1,77 @@
{
lib,
config,
}: let
cfg = config.aux.foundation.stages.stage1.gnused.boot;
builders = config.aux.foundation.builders;
stage1 = config.aux.foundation.stages.stage1;
in {
options.aux.foundation.stages.stage1.gnused.boot = {
package = lib.options.create {
type = lib.types.package;
description = "The package to use for gnused-boot.";
};
version = lib.options.create {
type = lib.types.string;
description = "Version of the package.";
};
src = lib.options.create {
type = lib.types.package;
description = "Source for the package.";
};
};
config = {
aux.foundation.stages.stage1.gnused.boot = {
version = "4.0.9";
src = builtins.fetchurl {
url = "https://ftpmirror.gnu.org/sed/sed-${cfg.version}.tar.gz";
sha256 = "0006gk1dw2582xsvgx6y6rzs9zw8b36rhafjwm288zqqji3qfrf3";
};
package = let
# Thanks to the live-bootstrap project!
# See https://github.com/fosslinux/live-bootstrap/blob/1bc4296091c51f53a5598050c8956d16e945b0f5/sysa/sed-4.0.9/sed-4.0.9.kaem
makefile = builtins.fetchurl {
url = "https://github.com/fosslinux/live-bootstrap/raw/1bc4296091c51f53a5598050c8956d16e945b0f5/sysa/sed-4.0.9/mk/main.mk";
sha256 = "0w1f5ri0g5zla31m6l6xyzbqwdvandqfnzrsw90dd6ak126w3mya";
};
in
builders.bash.boot.build {
name = "gnused-boot-${cfg.version}";
meta = stage1.gnused.meta;
deps.build.host = [
stage1.tinycc.mes.compiler.package
stage1.gnumake.package
];
script = ''
# Unpack
ungz --file ${cfg.src} --output sed.tar
untar --file sed.tar
rm sed.tar
cd sed-${cfg.version}
# Configure
cp ${makefile} Makefile
catm config.h
# Build
make \
CC="tcc -B ${stage1.tinycc.mes.libs.package}/lib" \
LIBC=mes
# Install
make install PREFIX=$out
'';
};
};
};
}

View file

@ -0,0 +1,49 @@
{
lib,
config,
}: let
cfg = config.aux.foundation.stages.stage1.gnused;
builders = config.aux.foundation.builders;
stage1 = config.aux.foundation.stages.gnused;
in {
includes = [
./boot.nix
];
options.aux.foundation.stages.stage1.gnused = {
meta = {
description = lib.options.create {
type = lib.types.string;
description = "Description for the package.";
default.value = "GNU sed, a batch stream editor.";
};
homepage = lib.options.create {
type = lib.types.string;
description = "Homepage for the package.";
default.value = "https://www.gnu.org/software/sed";
};
license = lib.options.create {
# TODO: Add a proper type for licenses.
type = lib.types.attrs.any;
description = "License for the package.";
default.value = lib.licenses.gpl3Plus;
};
platforms = lib.options.create {
type = lib.types.list.of lib.types.string;
description = "Platforms the package supports.";
default.value = ["x86_64-linux" "aarch64-linux" "i686-linux"];
};
mainProgram = lib.options.create {
type = lib.types.string;
description = "The main program of the package.";
default.value = "sed";
};
};
};
}

View file

@ -0,0 +1,76 @@
{
lib,
config,
}: let
cfg = config.aux.foundation.stages.stage1.gnutar.boot;
platform = config.aux.platform;
builders = config.aux.foundation.builders;
stage1 = config.aux.foundation.stages.stage1;
in {
options.aux.foundation.stages.stage1.gnutar.boot = {
package = lib.options.create {
type = lib.types.package;
description = "The package to use for gnutar-boot.";
};
version = lib.options.create {
type = lib.types.string;
description = "Version of the package.";
};
src = lib.options.create {
type = lib.types.package;
description = "Source for the package.";
};
};
config = {
aux.foundation.stages.stage1.gnutar.boot = {
version = "1.12";
src = builtins.fetchurl {
url = "https://ftpmirror.gnu.org/tar/tar-${cfg.version}.tar.gz";
sha256 = "02m6gajm647n8l9a5bnld6fnbgdpyi4i3i83p7xcwv0kif47xhy6";
};
package = let
in
builders.bash.boot.build {
name = "gnutar-boot-${cfg.version}";
meta = stage1.gnutar.meta;
deps.build.host = [
stage1.tinycc.mes.compiler.package
stage1.gnumake.package
stage1.gnused.boot.package
stage1.gnugrep.package
];
script = ''
# Unpack
ungz --file ${cfg.src} --output tar.tar
untar --file tar.tar
rm tar.tar
cd tar-${cfg.version}
# Configure
export CC="tcc -B ${stage1.tinycc.mes.libs.package}/lib"
bash ./configure \
--build=${platform.build} \
--host=${platform.host} \
--disable-nls \
--prefix=$out
# Build
make AR="tcc -ar"
# Install
make install
'';
};
};
};
}

View file

@ -0,0 +1,49 @@
{
lib,
config,
}: let
cfg = config.aux.foundation.stages.stage1.gnutar;
builders = config.aux.foundation.builders;
stage1 = config.aux.foundation.stages.gnutar;
in {
includes = [
./boot.nix
];
options.aux.foundation.stages.stage1.gnutar = {
meta = {
description = lib.options.create {
type = lib.types.string;
description = "Description for the package.";
default.value = "GNU implementation of the `tar' archiver";
};
homepage = lib.options.create {
type = lib.types.string;
description = "Homepage for the package.";
default.value = "https://www.gnu.org/software/tar";
};
license = lib.options.create {
# TODO: Add a proper type for licenses.
type = lib.types.attrs.any;
description = "License for the package.";
default.value = lib.licenses.gpl3Plus;
};
platforms = lib.options.create {
type = lib.types.list.of lib.types.string;
description = "Platforms the package supports.";
default.value = ["x86_64-linux" "aarch64-linux" "i686-linux"];
};
mainProgram = lib.options.create {
type = lib.types.string;
description = "The main program of the package.";
default.value = "tar";
};
};
};
}

View file

@ -0,0 +1,104 @@
{
lib,
config,
}: let
cfg = config.aux.foundation.stages.stage1.gzip;
builders = config.aux.foundation.builders;
stage1 = config.aux.foundation.stages.stage1;
in {
options.aux.foundation.stages.stage1.gzip = {
package = lib.options.create {
type = lib.types.package;
description = "The package to use for gnugrep.";
};
version = lib.options.create {
type = lib.types.string;
description = "Version of the package.";
};
src = lib.options.create {
type = lib.types.package;
description = "Source for the package.";
};
meta = {
description = lib.options.create {
type = lib.types.string;
description = "Description for the package.";
default.value = "GNU implementation of the Unix grep command";
};
homepage = lib.options.create {
type = lib.types.string;
description = "Homepage for the package.";
default.value = "https://www.gnu.org/software/grep";
};
license = lib.options.create {
# TODO: Add a proper type for licenses.
type = lib.types.attrs.any;
description = "License for the package.";
default.value = lib.licenses.gpl3Plus;
};
platforms = lib.options.create {
type = lib.types.list.of lib.types.string;
description = "Platforms the package supports.";
default.value = ["x86_64-linux" "aarch64-linux" "i686-linux"];
};
mainProgram = lib.options.create {
type = lib.types.string;
description = "The main program of the package.";
default.value = "grep";
};
};
};
config = {
aux.foundation.stages.stage1.gzip = {
version = "1.2.4";
src = builtins.fetchurl {
url = "https://ftpmirror.gnu.org/gzip/gzip-${cfg.version}.tar.gz";
sha256 = "0ryr5b00qz3xcdcv03qwjdfji8pasp0007ay3ppmk71wl8c1i90w";
};
package = let
in
builders.bash.boot.build {
name = "gnused-boot-${cfg.version}";
meta = cfg.meta;
deps.build.host = [
stage1.tinycc.mes.compiler.package
stage1.gnumake.package
stage1.gnused.boot.package
stage1.gnugrep.package
];
script = ''
# Unpack
ungz --file ${cfg.src} --output gzip.tar
untar --file gzip.tar
rm gzip.tar
cd gzip-${cfg.version}
# Configure
export CC="tcc -B ${stage1.tinycc.mes.libs.package}/lib -Dstrlwr=unused"
bash ./configure --prefix=$out
# Build
make
# Install
mkdir $out
make install
'';
};
};
};
}

View file

@ -0,0 +1,139 @@
{
lib,
config,
}: let
cfg = config.aux.foundation.stages.stage1.musl.boot;
platform = config.aux.platform;
builders = config.aux.foundation.builders;
stage1 = config.aux.foundation.stages.stage1;
in {
options.aux.foundation.stages.stage1.musl.boot = {
package = lib.options.create {
type = lib.types.package;
description = "The package to use for musl-boot.";
};
version = lib.options.create {
type = lib.types.string;
description = "Version of the package.";
};
src = lib.options.create {
type = lib.types.package;
description = "Source for the package.";
};
};
config = {
aux.foundation.stages.stage1.musl.boot = {
version = "1.1.24";
src = builtins.fetchurl {
url = "https://musl.libc.org/releases/musl-${cfg.version}.tar.gz";
sha256 = "E3DJqBKyzyp9koAlEMygBYzDfmanvt1wBR8KNAFQIqM=";
};
package = let
# Thanks to the live-bootstrap project!
# See https://github.com/fosslinux/live-bootstrap/blob/d98f97e21413efc32c770d0356f1feda66025686/sysa/musl-1.1.24/musl-1.1.24.sh
liveBootstrap = "https://github.com/fosslinux/live-bootstrap/raw/d98f97e21413efc32c770d0356f1feda66025686/sysa/musl-1.1.24";
patches = [
(builtins.fetchurl {
url = "${liveBootstrap}/patches/avoid_set_thread_area.patch";
sha256 = "TsbBZXk4/KMZG9EKi7cF+sullVXrxlizLNH0UHGXsPs=";
})
(builtins.fetchurl {
url = "${liveBootstrap}/patches/avoid_sys_clone.patch";
sha256 = "/ZmH64J57MmbxdfQ4RNjamAiBdkImMTlHsHdgV4gMj4=";
})
(builtins.fetchurl {
url = "${liveBootstrap}/patches/fenv.patch";
sha256 = "vMVGjoN4deAJW5gsSqA207SJqAbvhrnOsGK49DdEiTI=";
})
(builtins.fetchurl {
url = "${liveBootstrap}/patches/makefile.patch";
sha256 = "03iYBAUnsrEdLIIhhhq5mM6BGnPn2EfUmIHu51opxbw=";
})
(builtins.fetchurl {
url = "${liveBootstrap}/patches/musl_weak_symbols.patch";
sha256 = "/d9a2eUkpe9uyi1ye6T4CiYc9MR3FZ9na0Gb90+g4v0=";
})
(builtins.fetchurl {
url = "${liveBootstrap}/patches/set_thread_area.patch";
sha256 = "RIZYqbbRSx4X/0iFUhriwwBRmoXVR295GNBUjf2UrM0=";
})
(builtins.fetchurl {
url = "${liveBootstrap}/patches/sigsetjmp.patch";
sha256 = "wd2Aev1zPJXy3q933aiup5p1IMKzVJBquAyl3gbK4PU=";
})
# FIXME: this patch causes the build to fail
# (builtins.fetchurl {
# url = "${liveBootstrap}/patches/stdio_flush_on_exit.patch";
# sha256 = "/z5ze3h3QTysay8nRvyvwPv3pmTcKptdkBIaMCoeLDg=";
# })
# HACK: always flush stdio immediately
./patches/always-flush.patch
(builtins.fetchurl {
url = "${liveBootstrap}/patches/va_list.patch";
sha256 = "UmcMIl+YCi3wIeVvjbsCyqFlkyYsM4ECNwTfXP+s7vg=";
})
];
in
builders.bash.boot.build {
name = "musl-boot-${cfg.version}";
meta = stage1.musl.meta;
deps.build.host = [
stage1.tinycc.mes.compiler.package
stage1.gnumake.package
stage1.gnused.boot.package
stage1.gnugrep.package
stage1.gnupatch.package
stage1.gnutar.boot.package
stage1.gzip.package
];
script = ''
# Unpack
tar xzf ${cfg.src}
cd musl-${cfg.version}
# Patch
${lib.strings.concatMapSep "\n" (file: "patch -Np0 -i ${file}") patches}
# tcc does not support complex types
rm -rf src/complex
# Configure fails without this
mkdir -p /dev
# https://github.com/ZilchOS/bootstrap-from-tcc/blob/2e0c68c36b3437386f786d619bc9a16177f2e149/using-nix/2a3-intermediate-musl.nix
sed -i 's|/bin/sh|${stage1.bash.boot.package}/bin/bash|' \
tools/*.sh
chmod 755 tools/*.sh
# patch popen/system to search in PATH instead of hardcoding /bin/sh
sed -i 's|posix_spawn(&pid, "/bin/sh",|posix_spawnp(\&pid, "sh",|' \
src/stdio/popen.c src/process/system.c
sed -i 's|execl("/bin/sh", "sh", "-c",|execlp("sh", "-c",|'\
src/misc/wordexp.c
# Configure
bash ./configure \
--prefix=$out \
--build=${(builtins.trace platform.build) platform.build} \
--host=${platform.host} \
--disable-shared \
CC=tcc
# Build
make AR="tcc -ar" RANLIB=true CFLAGS="-DSYSCALL_NO_TLS"
# Install
make install
cp ${stage1.tinycc.mes.libs.package}/lib/libtcc1.a $out/lib
'';
};
};
};
}

View file

@ -0,0 +1,44 @@
{
lib,
config,
}: let
cfg = config.aux.foundation.stages.stage1.musl;
builders = config.aux.foundation.builders;
stage1 = config.aux.foundation.stages.musl;
in {
includes = [
./boot.nix
];
options.aux.foundation.stages.stage1.musl = {
meta = {
description = lib.options.create {
type = lib.types.string;
description = "Description for the package.";
default.value = "An efficient, small, quality libc implementation";
};
homepage = lib.options.create {
type = lib.types.string;
description = "Homepage for the package.";
default.value = "https://musl.libc.org";
};
license = lib.options.create {
# TODO: Add a proper type for licenses.
type = lib.types.attrs.any;
description = "License for the package.";
default.value = lib.licenses.mit;
};
platforms = lib.options.create {
type = lib.types.list.of lib.types.string;
description = "Platforms the package supports.";
# TODO: Support more platforms.
default.value = ["i686-linux"];
};
};
};
}

View file

@ -0,0 +1,13 @@
diff --git src/env/__libc_start_main.c src/env/__libc_start_main.c
index 8fbe526..9476c22 100644
--- src/env/__libc_start_main.c
+++ src/env/__libc_start_main.c
@@ -91,6 +91,7 @@ static int libc_start_main_stage2(int (*main)(int,char **,char **), int argc, ch
__libc_start_init();
/* Pass control to the application */
+ setbuf(stdout, NULL);
exit(main(argc, argv, envp));
return 0;
}

View file

@ -2,7 +2,7 @@
options.aux = {
system = lib.options.create {
type = lib.types.string;
default.value = "x86_64-linux";
default.value = "i686-linux";
description = ''
The system to build packages for. This value can be provided as either
`config.aux.system` or by setting the `system` argument for modules.