RTLD-AUDIT(7)

الاسم
rtld-audit - واجهة برمجة تطبيقات التدقيق للرابط الديناميكي

موجز
#define _GNU_SOURCE             /* See feature_test_macros(7) */
#include <link.h>

الوصف
يوفر الرابط الديناميكي لجنو (رابط وقت التشغيل) واجهة برمجة تطبيقات تدقيق تسمح للتطبيق بالإشعار عند حدوث أحداث ربط ديناميكي مختلفة. تشبه واجهة برمجة التطبيقات هذه إلى حد كبير واجهة التدقيق التي يوفرها رابط وقت تشغيل Solaris. تُعرّف الثوابت والنماذج الأولية اللازمة بتضمين <link.h>.

لاستخدام هذه الواجهة، ينشئ المبرمج مكتبة مشتركة تطبق مجموعة قياسية من أسماء الدوال. لا يلزم تنفيذ جميع الدوال: في معظم الحالات، إذا لم يكن المبرمج مهتمًا بفئة معينة من أحداث التدقيق، فلا حاجة لتوفير تنفيذ لدالة التدقيق المقابلة.

لاستخدام واجهة التدقيق، يجب تعريف متغير البيئة LD_AUDIT ليحتوي على قائمة مفصولة بنقطتين من المكتبات المشتركة، يمكن لكل منها تنفيذ (أجزاء من) واجهة برمجة تطبيقات التدقيق. عند حدوث حدث قابل للتدقيق، تُستدعى الدالة المقابلة في كل مكتبة، بالترتيب الذي تظهر به المكتبات.

  la_version()
unsigned int la_version(unsigned int version);

هذه هي الدالة الوحيدة التي يجب تعريفها بواسطة مكتبة التدقيق: تؤدي المصافحة الأولية بين الرابط الديناميكي ومكتبة التدقيق. عند استدعاء هذه الدالة، يمرر الرابط الديناميكي، في version، أعلى إصدار من واجهة التدقيق التي يدعمها الرابط.

يعيد التنفيذ النموذجي لهذه الدالة ببساطة الثابت LAV_CURRENT، الذي يشير إلى إصدار <link.h> المستخدم لبناء وحدة التدقيق. إذا لم يدعم الرابط الديناميكي هذا الإصدار من واجهة التدقيق، سيرفض تفعيل وحدة التدقيق هذه. إذا أعادت الدالة صفرًا، لن يفعل الرابط الديناميكي أيضًا وحدة التدقيق هذه.

لتمكين التوافق العكسي مع الروابط الديناميكية الأقدم، يمكن لوحدة التدقيق فحص وسيط version وإعادة إصدار أقدم من LAV_CURRENT، بافتراض أن الوحدة يمكنها تعديل تنفيذها لمطابقة متطلبات الإصدار السابق من واجهة التدقيق. لا ينبغي لدالة la_version إعادة قيمة version دون فحوصات إضافية لأنها قد تتوافق مع واجهة لا تطابق تعريفات <link.h> المستخدمة لبناء وحدة التدقيق.

  la_objsearch()
char *la_objsearch(const char *name, uintptr_t *cookie,
                   unsigned int flag);

يستدعي الرابط الديناميكي هذه الدالة لإعلام مكتبة التدقيق بأنه على وشك البحث عن كائن مشترك. وسيط name هو اسم الملف أو اسم المسار الذي سيُبحث عنه. يُعرّف cookie الكائن المشترك الذي بدأ البحث. يُضبط flag على إحدى القيم التالية:

LA_SER_ORIG
    هذا هو الاسم الأصلي الذي يُبحث عنه. عادةً، يأتي هذا الاسم من إدخال ELF DT_NEEDED، أو هو وسيط filename المعطى لـ dlopen(3).

LA_SER_LIBPATH
    name أُنشئ باستخدام دليل محدد في LD_LIBRARY_PATH.

LA_SER_RUNPATH
    name أُنشئ باستخدام دليل محدد في قائمة ELF DT_RPATH أو DT_RUNPATH.

LA_SER_CONFIG
    name وُجد عبر خبيئة ldconfig(8) (/etc/ld.so.cache).

LA_SER_DEFAULT
    name وُجد عبر بحث في أحد الأدلة المبدئية.

LA_SER_SECURE
    name خاص بكائن آمن (غير مستخدم على لينكس).

كنتيجة لدالتها، تُرجع la_objsearch() اسم المسار الذي ينبغي على الرابط الديناميكي استخدامه للمعالجة اللاحقة. إذا أُرجع NULL، يُتجاهل اسم المسار هذا للمعالجة اللاحقة. إذا كانت مكتبة التدقيق هذه تهدف ببساطة إلى مراقبة مسارات البحث، ينبغي إرجاع name.

  la_activity()
void la_activity( uintptr_t *cookie, unsigned int flag);

يستدعي الرابط الديناميكي هذه الدالة لإعلام مكتبة التدقيق بأن نشاط خريطة الوصل يحدث. يُحدد cookie الكائن في رأس خريطة الوصل. عندما يستدعي الرابط الديناميكي هذه الدالة، يُضبط flag على إحدى القيم التالية:

LA_ACT_ADD
    تُضاف كائنات جديدة إلى خريطة الوصل.

LA_ACT_DELETE
    تُزال كائنات من خريطة الوصل.

LA_ACT_CONSISTENT
    اكتمل نشاط خريطة الوصل: أصبحت الخريطة متسقة مرة أخرى.

  la_objopen()
unsigned int la_objopen(struct link_map *map, Lmid_t lmid,
                        uintptr_t *cookie);

يستدعي الرابط الديناميكي هذه الدالة عند تحميل كائن مشترك جديد. وسيطة map هي مؤشر لبنية خريطة وصل تصف الكائن. يحتوي حقل lmid على إحدى القيم التالية:

LM_ID_BASE
    خريطة الوصل جزء من النطاق الأولي.

LM_ID_NEWLM
    خريطة الوصل جزء من نطاق جديد طُلب عبر dlmopen(3).

cookie هو مؤشر لمعرّف لهذا الكائن. يُقدم المعرّف لاستدعاءات لاحقة لدوال في مكتبة التدقيق من أجل تحديد هذا الكائن. يُهيأ هذا المعرّف للإشارة إلى خريطة وصل الكائن، لكن يمكن لمكتبة التدقيق تغيير المعرّف إلى قيمة أخرى قد تفضلها لاستخدامها في تحديد الكائن.

كقيمة إرجاعها، تُرجع la_objopen() قناع بتات يُنشأ بإجراء عملية OR على صفر أو أكثر من الثوابت التالية، والتي تسمح لمكتبة التدقيق باختيار الكائنات التي ستُراقب بواسطة la_symbind*():

LA_FLG_BINDTO
    تدقيق ارتباطات الرموز بهذا الكائن.

LA_FLG_BINDFROM
    تدقيق ارتباطات الرموز من هذا الكائن.

قيمة إرجاع 0 من la_objopen() تشير إلى أنه لا ينبغي تدقيق أي ارتباطات رموز لهذا الكائن.

  la_objclose()
unsigned int la_objclose(uintptr_t *cookie);

يستدعي الرابط الديناميكي هذه الدالة بعد تنفيذ أي كود إنهاء للكائن، قبل تفريغ الكائن. وسيطة cookie هي المعرّف الذي حُصل عليه من استدعاء سابق لـ la_objopen().

في التطبيق الحالي، يتم تجاهل القيمة التي يعيدها la_objclose().

  la_preinit()
void la_preinit(uintptr_t *cookie);

يستدعي الرابط الديناميكي هذه الدالة بعد تحميل جميع الكائنات المشتركة، قبل تمرير التحكم إلى التطبيق (أي قبل استدعاء main()). لاحظ أن main() قد لا يزال يقوم لاحقًا بتحميل الكائنات ديناميكيًا باستخدام dlopen(3).

  la_symbind*()
uintptr_t la_symbind32(Elf32_Sym *sym, unsigned int ndx,
                       uintptr_t *refcook, uintptr_t *defcook,
                       unsigned int *flags, const char *symname);
uintptr_t la_symbind64(Elf64_Sym *sym, unsigned int ndx,
                       uintptr_t *refcook, uintptr_t *defcook,
                       unsigned int *flags, const char *symname);

يستدعي الرابط الديناميكي إحدى هذه الدوال عند حدوث ربط رمز بين كائنين مشتركين تم وضع علامة عليهما لإشعار التدقيق بواسطة la_objopen(). تُستخدم الدالة la_symbind32() على المنصات 32-بت؛ وتُستخدم الدالة la_symbind64() على المنصات 64-بت.

الوسيطة sym هي مؤشر إلى بنية توفر معلومات حول الرمز الذي يتم ربطه. يظهر تعريف البنية في <elf.h>. من بين حقول هذه البنية، يشير st_value إلى العنوان الذي يتم ربط الرمز به.

تعطي الوسيطة ndx فهرس الرمز في جدول الرموز للكائن المشترك المرتبط.

تحدد الوسيطة refcook الكائن المشترك الذي يقوم بمرجع الرمز؛ هذا هو نفس المعرف الذي يتم توفيره للدالة la_objopen() التي أعادت LA_FLG_BINDFROM. تحدد الوسيطة defcook الكائن المشترك الذي يعرف الرمز المشار إليه؛ هذا هو نفس المعرف الذي يتم توفيره للدالة la_objopen() التي أعادت LA_FLG_BINDTO.

تشير الوسيطة symname إلى سلسلة تحتوي على اسم الرمز.

الوسيطة flags هي قناع بت يوفر معلومات حول الرمز ويمكن استخدامه لتعديل التدقيق الإضافي لهذا الإدخال في PLT (جدول ربط الإجراءات). قد يوفر الرابط الديناميكي قيم البت التالية في هذه الوسيطة:

LA_SYMB_DLSYM
    نتج الربط عن استدعاء لـ dlsym(3).

LA_SYMB_ALTVALUE
    أعاد استدعاء سابق لـ la_symbind*() قيمة بديلة لهذا الرمز.

افتراضيًا، إذا نفذت مكتبة التدقيق دالتي la_pltenter() و la_pltexit() (انظر أدناه)، فسيتم استدعاء هذه الدوال، بعد la_symbind()، لإدخالات PLT، في كل مرة يتم فيها مرجع الرمز. يمكن إجراء عملية OR للأعلام التالية في *flags لتغيير هذا السلوك الافتراضي:

LA_SYMB_NOPLTENTER
    لا تستدع la_pltenter() لهذا الرمز.

LA_SYMB_NOPLTEXIT
    لا تستدع la_pltexit() لهذا الرمز.

قيمة الإرجاع لـ la_symbind32() و la_symbind64() هي العنوان الذي يجب تمرير التحكم إليه بعد إرجاع الدالة. إذا كانت مكتبة التدقيق تراقب ببساطة ربط الرموز، فيجب أن تعيد sym->st_value. قد يتم إرجاع قيمة مختلفة إذا رغبت المكتبة في توجيه التحكم إلى موقع بديل.

  la_pltenter()
يعتمد الاسم الدقيق وأنواع الوسائط لهذه الدالة على منصة العتاد. (يتم توفير التعريف المناسب بواسطة <link.h>.) إليك التعريف لـ x86-32:

Elf32_Addr la_i86_gnu_pltenter(Elf32_Sym *sym, unsigned int ndx,
                 uintptr_t *refcook, uintptr_t *defcook,
                 La_i86_regs *regs, unsigned int *flags,
                 const char *symname, long *framesizep);

تُستدعى هذه الدالة قبل استدعاء مدخل PLT مباشرة، بين كائنين مشتركين تم وضع علامة عليهما لإشعار الربط.

sym و ndx و refcook و defcook و symname هي كما في la_symbind*().

يشير وسيط regs إلى بنية (مُعرّفة في <link.h>) تحتوي على قيم السجلات المستخدمة لاستدعاء مدخل PLT هذا.

يشير وسيط flags إلى قناع بت ينقل معلومات حول مدخل PLT هذا، ويمكن استخدامه لتعديل التدقيق اللاحق له، كما في la_symbind*().

يشير وسيط framesizep إلى مخبأ long int يمكن استخدامه لتعيين حجم الإطار المستخدم لاستدعاء مدخل PLT هذا بشكل صريح. إذا أعادت استدعاءات مختلفة لـ la_pltenter() لهذا الرمز قيماً مختلفة، فتُستخدم القيمة القصوى المُعادة. تُستدعى دالة la_pltexit() فقط إذا تم تعيين هذا المخبأ بشكل صريح إلى قيمة مناسبة.

قيمة إرجاع la_pltenter() هي كما في la_symbind*().

  la_pltexit()
يعتمد الاسم الدقيق وأنواع الوسائط لهذه الدالة على منصة العتاد. (يتم توفير التعريف المناسب بواسطة <link.h>.) إليك التعريف لـ x86-32:

unsigned int la_i86_gnu_pltexit(Elf32_Sym *sym, unsigned int ndx,
                 uintptr_t *refcook, uintptr_t *defcook,
                 const La_i86_regs *inregs, La_i86_retval *outregs,
                 const char *symname);

تُستدعى هذه الدالة عندما يعود مدخل PLT، الذي أُنشئ بين كائنين مشتركين تم وضع علامة عليهما لإشعار الربط. تُستدعى الدالة قبل عودة التحكم إلى مستدعي مدخل PLT مباشرة.

sym و ndx و refcook و defcook و symname هي كما في la_symbind*().

يشير وسيط inregs إلى بنية (مُعرّفة في <link.h>) تحتوي على قيم السجلات المستخدمة لاستدعاء مدخل PLT هذا. يشير وسيط outregs إلى بنية (مُعرّفة في <link.h>) تحتوي على قيم الإرجاع لاستدعاء مدخل PLT هذا. يمكن للمستدعي تعديل هذه القيم، وستكون التغييرات مرئية لمستدعي مدخل PLT.

في التطبيق الحالي لـ GNU، يتم تجاهل قيمة إرجاع la_pltexit().

الإصدارات
تشبه واجهة API هذه إلى حد كبير واجهة Solaris API الموصوفة في دليل Solaris Linker and Libraries Guide، في الفصل Runtime Linker Auditing Interface.

المعايير
لا يوجد.

ملاحظات
لاحظ الاختلافات التالية عن واجهة API لتدقيق الرابط الديناميكي في Solaris:

•   واجهة Solaris la_objfilter() غير مدعومة في تطبيق GNU.

•   دالتا Solaris la_symbind32() و la_pltexit() لا توفران وسيط symname.

•   دالة Solaris la_pltexit() لا توفر وسيطي inregs و outregs (لكنها توفر وسيط retval مع قيمة إرجاع الدالة).

العلل
في إصدارات glibc حتى 2.9، يؤدي تحديد أكثر من مكتبة تدقيق في LD_AUDIT إلى تعطل وقت التشغيل. يُقال إن هذا أُصلح في glibc 2.10.

أمثلة
    #include <link.h>
    #include <stdio.h>

    unsigned int
    la_version(unsigned int version)
    {
        printf("la_version(): version = %u; LAV_CURRENT = %u\n",
                version, LAV_CURRENT);

        return LAV_CURRENT;
    }

    char *
    la_objsearch(const char *name, uintptr_t *cookie, unsigned int flag)
    {
        printf("la_objsearch(): name = %s; cookie = %p", name, cookie);
        printf("; flag = %s\n",
                (flag == LA_SER_ORIG) ?    "LA_SER_ORIG" :
                (flag == LA_SER_LIBPATH) ? "LA_SER_LIBPATH" :
                (flag == LA_SER_RUNPATH) ? "LA_SER_RUNPATH" :
                (flag == LA_SER_DEFAULT) ? "LA_SER_DEFAULT" :
                (flag == LA_SER_CONFIG) ?  "LA_SER_CONFIG" :
                (flag == LA_SER_SECURE) ?  "LA_SER_SECURE" :
                "???");

        return name;
    }

    void
    la_activity (uintptr_t *cookie, unsigned int flag)
    {
        printf("la_activity(): cookie = %p; flag = %s\n", cookie,
                (flag == LA_ACT_CONSISTENT) ? "LA_ACT_CONSISTENT" :
                (flag == LA_ACT_ADD) ?        "LA_ACT_ADD" :
                (flag == LA_ACT_DELETE) ?     "LA_ACT_DELETE" :
                "???");
    }

    unsigned int
    la_objopen(struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
    {
        printf("la_objopen(): loading \"%s\"; lmid = %s; cookie=%p\n",
                map->l_name,
                (lmid == LM_ID_BASE) ?  "LM_ID_BASE" :
                (lmid == LM_ID_NEWLM) ? "LM_ID_NEWLM" :
                "???",
                cookie);

        return LA_FLG_BINDTO | LA_FLG_BINDFROM;
    }

    unsigned int
    la_objclose (uintptr_t *cookie)
    {
        printf("la_objclose(): %p\n", cookie);

        return 0;
    }

    void
    la_preinit(uintptr_t *cookie)
    {
        printf("la_preinit(): %p\n", cookie);
    }

    uintptr_t
    la_symbind32(Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook,
            uintptr_t *defcook, unsigned int *flags, const char *symname)
    {
        printf("la_symbind32(): symname = %s; sym->st_value = %p\n",
                symname, sym->st_value);
        printf("        ndx = %u; flags = %#x", ndx, *flags);
        printf("; refcook = %p; defcook = %p\n", refcook, defcook);

        return sym->st_value;
    }

    uintptr_t
    la_symbind64(Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook,
            uintptr_t *defcook, unsigned int *flags, const char *symname)
    {
        printf("la_symbind64(): symname = %s; sym->st_value = %p\n",
                symname, sym->st_value);
        printf("        ndx = %u; flags = %#x", ndx, *flags);
        printf("; refcook = %p; defcook = %p\n", refcook, defcook);

        return sym->st_value;
    }

    Elf32_Addr
    la_i86_gnu_pltenter(Elf32_Sym *sym, unsigned int ndx,
            uintptr_t *refcook, uintptr_t *defcook, La_i86_regs *regs,
            unsigned int *flags, const char *symname, long *framesizep)
    {
        printf("la_i86_gnu_pltenter(): %s (%p)\n", symname, sym->st_value);

        return sym->st_value;
    }

انظر أيضًا
ldd(1), dlopen(3), ld.so(8), ldconfig(8)

ترجمة
تُرجمت هذه الصفحة من الدليل بواسطة زايد السعيدي <zayed.alsaidi@gmail.com>

هذه الترجمة هي وثيقة مجانية؛ راجع رخصة جنو العامة الإصدار 3 (https://www.gnu.org/licenses/gpl-3.0.html) أو ما بعده للاطلاع على شروط حقوق النشر. لا توجد أي ضمانات.

إذا وجدت أي أخطاء في ترجمة صفحة الدليل هذه، يرجى إرسال بريد إلكتروني إلى قائمة بريد المترجمين: kde-l10n-ar@kde.org.