Semihosting e syscalls: como o arm-box traduz chamadas de sistema EABI para o host
O que é semihosting?
Quando você roda código ARM em um emulador (ou em hardware real com debugger), o código não tem acesso direto ao sistema operacional do host. Mas muitas vezes ele precisa:
- Imprimir na tela (
printf) - Ler do teclado (
scanf) - Abrir/criar arquivos
- Sair do programa
O semihosting é a solução: o código executa uma instrução especial (BKPT ou SVC) com um número de chamada, e o depurador/emulador intercepta, executa a operação no host, e retorna o controle ao código.
O mecanismo: como funciona
Entrada do semihosting
No Thumb-2 (Cortex-M e Cortex-A em mode Thumb):
BKPT #0xAB— a instrução clássica de entrada (16 bits)- O número da chamada fica em R0 (código da operação)
- Argumentos adicionais em R1, R2, R3
No ARM mode (ARMv7-A):
SVC #0x12— Supervisor Call (32 bits)- O mesmo mecanismo de números de operação em R0
No AArch64 (ARMv8):
HVC #0ouSMC #0— Hypervisor/Secure Monitor Call
O protocolo EABI
A lista de operações é padronizada pelo AAPCS/EABI:
| Código (R0) | Operação | Descrição |
|---|---|---|
| 0x20 | SYS_WRITE0 | Escreve string terminada em NUL para stdout |
| 0x21 | SYS_WRITE | Escreve buffer para stdout |
| 0x22 | SYS_READ | Lê de stdin para buffer |
| 0x23 | SYS_OPEN | Abre arquivo |
| 0x24 | SYS_CLOSE | Fecha arquivo |
| 0x25 | SYS_WRSCAN | Formatted write to stdout |
| 0x26 | SYS_RDSCAN | Formatted read from stdin |
| 0x30 | SYS_EXIT | Termina o programa |
Bytes verificados (devkitARM)
/* Thumb-2 */
movs r0, #0x20 @ SYS_WRITE0
ldr r1, =msg
movs r2, #0
bkpt #0xAB @ semihosting call
b .| Instrução | Bytes (hex) | Tamanho |
|---|---|---|
movs r0, #0x20 | 20 20 | 16-bit |
ldr r1, =msg | 49 03 | 16-bit |
movs r2, #0 | 22 00 | 16-bit |
bkpt #0xAB | BE AB | 16-bit |
b . | E7 FE | 16-bit |
Nota sobre
bkpt:BKPT #0xAB=0xBEAB— o campoimm12codifica0x0AB. OBKPTé uma instrução de 16 bits em Thumb-2. O depurador reconhece o valor0xABcomo o entry point do semihosting ARM.
Semihosting no arm-box
Quando o arm-box executa com --arch=thumb2, o arm-jitter:
- Decodifica o
BKPT #0xAB - Lê os registradores (R0 = código da operação, R1/R2/R3 = argumentos)
- Traduz a chamada para a API correspondente do host
- Executa a operação (ex.: escreve na saída padrão do host)
- Retorna o controle ao código ARM
Para SYS_EXIT (código 0x30), o arm-box termina a execução do programa.
Tabela: instruções semihosting
| Instrução | Bytes (hex) | Tamanho | Uso |
|---|---|---|---|
BKPT #0xAB | BE AB | 16-bit | Entry point semihosting (Thumb) |
SVC #0x12 | — | 32-bit | Entry point semihosting (ARM mode) |
movs r0, #0x20 | 20 20 | 16-bit | SYS_WRITE0 |
movs r0, #0x30 | 20 30 | 16-bit | SYS_EXIT |
Glossário: instruções
| Instrução | O que faz |
|---|---|
BKPT #0xAB | Breakpoint — entry point do semihosting |
SVC #imm | Supervisor Call (ARM mode) |
HVC #imm | Hypervisor Call (AArch64) |
movs Rd, #imm | Move immediate + flags |
ldr Rd, =label | Carrega endereço de label |
Glossário: comandos
| Comando | O que faz |
|---|---|
arm-none-eabi-as -mcpu=cortex-m3 | Monta Thumb-2 |
arm-none-eabi-objdump -d | Mostra bytes e instruções |
--arch=thumb2 | Seleciona Thumb-2 no arm-box |
Bytes verificados com arm-none-eabi-as -mcpu=cortex-m3 + arm-none-eabi-objdump -d.