Чтение среды при выполнении функций диспетчерского эльфа

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

  •  25-08-2022
  •  | 
  •  

Вопрос

Механизм ifunc в недавних инструментах ELF на (по крайней мере) Linux позволяет выбрать реализацию функции во время выполнения. Посмотрите на атрибут IUNC в документации GCC для более подробного описания: http://gcc.gnu.org/onlinedocs/gcc/function-attributes.htmlДругое описание меканизма ifunc: http://www.agner.org/optimize/blog/read.php?i=167

Я хотел бы выбрать свою реализацию в зависимости от значения переменной среды. Тем не менее, мои эксперименты показывают мне, что LIBC (по крайней мере, часть окружающей среды) еще не инициализируется при выполнении функции разрешения. Таким образом, классические интерфейсы (Extern char ** Environ или getenv ()) не работают.

Кто -нибудь знает, как получить доступ к среде программы в Linux на очень ранней стадии? Среда настраивается ядром на системном вызове Execve (2), так что она уже где -то (но где именно?) В адресном пространстве программы при ранней инициализации.

Заранее спасибо, Винсент

Программа для тестирования:

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

Компиляция и исполнение:

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

В функции резолюра, environ null и getenv("TOTO") Возвращает ноль. в main Функция, информация здесь.

Это было полезно?

Решение

Указатель функций

Я не нашел никакого способа использовать ENV на ранней стадии на законных основаниях. Функция Resolver работает в линкере даже раньше, чем функции 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 insers

LD LD Glibc содержит локальный символ _environ И это инициализируется, но его довольно сложно извлечь. Есть другой способ, который я нашел, но это немного сложно и довольно ненадежно.

В точке входа в линкер _Начало Только стек инициализируется. Аргументы программы и экологические значения отправляются в процесс через стек. Аргументы хранятся в следующем порядке:

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

Линкер LD делится глобальным символом _dl_argv, что указывает на это место на стеке. С помощью этого мы можем извлечь все необходимые переменные:

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")));
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top