logo

bootstrap-initrd

Linux initrd to bootstrap from a small binary seed git clone https://anongit.hacktivis.me/git/bootstrap-initrd.git/

make-root.sh (9460B)


  1. #!/bin/sh
  2. # bootstrap-initrd: Linux initrd to bootstrap from a small binary seed
  3. # SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
  4. # SPDX-License-Identifier: MPL-2.0
  5. tarballs="
  6. oksh-oksh-7.8.tar.gz
  7. yacc-oyacc-6.6.tar.gz
  8. utils-std-0.3.0.tar.gz
  9. pdpmake-2.0.4.tgz
  10. nawk-20260426.tar.gz
  11. bzip2-bzip2-1.0.8.tar.gz
  12. zlib-1.3.2.tar.gz
  13. pigz-2.8.tar.gz
  14. heirloom-070715.tar.bz2
  15. heirloom-devtools-070527.tar.bz2
  16. sbase-b30fb568.tar.gz
  17. musl-fts-v1.2.7.tar.gz
  18. paxmirabilis-20201030.tgz
  19. "
  20. local_files="
  21. init.sh
  22. src/ls-stub.c
  23. src/grep-stub.c
  24. src/cp-stub.c
  25. src/getty-stub.c
  26. src/mount-stub.c
  27. src/reboot-stub.c
  28. src/aarch64.sha256
  29. src/i686.sha256
  30. src/x86_64.sha256
  31. src/riscv64.sha256
  32. "
  33. extra_tarballs="
  34. lua-5.5.0.tar.gz
  35. lua-5.5.0-tests.tar.gz
  36. pkgconf-pkgconf-2.5.1.tar.gz
  37. iproute2-6.18.0.tar.gz
  38. BearSSL-3c040368f6791553610e362401db1efff4b4c5b8.tar.gz
  39. tiny-curl-8.4.0.tar.gz
  40. git-2.53.0.tar.gz
  41. samurai-1.3.tar.gz
  42. muon-v0.5.0.tar.gz
  43. genext2fs-v1.6.2.tar.gz
  44. usign-f1f65026a94137c91b5466b149ef3ea3f20091e9.tar.gz
  45. "
  46. extra_distfiles="
  47. cacert-2026-03-19.pem
  48. "
  49. extra_local_files="
  50. extras-build-all.sh
  51. extras/bearssl.sh
  52. extras/genext2fs.sh
  53. extras/git.sh
  54. extras/iproute2.sh
  55. extras/lua.sh
  56. extras/muon-stage1.sh
  57. extras/muon-stage2.sh
  58. extras/pkgconf-lite.sh
  59. extras/pkgconf.sh
  60. extras/samurai.sh
  61. extras/tiny-curl.sh
  62. extras/usign.sh
  63. "
  64. dir0=$(dirname "$0")
  65. WORKDIR=$(realpath "$dir0")
  66. . "$WORKDIR/common.sh"
  67. : ${ALPINE_ARCH:=x86_64}
  68. name_base="bootstrap-initrd"
  69. out_base="${WORKDIR}/${name_base}/${ALPINE_ARCH}"
  70. test -z "${EXCLUDE_EXTRAS}" && out_base="${out_base}+extras"
  71. gen_oksh_tcc_h() {
  72. set -e
  73. cd oksh-*/
  74. cp "${WORKDIR}/src/oksh-7.6_pconfig.h" ./pconfig.h
  75. echo 'char *oksh_tcc_cmd[] = {'
  76. # -DSMALL to not need ncurses
  77. printf '\t"%s",\n' /usr/bin/tcc -o /bin/oksh \
  78. -D_GNU_SOURCE -DEMACS -DVI -DSMALL \
  79. *.c
  80. printf '\t0\n'
  81. echo '};'
  82. cd - >/dev/null
  83. }
  84. set -e
  85. sha512sum -c distfiles.SHA512SUM
  86. if test -e "$out_base"; then
  87. rm -fr "$out_base"
  88. fi
  89. mkdir -p "$out_base" || die "Failed: mkdir $out_base"
  90. cd "$out_base" || die "Failed: cd $out_base"
  91. mkdir -p src
  92. cd "${out_base}/src"
  93. for i in $tarballs; do
  94. tar xof "${WORKDIR}/distfiles/$i" || die "Failed extracting $i"
  95. done
  96. cd -
  97. for i in $local_files; do
  98. cp "${WORKDIR}/$i" ./"$i" || die "Failed copying $i"
  99. done
  100. if ! test "${EXCLUDE_EXTRAS:+y}" = "y"; then
  101. mkdir -p ./extras || die
  102. for i in $extra_tarballs; do
  103. tar xof "${WORKDIR}/distfiles/$i" -C ./extras/ || die "Failed extracting $i"
  104. done
  105. # remove blob
  106. rm ./extras/BearSSL-*/T0Comp.exe || die
  107. # Remove useless files to lower the size of the initrd (and so require less ram)
  108. rm -r ./extras/git-*/t ./extras/git-*/po ./extras/git-*/Documentation/RelNotes || die
  109. # config.status: error: cannot find input file: `tests/Makefile.in'
  110. #rm -r ./extras/tiny-curl-*/tests || die
  111. cp "${WORKDIR}"/extras/iproute2/meson.build ./extras/iproute2-*/ || die
  112. # use upstream samurai instead
  113. rm -r ./extras/muon-*/src/external/samurai ./extras/muon-*/include/external/samurai || die
  114. mkdir -p ./distfiles
  115. for i in $extra_distfiles
  116. do
  117. cp "${WORKDIR}/distfiles/$i" ./distfiles/ || die "Failed copying $i"
  118. done
  119. for i in $extra_local_files; do
  120. cp "${WORKDIR}/$i" ./"$i" || die "Failed copying $i"
  121. done
  122. patch -d ./extras/usign-*/ -p1 <"${WORKDIR}/extras/usign-patches/0001-Use-static-buffer-for-default-sigfile-path.patch"
  123. fi
  124. deblob -c || die
  125. mkdir -p dev proc sys etc usr/bin var/empty || die "Failed creating base directories"
  126. ln -s /proc/mounts etc/mtab || die "Failed symlink for /etc/mtab"
  127. ln -s bin sbin
  128. ln -s usr/lib lib
  129. ln -s usr/bin bin
  130. ln -s usr/bin sbin
  131. mkdir -m 777 tmp
  132. mkdir -m 700 root
  133. cat >etc/passwd <<EOF
  134. root:x:0:0:root:/root:/bin/sh
  135. nobody:x:65534:65534:nobody:/var/empty:/bin/false
  136. EOF
  137. cat >etc/group <<EOF
  138. root:x:0:root
  139. nobody:x:65534:
  140. nogroup:x:65533:
  141. EOF
  142. last_mod="$Format:%cI$"
  143. case "$last_mod" in
  144. *format:*)
  145. commit_time=$(git show --format='%cI' HEAD || echo 2026-03-21T18:52:34+01:00)
  146. ;;
  147. esac
  148. cat >etc/setdate.sh <<EOF
  149. #!/bin/sh
  150. exec date -f %Y-%m-%dT%TZ ${last_mod}
  151. EOF
  152. chmod +x etc/setdate.sh
  153. cat >root/.kshrc <<'EOF'
  154. PS1='$? $PWD # '
  155. EOF
  156. chmod +x root/.kshrc
  157. for i in fd stderr stdin stdout; do
  158. ln -fs proc/self/$i dev/$i
  159. done
  160. cp "${WORKDIR}/init.c" ./init || die "copying init"
  161. sed -i '1i#!/usr/bin/tcc -run' ./init || die "failed adding tcc shebang to init"
  162. chmod 755 init || die "init chmod"
  163. cp -p "${WORKDIR}/ar-stub.sh" ./bin/ar
  164. for apk in $APKS_main $APKS_community
  165. do
  166. tar xof "${WORKDIR}/distfiles/$apk.${ALPINE_ARCH}" --exclude '.*'
  167. done
  168. # Allows to shave off ~9.1M from the binary seed
  169. rm usr/lib/libc.a
  170. # tcc-0.9.27_git20250106-r0 only supports alloca(3) on __i386__, __x86_64__, __arm__ (32-bit)
  171. # removing the musl-provided header allows to compile git
  172. # (still breaks GNU make though)
  173. case "${ALPINE_ARCH}" in
  174. x86)
  175. # tcc x86 wants /usr/lib/i386-linux-gnu/crtn.o but it's installed at /usr/lib/crtn.o
  176. ln -s . usr/lib/i386-linux-gnu
  177. ;;
  178. x86_64|armhf)
  179. ;;
  180. *)
  181. rm usr/include/alloca.h
  182. sed -i '/alloca\.h/d' usr/include/stdlib.h
  183. ;;
  184. esac
  185. case "${ALPINE_ARCH}" in
  186. armhf|armv7|aarch64)
  187. # Compiling oksh-7.6
  188. # In file included from alloc.c:12:
  189. # In file included from sh.h:16:
  190. # /usr/lib/tcc/include/stddef.h:6: error: incompatible redefinition of 'wchar_t'
  191. #
  192. # Make it match musl's /usr/include/bits/alltypes.h
  193. #sed -i 's;#define __WCHAR_TYPE__ int;#define __WCHAR_TYPE__ unsigned;' usr/lib/tcc/include/tccdefs.h
  194. # Make musl match tcc (yuck)
  195. sed -i 's;typedef unsigned wchar_t;typedef int wchar_t;' usr/include/bits/alltypes.h
  196. ;;
  197. *)
  198. ;;
  199. esac
  200. cd "${out_base}/src"
  201. ln -s oksh "${out_base}/bin/sh"
  202. gen_oksh_tcc_h > oksh_tcc.h
  203. cd "${out_base}/src"
  204. oyacc=$(echo ./yacc-oyacc-*/)
  205. cat >"${oyacc}/config.h" <<EOF
  206. // __dead and __dead2 are absent in musl
  207. #define __dead __attribute__((__no_return__))
  208. // HAVE_PROGNAME: Absent in musl
  209. #define HAVE_ASPRINTF
  210. // HAVE_PLEDGE: Absent in musl
  211. #define HAVE_REALLOCARRAY
  212. #define HAVE_STRLCPY
  213. EOF
  214. sed -i -e '/CFLAGS/s; -g;;' bzip2-*/Makefile bzip2-*/Makefile-libbz2_so || die "Failed patching out `-g` in /bzip2-*/Makefile*"
  215. sed -i -e 's;bzip2-shared;bzip2;' bzip2-*/Makefile-libbz2_so || die "Failed patching /bzip2-*/Makefile-libbz2_so"
  216. sed -i -e 's;all: libbz2.a;all: libbz2.so;' -e 's;bzip2: libbz2.a;bzip2: libbz2.so;' bzip2-*/Makefile || die "Failed patching /bzip2-*/Makefile"
  217. rm bzip2-*/sample* || die "Failed removing sample bzip2 files"
  218. patch -p0 <"${WORKDIR}/zlib-1.3.2_no_staticlib.patch"
  219. patch -p0 <"${WORKDIR}/zlib-1.3.2-use-LDFLAGS-in-configure_no_gcc.patch"
  220. rm zlib-*/doc/crc-doc.1.0.pdf || die "Failed removing zlib crc-doc.1.0.pdf"
  221. rm zlib-*/crc32.h || die "Failed removing autogenerated zlib-*/crc32.h"
  222. rm -r zlib-*/examples/ || die "Failed removing zlib-*/examples/"
  223. rm -r zlib-*/contrib/ || die "Failed removing zlib-*/contrib/"
  224. sed -i 's;^clean: minizip-clean;clean:;' zlib-*/Makefile.in || die "Failed disabling minizip cleanup"
  225. sed -i \
  226. -e 's;INSTALL=.*;INSTALL=install;' \
  227. -e 's;PREFIX=.*;PREFIX=/usr;;' \
  228. -e 's;STRIP=.*;STRIP=true;' \
  229. -e 's;#?AR=.*;AR ?= ar;' \
  230. -e 's;RANLIB=.*;RANLIB ?= ranlib;' \
  231. -e 's;YACC=.*;YACC ?= yacc;' \
  232. heirloom-devtools-*/mk.config \
  233. || die "Failed configuring heirloom-devtools"
  234. # pdpmake-1.4.3+ consider them invalid "macros"
  235. sed -i \
  236. -e '/^\.c\.o:/s/;/\n\t/' \
  237. heirloom-devtools-*/lex/Makefile.mk heirloom-devtools-*/m4/Makefile.mk \
  238. || die "Failed patching .c.o: inferred targets in heirloom-devtools"
  239. # Only pick what we need from heirloom
  240. mkdir -p heirloom
  241. cd heirloom-0*/
  242. patch -p1 <"${WORKDIR}/heirloom-no_alloca.patch"
  243. mv \
  244. makefile Makefile.mk build \
  245. libcommon diff sort find stty \
  246. ../heirloom/ || die "Failed copying heirloom files"
  247. cd -
  248. rm -r heirloom-0*/ || die "Failed removing old heirloom unpacked directory"
  249. # - maninst Fails to create parent dir, just noop it, we don't have man anyway
  250. # -e 's;MANINST =.*;MANINST = $(SHELL) ../build/maninst.sh;' \
  251. # - Turns out tcc comes with an ar(1)
  252. # -e 's;LCOMMON =.*;LCOMMON = ../libcommon/*.c;' \
  253. sed -i \
  254. -e 's;DEFBIN =.*;DEFBIN = /bin;' \
  255. -e 's;DEFSBIN =.*;DEFSBIN = /bin;' \
  256. -e 's;STRIP =.*;STRIP = true;' \
  257. -e 's;SPELLHIST =.*;SPELLHIST = /dev/null;' \
  258. -e 's;UCBINST =.*;UCBINST = install;' \
  259. -e 's;MANINST =.*;MANINST = true;' \
  260. heirloom/build/mk.config \
  261. || die "Failed configuring heirloom (toolchest)"
  262. sed -i \
  263. -e 's;__GLIBC__;__linux__;' \
  264. -e '/#define getdents/s;^;//;' \
  265. heirloom/libcommon/getdir.c || die "Failed fixing heirloom libcommon for musl"
  266. sed -i \
  267. -e 's;#ifdef _AIX;#if defined(_AIX) || defined(__linux__);' \
  268. heirloom/find/find.c || die "Failed fixing heirloom find for musl"
  269. sed -i -e 's;getdir.o regexpr.o gmatch.o;getdir.o gmatch.o;' heirloom/libcommon/Makefile.mk || die
  270. rm -r pigz-*/zopfli || die
  271. rm -r heirloom-devtools-*/make heirloom-devtools-*/sccs || die
  272. rm -r awk-*/testdir || die
  273. cd "${out_base}/"
  274. deblob -n -j "${WORKDIR}"/make-root-deblob.json || die
  275. if command -v jq >/dev/null; then
  276. jq --raw-output0 '.[].path' "${WORKDIR}"/make-root-deblob.json \
  277. | xargs -0 du -bach \
  278. | sort -h
  279. elif command -v qjs >/dev/null; then
  280. <"${WORKDIR}"/make-root-deblob.json \
  281. qjs --std \
  282. -e 'let deblob_raw = std.in.readAsString(); let deblob = JSON.parse(deblob_raw); deblob.forEach((e) => {std.printf("%s\0", e.path);})' \
  283. | xargs -0 du -bach \
  284. | sort -h
  285. else
  286. echo "make-root: Command 'jq' and 'qjs' not found. Not going to list sizes of blobs" 1>&2
  287. fi
  288. cd "${WORKDIR}"