Los cuatro eslabones que enciende RLBox en firefox, sellados de punta a punta:
wasi-libc-headers b3:54c6e96d (andamio, rompe el ciclo del bootstrap)
wasi-compiler-rt b3:a3fe63d1 (libclang_rt.builtins-wasm32.a)
wasi-libc b3:8e50b145 (libc.a 970.734 bytes, 32 archivos .a)
wasi-sdk b3:c8a587fa (el .cfg que hace el sysroot invisible)
Los dos primeros ya estaban construidos; los dos últimos costaron dos fallos, y
ninguno de los dos era de la receta:
1. `check-symbols` moría por SESGO DE CLANG, no por un artefacto malo. El lab
trae clang 22.1.8 y este commit de wasi-libc (2025-06-26) congeló su snapshot
de macros predefinidos contra un clang anterior, así que el diff completo era
UNA línea: `+#define __wasip1__ 1`. La mitad que importa —los símbolos
definidos e indefinidos de la libc recién construida— coincidía byte a byte.
Por eso se RECONCILIA `expected/*/predefined-macros.txt` en vez de saltar el
chequeo con `make no-check-symbols`, que era la salida fácil: ese target
existe y se lleva por delante también la comparación de símbolos, que es la
que vale. Así cualquier OTRA deriva sigue matando el build.
2. `make TARGET_TRIPLE=NOBUILD ... install` NO es un no-op. La regla `install`
depende de `finish`, que depende de `libc`, así que make no lee el triple
inexistente como «no construyas» sino como «construí para el triple NOBUILD»
y muere en `build/NOBUILD/.../crt1-command.o` con `invalid thread model
'single'` — un error que nombra un fichero que nadie pidió. Se copia
`sysroot/{lib,share,include}` a mano, que es literalmente lo que hace la
regla de upstream (`SYSROOT ?= $(CURDIR)/sysroot`).
Cada receta lleva su guardián de contenido, porque acá el modo de fallo caro no
es el ausente sino el vacío: un sysroot con headers y sin libc.a pasa por bueno
y revienta dentro del build de firefox, horas después y sin nombrar a nadie de
esta cadena. Verificado en verde: los builtins son objetos wasm de verdad
(magic 0061736d, no x86_64 nativos) y el .cfg de wasi-sdk apunta a un sysroot
que existe.
El segundo fallo lo diagnosticamos entre dos sesiones: hammer-f8 aportó el
`invalid thread model 'single'` que explicaba la línea que yo veía.
Queda para la próxima unidad el flip de recipes/firefox.toml:
--without-wasm-sandboxed-libraries -> --with-wasi-sysroot, que es un rebuild
largo y su propia unidad de trabajo.
119 lines
6.3 KiB
TOML
119 lines
6.3 KiB
TOML
# wasi-libc — la libc de WebAssembly. Es el sysroot que firefox usa para compilar sus librerías
|
|
# enjauladas en wasm (RLBox): los parsers de fuentes y medios —graphite, ogg, expat, woff2—, o sea
|
|
# el código que come entrada no confiable de la red.
|
|
#
|
|
# ES EL ESLABÓN FINAL de la cadena wasm, y el que firefox consume de verdad:
|
|
# wasi-libc-headers (andamio, triple viejo) → wasi-compiler-rt (los builtins) → ESTE
|
|
#
|
|
# ⚠ COMMIT DISTINTO AL DE `wasi-libc-headers`, a propósito. Acá va el NUEVO (`3f7eb4c7`), cuyo
|
|
# triple por defecto es `wasm32-wasip1`. Ése es el que importa: en LLVM 22.1 el target `wasm32-wasi`
|
|
# se renombró a `wasm32-wasip1` y el propio `toolchain.configure` de Mozilla lo contempla
|
|
# explícitamente, así que el clang 22.1 del lab va a pedir el sysroot con ESE nombre. El commit
|
|
# viejo existe sólo para romper el ciclo de bootstrap; ver `wasi-libc-headers.toml`.
|
|
#
|
|
# Verificado: el sha512 de este tarball coincide byte a byte con el del APKBUILD de Alpine.
|
|
name = "wasi-libc"
|
|
version = "27.20250626"
|
|
license = "Apache-2.0 WITH LLVM-exception AND Apache-2.0 AND MIT AND CC0-1.0 AND BSD-2-Clause"
|
|
|
|
[source]
|
|
tarball = "https://github.com/WebAssembly/wasi-libc/archive/3f7eb4c7d6ede4dde3c4bffa6ed14e8d656fe93f.tar.gz"
|
|
sha256 = "6bb86e09dc5ed43260e81176537d702568689cf4203b8beb1f505609c4139ca5"
|
|
|
|
[build]
|
|
compiler = "clang"
|
|
|
|
[deps]
|
|
build = ["make", "wasi-compiler-rt"]
|
|
|
|
[build.phases]
|
|
configure = "true"
|
|
compile = '''
|
|
set -e
|
|
|
|
# ⚠ `BUILTINS_LIB` NO ES OPCIONAL ACÁ, y no por gusto: si no se define, el Makefile se BAJA una
|
|
# copia conocida desde la CI de wasi-sdk con `curl` (línea ~610 del Makefile upstream). Dentro del
|
|
# sandbox hermético eso no tiene red, así que el build moriría; y si la tuviera sería peor, porque
|
|
# estaríamos sellando un binario ajeno sin hash. Se le pasa el que construyó `wasi-compiler-rt`.
|
|
#
|
|
# Ruta ABSOLUTA, no la relativa que usa el APKBUILD de Alpine: el Makefile la consume como
|
|
# prerequisito de una regla (`$(BUILTINS_LIB_PATH): $(BUILTINS_LIB)`) y la resuelve contra su propio
|
|
# CURDIR, que acá es `/src` y no `/`.
|
|
BUILTINS=/usr/lib/llvm22/lib/clang/22/lib/wasi/libclang_rt.builtins-wasm32.a
|
|
test -f "$BUILTINS" || { echo "!! falta $BUILTINS (¿no llegó la dep wasi-compiler-rt?)" >&2; exit 1; }
|
|
|
|
unset CFLAGS CXXFLAGS LDFLAGS
|
|
|
|
# ⚠ `check-symbols` CONTRA UN CLANG MÁS NUEVO QUE EL SNAPSHOT DEL ÁRBOL.
|
|
#
|
|
# El target `check-symbols` del Makefile hace DOS cosas: compara los símbolos definidos/indefinidos
|
|
# de la libc recién construida contra `expected/`, y compara la lista de macros predefinidos del
|
|
# clang que la compiló contra otro snapshot del mismo directorio. Lo primero es una comprobación
|
|
# real del artefacto; lo segundo es un detector de deriva de toolchain.
|
|
#
|
|
# El lab trae clang 22.1.8 y este commit de wasi-libc (2025-06-26) fue congelado contra un clang
|
|
# anterior, así que el build muere con UNA sola línea de diferencia:
|
|
#
|
|
# +#define __wasip1__ 1
|
|
#
|
|
# La mitad que importa PASÓ: `defined-symbols.txt` y `undefined-symbols.txt` coinciden byte a byte;
|
|
# el diff completo son esas 3.196 líneas iguales y ese macro de más. O sea: la libc construida es la
|
|
# esperada y lo que cambió es que el compilador define un macro nuevo.
|
|
#
|
|
# Por eso se RECONCILIA el snapshot en vez de saltar el chequeo con `make no-check-symbols`, que era
|
|
# la salida fácil: ese target existe y se lleva por delante también la comparación de símbolos, que
|
|
# es justamente la que vale. Así el chequeo sigue vivo y CUALQUIER OTRA deriva —un símbolo que
|
|
# desaparece, otro macro nuevo— sigue matando el build.
|
|
#
|
|
# Se exige que la inserción ocurra: si upstream renombra el ancla, este bloque no puede quedarse
|
|
# callado dejando el mismo fallo para más adelante.
|
|
for T in wasm32-wasip1 wasm32-wasip1-threads; do
|
|
F="expected/$T/predefined-macros.txt"
|
|
grep -q '^#define __wasip1__ 1$' "$F" && continue
|
|
sed -i 's/^#define __wasm 1$/#define __wasip1__ 1\n#define __wasm 1/' "$F"
|
|
grep -q '^#define __wasip1__ 1$' "$F" || {
|
|
echo "!! no se pudo reconciliar $F: no apareció el ancla '#define __wasm 1'" >&2
|
|
exit 1
|
|
}
|
|
done
|
|
|
|
# Los dos triples que firefox puede pedir. NO se construye `wasm32-wasip2`: el APKBUILD de Alpine lo
|
|
# hace para rust, que acá no lo consume, y cada triple de más es tiempo de build sin destinatario.
|
|
make CC=clang TARGET_TRIPLE=wasm32-wasip1 BUILTINS_LIB="$BUILTINS"
|
|
make CC=clang TARGET_TRIPLE=wasm32-wasip1-threads THREAD_MODEL=posix BUILTINS_LIB="$BUILTINS"
|
|
|
|
# `make install` copia `sysroot/{lib,share,include}` en bloque y falla si `share/` no existe.
|
|
mkdir -p sysroot/share
|
|
'''
|
|
install = '''
|
|
set -e
|
|
mkdir -p /out/usr/share/wasi-sysroot
|
|
|
|
# SE COPIA A MANO EN VEZ DE `make install`, y no es atajo — `make install` NO SIRVE ACÁ.
|
|
#
|
|
# La regla de upstream son dos líneas: `mkdir -p $(INSTALL_DIR)` y
|
|
# `cp -p -r $(SYSROOT)/{lib,share,include} $(INSTALL_DIR)`, con `SYSROOT ?= $(CURDIR)/sysroot`.
|
|
# O sea que copia el sysroot ENTERO, sin mirar el triple. Pero la regla declara `install: finish`
|
|
# y `finish` depende de `libc`, así que invocarla vuelve a entrar al grafo de compilación.
|
|
#
|
|
# El truco de pasarle un triple inexistente (`TARGET_TRIPLE=NOBUILD`, que es lo que hacía la
|
|
# primera versión de esta receta) NO lo evita: make no lo lee como «no construyas», lo lee como
|
|
# «construí para el triple NOBUILD» y muere compilando `build/NOBUILD/.../crt1-command.o` — con
|
|
# un error que habla de un fichero que nadie pidió. Las dos vueltas de arriba ya dejaron el
|
|
# sysroot completo; lo único que falta es moverlo.
|
|
cp -p -r sysroot/lib sysroot/share sysroot/include /out/usr/share/wasi-sysroot/
|
|
|
|
# GUARDIÁN. Un sysroot con headers pero SIN `libc.a` es exactamente la clase de artefacto que pasa
|
|
# por bueno y revienta 50 minutos después, dentro del build de firefox, con un error que no nombra
|
|
# a wasi-libc. Se exige la libc del triple que firefox va a pedir.
|
|
LIBC=/out/usr/share/wasi-sysroot/lib/wasm32-wasip1/libc.a
|
|
test -s "$LIBC" || {
|
|
echo "!! el sysroot no trae $LIBC" >&2
|
|
find /out/usr/share/wasi-sysroot -name '*.a' 2>/dev/null | head -20 >&2
|
|
exit 1
|
|
}
|
|
test -f /out/usr/share/wasi-sysroot/include/wasm32-wasip1/stdlib.h || {
|
|
echo "!! el sysroot no trae los headers de wasm32-wasip1" >&2; exit 1; }
|
|
echo "guardián: sysroot wasm32-wasip1 completo (libc.a $(stat -c%s "$LIBC") bytes, $(find /out/usr/share/wasi-sysroot -name '*.a' | wc -l) archivos .a)"
|
|
'''
|