conteúdo da aula 7
This commit is contained in:
parent
c142033bf7
commit
d99402393f
2 changed files with 460 additions and 4 deletions
|
@ -346,7 +346,7 @@ _start:
|
||||||
o resultado em um registrador de destino.
|
o resultado em um registrador de destino.
|
||||||
#+end_quote
|
#+end_quote
|
||||||
|
|
||||||
** Acesso definindo 'main' no Assembly
|
** Acesso em Assembly exportando main
|
||||||
|
|
||||||
Exportando a rotina principal do programa com o nome =main=, nós podemos gerar
|
Exportando a rotina principal do programa com o nome =main=, nós podemos gerar
|
||||||
o executável com o =gcc= que, utilizando o objeto de inicialização da linguagem
|
o executável com o =gcc= que, utilizando o objeto de inicialização da linguagem
|
||||||
|
@ -446,7 +446,7 @@ da linguagem C (=crt0.o=) e outro totalmente em Assembly, mas com um pequeno
|
||||||
módulo de sub-rotinas para conversão e impressão que só será estudada nos
|
módulo de sub-rotinas para conversão e impressão que só será estudada nos
|
||||||
próximos tópicos.
|
próximos tópicos.
|
||||||
|
|
||||||
** Exemplo híbrido (Assembly+C)
|
** Exemplo em Assembly com main e gcc
|
||||||
|
|
||||||
Arquivo: [[exemplos/06/cargs.asm][cargs.asm]]
|
Arquivo: [[exemplos/06/cargs.asm][cargs.asm]]
|
||||||
|
|
||||||
|
@ -547,7 +547,7 @@ A volatilidade desses registradores não está relacionada com seu uso antes
|
||||||
da chamada da função (=call=), mas com o fato da ABI não obrigar a função
|
da chamada da função (=call=), mas com o fato da ABI não obrigar a função
|
||||||
chamada a salvar e restaurar seus conteúdos.
|
chamada a salvar e restaurar seus conteúdos.
|
||||||
|
|
||||||
** Exemplo em Assembly puro
|
** Exemplo com acesso direto à pilha
|
||||||
|
|
||||||
- Arquivo: [[exemplos/06/args.asm][args.asm]]
|
- Arquivo: [[exemplos/06/args.asm][args.asm]]
|
||||||
- Módulo: [[exemplos/06/print_utils.asm][print_utils.asm]]
|
- Módulo: [[exemplos/06/print_utils.asm][print_utils.asm]]
|
||||||
|
@ -640,7 +640,7 @@ argv[4]: d
|
||||||
* Exercícios propostos
|
* Exercícios propostos
|
||||||
|
|
||||||
1. Crie um programa em Assembly que receba seu nome como argumento e imprima =Salve, NOME!=.
|
1. Crie um programa em Assembly que receba seu nome como argumento e imprima =Salve, NOME!=.
|
||||||
2. Crie uma versão desse mesmo programa em Assembly híbrido (chamando funções da linguagem C).
|
2. Crie uma versão desse mesmo programa em Assembly chamando funções da linguagem C.
|
||||||
3. Utilizando o GDB e o exemplo =cargs.asm=, analise o comportamento dos registradores voláteis nas chamadas de funções da =glibc=.
|
3. Utilizando o GDB e o exemplo =cargs.asm=, analise o comportamento dos registradores voláteis nas chamadas de funções da =glibc=.
|
||||||
4. Com base nos exemplos do tópico, crie um programa que liste as variáveis no vetor de ambiente.
|
4. Com base nos exemplos do tópico, crie um programa que liste as variáveis no vetor de ambiente.
|
||||||
|
|
||||||
|
|
456
curso/aula-07.org
Normal file
456
curso/aula-07.org
Normal file
|
@ -0,0 +1,456 @@
|
||||||
|
#+title: Aula 7 – Vetor de ambiente (envp)
|
||||||
|
#+author: Blau Araujo
|
||||||
|
#+email: cursos@blauaraujo.com
|
||||||
|
|
||||||
|
#+options: toc:3
|
||||||
|
|
||||||
|
* Objetivos
|
||||||
|
|
||||||
|
- Entender o papel do vetor de ambiente (=envp=).
|
||||||
|
- Acessar as definições de variáveis exportadas para o processo.
|
||||||
|
- Exemplificar o uso do conteúdo de =envp= em programas escritos em baixo nível.
|
||||||
|
|
||||||
|
* Revisão: o quadro inicial da pilha do processo
|
||||||
|
|
||||||
|
Como vimos no [[aula-06.org][último tópico]], a pilha do processo é inicializada com diversos
|
||||||
|
conjuntos de dados relativos ao ambiente de execução do programa e outras
|
||||||
|
informações auxiliares para uso da /runtime/ da linguagem C. Também vimos que o
|
||||||
|
conteúdo inicial da pilha pode ser representado desta forma:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
┌────────────────────────────────┐
|
||||||
|
│ NÃO ESPECIFICADO │ ENDEREÇOS MAIS ALTOS
|
||||||
|
├────────────────────────────────┤ ─┐
|
||||||
|
│ STRINGS DE ARGUMENTOS │ │
|
||||||
|
├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─┤ │
|
||||||
|
│ STRINGS DE AMBIENTE │ ├─ BLOCO DE INFORMAÇÕES
|
||||||
|
├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─┤ │ (SEM ORDEM ESPECÍFICA)
|
||||||
|
│ DADOS BINÁRIOS AUXILIARES │ │
|
||||||
|
├────────────────────────────────┤ ─┘
|
||||||
|
│ NÃO ESPECIFICADO │
|
||||||
|
├────────────────────────────────┤
|
||||||
|
│ ENTRADA NULA DO VETOR AUXILIAR │ QWORD (0x00)
|
||||||
|
├────────────────────────────────┤
|
||||||
|
│ ENTRADAS DO VETOR AUXILIAR │ 2 QWORDS POR ENTRADA
|
||||||
|
├────────────────────────────────┤
|
||||||
|
│ SEPARADOR NULO │ QWORD (0X00)
|
||||||
|
├────────────────────────────────┤
|
||||||
|
│ VETOR AMBIENTE (ENVP) │ 1 QWORD POR ENDEREÇO
|
||||||
|
├────────────────────────────────┤
|
||||||
|
RSP + (8 * ARGC) + 8 │ SEPARADOR NULO │ QWORD (0X00)
|
||||||
|
├────────────────────────────────┤
|
||||||
|
RSP + 8 (ARGV[0]) │ VETOR DE ARGUMENTOS (ARGV) │ 1 QWORD POR ENDEREÇO
|
||||||
|
├────────────────────────────────┤
|
||||||
|
RSP │ ENDEREÇO DO VALOR DE ARGC │ 1 QWORD
|
||||||
|
├────────────────────────────────┤
|
||||||
|
│ NÃO DEFINIDO │ ENDEREÇO MAIS BAIXO
|
||||||
|
└────────────────────────────────┘
|
||||||
|
|
||||||
|
QWORD = 8 BYTES (64 BITS)
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
* Localizando o vetor de ambiente
|
||||||
|
|
||||||
|
O endereço inicial do vetor de ambiente (=envp=) é carregado na pilha 8 bytes
|
||||||
|
depois do endereço do separador nulo do vetor de argumentos. Sendo assim, nós
|
||||||
|
podemos determinar sua posição com:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
rsp + (8 * argc) + 16
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Para confirmar, vamos utilizar novamente o programa =exit42.asm= e o GDB:
|
||||||
|
|
||||||
|
Arquivo: [[exemplos/07/exit42.asm][exit42.asm]]
|
||||||
|
|
||||||
|
#+begin_src asm
|
||||||
|
; Retorna 42 como estado de término
|
||||||
|
|
||||||
|
section .text
|
||||||
|
global _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
mov rax, 60 ; syscall: exit
|
||||||
|
mov rdi, 42 ; código de saída
|
||||||
|
syscall
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
Montagem, início do GDB e execução do programa:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
:~$ nasm -g -f elf64 exit42.asm
|
||||||
|
:~$ ld -o exit42 exit42.o
|
||||||
|
:~$ gdb ./exit42
|
||||||
|
Reading symbols from ./exit42...
|
||||||
|
(gdb) run
|
||||||
|
Starting program: /home/blau/git/pbn/curso/exemplos/07/exit42
|
||||||
|
|
||||||
|
Breakpoint 1, _start () at exit42.asm:7
|
||||||
|
7 mov rax, 60 ; syscall: exit
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
O separador nulo, entre =argv= e =envp= deve estar em =rsp+16=, e nós podemos
|
||||||
|
verificar se é verdade com:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
(gdb) x /1gx $rsp+16
|
||||||
|
0x7fffffffdfe0: 0x0000000000000000
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Como o programa foi executado sem argumentos, =argc= terá valor =1=. Portanto,
|
||||||
|
o endereço de =envp[0]= estará 8 bytes depois em =rsp+24=:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
(gdb) x /1gx $rsp+24
|
||||||
|
0x7fffffffdfe8: 0x00007fffffffe318
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Nós poderíamos exibir a string neste endereço copiando e colando a saída do
|
||||||
|
comando anterior, mas também é possível exibi-la com a sintaxe:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
x /1s *((char **)($rsp + 24))
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Onde:
|
||||||
|
|
||||||
|
- =x=: comando /execute/.
|
||||||
|
- =/1s=: exibir conteúdo formatado como uma string (cadeia de caracteres terminada com =0x00=).
|
||||||
|
- =*(...ENDEREÇO...)=: exibir os dados em =ENDEREÇO=.
|
||||||
|
- =(char **)ENDEREÇO=: definição do tipo do dado em =ENDEREÇO= (ponteiro de ponteiro para caracteres).
|
||||||
|
- =($rsp + 24)=: o dado que está 24 bytes após o endereço em =rsp=.
|
||||||
|
|
||||||
|
Executando o comando:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
(gdb) x /1s *((char **)($rsp + 24))
|
||||||
|
0x7fffffffe318: "SHELL=/bin/bash"
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
E aqui nós temos a string da definição da primeira variável exportada no
|
||||||
|
processo do nosso programa -- uma string no formato ~NOME=VALOR~. Infelizmente,
|
||||||
|
diferente do vetor de argumentos, o vetor de ambiente não tem uma informação
|
||||||
|
de contagem associada (como =argc=), então não temos como saber onde as variáveis
|
||||||
|
terminam, a não ser percorrendo todos os endereços em =envp= até encontrarmos
|
||||||
|
outro separador nulo.
|
||||||
|
|
||||||
|
Na prática, porém, isso não é um problema, pois as exportação de variáveis
|
||||||
|
para processos tem usos muito específicos e são mais utilizadas por programas
|
||||||
|
escritos em linguagens que, de algum modo, abstraem a busca pelos nomes das
|
||||||
|
variáveis e a extração de seus respectivos valores. Este é o caso, por exemplo,
|
||||||
|
da função =getenv=, da =glibc=, declarada em =stdlib.h= como:
|
||||||
|
|
||||||
|
#+begin_src c
|
||||||
|
char *getenv(const char *name);
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
Recebendo o ponteiro para a string de um nome como argumento (=name=), a função
|
||||||
|
retorna o endereço do byte da string onde o valor atribuído à variável
|
||||||
|
exportada inicia:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
endereço de 'name'
|
||||||
|
↓
|
||||||
|
NOME=VALOR
|
||||||
|
↑
|
||||||
|
endereço retornado
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
* Extraindo valores de variáveis exportadas
|
||||||
|
|
||||||
|
O programa abaixo acessa as variáveis exportadas para o ambiente do processo
|
||||||
|
(=envp=) e simula o funcionamento de =getenv=, imprimindo o valor de uma variável
|
||||||
|
buscada, se ela existir. Com este exemplo, nossos objetivos são demonstrar o
|
||||||
|
uso de argumentos de linha de comando e o acesso ao vetor de ambiente em um
|
||||||
|
programa escrito sem qualquer abstração, totalmente em baixo nível.
|
||||||
|
|
||||||
|
#+begin_quote
|
||||||
|
Os detalhes sobre as chamadas de sistema envolvidas e criação de sub-rotinas
|
||||||
|
ficarão para os próximos tópicos.
|
||||||
|
#+end_quote
|
||||||
|
|
||||||
|
Arquivo: [[exemplos/07/getenv.asm][getenv.asm]]
|
||||||
|
|
||||||
|
#+begin_src asm :tangle exemplos/07/getenv.asm
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
; Montagem: nasm -f elf64 -o getenv.o getenv_v2.asm
|
||||||
|
; Ligação : ld -o getenv getenv.o
|
||||||
|
; Uso : ./getenv NOME_VARIAVEL
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
section .rodata
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
newline db 10, 0 ; caractere de nova linha
|
||||||
|
msg_nf db "Not found", 10, 0 ; mensagem de erro
|
||||||
|
msg_usage db "Usage: ./getenv VAR", 10, 0 ; mensagem de uso incorreto
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
section .bss
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
argc resq 1 ; quantidade de argumentos (argc)
|
||||||
|
argv resq 1 ; endereço do vetor de argumentos (argv)
|
||||||
|
envp resq 1 ; endereço do vetor de ambiente (envp)
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
section .text
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
global _start
|
||||||
|
_start:
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
; Carregar e salvar argc, argv e envp
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
mov rax, [rsp] ; argc está no topo da pilha
|
||||||
|
mov [argc], rax ; salva argc em argc
|
||||||
|
|
||||||
|
lea rax, [rsp + 8] ; argv começa logo após argc
|
||||||
|
mov [argv], rax ; salva ponteiro de argv
|
||||||
|
|
||||||
|
mov rbx, [argc]
|
||||||
|
lea rax, [rsp + rbx*8 + 16] ; envp vem após argc + argv[] + ponteiro nulo
|
||||||
|
mov [envp], rax ; salva ponteiro de envp
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
; Verificar se argc == 2
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
cmp qword [argc], 2
|
||||||
|
jl show_usage ; se argc < 2, mostrar uso correto
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
; Obter endereço do nome da variável: argv[1]
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
mov rax, [argv]
|
||||||
|
mov rsi, [rax + 8] ; rsi = argv[1]
|
||||||
|
; ------------------------------------------
|
||||||
|
; Iterar sobre envp para buscar a variável
|
||||||
|
; ------------------------------------------
|
||||||
|
mov rcx, [envp] ; rcx recebe endereço de envp[0]
|
||||||
|
.next_env:
|
||||||
|
mov rdx, [rcx] ; rdx recebe endereço da string "VAR=valor"
|
||||||
|
test rdx, rdx ; verifica se rdx = 0
|
||||||
|
jz not_found ; se rdx = 0, termina com erro (não encontrada)
|
||||||
|
; ------------------------------------------
|
||||||
|
; Comparar caractere a caractere: se argv[1] é prefixo de rdx
|
||||||
|
; e é seguido por '=' → variável encontrada
|
||||||
|
; ------------------------------------------
|
||||||
|
mov r8, rsi ; r8 = nome buscado (argv[1])
|
||||||
|
mov r9, rdx ; r9 = string atual do envp
|
||||||
|
.cmp_loop:
|
||||||
|
mov al, [r8] ; próximo char do nome buscado
|
||||||
|
mov bl, [r9] ; próximo char da string do envp
|
||||||
|
cmp al, 0
|
||||||
|
je .check_equals ; se al = 0, chegou ao fim do nome buscado: checar '='
|
||||||
|
cmp al, bl
|
||||||
|
jne .next ; se al != bl, não bateu, próxima variável
|
||||||
|
inc r8
|
||||||
|
inc r9
|
||||||
|
jmp .cmp_loop
|
||||||
|
; ------------------------------------------
|
||||||
|
; Comparar se o caractere no endereço é '='
|
||||||
|
; ------------------------------------------
|
||||||
|
.check_equals:
|
||||||
|
cmp byte [r9], '=' ; verifica se o caractere em envp é '='
|
||||||
|
jne .next ; não era uma '=': próximo elemento em envp
|
||||||
|
inc r9 ; avança para o início do valor
|
||||||
|
mov rsi, r9 ; rsi = endereço do primeiro byte do valor
|
||||||
|
mov rdi, 1 ; stdout
|
||||||
|
call print_string ; imprime valor
|
||||||
|
mov rsi, newline ; carrega endereço de '\n' em rsi
|
||||||
|
call print_string ; imprime \n
|
||||||
|
mov rdi, 0 ; estado de término 0 (sucesso)
|
||||||
|
call exit_program ; termina o programa
|
||||||
|
.next:
|
||||||
|
add rcx, 8 ; avança para próximo emdereço em envp
|
||||||
|
jmp .next_env
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
; Caso variável não seja encontrada
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
not_found:
|
||||||
|
lea rsi, [rel msg_nf]
|
||||||
|
mov rdi, 2 ; stderr
|
||||||
|
call print_string
|
||||||
|
mov rdi, 1 ; código de erro
|
||||||
|
call exit_program
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
; Caso uso incorreto (menos de 2 argumentos)
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
show_usage:
|
||||||
|
lea rsi, [rel msg_usage] ; endereço da mensagem de uso
|
||||||
|
mov rdi, 2 ; fd 2 = stderr
|
||||||
|
call print_string ; imprime a mensagem
|
||||||
|
mov rdi, 1 ; estado de término 1 (erro)
|
||||||
|
call exit_program ; termina o programa
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
; SUB-ROTINAS
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
print_string:
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
; Imprime string terminada em '\0' no descritor indicado
|
||||||
|
; Entrada:
|
||||||
|
; rdi = file descriptor (1 = stdout, 2 = stderr)
|
||||||
|
; rsi = ponteiro para string terminada em '\0'
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
push rdi ; salva rdi (fd)
|
||||||
|
mov rax, rsi
|
||||||
|
xor rcx, rcx ; contador = 0
|
||||||
|
.count:
|
||||||
|
cmp byte [rax + rcx], 0 ; fim da string?
|
||||||
|
je .write
|
||||||
|
inc rcx
|
||||||
|
jmp .count
|
||||||
|
.write:
|
||||||
|
mov rdx, rcx ; número de bytes a escrever
|
||||||
|
pop rax ; restaura fd original
|
||||||
|
mov rdi, rax
|
||||||
|
mov rax, 1 ; syscall: write
|
||||||
|
syscall
|
||||||
|
ret
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
exit_program:
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
; Encerra o programa com código em rdi
|
||||||
|
; Entrada:
|
||||||
|
; rdi = código de saída (0 = sucesso, 1 = erro)
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
mov rax, 60 ; syscall: exit
|
||||||
|
syscall
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
Montando e executando, nós teremos algo como:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
:~$ nasm -f elf64 getenv.asm
|
||||||
|
:~$ ld -o getenv getenv.o
|
||||||
|
:~$ ./getenv USER
|
||||||
|
blau
|
||||||
|
:~$ ./getenv
|
||||||
|
Usage: ./getenv VAR
|
||||||
|
:~$ ./getenv TESTE
|
||||||
|
Not found
|
||||||
|
:~$ TESTE=123 ./getenv TESTE
|
||||||
|
123
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
* Implementação com funções da linguagem C
|
||||||
|
|
||||||
|
As funções da =glibc= abstraem quase todo o trabalho que tivemos com o Assembly,
|
||||||
|
como podemos ver no exemplo a seguir.
|
||||||
|
|
||||||
|
Arquivo: [[exemplos/07/cgetenv.asm][cgetenv.asm]]
|
||||||
|
|
||||||
|
#+begin_src asm :tangle exemplos/07/cgetenv.asm
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
; Montagem: nasm -f elf64 cgetenv.asm
|
||||||
|
; Ligação : gcc -no-pie -o cgetenv cgetenv.o
|
||||||
|
; Uso : ./cgetenv PATH
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
; Funções importadas da glibc...
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
extern getenv
|
||||||
|
extern puts
|
||||||
|
extern fprintf
|
||||||
|
extern exit
|
||||||
|
extern stderr
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
section .rodata
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
msg_nf db "Not found", 10, 0
|
||||||
|
msg_usage db "Usage: ./getenv VAR", 10, 0
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
section .bss
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
argc resq 1 ; recebe quantidade de argumentos
|
||||||
|
argv resq 1 ; recebe endereço de argv[0]
|
||||||
|
envp resq 1 ; recebe endereço de envp[0]
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
section .text
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
global main
|
||||||
|
main:
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
; Iniciar quadro de pilha para main...
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
push rbp
|
||||||
|
mov rbp, rsp
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
; Salvar dados de argumentos e ambiente...
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
mov [argc], rdi ; salvar valor de argc
|
||||||
|
mov [argv], rsi ; salvar endereço de argv[0]
|
||||||
|
mov [envp], rdx ; salvar endereço de envp[0]
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
; Verificar se argc < 2
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
cmp rdi, 2
|
||||||
|
jl show_usage
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
; getenv(argv[1]);
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
mov rax, [argv] ; endereço de argv[0]
|
||||||
|
mov rdi, [rax + 8] ; argumento 1: endereço de argv[1]
|
||||||
|
call getenv ; chamar getenv
|
||||||
|
test rax, rax ; testar retorno (0 = não encontrada)
|
||||||
|
jz not_found
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
; Imprimir valor encontrado: puts(char *valor)
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
mov rdi, rax ; argumento 1: endereço do valor em rax
|
||||||
|
call puts ; chamar puts
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
; Término com sucesso: exit(int status);
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
mov edi, 0 ; argumento 1: (int)0 - sucesso
|
||||||
|
call exit ; chamar exit
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
; Se o nome da variável não for encontrado...
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
not_found:
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
; Mensagem de erro: fprintf(stderr, "Not found\n");
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
mov rdi, [stderr] ; argumento 1: stream de saída
|
||||||
|
lea rsi, [msg_nf] ; argumento 2: string de formato
|
||||||
|
call fprintf ; chamar fprintf
|
||||||
|
jmp exit_error ; termina com erro
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
; Quantidade de argumentos incorrerta...
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
show_usage:
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
; Mensagem de erro: fprintf(stderr, "Usage: ./getenv VAR\n");
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
mov rdi, [stderr] ; argumento 1: stream de saída
|
||||||
|
lea rsi, [msg_usage] ; argumento 2: string de formato
|
||||||
|
call fprintf ; chamar fprintf
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
; Término com erro
|
||||||
|
; ----------------------------------------------------------
|
||||||
|
exit_error:
|
||||||
|
mov edi, 1 ; argumento 1: (int)1 - erro
|
||||||
|
call exit ; chamar exit
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
Montagem, compilação e testes:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
:~$ nasm -f elf64 cgetenv.asm
|
||||||
|
:~$ gcc -no-pie -o cgetenv cgetenv.o
|
||||||
|
:~$ ./cgetenv USER
|
||||||
|
blau
|
||||||
|
:~$ ./cgetenv
|
||||||
|
Usage: ./getenv VAR
|
||||||
|
:~$ ./cgetenv TESTE
|
||||||
|
Not found
|
||||||
|
:~$ TESTE=123 ./cgetenv TESTE
|
||||||
|
123
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
* Exercícios propostos
|
||||||
|
|
||||||
|
1. Utilizando o GDB, execute os programas de exemplo buscando pela variável
|
||||||
|
exportada =LANG= e examine as strings obtidas até que a ela seja encontrada.
|
||||||
|
2. No exemplo =cgetenv.asm=, comente a inicialização da pilha, observe o
|
||||||
|
resultado e explique por que ele acontece.
|
||||||
|
3. Crie o programa =salve.asm= para imprimir =Salve, <nome>!=, onde =nome= pode ser
|
||||||
|
recebido tanto como um argumento quanto pela exportação da variável =NOME=.
|
||||||
|
|
||||||
|
* Referências
|
||||||
|
|
||||||
|
- =man 7 environ=
|
||||||
|
- =man 3 getenv=
|
||||||
|
- [[https://www.gnu.org/software/libc/manual/html_node/Environment-Variables.html][GNU libc: Environment Variables]]
|
||||||
|
- [[https://wiki.osdev.org/Calling_Conventions][OSDev: Calling Conventions]]
|
Loading…
Add table
Reference in a new issue