# Compiler et inspecter

> Des profils de compilation fixés à une seule sémantique, l’ELF que produit une compilation, la ProgramImage qu’en tire le chargeur, son rapport, et l’identité du programme qu’enregistre un vérificateur.

## Compiler

À partir du répertoire du programme invité, sans autre option que la cible :

```sh
cd guests/my-app
cargo build --release --target riscv32imac-unknown-none-elf     # .../release/my-app
cargo build --target riscv32imac-unknown-none-elf               # .../debug/my-app
```

L’ELF se retrouve dans l’unique répertoire cible de l’espace de travail des programmes invités, `guests/target/riscv32imac-unknown-none-elf/`. `guests/.cargo/config.toml` ajoute deux arguments de l’éditeur de liens que vous ne tapez jamais :

- **`-T crates/guest-sdk/link.ld`**, la carte mémoire, qui définit aussi les symboles qu’utilisent le code de démarrage et l’allocateur.
- **`--no-relax`**. La relaxation de l’éditeur de liens réécrit des séquences d’instructions et décale toutes les adresses qui suivent, et l’identité du programme lie ces adresses.

Il n’y a pas de `runner` : rien en dehors d’Apogee ne projette les régions d’un programme invité, si bien que `cargo run` n’a rien avec quoi l’exécuter. Vous exécutez un programme invité au moyen de l’émulateur ([Exécuter et profiler](https://apogee.gweb3networks.com/docs/launch/run)).

## Profils

`guests/Cargo.toml` fixe les deux profils à une seule sémantique. Ils ne diffèrent que par l’optimisation et par les assertions de débogage des dépendances :

| | dev | release |
| --- | --- | --- |
| `opt-level` | 0 | 3 |
| `overflow-checks` | activé | **activé** |
| `debug-assertions` | activé | activé dans la crate du programme invité, désactivé dans ses dépendances |
| `panic`, `codegen-units`, `debug`, `incremental` | `abort`, 1, désactivé, désactivé | identiques |

Le profil release par défaut de Cargo désactive les vérifications de dépassement, et dans un programme invité, ce n’est pas un réglage de performance. Cela change l’énoncé : `u32::MAX + 1` consignerait `00000000` et se terminerait avec le statut 0, là où la compilation dev panique avec le statut 101. L’espace de travail garde donc les vérifications actives dans les deux profils. Une assertion de débogage d’une dépendance vérifie un invariant propre à cette crate, et une dépendance correcte calcule la même chose sans elle : release les désactive donc et économise ces cycles, soit 6,8 % de l’exécution du programme invité Ethereum sans état.

**Prouvez la compilation release.** Chaque instruction exécutée est une ligne prouvée, `opt-level = 3` retire d’un quart à plus de la moitié de l’image d’un programme invité, et le code de chaque famille doit tenir dans sa table décodée : l’image debug du programme invité Ethereum exige des tables de `2^22` lignes, son image release, des tables de `2^20`. L’identité que vous publiez est celle de l’image release.

## Reproductibilité

Deux compilations propres sur une même machine produisent des ELF identiques. Des compilations sur deux machines, en général, non : l’ELF incorpore, dans les chaînes de localisation des paniques, les chemins absolus des sources de `core` de la chaîne d’outils, de `crates/guest-sdk` et du registre cargo, tandis que les fichiers propres au programme invité apparaissent relativement à `guests/`. Une compilation ailleurs donne une autre image, avec une autre identité.

Ce que vous enregistrez et transmettez est donc **l’ELF d’une compilation, et non une recette**. Conservez l’ELF que vous avez prouvé, et laissez quiconque veut vérifier l’identité la recalculer à partir de cet ELF, des paramètres et du fichier de cérémonie.

## Exporter l’image

```sh
cargo run -p artifact-dump -- guests/target/riscv32imac-unknown-none-elf/release/my-app --out artifacts
```

Cette commande écrit `artifacts/my-app.img`, la `ProgramImage` chargée dans son format de sérialisation (`postcard`, sans en-tête), et `artifacts/my-app.img.txt`, un rapport produit à partir de l’image relue par le lecteur validant. Elle affiche le point d’entrée, le nombre de segments et d’instructions, ainsi que la taille et le SHA-256 de l’artefact. Si la relecture diffère, ou si le chargeur refuse l’ELF, elle n’écrit rien.

Le `.img` est la description statique du programme, à conserver et à comparer; rien en aval n’en a besoin, puisque la mise en place et les outils prennent l’ELF. Son SHA-256 fixe des octets. Ce n’est **pas** l’identité du programme.

## Lire le rapport

| Section | Contenu |
| --- | --- |
| `entry and memory` | le point d’entrée, `_start` à `0x00010000`; la fenêtre de RAM; `slot_base` et l’étendue des emplacements |
| `segments` | l’adresse, la fin, `mem_len`, les octets issus du fichier, le remplissage à zéro et le nombre d’instructions de chaque segment : `.text`, `.rodata` s’il existe, et un segment inscriptible jusqu’à `0x80000000` pour `.data`, `.bss`, le tas et la pile |
| `instruction stream` | les instructions de quatre et de deux octets, les emplacements situés au milieu d’une instruction et les emplacements `not code`, dont la somme donne le nombre d’emplacements |
| `symbols` | les noms par adresse, tirés de la table des symboles de l’ELF, que l’artefact ne contient pas |
| `listing` | pour chaque instruction : l’adresse, la longueur, les octets en mémoire, le mot de 32 bits développé, le symbole |

Une instruction compressée garde son adresse et ses deux octets; seul `len` indique si le pc suivant est `pc + 2` ou `pc + 4`. Pour les mnémoniques, utilisez la vue `tables` ci-dessous ou le désassembleur de la chaîne d’outils fixée :

```sh
"$(rustc --print sysroot)"/lib/rustlib/*/bin/llvm-objdump \
    --disassemble --no-print-imm-hex -M no-aliases <elf>
```

### Les demi-mots `not code`

Un rapport peut afficher une ligne comme `---- not code: 0x00010f9a .. 0x00010f9c, 1 halfword ----`. C’est une sortie ordinaire du compilateur. LLVM a prouvé que la branche par défaut d’un `match` était inatteignable, rustc a abaissé le bloc inatteignable en `unimp`, et avec l’extension C, c’est `c.unimp`, le demi-mot entièrement nul, l’encodage que RVC définit comme illégal. Le chargeur l’enregistre comme une non-instruction et poursuit. Aucun pc ne l’atteint; un pc qui l’atteindrait arrêterait l’exécution avec `NotAnInstruction`.

## Ce que la VM prouvera

```sh
cargo run --release -p artifact-dump -- tables <elf> --ptau assets/ptau/ppot_0080_24.ptau
```

Cette commande affiche, aux paramètres par défaut, la `VmConfig` que dérive l’image : la hauteur de chaque famille, ses lignes actives et ses colonnes décodées. Elle affiche ensuite, pour chaque instruction, le pc, `next_pc`, la famille, le mnémonique et les champs. Avec `--ptau` et le fichier de cérémonie, elle affiche aussi l’**identité du programme**, la valeur qu’enregistre un vérificateur. Le [Démarrage rapide](https://apogee.gweb3networks.com/docs/launch/quickstart#inspect) en montre une réelle.

L’identité est un seul élément du corps. Elle lie chaque instruction avec son pc, sa longueur, ses opérandes et son type, chaque octet de l’image issu du fichier (`.text`, `.rodata`, `.data`), le point d’entrée, l’ensemble des familles, chaque hauteur, le plafond de taille du code et la version du code. Elle ne lie ni la table des symboles, ni `.bss`, ni rien de ce que choisit une exécution. Un même ELF avec deux réglages de hauteurs a deux identités.

La dérivation refuse le programme dans les cas suivants, en nommant le pc ou la taille :

| Refus | Cause |
| --- | --- |
| `Not all opcodes supported: pc=…` | un mot hors de RV32IMA n’importe où dans le code exécutable, comme un accès à un CSR en assembleur |
| `TableTooShort` | du code au-delà de la portée d’une famille, `pc ≤ 2h − 4` : 1,9375 MiB de code à `2^20` et 7,9375 MiB à `2^22` |
| `ProgramTooLarge` | l’image dépasse `bytecode_size_words`, 4 MiB par défaut |
| `ImageOutsideWindow` | un octet issu du fichier se trouve au-delà de la fenêtre de RAM 0 à la hauteur de fenêtre choisie |
| `UnknownDelegation` | l’image déclare un numéro de délégation auquel aucune famille ne répond |

## Vérifier une compilation

Compilez dans un répertoire cible neuf, exportez de nouveau, et comparez :

```sh
cd guests/my-app
CARGO_TARGET_DIR=/tmp/fresh cargo build --release --target riscv32imac-unknown-none-elf
cd ../..
cargo run -p artifact-dump -- /tmp/fresh/riscv32imac-unknown-none-elf/release/my-app --out /tmp/again
cmp artifacts/my-app.img /tmp/again/my-app.img
diff artifacts/my-app.img.txt /tmp/again/my-app.img.txt     # differs only in the `source ELF` line
```
