a leitura do ambiente durante a execução de ELFO IFUNC funções da expedição

StackOverflow https://stackoverflow.com/questions/20353246

  •  25-08-2022
  •  | 
  •  

Pergunta

O IFUNC mecanismo nos últimos ELF ferramentas (pelo menos) do Linux permite escolher uma implementação de uma função em tempo de execução.Olhar para o iunc atributo GCC documentação para uma descrição mais detalhada: http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html Outra descrição de IFUNC mecanism : http://www.agner.org/optimize/blog/read.php?i=167

Eu gostaria de escolher a minha implementação, dependendo do valor de uma variável de ambiente.No entanto, minha experiência me mostra que a libc (pelo menos a parte sobre o meio ambiente) ainda não é inicializado quando a resolução de função é executada.Assim, o clássico interfaces (extern char**environ ou função getenv()) não funcionam.

Alguém sabe como acessar o ambiente de um programa no Linux muito cedo ?O ambiente é o programa de configuração do kernel no execve(2) sistema de chamada, por isso já está em algum lugar (mas onde exatamente ?) no programa espaço de endereço no início de inicialização.

Obrigado antecipadamente Vicente

Programa para testar:

#include <stdio.h>
#include <stdlib.h>

extern char** environ;
char** save_environ;
char* toto;
int saved=0;

extern int fonction ();

int fonction1 () {
return 1;
}

int fonction2 () {
return 2;
}

static typeof(fonction) * resolve_fonction (void) {
saved=1;
save_environ=environ;
toto=getenv("TOTO");
/* no way to choose between fonction1 and fonction2 with the TOTO envvar */
return fonction1;
}

int fonction () __attribute__ ((ifunc ("resolve_fonction")));

void print_saved() {
printf("saved: %dn", saved);
if (saved) {
printf("prev environ: %pn", save_environ);
printf("prev TOTO: %sn", toto);
}
}

int main() {

print_saved();
printf("main environ: %pn", environ);
printf("main environ[0]: %sn", environ[0]);
printf("main TOTO: %sn", getenv("TOTO"));
printf("main value: %dn", fonction());

return 0;
}

Compilação e execução:

$ gcc -Wall -g ifunc.c -o ifunc
$ env TOTO=ok ./ifunc
saved: 1
prev environ: (nil)
prev TOTO: (null)
main environ: 0x7fffffffe288
main environ[0]: XDG_VTNR=7
main TOTO: ok
main value: 1
$ 

Na resolução de função, environ é NULO e getenv("TOTO") retorna NULL.No main função, a informação está aqui.

Foi útil?

Solução

Ponteiro De Função

Eu não encontrei nenhuma maneira de usar env em fase inicial legalmente.Resolução de função é executada no linker, mesmo anteriores, que preinit_array funções.A única forma legal de se resolver isso é usar o ponteiro de função e decidir qual função usar em função de .preinit_array seção:

extern char** environ;
int(*f)();

void preinit(int argc, char **argv, char **envp) {
        f = f1;
        environ = envp; // actually, it is done a bit later
        char *v = getenv("TOTO");
        if (v && strcmp(v, "ok") == 0) {
                f = f2;
        }
}

__attribute__((section(".preinit_array"))) typeof(preinit) *__preinit = preinit;

ifunc & o GNU ld inners

Glibc do linker ld contém um símbolo local _environ e ele é inicializado, mas é um pouco difícil de extrair.Não há outra maneira que eu encontrei, mas é um pouco complicado e bastante confiável.

Em vinculador ponto de entrada _start só pilha é inicializado.Programa de argumentos e valores ambientais são enviados para o processo através de pilha.Os argumentos são armazenados na seguinte ordem:

argc, argv, argv + 1, ..., argv + argc - 1, NULL, ENV...

Linker ld partilha um símbolo global _dl_argv, o que aponta para este lugar na pilha.Com a ajuda dela podemos extrair todas as variáveis necessárias:

extern char** environ;
extern char **_dl_argv;

char** get_environ() {
    int argc = *(int*)(_dl_argv - 1);
    char **my_environ = (char**)(_dl_argv + argc + 1);
    return my_environ;
}

typeof(f1) * resolve_f() {
    environ = get_environ();
    const char *var = getenv("TOTO");
    if (var && strcmp(var, "ok") == 0) {
        return f2;
    }
    return f1;
}

int f() __attribute__((ifunc("resolve_f")));
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top