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;

“Código del programa ps del sistema operativo BSD”

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;

    BibliotecaLocaciónPaquete que lo provee
    linux-vdso.so.1Memoria RAMEl propio kernel Linux
    libcrypt.so.2/usr/lib/libcrypt.so.2libxcrypt
    libpcre2-8.so.0/usr/lib/libpcre2-9.so.0pcre2
    libssl.so.3/usr/lib/libssl.so.3openssl
    libcrypto.so.3/usr/lib/libcrypto.so.3openssl
    libz.so.1/usr/lib/libz.so.1zlib
    libGeoIP.so.1/usr/lib/libGeoIP.so.1geoip
    libc.so.6/usr/lib/libc.so.6glibc, 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;

    CompiladorArgumento
    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;

  1. 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);

    1. 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.

    2. 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] ../

    3. 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]

  2. 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;

FlagPropósito
-march=nativeCompilar el programa de forma especifica para el CPU.
-O2Aplica el segundo nivel de optimización al binario.
-Wp,-D_FORTIFY_SOURCE=2Aplica fortificaciones de seguridad generales.
-fstack-clash-protectionAplica protecciones para evitar que un exploit tome control del programa ante un fallo en el stack.
-fcf-protectionHabilita 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-strongSe 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=moldUsar 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

FlagPropósito
-C target-cpu=nativeCompilar el programa de forma especifica para el CPU.
-C opt-level=2Aplica el segundo nivel de optimización al binario.
-C link-arg=-fuse-ld=moldUsar el enlazador “mold que es muy rápido y eficiente.
-C strip=debuginfo -C strip=symbols -C debug-assertions=falseEliminar todos los símbolos de depuración del binario final.

Para el enlazador (se explica todo en el siguiente capitulo);

FlagPropósito
-WlSe usa para pasar las próximas opciones directamente al enlazador
-O1Aplica optimizaciones básicas al proceso de enlazado, como la eliminación de símbolos no utilizados y la reorganización de secciones.
–sort-commonLe 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-neededIndica 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,relroEs una opción de seguridad que hace que algunas secciones del binario sean de solo lectura después de la fase de enlace.
-z,nowEs 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,defsEs 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=moldUsar 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;