summaryrefslogtreecommitdiff
path: root/bootstrap.sh
blob: 36fc599f3aca1a256d66ef6639a9e2cd892f4913 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#!/bin/zsh -e

BOOTSTRAP=$(pwd)/.bootstrap
MICROARCH="skylake"
PROCS=$(nproc)
SOURCES=$(pwd)/.treetap/sources
TARGET=x86_64-maple-linux-musl

# Fetch sources required for a bootstrap
./treetap fetch sources/busybox.spec
./treetap fetch sources/linux.spec
./treetap fetch sources/llvm.spec
./treetap fetch sources/musl.spec

# Simplified filesystem heirarchy with symlinks for compatibility
mkdir -p $BOOTSTRAP/root/{bin,boot/EFI/BOOT,dev,etc,home,lib,proc,run,sys,tmp,usr/{include,share},var/{cache,lib,log,spool,tmp}}
ln -sf bin $BOOTSTRAP/root/sbin
ln -sf ../bin $BOOTSTRAP/root/usr/bin
ln -sf ../bin $BOOTSTRAP/root/usr/sbin
ln -sf ../lib $BOOTSTRAP/root/usr/lib
ln -sf ../lib $BOOTSTRAP/root/usr/libexec
ln -sf ../run/lock $BOOTSTRAP/root/var/lock
ln -sf ../run $BOOTSTRAP/root/var/run

# Prepare for the build
ARCH=$(echo $TARGET | cut -d"-" -f1)
export AR=llvm-ar
export CC=clang
export CFLAGS="-fuse-ld=lld -O3 -march=$MICROARCH -pipe --sysroot=$BOOTSTRAP/root -Wno-unused-command-line-argument"
export CXX=clang++
export CXXFLAGS=$CFLAGS
export RANLIB=llvm-ranlib
export LD=ld.lld
export TREETAP_SYSROOT=$BOOTSTRAP/root
export TREETAP_TARGET=$TARGET
mkdir -p $BOOTSTRAP/build
cd $BOOTSTRAP/build

# Define the target for Maple Linux
cat << EOF > $BOOTSTRAP/$TARGET.cmake
set(CMAKE_ASM_COMPILER_TARGET $TARGET)
set(CMAKE_C_COMPILER $CC)
set(CMAKE_C_COMPILER_TARGET $TARGET)
set(CMAKE_C_FLAGS_INIT "$CFLAGS")
set(CMAKE_CXX_COMPILER $CXX)
set(CMAKE_CXX_COMPILER_TARGET $TARGET)
set(CMAKE_CXX_FLAGS_INIT "$CXXFLAGS")
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_LINKER_TYPE LLD)
set(CMAKE_SYSROOT "$BOOTSTRAP/root")
set(CMAKE_SYSTEM_NAME Linux)
EOF

# Install headers for Linux
tar xf $SOURCES/linux/*/linux-*.tar*
cd linux-*/
# NOTE: LLVM=1 is required here because GCC and other GNU tools are required in
#       some places still. This allows us to use LLVM and bypass the parts that
#       haven't become portable yet. ~ahill
LLVM=1 make -j $PROCS mrproper
# NOTE: We don't use the built-in headers_install target because it requires
#       rsync for some reason. ~ahill
LLVM=1 make -j $PROCS headers ARCH=$ARCH
find usr/include -type f ! -name "*.h" -delete
cp -r usr/include $BOOTSTRAP/root/usr
cd ..

# Install headers for musl
tar xf $SOURCES/musl/*/musl-*.tar*
cd musl-*/
# NOTE: We are intentionally not passing --target here because musl follows the
#       GNU approach when it comes to cross-compiling. This means the build
#       script prefaces the name of every build tool with the target triple
#       we're compiling for. This is unnecessary for LLVM, because we can simply
#       pass -target <triple> to the compiler and have it generate machine code
#       for that target. ~ahill
./configure \
    --bindir=/bin \
    --includedir=/usr/include \
    --libdir=/lib \
    --prefix=/
make -O -j $PROCS
make -O -j $PROCS install-headers DESTDIR=$BOOTSTRAP/root
cd ..

# Build and install compiler-rt builtins
tar xf $SOURCES/llvm/*/llvm-project-*.tar*
cd llvm-project-*/
cmake -S compiler-rt/lib/builtins -B build-builtins \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_INSTALL_PREFIX=$BOOTSTRAP/root \
    -DCMAKE_TOOLCHAIN_FILE=$BOOTSTRAP/$TARGET.cmake \
    -DCOMPILER_RT_DEFAULT_TARGET_ONLY=ON
cmake --build build-builtins --parallel $PROCS
cmake --install build-builtins --parallel $PROCS
cd ..

# Build musl for real this time
cd musl-*/
# TODO: CVE-2025-26519
make clean
# NOTE: LIBCC is required here because it will attempt to link with the build
#       system's runtime if this is not specified. ~ahill
LIBCC="$BOOTSTRAP/root/lib/linux/libclang_rt.builtins-x86_64.a" \
./configure \
    --bindir=/bin \
    --includedir=/usr/include \
    --libdir=/lib \
    --prefix=/
make -O -j $PROCS
make -O -j $PROCS install DESTDIR=$BOOTSTRAP/root
cd ..

# Include compiler-rt and musl in our environment
export CFLAGS="$CFLAGS -Qunused-arguments -rtlib=compiler-rt -Wl,--dynamic-linker=/lib/ld-musl-$ARCH.so.1"
export CXXFLAGS="$CXXFLAGS -Qunused-arguments -rtlib=compiler-rt -Wl,--dynamic-linker=/lib/ld-musl-$ARCH.so.1"

# Build the LLVM runtimes
# NOTE: When CMake tests the C++ compiler, it attempts to link libstdc++/libc++
#       before it even exists, causing an error. We can bypass this by simply
#       setting CMAKE_CXX_COMPILER_WORKS, therefore tricking CMake into
#       performing a successful build. Yet another instance where the genie in
#       the bottle does exactly what we asked, and not what we wanted. ~ahill
cd llvm-project-*/
cmake -S runtimes -B build-runtimes \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_CXX_COMPILER_WORKS=ON \
    -DCMAKE_INSTALL_INCLUDEDIR=$BOOTSTRAP/root/usr/include \
    -DCMAKE_INSTALL_PREFIX=$BOOTSTRAP/root \
    -DCMAKE_TOOLCHAIN_FILE=$BOOTSTRAP/$TARGET.cmake \
    -DCOMPILER_RT_USE_BUILTINS_LIBRARY=ON \
    -DLIBCXX_CXX_ABI=libcxxabi \
    -DLIBCXX_HAS_MUSL_LIBC=ON \
    -DLIBCXX_USE_COMPILER_RT=ON \
    -DLIBCXXABI_USE_COMPILER_RT=ON \
    -DLIBCXXABI_USE_LLVM_UNWINDER=ON \
    -DLIBUNWIND_USE_COMPILER_RT=ON \
    -DLLVM_ENABLE_RUNTIMES="compiler-rt;libcxx;libcxxabi;libunwind"
cmake --build build-runtimes --parallel $PROCS
cmake --install build-runtimes --parallel $PROCS
cd ..

# Now we can introduce libc++ into the environment
# NOTE: clang++ attempts to build with headers from the build system rather than
#       exclusively relying on the sysroot. Because of this, we must manually
#       define the proper include path. ~ahill
export CXXFLAGS="$CXXFLAGS -isystem $BOOTSTRAP/root/usr/include/c++/v1 -nostdinc++ -stdlib=libc++"

# Build LLVM itself
# NOTE: LLVM_ENABLE_ZSTD is disabled because we don't have zstd in the sysroot,
#       and because I don't believe that a library created by Facebook should
#       be required for an operating system to function. ~ahill
cd llvm-project-*/
cmake -S llvm -B build-llvm \
    -DCLANG_DEFAULT_CXX_STDLIB=libc++ \
    -DCLANG_DEFAULT_RTLIB=compiler-rt \
    -DCLANG_DEFAULT_UNWINDLIB=libunwind \
    -DCLANG_VENDOR=Maple \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_INSTALL_PREFIX=$BOOTSTRAP/root \
    -DCMAKE_TOOLCHAIN_FILE=$BOOTSTRAP/$TARGET.cmake \
    -DLLVM_ENABLE_LIBCXX=ON \
    -DLLVM_ENABLE_PROJECTS="clang;lld;llvm" \
    -DLLVM_ENABLE_ZSTD=OFF \
    -DLLVM_HOST_TRIPLE=$TARGET
cmake --build build-llvm --parallel $PROCS
# ...
cd ..