Quebrar a pilha no STM8

No processo de gravação do carregador de inicialização STM8uLoader para microcontroladores STM8, tornou-se necessário medir a profundidade da pilha.

Vamos fazer perguntas:

  • O que acontece se você tentar enviar mais informações na pilha do que na profundidade?
  • O que acontece se você tentar extrair mais informações da pilha do que a colocou?
  • O que acontece se você inicializar o ponteiro da pilha SP com um endereço que ultrapasse os limites da pilha?

A memória RAM e a profundidade da pilha podem variar para diferentes modelos STM8.
Para o estudo, foi escolhido o modelo STM8S103F3.

A documentação para o STM8S103F3 fornece os seguintes dados:
- profundidade da pilha 513 bytes;
- quando redefinido, o ponteiro SP é inicializado em 0x03FF (RAM END);
- a pilha cresce na direção de endereços decrescentes.

O cálculo mostra que o limite inferior da pilha é:

0x03FF - 513 = 0x01FF 

Para quebrar esse limite, você precisa enviar mais de 513 bytes na pilha.

O conteúdo da pilha não nos interessa. É suficiente conhecer o conteúdo do ponteiro para a pilha SP, que deve conter o endereço da próxima célula de memória RAM não utilizada.
Colocaremos os bytes sequencialmente com qualquer comando push (por exemplo, push A) e antes de cada etapa enviaremos o conteúdo do SPH mais alto e do byte SPL mais baixo do ponteiro da pilha SP para o UART.

Algoritmo de procedimento:

1 Inicialize o ponteiro da pilha com o valor 0x03FF e configure o UART;
2 Estamos aguardando qualquer byte do programa do terminal;
3 bytes aceitos;
4 Envie o conteúdo do ponteiro SP para o UART;
5 Empurramos o conteúdo da bateria para a pilha com o comando push A;
6 Se os ciclos de envio forem menores que 64, vá para a etapa 4;
7 Se o ciclo de envio de 64 for, vá para a etapa 2.

 ;  UART 9600/8N1 mov UART1_BRR2, #$00 ;     mov UART1_BRR1, #$0D ;  / mov UART1_CR2, #%00001100 ;   SP  $03FF ldw X, #$03FF ; X <= RAM END ldw SP, X ; SP <= X ;     wait_rx_byte: btjf UART1_SR, #5, wait_rx_byte ; ld A, UART1_DR ;   bset PB_DDR,#5 bset PB_CR1,#5 ldw Y, #64 ; Y <= 64 stack_cycle: ldw X, SP ; X <= SP ;  SPH  UART ; rlwa X ; A <- XH <- XL <- A ld A, XH ; A <- XH ld UART1_DR, A ; UART1_DR <= A wait_tx_byte_XH: btjf UART1_SR, #7, wait_tx_byte_XH ;  SPL  UART ; rlwa X ; A <- XH <- XL <- A ld A, XL ; A <- XL ld UART1_DR, A ; UART1_DR <= A wait_tx_byte_XL: btjf UART1_SR, #7, wait_tx_byte_XL ;  A   push A ; M(SP--) <= A decw Y jrne stack_cycle ;   bres PB_DDR,#5 bres PB_CR1,#5 jra wait_rx_byte 

Observamos como o programa do terminal aceita seqüencialmente o conteúdo do ponteiro SP a partir de 0x03FF:

  03 FF 03 FE 03 FD 03 FC 03 FB 03 FA 03 F9 03 F8 03 F7 03 F6 03 F5 03 F4 03 F3 03 F2 03 F1 03 F0 03 EF 03 EE 03 ED 03 EC 03 EB 03 EA 03 E9 03 E8 03 E7 03 E6 03 E5 03 E4 03 E3 03 E2 03 E1 03 E0 03 DF 03 DE 03 DD 03 DC 03 DB 03 DA 03 D9 03 D8 

Após o valor atingir 0x01FF (borda da pilha calculada anteriormente)
o ponteiro SP assumiu novamente o valor 0x03FF (a pilha fechada em um anel)
e começou a substituir os dados mais antigos

  02 0F 02 0E 02 0D 02 0C 02 0B 02 0A 02 09 02 08 02 07 02 06 02 05 02 04 02 03 02 02 02 01 02 00 01 FF 03 FF 03 FE 03 FD 03 FC 03 FB 03 FA 03 F9 03 F8 03 F7 03 F6 03 F5 03 F4 03 F3 03 F2 03 F1 03 F0 03 EF 03 EE 03 ED 03 EC 03 EB 03 EA 03 E9 

Agora vamos ver como o conteúdo do ponteiro SP se comporta se tentarmos recuperar ilimitadamente o conteúdo da pilha.

Algoritmo de procedimento:

1 Inicialize o ponteiro da pilha com o valor 0x03FF e configure o UART;
2 Estamos aguardando qualquer byte do programa do terminal;
3 bytes aceitos;
4 Extraímos o conteúdo da pilha com o comando “pop A” para a bateria;
5 Envia o conteúdo do ponteiro SP para o UART;
6 Se os ciclos de envio forem menores que 64, vá para a etapa 3;
7 Se o ciclo de envio de 64 for, vá para a etapa 2.

Os itens 4 e 5 do algoritmo e o comando "push A" são trocados pelo comando "pop A".
Apesar de termos inicializado o ponteiro SP com o valor 0x03FF já após o primeiro comando pop A, o ponteiro assumiu o valor 0x01FF e continuou a aumentar em direção a 0x03FF.

  01 FF 02 00 02 01 02 02 02 03 02 04 02 05 02 06 02 07 02 08 02 09 02 0A 02 0B 02 0C 02 0D 02 0E 02 0F 02 10 02 11 02 12 02 13 02 14 02 15 02 16 02 17 02 18 02 19 02 1A 02 1B 02 1C 02 1D 02 1E 02 1F 02 20 02 21 02 22 02 23 02 24 02 25 02 26 

Atingindo o valor 0x03FF. após o próximo comando pop A, o ponteiro novamente assumiu o valor 0x01FF e continuou a aumentar em direção a 0x03FF.

  03 EF 03 F0 03 F1 03 F2 03 F3 03 F4 03 F5 03 F6 03 F7 03 F8 03 F9 03 FA 03 FB 03 FC 03 FD 03 FE 03 FF 01 FF 02 00 02 01 02 02 02 03 02 04 02 05 02 06 02 07 02 08 02 09 02 0A 02 0B 02 0C 02 0D 02 0E 02 0F 02 10 02 11 02 12 02 13 02 14 02 15 

Na direção oposta, com um número excessivo de instruções pop (w), a pilha também é fechada em um anel de 513 bytes.

A pilha no STM8S103F3 é linear até você quebrar uma das bordas 0x01FF ou 0x03FF.

Assim que você quebra um dos limites, a pilha se torna um anel de 513 bytes.
Não importa em que lugar do anel (nos endereços 0x01FF ... 0x03FF) a parte superior / inferior da pilha estará localizada, podemos colocar um número ilimitado de bytes na pilha, mas podemos extrair não mais do que 513 bytes não danificados (os mais recentes).

Agora que a pilha está localizada em 0x01FF ... 0x03FF, é hora de interromper esse intervalo ao inicializar o ponteiro SP.

Na etapa 1 do primeiro procedimento, substituímos o valor 0x03FF para inicializar o ponteiro SP pelo valor 0x01FE.

Observamos como a pilha do endereço 0x01FE foi na direção da diminuição de endereços.

  01 FE 01 FD 01 FC 01 FB 01 FA 01 F9 01 F8 01 F7 01 F6 01 F5 01 F4 01 F3 01 F2 01 F1 01 F0 01 EF 01 EE 01 ED 01 EC 01 EB 01 EA 01 E9 01 E8 01 E7 01 E6 01 E5 01 E4 01 E3 01 E2 01 E1 01 E0 01 DF 01 DE 01 DD 01 DC 01 DB 01 DA 01 D9 01 D8 01 D7 

Tendo atingido o endereço 0x0000, a pilha saiu da memória RAM e entrou nas células de memória FLASH inacessíveis ao STM8S103F3.

  00 16 00 15 00 14 00 13 00 12 00 11 00 10 00 0F 00 0E 00 0D 00 0C 00 0B 00 0A 00 09 00 08 00 07 00 06 00 05 00 04 00 03 00 02 00 01 00 00 FF FF FF FE FF FD FF FC FF FB FF FA FF F9 FF F8 FF F7 FF F6 FF F5 FF F4 FF F3 FF F2 FF F1 FF F0 FF EF 

Não se pode falar de chamadas de sub-rotina ou interrupções depois que o ponteiro sai da memória RAM. É verdade que em algum lugar nas profundezas da pilha ainda restavam os dados mais "antigos", que tiveram a sorte de serem salvos na memória RAM.

Agora vamos tentar recuperar dados da pilha com a inicialização "proibida" (fora do intervalo 0x01FF ... 0x03FF) do ponteiro SP.

Vamos começar com endereços fora da RAM. Na etapa 1 do segundo procedimento, substituímos o valor 0x03FF da inicialização do ponteiro SP pelo valor 0xFFF8.

Observamos como a pilha entrou na memória RAM.

  FF E9 FF EA FF EB FF EC FF ED FF EE FF EF FF F0 FF F1 FF F2 FF F3 FF F4 FF F5 FF F6 FF F7 FF F8 FF F9 FF FA FF FB FF FC FF FD FF FE FF FF 00 00 00 01 00 02 00 03 00 04 00 05 00 06 00 07 00 08 00 09 00 0A 00 0B 00 0C 00 0D 00 0E 00 0F 00 10 

Cruzando a borda inferior 0x01FF, a pilha entrou em seu território.

  01 E9 01 EA 01 EB 01 EC 01 ED 01 EE 01 EF 01 F0 01 F1 01 F2 01 F3 01 F4 01 F5 01 F6 01 F7 01 F8 01 F9 01 FA 01 FB 01 FC 01 FD 01 FE 01 FF 02 00 02 01 02 02 02 03 02 04 02 05 02 06 02 07 02 08 02 09 02 0A 02 0B 02 0C 02 0D 02 0E 02 0F 02 10 

Tendo alcançado o endereço 0x03FF, a pilha foi fechada em um anel.

  03 E9 03 EA 03 EB 03 EC 03 ED 03 EE 03 EF 03 F0 03 F1 03 F2 03 F3 03 F4 03 F5 03 F6 03 F7 03 F8 03 F9 03 FA 03 FB 03 FC 03 FD 03 FE 03 FF 01 FF 02 00 02 01 02 02 02 03 02 04 02 05 02 06 02 07 02 08 02 09 02 0A 02 0B 02 0C 02 0D 02 0E 02 0F 

Conclusões:

A pilha no STM8S103F3 pode executar suas tarefas apenas dentro do intervalo 0x01FF ... 0x03FF.

Para obter a maior profundidade linear, o ponteiro da pilha SP em STM8S103F3 deve ser inicializado em 0x03FF.

A pilha no STM8S103F3 é linear até você quebrar o limite inferior 0x01FF.
Assim que você quebra o limite inferior, a pilha se torna um anel de 513 bytes.

Source: https://habr.com/ru/post/pt417203/


All Articles