Como acessar o argv sem declarar o dito cujo

Esqueceu de declarar argc e argv em main? Sem problemas… não é preciso declarar esses parâmetros manualmente, basta uma variável local e tudo se resolve.

/**
 * by Daemonio (Marcos Paulo Ferreira)
 * daemoniolabs.wordpress.com
 *
 * Thu Jun  9 22:27:26 BRT 2011
 *
 **/
#include 

int main() {
    int i = 0;
    while( *(&i+50+i) != NULL ) {
        printf("Argumento %d: %s\n", i, *(&i+50+i++)) ;
    }

    return 0;
}

Introdução

No formato ELF para arquivos binários, o sistema operacional reserva um pedaço de memória para armazenar os parâmetros da linha de comando e as variáveis do sistema. Sabe-se que esses parâmetros estão na parte de cima da stack no layout de memória de um executável.

Em C, você tem acesso aos argumentos da linha de comando através das variáveis argc e argv declaradas em main. O legal é que, mesmo se você não declarar essas variáveis, os parâmetros ainda serão acessíveis, pois eles não dependem da linguagem C e sim de um padrão para arquivos executáveis.

Encontrando o deslocamento “mágico”

Quando se declara uma variável local, o valor dela estará na pilha, dentro do stack frame da função em que ela reside. Através do operador & podemos encontrar a posição exata na pilha em que uma variável local ocupa. Como visto na introdução, os parâmetros da linha de comando estão em endereços mais altos da pilha e assim é possível calcular um deslocamento entre uma variável local até o primeiro parâmetro da linha de comando.

Qualquer variável local de qualquer função pode ser escolhida como base para se calcular esse deslocamento já que todas elas estão na pilha e abaixo da posição dos parâmetos. Aqui utilizarei uma variável na stack frame de main.

Para encontrar essa constante mágica devemos criar um programa em C que mostre a diferença entre o endereço da variável local e argv. Veja:

/*
 * Programa que calcula o deslocamento
 * de uma variavel local ate a localizacao
 * da lista de parametros da linha de comando.
 */
#include

int main(int argc, char **argv) {
    int i ;

    printf("%p\n%p\n", &i, argv) ;

    return 0;
}

Compilando e executando:

$ gcc -o prog1 prog1.c
$ ./prog1
0xbfe5ab1c
0xbfe5abe4

$ ./prog1
0xbf9f085c
0xbf9f0924

$ ./prog1
0xbfcc94fc
0xbfcc95c4

Em cada execução, observa-se que são gerados endereços diferentes tanto para i quanto para argv. Porém, se você calcular a diferença entre esses endereços você verá que &i está a 200 bytes abaixo de argv. Note que o valor 200 pode não ser o mesmo em seu sistema.

Concluímos então que:

argv = &i + 200 bytes

Mas como i é inteiro de 32-bits precisamos dividir 200 por 4, e assim descobrimos o deslocamento correto:

argv = &i + 50

Basta agora utilizar o endereço &i+50 no lugar de argv. É bom lembrar também que a string de argv[0] é acessível em *(&i + 50), e que argv[1] é acessível em *(&i+50+1), e por aí vai. Por fim, o último parâmetro é sempre NULL de acordo com a especificação ELF.

t+

Deixe um comentário