- Define o initrd como uma unidade de programa que o kernel interpreta e executa diretamente, reinterpretando o Linux como uma espécie de interpretador
- Usa
kexec, base64 e cpio para montar uma distribuição Linux recursiva que reinicia a si mesma, em que o initrd executa a si próprio novamente
- Se o script
/init for configurado para emitir sua própria imagem cpio, forma-se um initrd autorreplicante no estilo Quine
- Explica, por meio da estrutura de execução ELF, do
ld.so e do binfmt_misc, uma arquitetura em camadas de interpretadores que se estende até o kernel
- Com
kexec ou QEMU, é possível executar outro Linux sobre o Linux em recursão de cauda, expandindo experimentalmente as fronteiras entre kernel, virtualização e interpretadores
Engenharia reversa de rkx.gz e a estrutura de initrd autorrecursiva
- O comando
curl https://astrid.tech/rkx.gz | gunzip | sudo sh baixa e executa um script shell de 20 MB codificado em base64
- O script verifica privilégios de root e confirma a presença de
kexec, base64 e cpio
- Decodifica os dados em base64 para criar um arquivo cpio chamado
r e, a partir dele, extrai uma imagem de kernel chamada k
- Usa
kexec para carregar e executar k como kernel e r como ramdisk
- Dentro de
r.cpio há os arquivos /bin, /init e k; k é uma imagem do kernel Linux 6.18.18, e /init é um script shell
- O
/init monta /proc, empacota o sistema de arquivos atual em cpio em /r e então usa kexec para executar novamente /k e /r
- O resultado é uma distribuição Linux recursiva que continua reiniciando a si mesma
A perspectiva de ver o kernel Linux como um interpretador
- O initrd não é apenas um ramdisk de boot, mas pode ser visto como um programa que o kernel Linux interpreta e executa
- Assim como
curl | sh ou python3 script.py, o initrd também é uma forma de programa de entrada executada pelo kernel
- Portanto, o kernel Linux funciona como um interpretador do initrd
- Essa estrutura é semelhante à otimização de recursão de cauda (tail-call optimization)
- O
kexec carrega e executa o novo kernel em um novo espaço de memória, sem sobrescrever o kernel anterior
- Cada kernel não preserva o estado anterior e é substituído por um novo “stack frame”
Quine e a autorreplicação do initrd
- Quine significa um programa que imprime a si mesmo
- Se o script
/init executar cat /r ao final, ele produzirá um cpio idêntico a si próprio
- Nesse caso, forma-se um Quine do interpretador initrd do Linux
- Como todos os arquivos existem em
tmpfs na RAM, não ocorre I/O real em disco
ELF, ld.so e as camadas do interpretador
- Executáveis ELF incluem no cabeçalho o caminho do interpretador (
ld-linux-x86-64.so.2)
- Na execução, o kernel inicia primeiro o
ld.so, e o ld.so carrega as bibliotecas dinâmicas do ELF antes de executar o programa
- Portanto, o ELF também pode ser visto como uma espécie de linguagem interpretada
- O
/bin/sh é interpretado pelo ld.so, e o ld.so é interpretado diretamente pelo kernel
- Como o
ld.so é um ELF com linkagem estática, o kernel pode executá-lo diretamente
- Assim se forma o caso-base (base case) da hierarquia de interpretadores
Execução de CPIO com binfmt_misc
- Com
binfmt_misc, é possível executar arquivos com determinados magic bytes por meio de um interpretador especificado
- É possível registrar como interpretador um script que usa QEMU para executar o CPIO como initrd
- O QEMU inicializa uma máquina virtual com o kernel e o initrd especificados
- Como resultado, o interpretador do arquivo CPIO passa a ser o kernel Linux executado pelo QEMU
Interpretadores recursivos e o “loop mais estranho”
- Um interpretador baseado em QEMU cria um novo stack frame de ambiente Linux
- É uma estrutura que executa outro Linux sobre o Linux, podendo aninhar-se até o limite de memória
- Se for substituído por um interpretador baseado em
kexec, torna-se possível uma execução Linux recursiva com otimização de chamada de cauda
- Se o
/init for configurado para registrar binfmt_misc e executar /r,
completa-se um initrd que executa a si mesmo
- O
/r é o próximo processo init em formato CPIO e, ao ser executado, interpreta a si próprio novamente
Conclusão
- O initrd não é apenas uma ferramenta de boot, mas uma unidade de programa interpretada pelo kernel Linux
- Com
kexec e binfmt_misc, é possível executar o próprio Linux recursivamente como se fosse um interpretador
- Essa estrutura é um conceito experimental que desfaz as fronteiras entre kernel, virtualização, interpretadores e programas autorreplicantes
- O código-fonte relacionado está disponível no repositório GitHub ifd3f/rekexec
2 comentários
Dizem que a ignorância traz coragem... espero que evitemos esse tipo de texto.
Comentários do Hacker News
Sofri ao ler este texto por causa de equívocos demais
Um arquivo cpio não é um sistema de arquivos. O autor usa initramfs, que é baseado em tmpfs. O Linux consegue extrair um cpio para tmpfs. Um arquivo de arquivos e diretórios não é, por si só, um programa
Só porque algo parece semelhante não quer dizer que seja a mesma coisa. Um programa binário é executado na CPU e, se existe um interpretador, ele está escondido no ambiente de hardware. Isso está fora do escopo do kernel
Para executar um shell script, é preciso um shell que interprete esse script. O autor omite essa parte e confunde o kernel com o programa shell
O Linux pode ser compilado sem initramfs ou ramdisk e ainda assim executar o userland de um sistema de arquivos
A expressão “Linux initrd interpreter” é realmente uma descrição errada
ld.sodescompactar um ELF na memória e executar o ponto de entrada, e o kernel descompactar um initramfs e executar o ponto de entrada, são conceitos parecidosTodo sistema operacional não acaba funcionando como um interpretador de código de máquina com privilégios de kernel?
Este texto funciona se você entender “Linux é um interpretador” como um modelo mental, mas está errado se levado ao pé da letra
Faz mais sentido vê-lo não como interpretação no nível de instruções da CPU, mas como o papel do kernel em orquestrar formatos executáveis como ELF, scripts com shebang e initramfs. A confusão parece vir da mistura de dois sentidos diferentes de “interpretador”
O ponto central não é se a analogia está certa, mas mostrar o quanto o conceito de “execução” depende do ambiente
“Tudo é um interpretador?”
O Theta Combinator de Turing
Num texto anterior da série, o autor disse que criou a própria imagem de VPS porque não queria usar o object storage da Contabo
Acho que existe um ponto de equilíbrio entre gastar 50 horas para economizar US$ 1,50 por mês e gastar US$ 250 mil em tokens.
Se você não consegue arcar com os custos de infraestrutura, talvez o problema seja mais social do que técnico. Ficar obcecado em rodar Doom via curl não me parece produtivo
Em
man ld.so, está explícito que o linker dinâmico armazenado na seção.interpdo ELF é executado. O nome da própria seção é interessanteO Linux é muito útil como uma interface programável. O Windows também permite isso, mas sinto que o Linux é mais adequado
Acho a GUI do Windows melhor, mas também não gosto de GNOME nem KDE. Por isso uso fluxbox, icewm e, às vezes, xfce ou mate-desktop. Hoje em dia prefiro ambientes simples e rápidos. Faço a maior parte do trabalho na linha de comando e editando código