سؤال

تسمح آلية IFUNC في أدوات ELF الحديثة على (على الأقل) Linux باختيار تنفيذ وظيفة في وقت التشغيل. انظر إلى سمة IUNC في وثائق مجلس التعاون الخليجي للحصول على وصف أكثر تفصيلاً: http://gcc.gnu.org/onlinedocs/gcc/function-attributes.htmlوصف آخر لـ Ifunc Mecanism: http://www.agner.org/optimize/blog/read.php؟i=167

أود اختيار تنفيذي اعتمادًا على قيمة متغير البيئة. ومع ذلك ، تبين لي تجاربي أن LIBC (على الأقل الجزء المتعلق بالبيئة) لم يتم تهيئته بعد عند تشغيل وظيفة الحل. لذلك ، لا تعمل الواجهات الكلاسيكية (extern char ** environ أو getenv ()).

هل يعرف أي شخص كيفية الوصول إلى بيئة البرنامج في Linux في مرحلة مبكرة جدًا؟ يتم إعداد البيئة بواسطة kernel في استدعاء النظام 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 هو لاغ و getenv("TOTO") يعود فارغة. في ال main الوظيفة ، المعلومات هنا.

هل كانت مفيدة؟

المحلول

مؤشر وظيفة

لم أجد أي وسيلة لاستخدام ENV في المرحلة المبكرة بشكل قانوني. تعمل دالة Resolver في Linker حتى وقت سابق ، من وظائف 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 على رمز محلي _environ ويتم تهيئتها ، ولكن من الصعب استخراجها. هناك طريقة أخرى وجدتها ، لكنها صعبة بعض الشيء وغير موثوق بها إلى حد ما.

عند نقطة دخول Linker _بداية تتم تهيئة المكدس فقط. يتم إرسال وسيطات البرنامج والقيم البيئية إلى العملية عبر Stack. يتم تخزين الحجج بالترتيب التالي:

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