Um Binário Rust Independente
Conteúdo Traduzido: Esta é uma tradução comunitária do post A Freestanding Rust Binary. Pode estar incompleta, desatualizada ou conter erros. Por favor, reporte qualquer problema!
Traduzido por @richarddalves.
O primeiro passo para criar nosso próprio kernel de sistema operacional é criar um executável Rust que não vincule a biblioteca padrão. Isso torna possível executar o código Rust no bare metal sem um sistema operacional subjacente.
Este blog é desenvolvido abertamente no GitHub. Se você tiver algum problema ou dúvida, abra um issue lá. Você também pode deixar comentários na parte inferior. O código-fonte completo desta publicação pode ser encontrado na banch post-01.
Tabela de Conteúdos
🔗Introdução
Para escrever um kernel de sistema operacional, precisamos de código que não dependa de nenhum recurso do sistema operacional. Isso significa que não podemos usar threads, arquivos, memória heap, rede, números aleatórios, saída padrão ou qualquer outro recurso que exija abstrações do sistema operacional ou hardware específico. O que faz sentido, já que estamos tentando escrever nosso próprio sistema operacional e nossos próprios drivers.
Isso significa que não podemos usar a maior parte da [biblioteca padrão do Rust], mas há muitos recursos do Rust que podemos usar. Por exemplo, podemos usar iteradores, closures, pattern matching, option e result, formatação de string e, claro, o sistema de ownership. Esses recursos tornam possível escrever um kernel de uma maneira muito expressiva e de alto nível, sem nos preocuparmos com undefined behavior ou memory safety.
Para criar um kernel de sistema operacional em Rust, precisamos criar um executável que possa ser executado sem um sistema operacional subjacente. Esse executável é frequentemente chamado de executável “autônomo” ou “bare-metal”.
Este post descreve as etapas necessárias para criar um binário Rust independente e explica por que essas etapas são necessárias. Se você estiver interessado apenas em um exemplo mínimo, pode ir para o resumo.
🔗Desativando a biblioteca padrão
Por padrão, todos as crates Rust vinculam a biblioteca padrão, que depende do sistema operacional para recursos como threads, arquivos ou rede. Ela também depende da biblioteca padrão C libc, que interage intimamente com os serviços do sistema operacional. Como nosso plano é escrever um sistema operacional, não podemos usar nenhuma biblioteca dependente de um sistema operacional.
Portanto, temos que desativar a inclusão automática da biblioteca padrão por meio do atributo no_std.
Começamos criando um novo projeto de binário cargo. A maneira mais fácil de fazer isso é através da linha de comando:
cargo new blog_os --bin --edition 2024
Eu nommei o projeto blog_os, mas claro que você pode escolher o seu próprio nome. A flag --bin especifica que queremos criar um executável binário (em contraste com uma biblioteca) e a flag --edition 2024 especifica que queremos usar a edição 2024 de Rust para nossa crate. Quando executamos o comando, o cargo cria a seguinte estrutura de diretório para nós:
blog_os
├── Cargo.toml
└── src
└── main.rs
O Cargo.toml contém a configuração da crate, por exemplo o nome da crate, o autor, o número da versão semântica e dependências. O arquivo src/main.rs contém o módulo raiz da nossa crate e nossa função main. Você pode compilar sua crate através de cargo build e então executar o binário compilado blog_os na subpasta target/debug.
🔗O Atributo no_std
Agora nossa crate implicitamente vincula a biblioteca padrão. Vamos tentar desativar isso adicionando o atributo no_std:
// main.rs
#![no_std]
fn main() {
println!("Olá, mundo!");
}
Quando tentamos compilá-lo agora (executando cargo build), o seguinte erro ocorre:
error: cannot find macro `println!` in this scope
--> src/main.rs:4:5
|
4 | println!("Olá, mundo!");
| ^^^^^^^
A razão deste erro é que a macro println é parte da biblioteca padrão, que não incluímos mais. Então não conseguimos mais imprimir coisas. Isso faz sentido, já que println escreve no standard output, que é um descritor de arquivo especial fornecido pelo sistema operacional.
Então vamos remover o println!() e tentar novamente com uma função main vazia:
// main.rs
#![no_std]
fn main() {}
> cargo build
error: `#[panic_handler]` function required, but not found
error: language item required, but not found: `eh_personality`
Agora o compilador está pedindo uma função #[panic_handler] e um item de linguagem.
🔗Implementação de Panic
O atributo panic_handler define a função que o compilador deve invocar quando ocorre um panic. A biblioteca padrão fornece sua própria função de tratamento de panic, mas em um ambiente no_std precisamos defini-la nós mesmos:
// in main.rs
use core::panic::PanicInfo;
/// Esta função é chamada em caso de pânico.
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}
O parâmetro PanicInfo contém o arquivo e a linha onde o panic aconteceu e a mensagem de panic opcional. A função nunca deve retornar, então é marcada como uma função divergente ao retornar o tipo “never” !. Não há muito que possamos fazer nesta função por enquanto, então apenas fazemos um loop infinito.
🔗O Item de Linguagem eh_personality
Items de linguagem são funções e tipos especiais necessários internamente pelo compilador. Por exemplo, a trait Copy é um item de linguagem que diz ao compilador quais tipos têm semântica de cópia. Quando olhamos para a implementação, vemos que tem o atributo especial #[lang = "copy"] que o define como um item de linguagem (Language Item em inglês).
Enquanto é possível fornecer implementações customizadas de items de linguagem, isso deve ser feito apenas como último recurso. A razão é que items de linguagem são detalhes de implementação altamente instáveis e nem mesmo verificados de tipo (então o compilador não verifica se uma função tem os tipos de argumento corretos). Felizmente, há uma forma mais estável de corrigir o erro de item de linguagem acima.
O item de linguagem eh_personality marca uma função que é usada para implementar stack unwinding. Por padrão, Rust usa unwinding para executar os destructores de todas as variáveis da stack vivas em caso de panic. Isso garante que toda memória usada seja liberada e permite que a thread pai capture o panic e continue a execução. Unwinding, no entanto, é um processo complicado e requer algumas bibliotecas específicas do SO (por exemplo, libunwind no Linux ou tratamento estruturado de exceção no Windows), então não queremos usá-lo para nosso sistema operacional.
🔗Desativando o Unwinding
Existem outros casos de uso também para os quais unwinding é indesejável, então Rust fornece uma opção para abortar no panic em vez disso. Isso desativa a geração de informações de símbolo de desenrolar e reduz consideravelmente o tamanho do binário. Há múltiplos locais onde podemos desativar o unwinding. A forma mais fácil é adicionar as seguintes linhas ao nosso Cargo.toml:
[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"
Isso define a estratégia de panic para abort tanto para o perfil dev (usado para cargo build) quanto para o perfil release (usado para cargo build --release). Agora o item de linguagem eh_personality não deve mais ser necessário.
Agora corrigimos ambos os erros acima. No entanto, se tentarmos compilar agora, outro erro ocorre:
> cargo build
error: requires `start` lang_item
Está faltando o item de linguagem start no nosso programa, que define o ponto de entrada.
🔗O Atributo start
Alguém pode pensar que a função main é a primeira função chamada quando você executa um programa. No entanto, a maioria das linguagens tem um sistema de runtime, que é responsável por coisas como coleta de lixo (por exemplo, em Java) ou threads de software (por exemplo, goroutines em Go). Este runtime precisa ser chamado antes de main, já que ele precisa se inicializar a si mesmo.
Em um binário Rust típico que vincula a biblioteca padrão, a execução começa em uma biblioteca de runtime C chamada crt0 (“C runtime zero”), que configura o ambiente para uma aplicação C. Isso inclui criar um stack e colocar os argumentos nos registradores certos. O runtime C então invoca o ponto de entrada do runtime Rust, que é marcado pelo item de linguagem start. Rust tem apenas um runtime muito mínimo, que cuida de algumas poucas coisas como configurar guardas de estouro do stack ou imprimir um backtrace ao fazer panic. O runtime então finalmente chama a função main.
Nosso executável independente não tem acesso ao runtime Rust e ao crt0, então precisamos definir nosso próprio ponto de entrada. Implementar o item de linguagem start não ajudaria, já que ainda exigiria crt0. Em vez disso, precisamos sobrescrever diretamente o ponto de entrada crt0.
🔗Sobrescrevendo o Ponto de Entrada (Entry Point)
Para dizer ao compilador Rust que não queremos usar a cadeia normal de ponto de entrada, adicionamos o atributo #![no_main].
#![no_std]
#![no_main]
use core::panic::PanicInfo;
/// Esta função é chamada em caso de pânico.
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}
Você pode notar que removemos a função main. A razão é que um main não faz sentido sem um runtime subjacente que o chame. Em vez disso, estamos agora sobrescrevendo o ponto de entrada do sistema operacional com nossa própria função _start:
#[unsafe(no_mangle)]
pub extern "C" fn _start() -> ! {
loop {}
}
Ao usar o atributo #[unsafe(no_mangle)], desativamos mangling de nomes para garantir que o compilador Rust realmente produza uma função com o nome _start. Sem o atributo, o compilador geraria algum símbolo criptografado como _ZN3blog_os4_start7hb173fedf945531caE para dar a cada função um nome único. O atributo é necessário porque precisamos dizer o nome da função do ponto de entrada ao linker no próximo passo.
Também temos que marcar a função como extern "C" para dizer ao compilador que ele deve usar a convenção de chamada C para esta função (em vez da convenção de chamada Rust não especificada). A razão de nomear a função _start é que este é o nome do ponto de entrada padrão para a maioria dos sistemas.
O tipo de retorno ! significa que a função é divergente, ou seja, não é permitida retornar nunca. Isso é necessário porque o ponto de entrada não é chamado por nenhuma função, mas invocado diretamente pelo sistema operacional ou bootloader. Então em vez de retornar, o ponto de entrada deve por exemplo invocar a chamada de sistema exit do sistema operacional. No nosso caso, desligar a máquina poderia ser uma ação razoável, já que não há nada mais a fazer se um binário independente retorna. Por enquanto, cumprimos o requisito fazendo um loop infinito.
Quando executamos cargo build agora, recebemos um feio erro de linker.
🔗Erros do Linker
O linker é um programa que combina o código gerado em um executável. Como o formato executável difere entre Linux, Windows e macOS, cada sistema tem seu próprio linker que lança um erro diferente. A causa fundamental dos erros é a mesma: a configuração padrão do linker assume que nosso programa depende do runtime C, o que não é o caso.
Para resolver os erros, precisamos dizer ao linker que ele não deve incluir o runtime C. Podemos fazer isso passando um certo conjunto de argumentos ao linker ou compilando para um alvo bare metal.
🔗Compilando para um Alvo Bare Metal
Por padrão, Rust tenta construir um executável que seja capaz de executar no seu ambiente de sistema atual. Por exemplo, se você estiver usando Windows em x86_64, Rust tenta construir um executável .exe Windows que usa instruções x86_64. Este ambiente é chamado seu sistema “host”.
Para descrever ambientes diferentes, Rust usa uma string chamada target triple. Você pode ver o target triple do seu sistema host executando rustc --version --verbose:
rustc 1.91.0 (f8297e351 2025-10-28)
binary: rustc
commit-hash: f8297e351a40c1439a467bbbb6879088047f50b3
commit-date: 2025-10-28
host: x86_64-unknown-linux-gnu
release: 1.91.0
LLVM version: 21.1.2
A saída acima é de um sistema Linux x86_64. Vemos que o triple host é x86_64-unknown-linux-gnu, que inclui a arquitetura de CPU (x86_64), o vendor (unknown), o sistema operacional (linux), e a ABI (gnu).
Ao compilar para nosso triple host, o compilador Rust e o linker assumem que há um sistema operacional subjacente como Linux ou Windows que usa o runtime C por padrão, o que causa os erros do linker. Então, para evitar os erros do linker, podemos compilar para um ambiente diferente sem nenhum sistema operacional subjacente.
Um exemplo de tal ambiente bare metal é o target triple thumbv7em-none-eabihf, que descreve um sistema embarcado ARM. Os detalhes não são importantes, tudo o que importa é que o target triple não tem nenhum sistema operacional subjacente, o que é indicado pelo none no target triple. Para ser capaz de compilar para este alvo, precisamos adicioná-lo em rustup:
rustup target add thumbv7em-none-eabihf
Isso baixa uma cópia da biblioteca padrão std (e core) para o sistema. Agora podemos compilar nosso executável independente para este alvo:
cargo build --target thumbv7em-none-eabihf
Ao passar um argumento --target, nós fazemos uma compilação cross compile nosso executável para um sistema alvo bare metal. Como o sistema alvo não tem sistema operacional, o linker não tenta vincular o runtime C e nossa compilação é bem-sucedida sem nenhum erro de linker.
Esta é a abordagem que usaremos para construir nosso kernel de SO. Em vez de thumbv7em-none-eabihf, usaremos um alvo customizado que descreve um ambiente bare metal x86_64. Os detalhes serão explicados no próximo post.
🔗Argumentos do Linker
Em vez de compilar para um sistema bare metal, também é possível resolver os erros do linker passando um certo conjunto de argumentos ao linker. Esta não é a abordagem que usaremos para nosso kernel, portanto esta seção é opcional e fornecida apenas para completude. Clique em “Argumentos do Linker” abaixo para mostrar o conteúdo opcional.
Argumentos do Linker
Nesta seção discutimos os erros do linker que ocorrem no Linux, Windows e macOS, e explicamos como resolvê-los passando argumentos adicionais ao linker. Note que o formato executável e o linker diferem entre sistemas operacionais, então que um conjunto diferente de argumentos é necessário para cada sistema.
🔗Linux
No Linux, o seguinte erro de linker ocorre (encurtado):
error: linking with `cc` failed: exit code: 1
|
= note: "cc" […]
= note: /usr/lib/gcc/../x86_64-linux-gnu/Scrt1.o: In function `_start':
(.text+0x12): undefined reference to `__libc_csu_fini'
/usr/lib/gcc/../x86_64-linux-gnu/Scrt1.o: In function `_start':
(.text+0x19): undefined reference to `__libc_csu_init'
/usr/lib/gcc/../x86_64-linux-gnu/Scrt1.o: In function `_start':
(.text+0x25): undefined reference to `__libc_start_main'
collect2: error: ld returned 1 exit status
O problema é que o linker inclui a rotina de inicialização do runtime C por padrão, que também é chamada _start. Ela requer alguns símbolos da biblioteca padrão C libc que não incluímos devido ao atributo no_std, portanto o linker não consegue resolver estas referências. Para resolver isso, podemos dizer ao linker que ele não deve vincular a rotina de inicialização C passando a flag -nostartfiles.
Uma forma de passar atributos de linker via cargo é o comando cargo rustc. O comando se comporta exatamente como cargo build, mas permite passar opções para rustc, o compilador Rust subjacente. rustc tem a flag -C link-arg, que passa um argumento ao linker. Combinados, nosso novo comando de compilação se parece com isso:
cargo rustc -- -C link-arg=-nostartfiles
Agora nossa crate compilada como um executável independente no Linux!
Não precisávamos especificar o nome da nossa função de ponto de entrada explicitamente, já que o linker procura por uma função com o nome _start por padrão.
🔗Windows
No Windows, um erro de linker diferente ocorre (encurtado):
error: linking with `link.exe` failed: exit code: 1561
|
= note: "C:\\Program Files (x86)\\…\\link.exe" […]
= note: LINK : fatal error LNK1561: entry point must be defined
O erro “entry point must be defined” (ponto de entrada deve ser definido) significa que o linker não consegue encontrar o ponto de entrada. No Windows, o nome do ponto de entrada padrão depende do subsistema usado. Para o subsistema CONSOLE, o linker procura por uma função nomeada mainCRTStartup e para o subsistema WINDOWS, ele procura por uma função nomeada WinMainCRTStartup. Para sobrescrever o padrão e dizer ao linker para procurar por nossa função _start em vez disso, podemos passar um argumento /ENTRY ao linker:
cargo rustc -- -C link-arg=/ENTRY:_start
Do formato de argumento diferente, vemos claramente que o linker Windows é um programa completamente diferente do linker Linux.
Agora um erro de linker diferente ocorre:
error: linking with `link.exe` failed: exit code: 1221
|
= note: "C:\\Program Files (x86)\\…\\link.exe" […]
= note: LINK : fatal error LNK1221: a subsystem can't be inferred and must be
defined
Este erro ocorre porque os executáveis Windows podem usar [subsistemas] diferenteswindows-subsystems. Para programas normais, eles são inferidos dependendo do nome do ponto de entrada: Se o ponto de entrada é nomeado main, o subsistema CONSOLE é usado, e se o ponto de entrada é nomeado WinMain, o subsistema WINDOWS é usado. Como nossa função _start tem um nome diferente, precisamos especificar o subsistema explicitamente:
cargo rustc -- -C link-args="/ENTRY:_start /SUBSYSTEM:console"
Usamos o subsistema CONSOLE aqui, mas o subsistema WINDOWS funcionaria também. Em vez de passar -C link-arg múltiplas vezes, usamos -C link-args que leva uma lista de argumentos separados por espaço.
Com este comando, nosso executável deve compilar com sucesso no Windows.
🔗macOS
No macOS, o seguinte erro de linker ocorre (encurtado):
error: linking with `cc` failed: exit code: 1
|
= note: "cc" […]
= note: ld: entry point (_main) undefined. for architecture x86_64
clang: error: linker command failed with exit code 1 […]
Esta mensagem de erro nos diz que o linker não consegue encontrar uma função de ponto de entrada com o nome padrão main (por alguma razão, todas as funções são prefixadas com um _ no macOS). Para definir o ponto de entrada para nossa função _start, passamos o argumento de linker -e:
cargo rustc -- -C link-args="-e __start"
A flag -e especifica o nome da função de ponto de entrada. Como todas as funções têm um _ adicional prefixado no macOS, precisamos definir o ponto de entrada para __start em vez de _start.
Agora o seguinte erro de linker ocorre:
error: linking with `cc` failed: exit code: 1
|
= note: "cc" […]
= note: ld: dynamic main executables must link with libSystem.dylib
for architecture x86_64
clang: error: linker command failed with exit code 1 […]
macOS não oferece suporte oficial a binários vinculados estaticamente e requer que programas vinculem a biblioteca libSystem por padrão. Para sobrescrever isto e vincular um binário estático, passamos a flag -static ao linker:
cargo rustc -- -C link-args="-e __start -static"
Isso ainda não é suficiente, pois um terceiro erro de linker ocorre:
error: linking with `cc` failed: exit code: 1
|
= note: "cc" […]
= note: ld: library not found for -lcrt0.o
clang: error: linker command failed with exit code 1 […]
Este erro ocorre porque programas no macOS vinculam a crt0 (“C runtime zero”) por padrão. Isto é similar ao erro que tivemos no Linux e também pode ser resolvido adicionando o argumento de linker -nostartfiles:
cargo rustc -- -C link-args="-e __start -static -nostartfiles"
Agora nosso programa deve compilar com sucesso no macOS.
🔗Unificando os Comandos de Compilação
Agora temos diferentes comandos de compilação dependendo da plataforma host, o que não é o ideal. Para evitar isto, podemos criar um arquivo nomeado .cargo/config.toml que contém os argumentos específicos de plataforma:
# in .cargo/config.toml
[target.'cfg(target_os = "linux")']
rustflags = ["-C", "link-arg=-nostartfiles"]
[target.'cfg(target_os = "windows")']
rustflags = ["-C", "link-args=/ENTRY:_start /SUBSYSTEM:console"]
[target.'cfg(target_os = "macos")']
rustflags = ["-C", "link-args=-e __start -static -nostartfiles"]
A key rustflags contém argumentos que são automaticamente adicionados a cada invocação de rustc. Para mais informações sobre o arquivo .cargo/config.toml, veja a documentação oficial.
Agora nosso programa deve ser compilável em todas as três plataformas com um simples cargo build.
🔗Você Deveria Fazer Isto?
Enquanto é possível construir um executável independente para Linux, Windows e macOS, provavelmente não é uma boa ideia. A razão é que nosso executável ainda espera por várias coisas, por exemplo que uma pilha seja inicializada quando a função _start é chamada. Sem o runtime C, alguns desses requisitos podem não ser atendidos, o que pode causar nosso programa falhar, por exemplo através de um segmentation fault.
Se você quiser criar um binário mínimo que execute em cima de um sistema operacional existente, incluindo libc e definindo o atributo #[start] conforme descrito aqui é provavelmente uma melhor ideia.
🔗Resumo
Um binário Rust independente mínimo se parece com isto:
src/main.rs:
#![no_std] // Não vincule a biblioteca padrão do Rust
#![no_main] // desativar todos os pontos de entrada no nível Rust
use core::panic::PanicInfo;
#[unsafe(no_mangle)] // não altere (mangle) o nome desta função
pub extern "C" fn _start() -> ! {
// essa função é o ponto de entrada, já que o vinculador procura uma função
// denominado `_start` por padrão
loop {}
}
/// Esta função é chamada em caso de pânico.
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}
Cargo.toml:
[package]
name = "crate_name"
version = "0.1.0"
authors = ["Author Name <author@example.com>"]
# o perfil usado para `cargo build`
[profile.dev]
panic = "abort" # desativar o unwinding do stack em caso de pânico
# o perfil usado para `cargo build --release`
[profile.release]
panic = "abort" # desativar o unwinding do stack em caso de pânico
Para construir este binário, precisamos compilar para um alvo bare metal como thumbv7em-none-eabihf:
cargo build --target thumbv7em-none-eabihf
Alternativamente, podemos compilá-lo para o sistema host passando argumentos adicionais de linker:
# Linux
cargo rustc -- -C link-arg=-nostartfiles
# Windows
cargo rustc -- -C link-args="/ENTRY:_start /SUBSYSTEM:console"
# macOS
cargo rustc -- -C link-args="-e __start -static -nostartfiles"
Note que isto é apenas um exemplo mínimo de um binário Rust independente. Este binário espera por várias coisas, por exemplo, que um stack seja inicializado quando a função _start é chamada. Portanto para qualquer uso real de tal binário, mais passos são necessários.
🔗Deixando rust-analyzer Feliz
O projeto rust-analyzer é uma ótima forma de obter autocompletar e suporte “ir para definição” (e muitos outros recursos) para código Rust no seu editor.
Funciona muito bem para projetos #![no_std] também, então recomendo usá-lo para desenvolvimento de kernel!
Se você estiver usando a funcionalidade checkOnSave de rust-analyzer (habilitada por padrão), ela pode relatar um erro para a função panic do nosso kernel:
found duplicate lang item `panic_impl`
A razão para este erro é que rust-analyzer invoca cargo check --all-targets por padrão, que também tenta construir o binário em modo teste e benchmark.
🔗Os dois significados de “target”
A flag --all-targets é completamente não relacionada ao argumento --target.
Há dois significados diferentes do termo “target” no cargo:
- A flag
--targetespecifica o alvo de compilação que deve ser passado ao compiladorrustc. Isso deve ser definido como o target triple da máquina que deve executar nosso código. - A flag
--all-targetsreferencia o _alvo do package do Cargo. Pacotes Cargo podem ser uma biblioteca e binário ao mesmo tempo, então você pode especificar de qual forma você gostaria de construir sua crate. Além disso, Cargo também tem alvos de package para exemplos, testes, e benchmarks. Esses alvos de pacote podem coexistir, então você pode construir/verificar a mesma crate por exemplo em modo biblioteca ou modo teste.
Por padrão, cargo check apenas constrói o biblioteca e os alvos de pacote binário.
No entanto, rust-analyzer escolhe verificar todos os alvos de pacote por padrão quando checkOnSave é habilitado.
Esta é a razão pela qual rust-analyzer relata o erro de lang item acima que não vemos em cargo check.
Se executarmos cargo check --all-targets, vemos o erro também:
error[E0152]: found duplicate lang item `panic_impl`
--> src/main.rs:13:1
|
13 | / fn panic(_info: &PanicInfo) -> ! {
14 | | loop {}
15 | | }
| |_^
|
= note: the lang item is first defined in crate `std` (which `test` depends on)
= note: first definition in `std` loaded from /home/[...]/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd-8df6be531efb3fd0.rlib
= note: second definition in the local crate (`blog_os`)
A primeira note nos diz que o item de linguagem panic já está definido na crate std, que é uma dependência da crate test.
A crate test é automaticamente incluída ao construir uma crate em modo teste.
Isso não faz sentido para nosso kernel #![no_std] já que não há forma de suportar a biblioteca padrão em bare metal.
Então este erro não é relevante para nosso projeto e podemos seguramente ignorá-lo.
A forma apropriada de evitar este erro é especificar em nosso Cargo.toml que nosso binário não suporta construção em modos test e bench.
Podemos fazer isso adicionando uma seção [[bin]] em nosso Cargo.toml para configurar a construção do nosso binário:
# no Cargo.toml
[[bin]]
name = "blog_os"
test = false
bench = false
Os colchetes duplos ao redor de bin não é um erro, isto é como o formato TOML define chaves que podem aparecer múltiplas vezes.
Como uma crate pode ter múltiplos binários, a seção [[bin]] pode aparecer múltiplas vezes em Cargo.toml também.
Esta é também a razão para o campo name obrigatório, que precisa corresponder ao nome do binário (para que cargo saiba quais configurações devem ser aplicadas a qual binário).
Ao definir os campos test e bench para false, instruímos cargo a não construir nosso binário em modo teste ou benchmark.
Agora cargo check --all-targets não deve lançar mais erros, e a implementação de checkOnSave de rust-analyzer também deve estar feliz.
🔗O que vem a seguir?
O próximo post explica os passos necessários para transformar nosso binário independente em um kernel mínimo do sistema operacional. Isso inclui criar um alvo customizado, combinar nosso executável com um bootloader, e aprender como imprimir algo na tela.
Comentários
Teve algum problema, quer deixar um feedback ou discutir mais ideias? Fique à vontade para deixar um comentário aqui! Por favor, use o inglês e siga o código de conduta do Rust. Este tópico de comentários está diretamente vinculado a uma discussão no GitHub, então você também pode comentar lá se preferir.
Instead of authenticating the giscus application, you can also comment directly on GitHub.
Por favor, deixe seus comentários em inglês se possível.