gpt4 book ai didi

c - 执行 ELF IFUNC 调度函数时读取环境

转载 作者:太空狗 更新时间:2023-10-29 11:22:22 26 4
gpt4 key购买 nike

(至少)Linux 上最近的 ELF 工具中的 IFUNC 机制允许在运行时选择函数的实现。查看 GCC 文档中的 iunc 属性以获得更详细的描述:http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html IFUNC 机制的另一种描述:http://www.agner.org/optimize/blog/read.php?i=167

我想根据环境变量的值选择我的实现。但是,我的实验告诉我,当解析器函数运行时,libc(至少关于环境的部分)尚未初始化。因此,经典接口(interface)(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") 返回 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 并且它已被初始化,但很难提取它。我找到了另一种方法,但它有点棘手而且相当不可靠。

在链接器的入口点_start 仅初始化堆栈。程序参数和环境值通过堆栈发送到进程。参数按以下顺序存储:

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")));

关于c - 执行 ELF IFUNC 调度函数时读取环境,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20353246/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com