سؤال

أرغب في تجاوز استدعاءات وظائف معينة لواجهات برمجة التطبيقات المختلفة من أجل تسجيل المكالمات، ولكن قد أرغب أيضًا في معالجة البيانات قبل إرسالها إلى الوظيفة الفعلية.

على سبيل المثال، لنفترض أنني أستخدم دالة تسمى getObjectName آلاف المرات في كود المصدر الخاص بي.أرغب في تجاوز هذه الوظيفة مؤقتًا أحيانًا لأنني أرغب في تغيير سلوك هذه الوظيفة لرؤية النتيجة المختلفة.

أقوم بإنشاء ملف مصدر جديد مثل هذا:

#include <apiheader.h>    

const char *getObjectName (object *anObject)
{
    if (anObject == NULL)
        return "(null)";
    else
        return "name should be here";
}

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

هل هناك طريقة أسهل "لتجاوز" وظيفة دون الحصول على أخطاء/تحذيرات مرتبطة/تجميعية؟من الناحية المثالية، أريد أن أكون قادرًا على تجاوز الوظيفة بمجرد تجميع ملف إضافي أو اثنين وربطهما بدلاً من العبث بخيارات الربط أو تغيير كود المصدر الفعلي لبرنامجي.

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

المحلول

إذا انها فقط لمدة المصدر الذي تريد التقاط / تعديل المكالمات، وأبسط حل هو وضع معا ملف رأس (intercept.h) بما يلي:

#ifdef INTERCEPT
    #define getObjectName(x) myGetObectName(x)
#endif

ووتنفيذ وظيفة على النحو التالي (intercept.c التي <م> لا تشمل intercept.h):

const char *myGetObjectName (object *anObject) {
    if (anObject == NULL)
        return "(null)";
    else
        return getObjectName(anObject);
}

وبعد التأكد من كل ملف المصدر حيث تريد اعتراض المكالمة بما يلي:

#include "intercept.h"

في الجزء العلوي.

وبعد ذلك، عند ترجمة مع "-DINTERCEPT"، وكلها ملفات سيدعو ظيفة الخاص بك بدلا من واحد حقيقي وظيفة الخاص بك لا يزال استدعاء واحد حقيقي.

وتجميع بدون "-DINTERCEPT" سيمنع اعتراض من الحدوث.

وانها اصعب قليلا إذا كنت ترغب في اعتراض جميع المكالمات (وليس فقط تلك من المصدر) - وهذا عموما يمكن أن يتم ذلك مع تحميل الديناميكي وقرار من وظيفة حقيقية (مع المكالمات dlload- وdlsym-type)، ولكن لا أعتقد من الضروري في قضيتك.

نصائح أخرى

ومع دول مجلس التعاون الخليجي، في لينكس يمكنك استخدام العلم --wrap رابط مثل هذا:

gcc program.c -Wl,-wrap,getObjectName -o program

وتحديد وظيفة الخاص بك على النحو التالي:

const char *__wrap_getObjectName (object *anObject)
{
    if (anObject == NULL)
        return "(null)";
    else
        return __real_getObjectName( anObject ); // call the real function
}

وهذا من شأنه ضمان إعادة توجيه كافة المكالمات إلى getObjectName() إلى وظيفة المجمع الخاص بك (في وقت وصلة). هذا العلم مفيد جدا بيد أن تغيب في دول مجلس التعاون الخليجي تحت Mac OS X.

وتذكر أن بتعريف الدالة المجمع مع extern "C" إذا كنت ترجمة مع ز ++ بالرغم من ذلك.

ويمكنك تجاوز وظيفة باستخدام LD_PRELOAD خدعة - راجع man ld.so. يمكنك ترجمة ليب المشتركة مع وظيفة والبدء في ثنائي (لك حتى لا تحتاج إلى تعديل ثنائي!) مثل LD_PRELOAD=mylib.so myprog.

في الجسم وظيفة (في ليب المشتركة) تكتب هكذا:

const char *getObjectName (object *anObject) {
  static char * (*func)();

  if(!func)
    func = (char *(*)()) dlsym(RTLD_NEXT, "getObjectName");
  printf("Overridden!\n");     
  return(func(anObject));    // call original function
}

ويمكنك تجاوز أي وظيفة من مكتبة مشتركة، حتى من stdlib، دون تعديل / اعادة تجميع البرنامج، لذلك يمكن أن تفعل خدعة على البرامج التي ليس لديها مصدر لل. أليس لطيفة؟

إذا كنت تستخدم دول مجلس التعاون الخليجي، يمكنك جعل وظيفتك weak.أولئك يمكن تجاوزها بواسطة وظائف غير ضعيفة:

اختبار.ج:

#include <stdio.h>

__attribute__((weak)) void test(void) { 
    printf("not overridden!\n"); 
}

int main() {
    test();
}

ماذا تعمل، أو ماذا تفعل؟

$ gcc test.c
$ ./a.out
not overridden!

test1.c:

#include <stdio.h>

void test(void) {
    printf("overridden!\n");
}

ماذا تعمل، أو ماذا تفعل؟

$ gcc test1.c test.c
$ ./a.out
overridden!

للأسف، هذا لن ينجح مع المترجمين الآخرين.ولكن يمكن أن يكون لديك الإعلانات الضعيفة التي تحتوي على وظائف يمكن تجاوزها في ملفها الخاص، مع وضع مجرد تضمين في ملفات تنفيذ واجهة برمجة التطبيقات (API) إذا كنت تقوم بالتجميع باستخدام مجلس التعاون الخليجي:

ضعيفdecls.h:

__attribute__((weak)) void test(void);
... other weak function declarations ...

وظائف. ج:

/* for GCC, these will become weak definitions */
#ifdef __GNUC__
#include "weakdecls.h"
#endif

void test(void) { 
    ...
}

... other functions ...

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

<اقتباس فقرة>   

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

وهذا هو PDF كبير يغطي كيفية القيام بذلك على OS X، لينكس وويندوز.

وأنه ليس لديه أي حيل مذهلة التي لم يتم توثيقها هنا (هذه مجموعة مذهلة من ردود راجع للشغل) ... وإنما هو قراءة لطيفة.

اعتراض ظائف التعسفية على ويندوز ويونيكس، وماكنتوش OS منصات X (2004)، بقلم دانيال S. مايرز وآدم L. Bazinet .

ويمكنك تحميل PDF مباشرة من موقع بديل (للتكرار) .

وأخيرا، ينبغي أن مصادر السابقتين تذهب بطريقة أو بأخرى عليها في النيران، <وأ href = "https://www.google.com/search؟q=Intercepting+arbitrary+functions+on+Windows٪2C+UNIX٪ 2C + + وماكنتوش OS + + X + منصات٪ 2C + من + دانيال + S. + مايرز + و + آدم + L. + Bazinet "يختلط =" نوفولو noreferrer "> وهنا نتائج بحث Google لذلك .

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

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

وfoo.h:

typedef const char* (*GetObjectNameFuncPtr)(object *anObject);
extern GetObjectNameFuncPtr GetObjectName;

وfoo.cpp:

const char* GetObjectName_real(object *anObject)
{
    return "object name";
}

const char* GetObjectName_logging(object *anObject)
{
    if (anObject == null)
        return "(null)";
    else
        return GetObjectName_real(anObject);
}

GetObjectNameFuncPtr GetObjectName = GetObjectName_real;

void main()
{
    GetObjectName(NULL); // calls GetObjectName_real();

    if (isLoggingEnabled)
        GetObjectName = GetObjectName_logging;

    GetObjectName(NULL); // calls GetObjectName_logging();
}

وهناك أيضا وسيلة خادعة للقيام بذلك في رابط تنطوي على مكتبتين كعب.

ويرتبط مكتبة # 1 مقابل مكتبة المضيف ويعرض رمز يجري إعادة تعريف تحت اسم آخر.

ويرتبط مكتبة # 2 ضد مكتبة # 1، interecepting الدعوة واستدعاء النسخة تعريف في مكتبة # 1.

ونكون حذرين للغاية مع أوامر الرابط هنا أو أنها لن تنجح.

بناءً على إجابة @Johannes Schaub مع حل مناسب للكود الذي لا تملكه.

الاسم المستعار للوظيفة التي تريد تجاوزها إلى وظيفة محددة بشكل ضعيف، ثم أعد تنفيذها بنفسك.

تجاوز. ح

#define foo(x) __attribute__((weak))foo(x)

foo.c

function foo() { return 1234; }

تجاوز. ج

function foo() { return 5678; }

يستخدم قيم متغيرة خاصة بالنمط في Makefile الخاص بك لإضافة علامة المترجم -include override.h.

%foo.o: ALL_CFLAGS += -include override.h

جانبا:ربما يمكنك أيضًا استخدام -D 'foo(x) __attribute__((weak))foo(x)' لتحديد وحدات الماكرو الخاصة بك.

قم بتجميع الملف وربطه بإعادة التنفيذ (override.c).

  • يتيح لك ذلك تجاوز وظيفة واحدة من أي ملف مصدر، دون الحاجة إلى تعديل التعليمات البرمجية.

  • الجانب السلبي هو أنه يجب عليك استخدام ملف رأس منفصل لكل ملف تريد تجاوزه.

هل يمكن استخدام مكتبة مشتركة (يونكس) أو DLL (ويندوز) للقيام بذلك، وكذلك (سيكون قليلا من ركلة جزاء الأداء). ثم يمكنك تغيير DLL / بحيث يحصل على تحميل (إصدار واحد للتصحيح، إصدار واحد لعدم التصحيح).

ولقد فعلت شيئا مماثلا في الماضي (وليس لتحقيق ما تحاول تحقيقه، ولكن الافتراض الأساسي هو نفسه) وانها عملت بشكل جيد.

[عدل بناء على OP تعليق]

<اقتباس فقرة>   

في الواقع واحدة من الأسباب التي أريد أن   وظائف تجاوز هو لأنني   أظن أنها تتصرف بشكل مختلف على   أنظمة تشغيل مختلفة.

وهناك نوعان من الطرق الشائعة (وأنا أعلم أن من) للتعامل مع ذلك، فإن المشترك ليب / وسيلة دلل أو كتابة تطبيقات المختلفة التي تصل ضده.

لكلا حلول (مشتركة يبس أو ربط مختلف) عملتم foo_linux.c، foo_osx.c، foo_win32.c (أو طريقة أفضل هو لينكس / foo.c، OSX / foo.c و Win32 / foo.c ) ومن ثم تجميع وربط مع مناسبة واحدة.

إذا كنت تبحث عن وكلاهما رمز مختلف للمنصات والتصحيح -vs- إطلاق سراح أنا ربما يكون يميل للذهاب مع الحل ليب / DLL المشتركة كما هو الأكثر مرونة مختلفة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top