Anotações aula 10 #19

Open
opened 2025-07-23 21:21:49 -03:00 by victor_marinho · 0 comments

LENBUF

Buffer é um espaço na memória utilizado para acumular cadeia de bytes, normalmente caracteres.

E quando falamos em assembly, não temos as caracteristícas de alto nível, como o conceito de vetor, que é uma estrutura de dados do mesmo tipo/tamanho.

Em baixo nível não temos a medida, somos nós é que vamos dizer. Se reservarmos 20 bytes para receber dados e reservar 5 double words para o baixo nível isto não faz diferença. Depende de de como vamos acessar o endereço do primeiro dado.

Se puxarmos os conceitos de alto nível vamos pensar que o programa vai pensar as convenções de tipo, mas ele não vai. Outro problema é termos dificuldade de elaborar uma solução por estarmos justamente preocupados com isso.

Outro ponto importante é que em alto nível, temos a tradução direta de, por exemplo, um dado escrito na memória com a ordem invertida dos bytes little endian e quando acessamos está na ordem "correta". Mas em baixo nível isto acontece.

A ordem com que processamos certas informações, principalmente quando vamos escrever em buffers vai afetar a sequência e a localização que os bites serão acessados.

Depois, entramos no exemplo que está nas issues para aula 10.

pbuf.asm

Este exemplo reúne vários problemas e antecipa possíveis soluções para outros que ainda não conhecemos.

Reservamos em .bss um espaço de 5 double words(4 bytes cada) _ buf resd 5_ que multiplicado por 5, temos 20. No endereço, rotulado buf temos este espaço para 20 bytes. Que podemos utilizar para 19 caracteres mais o nulo.

Este número também é suficiente para escrevermos o maior número inteiro longo sem sinal neste espaço - razão para escolhermos este valor.

No espaço em .rodata temos uma string que é uma sequência de 10 caracteres e terminador nulo. Pois vamos imprimir e manipular o conteúdo do espaço da memória.

É necessário colocar ,0 para termos o terminador nulo, após as aspas da string.

Depois entramos na seção .text e na entrada do programa.

E vamos calcular o tamanho da string em txt.

Para isso, apresentamos a importância de trabalhar com subrotinas. Depois conversamos sobre a importância delas, para organizar o código e possíveis abstrações.

Começamos pela implementação da subrotina para obter o tamanho da rotina até exclusive ou terminador nulo: a estratégia é percorrer cada um dos bytes e implementar um contador.

Na subrotina str_len temos organizadas a entrada, os dados onde se esperam alterações (saída) e os registradores afetados.

Por exemplo, vimos que a chamada syscall altera alguns registradores e é importante preservar seja na subrotina ou antes de fazer a chamada da subrotina. Por isso é importante comentar.

Na abordagem, sabemos que o registrador rsi tem nome de source index, então todas as vezes que pensamos em origem, já utilizamos ele. Nela que vamos receber o tamanho da string. Endereço do primeiro byte.

A mesma coisa em relação a rax que é acumulador, porém, todas as convenções que temos, de chamada de função e de chamadas de sistema, os retornos das funções são dados pelo registrador rax primeiramente. Ao manter esta coerência, quando tenho que dar um retorno, utilizamos ele como saída.

A rotina é extremamente simples. Vamos utilizar rcx como contador, também pelo nome associado, mas poderia ser outro.

Este contador deve ser inicializado por 0, como é normalmente um índice. A estratégia será utilizador o registrador rax neste momento para receber cada um dos bytes do endereço em rsi, incrementar o contador, o endereço e fazer o teste para saber se este byte jé é o zero. Em loop, resolve o problema.

Agora vamos utilizar o byte menos significativa de rax utilizado em al para receber o byte em rsi. Comparamos com zero e se for, chegamos ao fim da string. Se não for, incrementamos rsi e rcx.

Em .done a subrotina move os dados em rax conforme especificação criada no próprio programa.

Conversamos que em assembly string não é necessariamente uma cadeia de caracteres terminada em zero, este é um conceito de alto nível, mas existem várias convenções que utilizam esta implementação.

Comentários do Juca no chat:

existem programas que trabalham com 1 byte indicando o número de caracteres da string seguido pelons N bytes

20:12

Felipe "Juca" Sanches disse:existe até programas que usam 2 bytes para o número de caracteres

20:13

Felipe "Juca" Sanches disse:a linguagem C veio antes do formato ELF

No compilador C, as regras foram implementadas em determinado momento.

Em assembly, você convenciona do jeito que queremos. Estamos apenas convenciando próximo ao nosso sistema que é mais natural, para seguirmos algumas soluções da glib formato ELF, etc.

Xico comentou sobre utilizarmos uma operação de soma em .count_loop.

Como poderíamos testar se rax tem de fato a quantidade de caracteres, no caso 10?

gdb, marcando breakpoint e manda rodar, quando chegar no ponto de parada temos o info registers rax.

Depois, voltamos para a impressão da string que está no endereço txt com _bufp_Onde vamos utilizar rsi de novo para conseguir o primeiro endereço de txt. Ela chama a subrotina echo_str, que chama outras duas subrotinas.

Uma maneira de deixar o código mais enxuto.

Outra coisa que normalmente precisamos fazer é copiar um string/endereço de origem para um endeço de destino, por isso a subrotina write_buf. Utilizamos rdi neste caso, por padronização, já d é de destination.

Em .write_loop copiamos e escrevemos os dados que vão ser manipulados.

Após copiar e imprimir o buffer. Vamos inverter o conteúdo da string com a subrotina rev_buf

# LENBUF Buffer é um espaço na memória utilizado para acumular cadeia de bytes, normalmente caracteres. E quando falamos em assembly, não temos as caracteristícas de alto nível, como o conceito de vetor, que é uma estrutura de dados do mesmo tipo/tamanho. Em baixo nível não temos a medida, somos nós é que vamos dizer. Se reservarmos 20 bytes para receber dados e reservar 5 double words para o baixo nível isto não faz diferença. Depende de de como vamos acessar o endereço do primeiro dado. Se puxarmos os conceitos de alto nível vamos pensar que o programa vai pensar as convenções de tipo, mas ele não vai. Outro problema é termos dificuldade de elaborar uma solução por estarmos justamente preocupados com isso. Outro ponto importante é que em alto nível, temos a tradução direta de, por exemplo, um dado escrito na memória com a ordem invertida dos bytes _little endian_ e quando acessamos está na ordem "correta". Mas em baixo nível isto acontece. A ordem com que processamos certas informações, principalmente quando vamos escrever em _buffers_ vai afetar a sequência e a localização que os bites serão acessados. Depois, entramos no exemplo que está nas _issues_ para aula 10. ## pbuf.asm Este exemplo reúne vários problemas e antecipa possíveis soluções para outros que ainda não conhecemos. Reservamos em _.bss_ um espaço de 5 double words(4 bytes cada) _ buf resd 5_ que multiplicado por 5, temos 20. No endereço, rotulado _buf_ temos este espaço para 20 bytes. Que podemos utilizar para 19 caracteres mais o nulo. Este número também é suficiente para escrevermos o maior número inteiro longo sem sinal neste espaço - razão para escolhermos este valor. No espaço em _.rodata_ temos uma string que é uma sequência de 10 caracteres e terminador nulo. Pois vamos imprimir e manipular o conteúdo do espaço da memória. É necessário colocar _,0_ para termos o terminador nulo, após as aspas da string. Depois entramos na seção _.text_ e na entrada do programa. E vamos calcular o tamanho da string em txt. Para isso, apresentamos a importância de trabalhar com subrotinas. Depois conversamos sobre a importância delas, para organizar o código e possíveis abstrações. Começamos pela implementação da subrotina para obter o tamanho da rotina até _exclusive_ ou terminador nulo: a estratégia é percorrer cada um dos bytes e implementar um contador. Na subrotina _str\_len_ temos organizadas a entrada, os dados onde se esperam alterações (saída) e os registradores afetados. Por exemplo, vimos que a chamada _syscall_ altera alguns registradores e é importante preservar seja na subrotina ou antes de fazer a chamada da subrotina. Por isso é importante comentar. Na abordagem, sabemos que o registrador _rsi_ tem nome de source index, então todas as vezes que pensamos em origem, já utilizamos ele. Nela que vamos receber o tamanho da string. Endereço do primeiro byte. A mesma coisa em relação a _rax_ que é acumulador, porém, todas as convenções que temos, de chamada de função e de chamadas de sistema, os retornos das funções são dados pelo registrador _rax_ primeiramente. Ao manter esta coerência, quando tenho que dar um retorno, utilizamos ele como saída. A rotina é extremamente simples. Vamos utilizar _rcx_ como contador, também pelo nome associado, mas poderia ser outro. Este contador deve ser inicializado por 0, como é normalmente um índice. A estratégia será utilizador o registrador _rax_ neste momento para receber cada um dos bytes do endereço em _rsi_, incrementar o contador, o endereço e fazer o teste para saber se este byte jé é o zero. Em loop, resolve o problema. Agora vamos utilizar o byte menos significativa de _rax_ utilizado em _al_ para receber o byte em _rsi_. Comparamos com zero e se for, chegamos ao fim da string. Se não for, incrementamos _rsi_ e _rcx_. Em _.done_ a subrotina move os dados em _rax_ conforme especificação criada no próprio programa. Conversamos que em assembly string não é necessariamente uma cadeia de caracteres terminada em zero, este é um conceito de alto nível, mas existem várias convenções que utilizam esta implementação. Comentários do Juca no chat: > existem programas que trabalham com 1 byte indicando o número de caracteres da string seguido pelons N bytes 20:12 Felipe "Juca" Sanches disse:existe até programas que usam 2 bytes para o número de caracteres 20:13 Felipe "Juca" Sanches disse:a linguagem C veio antes do formato ELF > No compilador C, as regras foram implementadas em determinado momento. Em assembly, você convenciona do jeito que queremos. Estamos apenas convenciando próximo ao nosso sistema que é mais natural, para seguirmos algumas soluções da _glib_ formato _ELF_, etc. Xico comentou sobre utilizarmos uma operação de soma em _.count\_loop_. Como poderíamos testar se _rax_ tem de fato a quantidade de caracteres, no caso 10? _gdb_, marcando breakpoint e manda rodar, quando chegar no ponto de parada temos o info registers rax. Depois, voltamos para a impressão da string que está no endereço txt com _bufp_Onde vamos utilizar _rsi_ de novo para conseguir o primeiro endereço de _txt_. Ela chama a subrotina _echo\_str_, que chama outras duas subrotinas. Uma maneira de deixar o código mais enxuto. Outra coisa que normalmente precisamos fazer é copiar um string/endereço de origem para um endeço de destino, por isso a subrotina _write\_buf_. Utilizamos _rdi_ neste caso, por padronização, já d é de destination. Em _.write\_loop_ copiamos e escrevemos os dados que vão ser manipulados. Após copiar e imprimir o buffer. Vamos inverter o conteúdo da string com a subrotina _rev\_buf_
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: blau_araujo/pbn#19
No description provided.