最近的ELF工具(至少)Linux上的IFUNC机制允许在运行时选择功能的实现。查看GCC文档中的IUNC属性以获取更详细的描述: http://gcc.gnu.org/onlinedocs/gcc/function-attributes.htmlifunc mecanism的另一个描述: http://www.agner.org/optimize/blog/read.php?i=167

我想根据环境变量的价值选择实现。但是,我的实验表明,在运行解析器函数时,LIBC(至少有关环境的部分)尚未初始化。因此,经典界面(Extern Char **环境或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
$ 

在解析器功能中, environ 是无效的 getenv("TOTO") 返回null。在里面 main 功能,信息在这里。

有帮助吗?

解决方案

功能指针

我发现在合法的早期阶段没有办法使用ENV。分辨率函数甚至比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 inners

GLIBC的Linker LD包含一个本地符号 _environ 它是初始化的,但是很难提取它。我发现了另一种方式,但这有点棘手,而且不可靠。

在Linker的入口点 _开始 只有堆栈是初始化的。程序参数和环境价值通过堆栈发送到该过程。参数按以下顺序存储:

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