Olhando para a história das gerações ARM usadas pelo Raspberry Pi, surpreende como o chip era antigo
Mesmo em 2014, quando o Raspberry Pi B+ saiu, o núcleo ARM1176 usado era de 2003, então já tinha 11 anos
Por isso, não é estranho que, ao compilar em outra plataforma, como um Raspberry Pi mais recente, seja necessário especificar flags de arquitetura para gerar código compatível
Mas, se nem ao compilar no próprio Raspberry Pi B+ a arquitetura correta for usada por padrão, isso parece um erro de configuração nos padrões da distribuição
Pelo que me lembro, o Pi original usava chips que sobraram de TV boxes
Produtos desse tipo quase nunca colocam mais desempenho de computação do que o necessário, por causa do preço
Desde o início, era um produto pensado para ser um computador de baixo custo
Na verdade, não foi compilado no B+
O texto diz que “peguei um binário do host de build, um Pi 4B muito mais rápido, e tentei executá-lo nesse dispositivo antigo, resultando em illegal instruction”
Isso é parecido com tentar executar em um PC antigo com Windows XP um .EXE compilado no Windows 11 com o MSVC mais recente
Provavelmente a distribuição do Pi inteira rodando no Pi 4B também não funcionaria no B+, e até o kernel pode ter sido compilado da mesma forma
Acho que o texto ou o artigo não deixa isso claro, mas isso não é um bug?
Procurei bugs do LLVM e encontrei algo que parecia quase o mesmo problema, mas era um bug de 2012 e já estava fechado. Pelos últimos comentários, parecia que talvez não tivesse sido corrigido de fato, mas só dei uma olhada rápida e posso ter entendido errado https://github.com/llvm/llvm-project/issues/13989
Revendo, no fim do texto ele diz que, ao passar explicitamente o alvo, o programa gerado funciona. Então parece uma espécie de bug de configuração; no Unix, eu esperaria que o alvo padrão fosse o processador atual, mas não tenho certeza
O bug que linkei parece ter sido um problema em que o compilador gerava código errado mesmo quando o alvo era configurado corretamente; felizmente, não parece ser esse o caso agora
Isso mesmo. O bug linkado era que, mesmo dizendo ao compilador para mirar em armv6, ele emitia instruções armv7
O problema da Rachel foi resolvido ao informar ao compilador que o alvo era armv6, então aquele bug já foi corrigido e parece ser separado deste problema
Claro que é um bug, mas parece que o autor, em vez de reportá-lo, preferiu escrever um post com um título meio caça-cliques e terminar com “muito estranho”
O banco de dados em que trabalho, o ClickHouse, se esforça bastante para manter compatibilidade com hardware bem antigo
O binário ARM padrão exige Armv8.2, de 2016, e pode ser usado nos modelos a partir do Raspberry Pi 2. O binário x86 roda em hardware por volta de 2010 que tenha SSE4.2 e instruções pclmul* para CRC rápido
Também compilamos binários para sistemas com apenas Armv8.0 e SSE2, mas não os testamos em CI. O script de instalação rápida baixa e descompacta o binário adequado para o host de destino
No geral, acho difícil equilibrar compatibilidade retroativa com o aproveitamento dos recursos de CPU das gerações AArch64 mais recentes https://en.wikipedia.org/wiki/AArch64
Havia um número surpreendente de instituições com orçamento apertado, por exemplo universidades de países emergentes, e usuários hobbystas sem condições de atualizar hardware
Tecnicamente, era bem incômodo o fato de as flags de CPU em /proc/cpuinfo nem sempre corresponderem às flags -march= passadas ao compilador. Por exemplo, aparecem de formas diferentes, como "lrcpc" e "rcpc"
Para fazer isso funcionar corretamente, na prática é preciso manter dois conjuntos de flags
Nesse caso, acho que seria melhor para todos oferecer várias builds, para que os clientes possam escolher a mais próxima da própria arquitetura
O problema provavelmente é que o alvo configurado mudou no pacote clang-13 atual do bookworm
Especificamente, no bullseye e no clang-11, o alvo padrão é armv6k-unknown-linux-gnueabihf, enquanto no bookworm e no clang-13 é arm-unknown-linux-gnueabihf
Ou talvez o padrão dessa configuração de build tenha mudado no próprio LLVM
Não parece ter sido uma mudança intencional
Como comentários próximos mencionaram /etc/env.d/gcc, é bem possível que seja a forma como ele lê informações do ambiente
O triple padrão deve ser algo como arm-unknown-linux se o clang não encontrar nem receber informações mais específicas, mas parece que o mecanismo que informa um alvo mais específico quebrou
Isso pode significar que não há um buildbot ARMv6, ou que há um buildbot, mas nele a configuração implícita ainda funciona corretamente
O LLVM é um cross-compiler realmente bom. Dá para compilar de qualquer alvo para qualquer alvo sem grandes problemas
O Clang é menos atraente que isso. Se ele foi compilado com suporte ao alvo e se você conseguir informar corretamente para qual alvo compilar, provavelmente lida com isso do jeito certo. Mesmo neste texto, o palpite estava errado, mas, ao receber mais informações, ele funcionou corretamente
A situação das bibliotecas de runtime é pior. Mesmo que você compile para um alvo como armv4, ainda precisa encontrar a libc correspondente etc., e talvez precise informar ao compilador onde ficam essas bibliotecas e headers; nessa parte, os detalhes ainda não são claros
A maioria das distribuições e compiladores praticamente abandonou o suporte a ARMv6 alguns anos atrás
Passei por um problema parecido ao compilar binários para um NAS antigo da Synology
Por que o Clang teria que ler informações de /etc/env.d/gcc?
clang/clang++ lêem as flags de alvo e o perfil em /etc/env.d/gcc, e cabe ao sistema operacional manter isso corretamente
Nesse sistema operacional, parece que essa manutenção não foi feita direito
Meu SBC ARM com Gentoo, baseado na arquitetura ainda mais antiga armv4, continua rodando bem mesmo com as atualizações recentes de gcc/clang grep CTARGET /etc/env.d/gcc -r /etc/env.d/gcc/armv4tl-softfloat-linux-gnueabi-11.3.0:CTARGET="armv4tl-softfloat-linux-gnueabi"
/etc/env.d é um diretório específico do Gentoo que define variáveis de ambiente padrão das sessões de usuário
O Clang não tem um recurso para ler esse diretório, então não se deve presumir que ele exista em outras distribuições
É apenas que a configuração de compilador do Gentoo lê a variável de ambiente CTARGET para escolher o alvo, e o Gentoo usa /etc/env.d para definir esse valor
O texto não diz se é Debian ou Raspbian nem, se for Debian, se é o port armel ou armhf
Sem essa informação, não faz muito sentido discutir para qual conjunto de instruções o LLVM compila. Isso depende da configuração de alvo nativo do LLVM
Como referência, o llvm-toolchaim-snapshot do Debian ainda oferece suporte a armel, que usa ARMv5T como baseline. No entanto, atualmente há um bug separado na biblioteca OpenMP do LLVM que impede a compilação de ter sucesso
O estranho é que o próprio binário do Clang foi compilado com um conjunto de instruções compatível com o Pi B+, mas, na prática, não mira um conjunto de instruções compatível com o Pi B+
Isso é realmente estranho. Como não é para ser usado como cross-compiler, em teoria host e alvo deveriam ser iguais
A imagem provavelmente é Raspbian. Não vejo motivo para não presumir isso
Seria útil saber a saída do comando dpkg-architecture e o conteúdo do arquivo /etc/os-release
Sem isso, fica difícil fazer um comentário útil
O título infelizmente é sensacionalista
Isto é uma mudança no alvo padrão, e o clang ainda consegue compilar binários para o Pi B+
Basta especificar explicitamente a arquitetura. Então acho melhor mudar um pouco o título para deixar mais claro que se trata de uma mudança na configuração padrão
Se, mesmo compilando na própria máquina-alvo, ele não consegue gerar binários para essa máquina-alvo, não me parece tão sensacionalista assim
Achei interessante porque, ao depurar por que um programa não roda em ARM, parece que a abordagem é mais ou menos essa
Tenho uma build Linux do Unity que não roda dentro de um contêiner, e mesmo passando a flag amd64 ao executar o Docker, o Unity mono tenta fazer uma chamada de sistema indisponível
Encontrei um contorno e ainda não depurei. Ativei o modo de desenvolvimento e mudei as configurações de build para não usar mono
Algum dia quero voltar a investigar isso para aprender mais
1 comentários
Comentários do Hacker News
Olhando para a história das gerações ARM usadas pelo Raspberry Pi, surpreende como o chip era antigo
Mesmo em 2014, quando o Raspberry Pi B+ saiu, o núcleo ARM1176 usado era de 2003, então já tinha 11 anos
Por isso, não é estranho que, ao compilar em outra plataforma, como um Raspberry Pi mais recente, seja necessário especificar flags de arquitetura para gerar código compatível
Mas, se nem ao compilar no próprio Raspberry Pi B+ a arquitetura correta for usada por padrão, isso parece um erro de configuração nos padrões da distribuição
Produtos desse tipo quase nunca colocam mais desempenho de computação do que o necessário, por causa do preço
O texto diz que “peguei um binário do host de build, um Pi 4B muito mais rápido, e tentei executá-lo nesse dispositivo antigo, resultando em illegal instruction”
Isso é parecido com tentar executar em um PC antigo com Windows XP um .EXE compilado no Windows 11 com o MSVC mais recente
Provavelmente a distribuição do Pi inteira rodando no Pi 4B também não funcionaria no B+, e até o kernel pode ter sido compilado da mesma forma
Acho que o texto ou o artigo não deixa isso claro, mas isso não é um bug?
Procurei bugs do LLVM e encontrei algo que parecia quase o mesmo problema, mas era um bug de 2012 e já estava fechado. Pelos últimos comentários, parecia que talvez não tivesse sido corrigido de fato, mas só dei uma olhada rápida e posso ter entendido errado
https://github.com/llvm/llvm-project/issues/13989
Revendo, no fim do texto ele diz que, ao passar explicitamente o alvo, o programa gerado funciona. Então parece uma espécie de bug de configuração; no Unix, eu esperaria que o alvo padrão fosse o processador atual, mas não tenho certeza
O bug que linkei parece ter sido um problema em que o compilador gerava código errado mesmo quando o alvo era configurado corretamente; felizmente, não parece ser esse o caso agora
O problema da Rachel foi resolvido ao informar ao compilador que o alvo era armv6, então aquele bug já foi corrigido e parece ser separado deste problema
O banco de dados em que trabalho, o ClickHouse, se esforça bastante para manter compatibilidade com hardware bem antigo
O binário ARM padrão exige Armv8.2, de 2016, e pode ser usado nos modelos a partir do Raspberry Pi 2. O binário x86 roda em hardware por volta de 2010 que tenha SSE4.2 e instruções pclmul* para CRC rápido
Também compilamos binários para sistemas com apenas Armv8.0 e SSE2, mas não os testamos em CI. O script de instalação rápida baixa e descompacta o binário adequado para o host de destino
No geral, acho difícil equilibrar compatibilidade retroativa com o aproveitamento dos recursos de CPU das gerações AArch64 mais recentes
https://en.wikipedia.org/wiki/AArch64
Havia um número surpreendente de instituições com orçamento apertado, por exemplo universidades de países emergentes, e usuários hobbystas sem condições de atualizar hardware
Tecnicamente, era bem incômodo o fato de as flags de CPU em /proc/cpuinfo nem sempre corresponderem às flags -march= passadas ao compilador. Por exemplo, aparecem de formas diferentes, como "lrcpc" e "rcpc"
Para fazer isso funcionar corretamente, na prática é preciso manter dois conjuntos de flags
O problema provavelmente é que o alvo configurado mudou no pacote clang-13 atual do bookworm
Especificamente, no bullseye e no clang-11, o alvo padrão é armv6k-unknown-linux-gnueabihf, enquanto no bookworm e no clang-13 é arm-unknown-linux-gnueabihf
Ou talvez o padrão dessa configuração de build tenha mudado no próprio LLVM
Mas, comparando [1] e [2], há no arquivo rules um teste simples que diz “se DEB_HOST_ARCH for armhf, defina LLVM_HOST_TRIPLE como armv6k”, o que parece confirmar a mudança na configuração de build
[1] http://raspbian.raspberrypi.org/raspbian/pool/main/l/llvm-to...
[2] http://raspbian.raspberrypi.org/raspbian/pool/main/l/llvm-to...
Não parece ter sido uma mudança intencional
Como comentários próximos mencionaram
/etc/env.d/gcc, é bem possível que seja a forma como ele lê informações do ambienteO triple padrão deve ser algo como
arm-unknown-linuxse o clang não encontrar nem receber informações mais específicas, mas parece que o mecanismo que informa um alvo mais específico quebrouIsso pode significar que não há um buildbot ARMv6, ou que há um buildbot, mas nele a configuração implícita ainda funciona corretamente
O LLVM é um cross-compiler realmente bom. Dá para compilar de qualquer alvo para qualquer alvo sem grandes problemas
O Clang é menos atraente que isso. Se ele foi compilado com suporte ao alvo e se você conseguir informar corretamente para qual alvo compilar, provavelmente lida com isso do jeito certo. Mesmo neste texto, o palpite estava errado, mas, ao receber mais informações, ele funcionou corretamente
A situação das bibliotecas de runtime é pior. Mesmo que você compile para um alvo como
armv4, ainda precisa encontrar alibccorrespondente etc., e talvez precise informar ao compilador onde ficam essas bibliotecas e headers; nessa parte, os detalhes ainda não são clarosPassei por um problema parecido ao compilar binários para um NAS antigo da Synology
/etc/env.d/gcc?clang/clang++lêem as flags de alvo e o perfil em/etc/env.d/gcc, e cabe ao sistema operacional manter isso corretamenteNesse sistema operacional, parece que essa manutenção não foi feita direito
Meu SBC ARM com Gentoo, baseado na arquitetura ainda mais antiga
armv4, continua rodando bem mesmo com as atualizações recentes degcc/clanggrep CTARGET /etc/env.d/gcc -r/etc/env.d/gcc/armv4tl-softfloat-linux-gnueabi-11.3.0:CTARGET="armv4tl-softfloat-linux-gnueabi"/etc/env.dé um diretório específico do Gentoo que define variáveis de ambiente padrão das sessões de usuárioO Clang não tem um recurso para ler esse diretório, então não se deve presumir que ele exista em outras distribuições
É apenas que a configuração de compilador do Gentoo lê a variável de ambiente
CTARGETpara escolher o alvo, e o Gentoo usa/etc/env.dpara definir esse valorO texto não diz se é Debian ou Raspbian nem, se for Debian, se é o port
armelouarmhfSem essa informação, não faz muito sentido discutir para qual conjunto de instruções o LLVM compila. Isso depende da configuração de alvo nativo do LLVM
Como referência, o
llvm-toolchaim-snapshotdo Debian ainda oferece suporte aarmel, que usa ARMv5T como baseline. No entanto, atualmente há um bug separado na biblioteca OpenMP do LLVM que impede a compilação de ter sucessoIsso é realmente estranho. Como não é para ser usado como cross-compiler, em teoria host e alvo deveriam ser iguais
A imagem provavelmente é Raspbian. Não vejo motivo para não presumir isso
Seria útil saber a saída do comando
dpkg-architecturee o conteúdo do arquivo/etc/os-releaseSem isso, fica difícil fazer um comentário útil
O título infelizmente é sensacionalista
Isto é uma mudança no alvo padrão, e o clang ainda consegue compilar binários para o Pi B+
Basta especificar explicitamente a arquitetura. Então acho melhor mudar um pouco o título para deixar mais claro que se trata de uma mudança na configuração padrão
Achei interessante porque, ao depurar por que um programa não roda em ARM, parece que a abordagem é mais ou menos essa
Tenho uma build Linux do Unity que não roda dentro de um contêiner, e mesmo passando a flag
amd64ao executar o Docker, o Unity mono tenta fazer uma chamada de sistema indisponívelEncontrei um contorno e ainda não depurei. Ativei o modo de desenvolvimento e mudei as configurações de build para não usar mono
Algum dia quero voltar a investigar isso para aprender mais