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")));