Pregunta

El mecanismo IFUNC en las herramientas ELF recientes en (al menos) Linux permite elegir una implementación de una función en tiempo de ejecución. Mire el atributo IUNC en la documentación de GCC para una descripción más detallada: http://gcc.gnu.org/onlinedocs/gcc/function-attributes.htmlOtra descripción del mecanismo ifunc: http://www.agner.org/optimize/blog/read.php?i=167

Me gustaría elegir mi implementación dependiendo del valor de una variable de entorno. Sin embargo, mis experimentos me muestran que el libc (al menos la parte sobre el entorno) aún no se inicializa cuando se ejecuta la función del resolución. Entonces, las interfaces clásicas (extern char ** ambiente o getenv ()) no funcionan.

¿Alguien sabe cómo acceder al entorno de un programa en Linux en una etapa muy temprana? El entorno está configurado por el núcleo en la llamada del sistema EXECVE (2), por lo que ya está en algún lugar (pero ¿dónde exactamente?) En el espacio de direcciones del programa en la inicialización temprana.

Gracias de antemano Vincent

Programa para probar:

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

Compilación y ejecución:

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

En la función resolución, environ es nulo y getenv("TOTO") Devuelve nulo. En el main función, la información está aquí.

¿Fue útil?

Solución

Puntero de funciones

No encontré ninguna forma de usar Env en la etapa temprana legalmente. La función de resolución se ejecuta en enlazador incluso antes, que las funciones preinit_array. La única forma legal de resolver esto es usar el puntero de la función y decidir qué función usar en la función .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 y gnu ld Inners

El enlazador de GLIBC LD contiene un símbolo local _reinar Y se inicializa, pero es bastante difícil extraerlo. Hay otra forma en que encontré, pero es un poco complicado y poco confiable.

En el punto de entrada de Linker _comienzo Solo se inicializa la pila. Los argumentos del programa y los valores ambientales se envían al proceso a través de la pila. Los argumentos se almacenan en el siguiente orden:

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

Linker Ld comparte un símbolo global _dl_argv, que apunta a este lugar en la pila. Con la ayuda, podemos extraer todas las variables necesarias:

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 bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top