Obrigado pelas indicações, markt e chris-stratton. A opção de semi-hospedagem acabou sendo bastante direta. Consegui encontrar a fonte para algumas rotinas simples de log que podem enviar mensagens para o console do OpenOCD. Vou publicá-los aqui, pois (i) eles precisaram de alguma modificação para funcionar e (ii) acho que essa informação não é super fácil de encontrar para pessoas que estão apenas começando.
Primeiro, o código D aqui é facilmente adaptado para fornecer a seguinte função C:
void send_command(int command, void *message)
{
asm("mov r0, %[cmd];"
"mov r1, %[msg];"
"bkpt #0xAB"
:
: [cmd] "r" (command), [msg] "r" (message)
: "r0", "r1", "memory");
}
Exemplo de chamada do send_command para gravar uma sequência no console do OpenOCD:
const char s[] = "Hello world\n";
uint32_t m[] = { 2/*stderr*/, (uint32_t)s, sizeof(s)/sizeof(char) - 1 };
send_command(0x05/* some interrupt ID */, m);
Segundo, a função putChar dada nos comentários aqui funciona bem, exceto que eu tive que adicionar um '#' antes de 0x03:
void put_char(char c)
{
asm (
"mov r0, #0x03\n" /* SYS_WRITEC */
"mov r1, %[msg]\n"
"bkpt #0xAB\n"
:
: [msg] "r" (&c)
: "r0", "r1"
);
}
Para observar a saída dessas funções, inicio o OpenOCD e, em seguida, conecto usando arm-none-eabi-gdb da seguinte maneira:
target remote localhost:3333
monitor arm semihosting enable
monitor reset halt
load code.elf
continue
Observe que as mensagens aparecem no stdout do processo OpenOCD, não no console do GDB.