Compilación y enlazamiento
Ahora que entendemos que es un procesador, que arquitecturas hay, que especializaciones existen, como funcionan (a grandes rasgos) y que es la memoria podemos ir hacia; la compilación
Cuando uno escribe un programa, sea el lenguaje que sea (Rust, C, C++, JavaScript, etc) estamos usando el lenguaje humano;
Sin embargo, el procesador no entiende este lenguaje humano, como todo componente de electrónica entiende y se basa en pulsos eléctricos codificados en binario (ceros 0 y unos 1) por lo que necesitamos de un programa que pueda entender ese lenguaje humano, entender su lógica y poder traducirlo (en el lenguaje ensamblador respectivo de la arquitectura del CPU) para que sea funcional, ese programa es lo que llamamos ‘compilador’.
A lo largo de la historia han existido una cantidad indefinida de compiladores. Existen dos tipos de compiladores que determinan también al lenguaje de programación que soportan;
-
Lenguajes de programación compilados
Por el nombre puede dar a confusión, pero los compiladores a secas son los que primero compilan el código (de humano a máquina) y luego permiten que se pueda ejecutar. Son las primeras generaciones de los compiladores para los lenguajes de programación más veteranos y muchos de los más nuevos también.
En esta categoría de lenguajes compilados entran lenguajes como; C, C++, Haskell, Rust, Go, Carbon (de Google), ensamblador, etc.
En todas las arquitecturas el proceso es siempre el mismo para los programas en lenguajes compilados; se crea un código en el lenguaje de programación “X”, se ejecuta un compilador que lo reconoce y se compila para la arquitectura “Y” para finalmente ser enlazado.
Ahora, ese binario ¿cómo maneja sus necesidades de interactividad con el sistema operativo y otras funcionalidades como sockets?;
Para tratar ese problema tenemos las “dependencias” ó “bibliotecas”; como su nombre lo indica son dependencias que tiene el programa compilado para poder realizar una funcionalidad determinada de forma correcta.
En los sistemas GNU/Linux se provee una herramienta llamada; “ldd” el cual al pasarle como argumento el path absoluto de un binario, devolverá al stdout la información sobre que bibliotecas externas que utiliza.
Por ejemplo, estas son las dependencias externas para Nginx en un Arch Linux x86_64;
Como se puede apreciar, el binario de Nginx depende de que en el sistema donde se ejecutan se encuentren tales bibliotecas. El “so” como extensión de archivo significa; “Shared Object” (“Objeto compartido). En este caso en concreto;
Biblioteca Locación Paquete que lo provee linux-vdso.so.1 Memoria RAM El propio kernel Linux libcrypt.so.2 /usr/lib/libcrypt.so.2 libxcrypt libpcre2-8.so.0 /usr/lib/libpcre2-9.so.0 pcre2 libssl.so.3 /usr/lib/libssl.so.3 openssl libcrypto.so.3 /usr/lib/libcrypto.so.3 openssl libz.so.1 /usr/lib/libz.so.1 zlib libGeoIP.so.1 /usr/lib/libGeoIP.so.1 geoip libc.so.6 /usr/lib/libc.so.6 glibc, aarch64-linux-gnu-glibc, riscv64-linux-gnu-glibc, etc Esto, en donde se suplen necesidades de un programa mediante dependencias externas alocadas en el sistema operativo mediante bibliotecas, se llama “compilación con bibliotecas compartidas”. En este proceso, el compilador llama luego al enlazador para que pueda referir en el binario a que bibliotecas (“shared object”) debe recurrir.
Todo esto se vera en mayor profundidad en el siguiente capitulo.
Como podrá apreciar, el binario de Nginx va a necesitar de que el sistema que lo ejecuta no solamente sea uno con un kernel Linux, Si no que además tenga todos esos paquetes instalados, si no fallará al funcionar.
Si se desea indicar que esas bibliotecas sean embebidas dentro del binario, a fin de que sea tan autosuficiente como sea posible (necesitando solamente que sea un sistema Linux) y no se necesiten programas adicionales externos se debe indicar al compilador que utilice un proceso de compilación estático;
Compilador Argumento GCC -static -static-libgcc -static-libstdc++ -static-libasan -static-libtsan -static-liblsan -static-libubsan Clang –emit-static-lib -static-libgcc -static-libsan -static-libstdc++ -static-openmp -static Rustc -C target-feature=+crt-static -
Lenguajes de programación interpretados
Generalmente los intérpretes lo que hacen es tomar el código e ir compilándolo en tiempo real a medida que se ejecuta el programa. En esta categoría caen lenguajes de programación como; Python, Java, JavaScript (dependiendo del intérprete), Ruby, Perl, etc.
Debido a que tienen que ir interpretando en tiempo de ejecución el código (véase, lo van ejecutando a medida que el programa se lee en tiempo real) tienen muchísima menos performance que los lenguajes compilados.
Así mismo, debido a que estos intérpretes/compiladores tienen que ejecutar siempre el mismo código en diferentes plataformas también se los llama ‘Engines’ (motores).
Arquitectura de compilación
No se puede ejecutar un programa compilado para una arquitectura de CPU diferente a la del CPU en donde se intenta ejecutar (véase si compilas un programa para ARM, no vas a poder ejecutarlo en x86).
Pero sí se puede realizar un proceso de traducción el cual va a intentar ir traduciendo e interpretando en tiempo real para dar soporte al programa aunque sean dos arquitecturas diferentes, como hace el programa QEMU, pero generalmente suele haber muchísimos errores. Por lo tanto se debe tener en consideración eso.
Compilación nativa para ARM
Si se realiza una compilación nativa para un procesador ARM (o más comúnmente un SoC) se debe tener en conocimiento previo;
-
El ABI (“Application Binary Interface”; “Interfaz Binaria de la aplicación”)
Esto determinará si, a nivel de la bibliotecas estándar de C utilizada en el sistema operativo, los tipos de variables; “int”, “long int” y los punteros en memorias son de 32 bits todos (ilp32) o si en cambio los “int” se mantienen en 32 bits pero los “long int” y los punteros pasan a ser de 64 bits (lp64).
Se debe tener en cuenta que todo el programa debe ser compilado con la misma ABI.
https://gcc.gnu.org/onlinedocs/gcc/AArch64-Options.html
Listado de compiladores
Obviamente para poder traducir o interpretar el código de lenguaje humano a máquina se debe tener soporte de tal, por lo que no cualquier compilador sirve para cualquier lenguaje. Acá un listado de los más comunes;
-
GCC (Gnu Compiler Collection)
Es una colección de compiladores del proyecto GNU, no es un intérprete y soporta lenguajes como; C, C++, Objective-C, Objective-C++, Fortran, Ada, D, y Go.
-
LLVM
Es una colección de herramientas para compiladores, al igual que GCC no es un intérprete y soporta lenguajes como; ActionScript, Ada, C#, Common Lisp, PicoLisp, Crystal, CUDA, D, Delphi, Dylan, Forth, Fortran, Free Basic, Free Pascal, Graphical G, Halide, Haskell, Java bytecode, Julia, Kotlin, Lua, Objective-C, OpenCL, PostgreSQL’s SQL and PLpgSQL, Ruby, Rust, Scala, Swift, XC, Xojo y Zig.
El compilador de Rust (rustc) está construido usando LLVM.
-
Java Virtual Machine (JVM)
Es el compilador e intérprete para el lenguaje de programación Java.
-
CPython
Es el compilador e intérprete por defecto para el lenguaje de programación Python.
-
Gecko / Servo
Es el compilador e intérprete que usa Mozilla Firefox para el lenguaje de programación JavaScript.
-
V8
Es el compilador e intérprete que usa Google Chrome, Chromium, Atom edit, Pulsar Edit, Microsoft Edge, Opera Browser y varios más para JavaScript.
Proceso de compilación
El proceso de compilación de un programa puede ser tan simple o tan complejo como sea el proyecto en si mismo.
Debido a la complejidad de los proyectos actuales existen diversas formas de realizar este proceso, pero en lineas generales todos siguen este mismo proceso;
-
Configurar el código fuente y creación del script de compilación
Mediante scripts o argumentos se seleccionan que características (features) se quieren o no en el programa final y el programa/script se encarga de verificar que existen las dependencias para cumplir con la compilación correctamente.
Luego Se crea, en un mismo directorio u otro aparte, los archivos necesarios para saber que partes del programa deben ser compilados en el binario final según la configuración que hizo el paso anterior. Acá se incluyen diversas opciones/argumentos que se pasan al compilador final.
Nos vamos a encontrar generalmente con varias opciones posibles (dependiendo del programa en que fueron programados);
-
Autotools ( el script; “configure” )
Este script se encarga de configurar las características finales de programas creados en C,C++ y otros programas soportados por “make” (ya lo veremos).
Se tiene dos posibilidades, o lo ejecutas sin argumentos para que deje las opciones por defecto o;
./configure --help
Ese comando te devolverá todas las opciones que se pueden aplicar al proceso de compilación, desde que características podes habilitar o deshabilitar hasta otras cosas mas extravagantes.
Una vez sepamos que opciones y características queremos habilitar/deshabilitar, las pasamos como argumentos del script de “configure”;
./configure [argumentos]
Luego el mismo empezara a verificar si tenes las librerías necesarias y te avisara si falta alguna.
Programas que usan “configure”; coreutils, bash, grep, nginx, libreoffice, etc.
Pueden haber casos (como en los ports de los sistemas BSD) en donde no se use principalmente “configure” si no “make configure” debido a que los sistemas BSD suelen incluir parches y otras cuestiones.
-
CMake
Como tal CMake se encarga de crear los archivos de configuración que distintos compiladores y entornos entienden para luego compilar de forma apropiada el programa.
Se le dice a CMake como; “un meta sistema de construcción”. Ya que no construye nada, si no que crea los archivos de configuración.
Puede crear archivos de configuración para Make (los Makefile, al igual que el script “configure”), para Meson - Ninja, y para multitud.
Todos los proyectos que utilizan CMake tienen estos archivos en sus directorios;
-
CMakeLists.txt
Este es el archivo principal de configuración que se utiliza para describir cómo debe ser construido el proyecto. Contiene las instrucciones para CMake, como qué archivos fuente deben ser compilados, qué bibliotecas deben ser vinculadas, y más.
-
CMakeCache.txt
Este archivo es generado automáticamente por CMake. Contiene valores de configuración que CMake utiliza para configurar el proyecto. Por ejemplo, puede almacenar rutas a bibliotecas o herramientas específicas que el proyecto necesita.
El proceso de CMake es;
Primero vamos al directorio donde esta el proyecto con CMake, luego creamos un directorio llamado “build” y entramos en el;
mkdir build && cd build
Luego vemos que opciones podemos habilitar o deshabilitar del programa;
cmake -L ../
Elegimos que opciones habilitamos o deshabilitados;
cmake -D[opción-1]=[ON/OFF] -D[opción-2]=[ON/OFF] -D[opción-N]=[ON/OFF] ../
-
-
Meson - Ninja
Meson (https://github.com/mesonbuild/meson ) es un frontend amigable para el backend Ninja (https://github.com/ninja-build/ninja) , en donde ambos estando programados en Python sirven como un sistema automatizado para compilar. Por ende, Python es un requisito para ambos.
Meson se encarga de, al igual que CMake, construir el archivo de configuración. Normalmente se utiliza en conjunto con Ninja.
Ninja luego lee sus archivos y empieza el proceso de compilación utilizando compiladores como; GCC, Clang, etc.
Todos los proyectos que utilizan Meson tienen estos archivos en sus directorios;
-
meson.build
Este es el archivo principal de configuración en Meson. Es equivalente al CMakeLists.txt en CMake y define cómo debe ser construido el proyecto.
-
meson-options.txt:
Este archivo es opcional y se utiliza para definir las opciones de configuración del proyecto, como si un usuario desea habilitar o deshabilitar ciertas características.
Todos los proyectos que utilizan Ninja tienen estos archivos en sus directorios;
-
build.ninja:
Este es el archivo principal que Ninja utiliza para saber qué pasos ejecutar para construir el proyecto. Si se usa Ninja directamente, este archivo debe haber sido generado previamente por herramientas como CMake o Meson.
-
.ninja_log:
Este archivo es creado por Ninja durante la construcción. Contiene información sobre las acciones ejecutadas, como compilación de archivos o ejecución de scripts.
El proceso de Meson es;
Primero vamos al directorio donde esta el proyecto con Meson, luego creamos un directorio llamado “build”;
mkdir build
Luego lo configuramos para setear las opciones que queremos pasando el directorio “build”;
meson configure build/
También podemos hacerlo de otra forma si ya conocemos las opciones o si necesitamos automatizar el proceso;
meson setup build/ . -D[opción-1]=[ON/OFF] -D[opción-2]=[ON/OFF] -D[opción-N]=[ON/OFF]
-
-
-
Compilación
El compilador es llamado en cada paso y realiza las operaciones pertinentes.
Acá voy a entrar en un pequeño detalle importante; soy muy partidario de optimizar el programa final mediante la deshabilitacion de características que no se quieren (véase, todo lo descrito en el punto anterior con “configure”, CMake o Meson) y de la optimización del binario final (mas adelante te muestro).
Una vez que configuramos el proyecto procederemos a iniciar el proceso de compilación (el cual esta automatizado para que no lo hagamos a mano). Debemos prestar atención al directorio “build” que usamos para construir (no confundir con compilar) el proyecto.
Si dentro encontramos un archivo “Makefile” entonces el programa que se encarga de llamar al compilador (GCC, Clang, etc) y realizar el proceso es “make”. Iniciamos el proceso de compilación con;
make -j$(nproc)
Una vez finalizado lo instalamos;
sudo make install
Si por el contrario, encontramos un archivo “build.ninja” significa que el programa que se encarga de llamar al compilador (GCC, Clang, etc) y realizar el proceso es “ninja”. Iniciamos el proceso de compilación con;
ninja -c build
Una vez finalizado lo instalamos;
ninja -c build install
Esto es el proceso de configuración y compilación básica, pero podemos realizar optimizaciones para que el programa sea compilado específicamente para nuestra CPU, así como aumentar el nivel de seguridad del binario final.
Lo primero, lo de compilar el programa especifícamele para el CPU, es por lo siguiente; todos los compiladores tienen la capacidad de tener como objetivo (“target”) una arquitectura o modelo especifico de CPU. Por ende, podemos compilar de forma general un programa para toda la arquitectura x86_64, o podemos apuntar de forma especifica a un Intel Core i7-12700H. Si hacemos lo ultimo, el programa sera; mas chico, mas eficiente y mas rápido, ya que el compilador podrá aprovechar todas las características especificas de ese CPU que no necesariamente se comparten con el resto de procesadores x86_64 si lo compiláramos de forma general.
Eso es lo que permite que distribuciones Linux como; Gentoo, Funtoo, Slackware, etc tengan un rendimiento tan alto cuando se configura la compilación especifica de un programa para el CPU del usuario.
Por otro lado, el binario final puede incluir (o no) medidas de seguridad a muy bajo nivel como; escritura de ceros para los valores en memoria RAM antes del retorno y liberación de esa memoria, terminación automática y completa del programa si se detecta un fallo en concreto dentro del stack/heap de la memoria del programa (para evitar casos de toma de control de punteros), entre muchos otros.
Acá te dejo una tabla de los flags que recomiendo incluir siempre para C;
Flag | Propósito |
---|---|
-march=native | Compilar el programa de forma especifica para el CPU. |
-O2 | Aplica el segundo nivel de optimización al binario. |
-Wp,-D_FORTIFY_SOURCE=2 | Aplica fortificaciones de seguridad generales. |
-fstack-clash-protection | Aplica protecciones para evitar que un exploit tome control del programa ante un fallo en el stack. |
-fcf-protection | Habilita una característica de protección del flujo de control en el binario, que se logra mediante una serie de mecanismos que aseguran que las direcciones de salto (como las llamadas a funciones o las instrucciones return) no puedan ser manipuladas por un atacante. |
-fstack-protector-strong | Se utiliza para habilitar una protección de la pila más fuerte, protegiendo el programa contra desbordamientos de buffer y otros ataques relacionados con la manipulación de la memoria. |
-fuse-ld=mold | Usar el enlazador “mold” que es muy rápido y eficiente. |
Para C++ recomiendo el flag; “Wp,-D_GLIBCXX_ASSERTIONS”.
Para Rust (y su compilador rustc) recomiendo estos flags que suelen ir en la
Flag | Propósito |
---|---|
-C target-cpu=native | Compilar el programa de forma especifica para el CPU. |
-C opt-level=2 | Aplica el segundo nivel de optimización al binario. |
-C link-arg=-fuse-ld=mold | Usar el enlazador “mold que es muy rápido y eficiente. |
-C strip=debuginfo -C strip=symbols -C debug-assertions=false | Eliminar todos los símbolos de depuración del binario final. |
Para el enlazador (se explica todo en el siguiente capitulo);
Flag | Propósito |
---|---|
-Wl | Se usa para pasar las próximas opciones directamente al enlazador |
-O1 | Aplica optimizaciones básicas al proceso de enlazado, como la eliminación de símbolos no utilizados y la reorganización de secciones. |
–sort-common | Le dice al enlazador que ordene las secciones common (las que contienen variables globales no inicializadas) de forma que las variables más grandes se encuentren primero. Mejorando el rendimiento |
–as-needed | Indica al enlazador que solo debe incluir las bibliotecas que realmente se necesiten. Si una biblioteca está incluida en la lista de bibliotecas del enlazado, pero no se usan símbolos de esa biblioteca en el código, la biblioteca no será incluida en el binario final. |
-z,relro | Es una opción de seguridad que hace que algunas secciones del binario sean de solo lectura después de la fase de enlace. |
-z,now | Es una opción de seguridad que le indica al enlazador que todas las reubicaciones de bibliotecas compartidas deben resolverse al inicio del programa (es decir, en el momento de la carga), en lugar de hacerlo cuando se hace uso de las funciones o símbolos de esas bibliotecas. |
-z,defs | Es una opción de seguridad que se debe generar un error si se encuentra una referencia a un símbolo que no esté definido en el binario o en las bibliotecas que se están enlazando. |
-fuse-ld=mold | Usar el enlazador “mold que es muy rápido y eficiente. |
Todas estas opciones se realizan indicando a “configure”, CMake o Meson los argumentos que le deben pasar Ninja y Make a GCC, Clang, etc cada vez que se encarguen de compilar un binario u objeto;
Nota |
---|
“C” es para el lenguaje C, y “CXX” es para el lenguaje C++ |
-
Autotools (el script; “configure”)
Cuando ejecutamos el script debemos añadir antes las variables de entorno con los flags de compilación respectivos;
LDFLAGS=“[flags-ld]” RUSTFLAGS=“[flags]” CFLAGS=“[flags]” CXXFLAGS=“[flags]” ./configure [opciones/argumentos]
-
CMake
Editaremos el archivo “CMakeLists.txt” del proyecto;
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} [flags_de_compilacion]")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} [flags_de_compilacion]")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} [flags-ld] -lm")
set(RUST_FLAGS "[flags_de_compilacion]")
Si no queremos editar el archivo, lo podemos hacer al momento de configurar el proyecto cuando elegimos que opciones habilitamos o deshabilitados;
cmake -DCMAKE_C_FLAGS=“[flags_de_compilación]” -DCMAKE_CXX_FLAGS=“[flags_de_compilación]” -DCMAKE_EXE_LINKER_FLAGS=“[flags-ld]” ../
-
Meson
Editaremos el archivo “meson.build” del proyecto;
add_project_arguments('[flag_1]', '[flag_2]', '[flag_3]', language: 'c')
add_project_arguments('[flag_1]', '[flag_2]', '[flag_3]', language: 'cpp')
add_project_link_arguments('[flags-ld]', '-lm', language: 'c')
add_project_link_arguments('[flags-ld]', '-lm', language: 'cpp')
rustc = import('rust').rustc
rustc.set_option('[flag_1]')
rustc.set_option('[flag_X]')
Si no queremos editar el archivo, lo podemos hacer al momento de configurar el proyecto cuando elegimos que opciones habilitamos o deshabilitados;
RUSTFLAGS=“[flags]” meson setup builddir –cflags “ [flags_de_compilación] “ –cxxflags “ [flags_de_compilación] “
Automatización
Personalmente recomiendo siempre que primero hagas todo a mano, y cuando hallas aprendido lo automatices.
Dependiendo de la distribución y mil cosas adicionales, pueden haber mil y un formas de automatizar la compilación de un programa. Yo, como usuario de Artix Linux (distribución basada en Arch pero con OpenRC en vez de SystemD), hago uso de los archivos “PKGBUILD”.
Los archivos PKGBUILD son documentos en texto plano, sin extensión, que contienen todas y cada una de las instrucciones que debe seguir el gestor de paquetes “pacman” para; descargar un programa, compilarlo paso a paso y luego crear el paquete instalable.
Podes revisar mi repositorio personal en donde tengo muchos programas modificados para hacerlos livianos y seguros; https://github.com/ShyanJMC/minimal_packages.
Estructura del archivo PKGBUILD de LibreWolf (navegador basado en Firefox);
# Maintainer: ShyanJMC <shyan@shyanjmc.com>
pkgname=librewolf
_pkgname=LibreWolf
pkgver=132.0
pkgrel=1
pkgdesc="Community-maintained fork of Firefox, focused on privacy, security and freedom."
url="https://librewolf.net/"
arch=(x86_64)
license=(
GPL
LGPL
MPL
)
depends=(
dbus-glib
ffmpeg
gtk3
libpulse
libxss
libxt
mime-types
nss
ttf-font
)
makedepends=(
binutils cbindgen ccache clang diffutils dump_syms git imake inetutils lld
llvm mesa mold nasm nodejs pciutils pipewire pipewire-alsa pipewire-pulse
python rust sndio unzip 'wasi-compiler-rt>15' 'wasi-libc++>15' 'wasi-libc++abi>15'
'wasi-libc>=1:0+314+a1c7c2c' wireplumber xorg-server-xvfb yasm zip
)
optdepends=(
'hunspell-en_US: Spell checking, American English'
'libnotify: Notification integration'
'networkmanager: Location detection via available WiFi networks'
'pipewire: Audio and screensharing support'
'speech-dispatcher: Text-to-Speech'
'xdg-desktop-portal: Screensharing with Wayland'
)
backup=('usr/lib/librewolf/librewolf.cfg'
'usr/lib/librewolf/distribution/policies.json')
options=(
!debug
!emptydirs
!lto
!makeflags
strip
)
_arch_git=https://raw.githubusercontent.com/archlinux/svntogit-packages/packages/firefox/trunk
_arch_git_blob=https://raw.githubusercontent.com/archlinux/svntogit-packages
install='librewolf.install'
source=(
https://gitlab.com/api/v4/projects/32320088/packages/generic/librewolf-source/${pkgver}-${pkgrel}/librewolf-${pkgver}-${pkgrel}.source.tar.gz # {,.sig} sig files are currently broken, it seems
$pkgname.desktop
"default192x192.png"
)
sha256sums=('f4ba8d96e73cc3f8449979e8ff47aaa4baa9d229c24f50793b6f0bed73eeed7c'
'21054a5f41f38a017f3e1050ccc433d8e59304864021bef6b99f0d0642ccbe93'
'959c94c68cab8d5a8cff185ddf4dca92e84c18dccc6dc7c8fe11c78549cdc2f1')
validpgpkeys=('034F7776EF5E0C613D2F7934D29FBD5F93C0CFC3') # maltej(?)
prepare() {
export MOZ_APP_REMOTINGNAME=${_pkgname}
mkdir -p mozbuild
cd librewolf-$pkgver-$pkgrel
mv mozconfig ../mozconfig
cat >>../mozconfig <<END
ac_add_options --enable-application=browser
ac_add_options --enable-linker=mold
ac_add_options --prefix=/usr
ac_add_options --disable-debug-symbols
ac_add_options --disable-rust-debug
ac_add_options --disable-rust-tests
#ac_add_options --enable-address-sanitizer
ac_add_options --disable-fuzzing
#ac_add_options --enable-signed-overflow-sanitizer
#ac_add_options --disable-nodejs
ac_add_options --enable-strip
ac_add_options --disable-accessibility
ac_add_options --disable-parental-controls
ac_add_options --disable-synth-speechd
ac_add_options --disable-webspeech
ac_add_options --disable-webspeechtestbackend
ac_add_options --disable-wmf
ac_add_options --disable-debug-js-modules
ac_add_options --enable-sandbox
ac_add_options --enable-webrtc
ac_add_options --disable-bootstrap
ac_add_options --enable-release
ac_add_options --with-app-name=${pkgname}
ac_add_options --with-app-basename=${pkgname}
ac_add_options --enable-update-channel=release
ac_add_options --with-distribution-id=com.shyanjmc.librewolf
ac_add_options --disable-unverified-updates
ac_add_options --with-system-nspr
ac_add_options --with-system-nss
ac_add_options --enable-alsa
ac_add_options --disable-jack
ac_add_options --enable-hardening
ac_add_options --enable-optimize
ac_add_options --enable-rust-simd
ac_add_options --disable-crashreporter
ac_add_options --disable-updater
ac_add_options --disable-tests
ac_add_options --disable-debug
ac_add_options --disable-elf-hack
ac_add_options --enable-lto
ac_add_options --without-wasm-sandboxed-libraries
ac_add_options --enable-wasm-memory64
ac_add_options --enable-wasm-memory-control
ac_add_options --enable-wasm-multi-memory
ac_add_options --enable-wasm-relaxed-simd
ac_add_options --enable-wasm-avx
ac_add_options --enable-wasm-simd
ac_add_options --disable-valgrind
ac_add_options --disable-gtest-in-build
# This requires cargo nightly, do not enable
#ac_add_options --enable-thread-sanitizer
#ac_add_options --enable-undefined-sanitizer
#ac_add_options --enable-unsigned-overflow-sanitizer
ac_add_options --enable-frame-pointers
ac_add_options --enable-audio-backends=pulseaudio
ac_add_options --enable-pulseaudio
ac_add_options --enable-sndio
ac_add_options --enable-default-toolkit=cairo-gtk3-wayland-only
ac_add_options --disable-webdriver
ac_add_options --enable-proxy-bypass-protection
ac_add_options --disable-proxy-direct-failover
ac_add_options --disable-backgroundtasks
ac_add_options --disable-legacy-profile-creation
END
}
build() {
cd librewolf-$pkgver-$pkgrel
echo "Cleaning old files"
make clean
export MOZCONFIG="$srcdir/mozconfig"
export MOZ_NOSPAM=1
export MOZBUILD_STATE_PATH="$srcdir/mozbuild"
export MACH_BUILD_PYTHON_NATIVE_PACKAGE_SOURCE=pip
# LTO needs more open files
ulimit -n 4096
./mach build
}
package() {
cd librewolf-$pkgver-$pkgrel
DESTDIR="$pkgdir" ./mach install
# mv ${pkgdir}/usr/local/lib ${pkgdir}/usr/lib/
# mv ${pkgdir}/usr/local/bin ${pkgdir}/usr/bin/
# rm -r ${pkgdir}/usr/local
local vendorjs="$pkgdir/usr/local/lib/$pkgname/browser/defaults/preferences/vendor.js"
install -Dvm644 /dev/stdin "$vendorjs" <<END
// Use LANG environment variable to choose locale
pref("intl.locale.requested", "");
// Use system-provided dictionaries
pref("spellchecker.dictionary_path", "/usr/share/hunspell");
// Disable default browser checking.
pref("browser.shell.checkDefaultBrowser", false);
// Dont disable extensions in the application directory
pref("extensions.autoDisableScopes", 11);
// More time before save file session in disk, this is to avoid consume SSD cycles
pref("browser.sessionstore.interval", 1800000);
// Hardening configurations/options guide
// https://brainfucksec.github.io/firefox-hardening-guide
// Disable startup warning
pref("browser.aboutConfig.showWarning", false);
// Home as startup page
// 0 = blank
// 1 = home
// 2 = last visited page
// 3 = resume previous session
pref("browser.startup.page", 1);
pref("browser.startup.homepage", "about:home");
// Disable Activity Stream on new windows and tab pages
pref("browser.newtabpage.enabled", false);
pref("browser.newtab.preload", false);
pref("browser.newtabpage.activity-stream.feeds.telemetry", false);
pref("browser.newtabpage.activity-stream.telemetry", false);
pref("browser.newtabpage.activity-stream.feeds.snippets", false);
pref("browser.newtabpage.activity-stream.feeds.section.topstories", false);
pref("browser.newtabpage.activity-stream.section.highlights.includePocket", false);
pref("browser.newtabpage.activity-stream.feeds.discoverystreamfeed", false);
pref("browser.newtabpage.activity-stream.showSponsored", false);
pref("browser.newtabpage.activity-stream.default.sites", false);
pref("browser.newtabpage.activity-stream.default.sites", "");
// Geolocation
pref("geo.provider.network.url", "");
// Disable using the OS’s geolocation service
pref("geo.provider.use_gpsd", false );
pref("geo.provider.use_geoclue", false);
// Disable region updates:
pref("browser.region.network.url", "");
pref("browser.region.update.enabled", false);
// Auto-updates / Recommendations
pref("pp.update.auto", false);
// Disable addons recommendations (uses Google Analytics)
pref("extensions.getAddons.showPane", false);
pref("extensions.htmlaboutaddons.recommendations.enabled", false);
pref("browser.discovery.enabled", false);
// Disable telemetry
pref("datareporting.policy.dataSubmissionEnabled", false);
pref("datareporting.healthreport.uploadEnabled", false);
pref("toolkit.telemetry.enabled", false);
pref("toolkit.telemetry.unified", false);
pref("toolkit.telemetry.server","data:,");
pref("toolkit.telemetry.archive.enabled", false);
pref("toolkit.telemetry.newProfilePing.enabled", false);
pref("toolkit.telemetry.shutdownPingSender.enabled", false);
pref("toolkit.telemetry.updatePing.enabled", false);
pref("toolkit.telemetry.bhrPing.enabled", false);
pref("toolkit.telemetry.firstShutdownPing.enabled", false);
pref("toolkit.telemetry.coverage.opt-out", true);
pref("toolkit.coverage.opt-out", true);
pref("toolkit.coverage.endpoint.base", "");
pref("browser.ping-centre.telemetry", false);
pref("beacon.enabled", false);
// Disable studies:
pref("app.shield.optoutstudies.enabled", false);
// Disable Normandy/Shield:
pref("app.normandy.enabled", false);
pref("app.normandy.api_url", "");
// Disable crash reports
pref("breakpad.reportURL", "");
pref("browser.tabs.crashReporting.sendReport", false);
// Disable captive portal detection
pref("captivedetect.canonicalURL", "");
pref("network.captive-portal-service.enabled", false);
// Disable network connections checks
pref("network.connectivity-service.enabled", false);
// Disable safe browsing service
pref("browser.safebrowsing.malware.enabled", false);
pref("browser.safebrowsing.phishing.enabled", false);
// Disable list of blocked URI
// pref("browser.safebrowsing.blockedURIs.enabled", false);
// Disable fetch of updates
pref("browser.safebrowsing.provider.google4.gethashURL", "");
pref("browser.safebrowsing.provider.google4.updateURL","");
pref("browser.safebrowsing.provider.google.gethashURL","");
pref("browser.safebrowsing.provider.google.updateURL","");
pref("browser.safebrowsing.provider.google4.dataSharingURL","");
// Disable checks for downloads
pref("browser.safebrowsing.downloads.enabled", false);
pref("browser.safebrowsing.downloads.remote.enabled",false);
pref("browser.safebrowsing.downloads.remote.url", "");
// Disable checks for unwanted software
pref("browser.safebrowsing.downloads.remote.block_potentially_unwanted", false);
pref("browser.safebrowsing.downloads.remote.block_uncommon", false);
// Disable bypasses the block of safe browsing with a click for current session:
pref("browser.safebrowsing.allowOverride", false);
// Disable link prefetching
pref("network.prefetch-next", false);
// Disable DNS prefetching
pref("network.dns.disablePrefetch", false);
// Disable predictor
pref("network.predictor.enabled", false);
// Disable link-mouseover opening connection to linked server
pref("network.http.speculative-parallel-limit", 0);
// Disable mousedown speculative connections on bookmarks and history
pref("browser.places.speculativeConnect.enabled", false);
// Disable IPv6
// pref("network.dns.disableIPv6", false);
// Disable GIO protocols as a potential proxy bypass vectors
pref("network.gio.supported-protocols", "");
// Disable using UNC (Uniform Naming Convention) paths (prevent proxy bypass)
pref("network.file.disable_unc_paths", true);
// Remove special permissions for certain mozilla domains
pref("permissions.manager.defaultsUrl", "");
// Use Punycode in Internationalized Domain Names to eliminate possible spoofing
pref("network.IDN_show_punycode",true);
// Disable search suggestions
pref("browser.search.suggest.enabled", false);
pref("browser.urlbar.suggest.searches", false);
// Disable location bar domain guessing:
pref("browser.fixup.alternate.enabled", false);
// Display all parts of the url in the bar:
pref("browser.urlbar.trimURLs", false);
// Disable location bar making speculative connections:
pref("browser.urlbar.speculativeConnect.enabled", false);
// Disable form autofill:
pref("browser.formfill.enable", false);
pref("extensions.formautofill.addresses.enabled", false);
pref("extensions.formautofill.available", "off");
pref("extensions.formautofill.creditCards.available", false);
pref("extensions.formautofill.creditCards.enabled", false);
pref("extensions.formautofill.heuristics.enabled", false);
// Disable location bar contextual suggestions:
pref("browser.urlbar.quicksuggest.scenario","history");
pref("browser.urlbar.quicksuggest.enabled", false);
pref("browser.urlbar.suggest.quicksuggest.nonsponsored", false);
pref("browser.urlbar.suggest.quicksuggest.sponsored", false);
// Disable saving passwords:
pref("signon.rememberSignons", false);
// Disable autofill login and passwords:
pref("signon.autofillForms", false);
// Disable formless login capture for Password Manager:
pref("signon.formlessCapture.enabled", false);
// Hardens against potential credentials phishing:
// 0 = don’t allow sub-resources to open HTTP authentication credentials dialogs
// 1 = don’t allow cross-origin sub-resources to open HTTP authentication credentials dialogs
// 2 = allow sub-resources to open HTTP authentication credentials dialogs (default)
pref("network.auth.subresource-http-auth-allow", 1);
// Disable disk cache:
pref("browser.cache.disk.enable", false);
// Disable storing extra session data:
// 0 = everywhere
// 1 = unencrypted sites
// 2 = nowhere
pref("browser.sessionstore.privacy_level", 2);
// Disable resuming session from crash:
pref("browser.sessionstore.resume_from_crash", false);
// Disable page thumbnail collection
pref("browser.pagethumbnails.capturing_disabled", true);
// Disable favicons in profile folder
pref("browser.shell.shortcutFavicons", false);
// Delete temporary files opened with external apps:
pref("browser.helperApps.deleteTempFileOnExit", true);
// Enable HTTPS-Only mode in all windows:
pref("dom.security.https_only_mode", true);
// Disable sending HTTP request for checking HTTPS support by the server:
pref("dom.security.https_only_mode_send_http_background_request", false);
// Display advanced information on Insecure Connection warning pages:
pref("browser.xul.error_pages.expert_bad_cert", true);
// Disable TLS1.3 0-RTT (round-trip time):
pref("security.tls.enable_0rtt_data", false);
// Set OCSP to terminate the connection when a CA isn’t validate:
pref("security.OCSP.require", true);
// Disable SHA-1 certificates:
pref("security.pki.sha1_enforcement_level", 1);
// Enable strict pinning (PKP (Public Key Pinning)):
// 0 = disabled
// 1 = allow user MiTM (i.e. your Antivirus)
// 2 = strict
pref("security.cert_pinning.enforcement_level", 2);
// Enable CRLite
// 0 = disabled
// 1 = consult CRLite but only collect telemetry (default)
// 2 = consult CRLite and enforce both “Revoked” and “Not Revoked” results
// 3 = consult CRLite and enforce “Not Revoked” results, but defer to OCSP for “Revoked”
pref("security.remote_settings.crlite_filters.enabled", true);
pref("security.pki.crlite_mode", 2);
// Control when to send a referer:
// 0 = always (default)
// 1 = only if base domains match
// 2 = only if hosts match
pref("network.http.referer.XOriginPolicy", 2);
// Control the amount of information to send:
// 0 = send full URI (default): https://example.com:8888/foo/bar.html?id=1234
// 1 = scheme+host+port+path: https://example.com:8888/foo/bar.html
// 2 = scheme+host+port: https://example.com:8888
pref("network.http.referer.XOriginTrimmingPolicy", 2);
// Disable WebRTC
// pref("media.peerconnection.enabled", false);
// Force WebRTC inside the proxy:
pref("media.peerconnection.ice.proxy_only_if_behind_proxy", true);
// Force a single network interface for ICE candidates generation:
pref("media.peerconnection.ice.default_address_only", true);
// Force exclusion of private IPs from ICE candidates:
pref("media.peerconnection.ice.no_host", true);
// Disable WebGL (Web Graphics Library):
// pref("webgl.disabled", true);
// Disable autoplay of HTML5 media:
// 0 = allow all
// 1 = block non-muted media (default)
// 5 = block all
pref("media.autoplay.default", 5);
// Disable DRM Content:
// pref("media.eme.enabled", false);
// Always ask you where to save files:
pref("browser.download.useDownloadDir", false);
// Disable adding downloads to system’s “recent documents” list:
pref("browser.download.manager.addToRecentDocs", false);
// Enable ETP (Enhanced Tracking Protection), ETP strict mode enables Total Cookie Protection (TCP):
pref("browser.contentblocking.category", "strict");
// Enable state partitioning of service workers:
pref("privacy.partition.serviceWorkers", true);
// Enable APS (Always Partitioning Storage)
pref("privacy.partition.always_partition_third_party_non_cookie_storag", true);
pref("privacy.partition.always_partition_third_party_non_cookie_storage.exempt_sessionstorage", true);
// Block popup windows:
pref("dom.disable_open_during_load", true);
// Limit events that can cause a popup:
pref("dom.popup_allowed_events", "click dblclick mousedown pointerdown");
// Disable Pocket extension:
pref("extensions.pocket.enabled", false);
// Disable Screenshots extension:
//pref("extensions.Screenshots.disabled", true);
// Disable PDJFS scripting:
pref("pdfjs.enableScripting", false);
// Enable Containers and show the UI settings:
pref("privacy.userContext.enabled", true);
pref("privacy.userContext.enabled", true);
// Set extensions to work on restricted domains, and their scopeis to “profile+applications”:
pref("extensions.enabledScopes", 5);
pref("extensions.webextensions.restrictedDomains", "");
// Display always the installation prompt:
pref("extensions.postDownloadThirdPartyPrompt", false);
// Clear history, cookies and site data when Firefox closes:
pref("network.cookie.lifetimePolicy", 2);
pref("privacy.sanitize.sanitizeOnShutdown", true);
pref("privacy.clearOnShutdown.cache", true);
pref("privacy.clearOnShutdown.cookies", true);
pref("privacy.clearOnShutdown.downloads", true);
pref("privacy.clearOnShutdown.formdata", true);
pref("privacy.clearOnShutdown.history", true);
pref("privacy.clearOnShutdown.offlineApps", true);
pref("privacy.clearOnShutdown.sessions", true);
pref("privacy.clearOnShutdown.sitesettings", false);
pref("privacy.sanitize.timeSpan", 0);
// Enable RFP:
pref("privacy.resistFingerprinting", true);
// Set new window size rounding max values:
pref("privacy.window.maxInnerWidth", 1600);
pref("privacy.window.maxInnerHeight", 900);
// Disable mozAddonManager Web API:
pref("privacy.resistFingerprinting.block_mozAddonManager", true);
// Disable using system colors:
pref("browser.display.use_system_colors", false);
// Disable showing about:blank page when possible at startup
pref("browser.startup.blankWindow", false);
// Disable using system colors:
pref("browser.display.use_system_colors", false);
END
local distini="$pkgdir/usr/lib/$pkgname/distribution/distribution.ini"
install -Dvm644 /dev/stdin "$distini" <<END
[Global]
id=io.gitlab.${pkgname}-community
version=1.0
about=LibreWolf
[Preferences]
app.distributor="LibreWolf Community"
app.distributor.channel=$pkgname
app.partner.librewolf=$pkgname
END
for i in 16 32 48 64 128; do
install -Dvm644 browser/branding/${pkgname}/default$i.png \
"$pkgdir/usr/share/icons/hicolor/${i}x${i}/apps/$pkgname.png"
done
# install -Dvm644 browser/branding/librewolf/content/about-logo.png \
# "$pkgdir/usr/share/icons/hicolor/192x192/apps/$pkgname.png"
install -Dvm644 ${srcdir}/default192x192.png \
"$pkgdir/usr/share/icons/hicolor/192x192/apps/$pkgname.png"
# arch upstream provides a separate svg for this. we don't have that, so let's re-use 16.png
install -Dvm644 browser/branding/${pkgname}/default16.png \
"$pkgdir/usr/share/icons/hicolor/symbolic/apps/$pkgname-symbolic.png"
install -Dvm644 ../$pkgname.desktop \
"$pkgdir/usr/share/applications/$pkgname.desktop"
# Install a wrapper to avoid confusion about binary path
install -Dvm755 /dev/stdin "$pkgdir/usr/bin/$pkgname" <<END
#!/bin/sh
exec /usr/local/lib/$pkgname/librewolf "\$@"
END
# Replace duplicate binary with wrapper
# https://bugzilla.mozilla.org/show_bug.cgi?id=658850
ln -srfv "$pkgdir/usr/local/bin/$pkgname" "$pkgdir/usr/local/lib/$pkgname/librewolf-bin"
# Use system certificates
local nssckbi="$pkgdir/usr/local/lib/$pkgname/libnssckbi.so"
if [[ -e $nssckbi ]]; then
ln -srfv "$pkgdir/usr/local/lib/libnssckbi.so" "$nssckbi"
fi
}
Finalmente simplemente ejecuto “makepkg” y el proceso inicia, creando finalmente el archivo instalable para pacman.
LibreWolf puede ser un ejemplo complicado (aunque muy completo), por lo que acá te dejo la documentación oficial de Arch Linux de como crear el documento paso a paso de forma clara y sencilla;
Alpine Linux tiene un archivo de una estructura muy parecida llamado APKBUILD;