문제

Linux의 최근 ELF 도구의 IFUNC 메커니즘을 사용하면 런타임에 기능을 구현할 수 있습니다. 자세한 내용은 GCC 문서의 iunc 속성을보십시오. http://gcc.gnu.org/onlinedocs/gcc/function-attributes.htmlIfunc Mecanism에 대한 또 다른 설명 : http://www.agner.org/optimize/blog/read.php?i=167

환경 변수의 값에 따라 구현을 선택하고 싶습니다. 그러나 내 실험에 따르면 Resolver 함수가 실행될 때 LIBC (적어도 환경에 대한 부분)가 아직 초기화되지 않았다는 것을 보여줍니다. 따라서 고전적인 인터페이스 (Extern Char ** Environ 또는 Getenv ())는 작동하지 않습니다.

매우 초기 단계에서 Linux의 프로그램 환경에 액세스하는 방법을 아는 사람이 있습니까? 환경은 EXECVE (2) 시스템 호출의 커널에 의해 설정되므로 초기 초기화시 공간을 주소 공간에서 이미 어딘가에 있습니다 (그러나 정확히 어디에 있습니까?).

미리 감사합니다 Vincent

테스트 할 프로그램 :

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

Resolver 기능에서 environ NULL이고 getenv("TOTO") NULL을 반환합니다. 에서 main 기능, 정보는 여기에 있습니다.

도움이 되었습니까?

해결책

기능 포인터

초기 단계에서 합법적으로 ENV를 사용할 방법을 찾지 못했습니다. Resolver 기능은 Preinit_array 함수보다 더 일찍 링커에서 실행됩니다. 이를 해결하는 유일한 법적 방법은 기능 포인터를 사용하고 .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 이너

GLIBC의 링커 LD에는 로컬 기호가 포함되어 있습니다 _environ 그리고 초기화되었지만 추출하기가 다소 어렵습니다. 내가 찾은 또 다른 방법이 있지만 약간 까다 롭고 다소 신뢰할 수 없습니다.

링커의 진입 점에서 _시작 스택 만 초기화됩니다. 프로그램 인수 및 환경 값은 스택을 통해 프로세스로 전송됩니다. 인수는 다음 순서로 저장됩니다.

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

Linker 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