Domanda

Il meccanismo IFUNC nei recenti strumenti ELF su (almeno) Linux consente di scegliere un'implementazione di una funzione in fase di esecuzione. Guarda l'attributo IUNC nella documentazione GCC per una descrizione più dettagliata: http://gcc.gnu.org/onlinerocs/gcc/function-attributes.htmlUn'altra descrizione del mecanismo ifunc: http://www.agner.org/optimize/blog/read.php?i=167

Vorrei scegliere la mia implementazione in base al valore di una variabile di ambiente. Tuttavia, i miei esperimenti mi mostrano che la libc (almeno la parte sull'ambiente) non è ancora inizializzata quando viene eseguita la funzione Resolver. Quindi, le interfacce classiche (esterne char ** ambier o getenv ()) non funzionano.

Qualcuno sa come accedere all'ambiente di un programma a Linux in fase molto precoce? L'ambiente è configurato dal kernel alla chiamata di sistema Execve (2), quindi è già da qualche parte (ma dove esattamente?) Nello spazio del programma di indirizzi all'inizializzazione precoce.

Grazie in anticipo Vincent

Programma da testare:

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

Compilazione ed esecuzione:

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

Nella funzione Resolver, environ è nullo e getenv("TOTO") Restituisce null. Nel main funzione, le informazioni sono qui.

È stato utile?

Soluzione

Puntatore della funzione

Non ho trovato modo di usare Env nella fase iniziale legalmente. La funzione Resolver funziona in Linker anche prima, rispetto alle funzioni preinit_array. L'unico modo legale per risolverlo è utilizzare il puntatore della funzione e decidere quale funzione utilizzare in funzione della sezione .preinit_array:

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 di GLIBC LD contiene un simbolo locale _environ Ed è inizializzato, ma è piuttosto difficile estrarlo. C'è un altro modo in cui ho trovato, ma è un po 'complicato e piuttosto inaffidabile.

Al punto di ingresso di Linker _inizio Solo lo stack è inizializzato. Gli argomenti del programma e i valori ambientali vengono inviati al processo tramite Stack. Gli argomenti sono archiviati nel seguente ordine:

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

Linker LD condivide un simbolo globale _dl_argv, che indica questo posto sullo stack. Con l'aiuto possiamo estrarre tutte le variabili necessarie:

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")));
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top