Lire l'environnement lors de l'exécution des fonctions de répartition IFUNC ELF

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

  •  25-08-2022
  •  | 
  •  

Question

Le mécanisme IFUNC dans les outils ELF récents sur (au moins) Linux permet de choisir une implémentation d'une fonction à l'exécution. Regardez l'attribut iunc dans la documentation GCC pour une description plus détaillée: http://gccc.gnu.org/onlinedocs/gcc/function-attributes.htmlUne autre description du mécanisme ifunc: http://www.agner.org/optimize/blog/read.php?i=167

Je voudrais choisir mon implémentation en fonction de la valeur d'une variable d'environnement. Cependant, mes expériences me montrent que la LIBC (au moins la partie de l'environnement) n'est pas encore initialisée lorsque la fonction de résolveur est exécutée. Ainsi, les interfaces classiques (char ** Environ ou GETENV ()) ne fonctionnent pas.

Quelqu'un sait-il comment accéder à l'environnement d'un programme à Linux à très tôt? L'environnement est configuré par le noyau de l'appel système Execve (2), il est donc déjà quelque part (mais où exactement?) Dans l'espace d'adressage du programme à l'initialisation précoce.

Merci d'avance Vincent

Programme à tester:

#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;
}

Compilation et exécution:

$ 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
$ 

Dans la fonction Resolver, environ est nul et getenv("TOTO") retourne null. Dans le main fonction, l'information est là.

Était-ce utile?

La solution

Pointeur de fonction

Je n'ai trouvé aucun moyen d'utiliser Env à un stade précoce légalement. La fonction Resolver s'exécute dans Linker encore plus tôt, que les fonctions preinit_array. Le seul moyen légal de résoudre ceci est d'utiliser le pointeur de fonction et de décider quelle fonction utiliser en fonction de .preinit_array section:

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 & gnu ld Inners

Linker LD de GLIBC contient un symbole local _environ Et il est initialisé, mais il est assez difficile de l'extraire. Il y a une autre façon que j'ai trouvée, mais c'est un peu délicat et plutôt peu fiable.

Au point d'entrée de Linker _début Seule la pile est initialisée. Les arguments du programme et les valeurs environnementales sont envoyées au processus via la pile. Les arguments sont stockés dans l'ordre suivant:

Argc, argv, argv + 1, ..., argv + argc - 1, null, env ...

Linker LD partage un symbole mondial _dl_argv, qui pointe vers cet endroit sur la pile. Avec l'aide, nous pouvons extraire toutes les variables nécessaires:

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")));
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top