質問

(少なくとも)Linuxの最近のELFツールのIFUNCメカニズムにより、実行時に関数の実装を選択できます。詳細な説明については、GCCドキュメントのIUNC属性をご覧ください。 http://gcc.gnu.org/onlinedocs/gcc/function-attributes.htmlifuncメカニズムの別の説明: http://www.agner.org/optimize/blog/read.php?i=167

環境変数の値に応じて、実装を選択したいと思います。ただし、私の実験では、RIBC(少なくとも環境に関する部分)は、リゾルバー関数が実行されたときにまだ初期化されていないことがわかります。したがって、古典的なインターフェイス(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 Inners

GlibcのリンカーLDにはローカルシンボルが含まれています _環境 初期化されていますが、抽出するのはかなり困難です。私が見つけた別の方法がありますが、それは少しトリッキーで、かなり信頼できません。

リンカーのエントリポイントで _始める スタックのみが初期化されます。プログラムの引数と環境値は、スタックを介してプロセスに送信されます。引数は次の順序で保存されます。

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