문제

내가 원하는 재정의 함수를 호출하여 다양한 Api 를 위해서 로그인의 통화만,나는 또한 데이터를 조작하기 전에 보낸 실제적인 기능입니다.

예를 들어,말 기능을 사용하라 getObjectName 수천의 시대에서 내 소스 코드입니다.고 싶은 일시적으로 이 설정을 무시 기능이기 때문에 때로는 내가 원하는 동작을 변경하는 이 기능을 참조 다른 결과입니다.

내가 만드는 새로운 소스 파일에 다음과 같다:

#include <apiheader.h>    

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

나는 컴파일하는 모든 다른 근원으로 나는 일반적으로,그러나 나는 링크에 그것이 기능하기 전에 먼저와 연 API 의 라이브러리입니다.이 있습니다 분명 전화의 실제 기능을 내 무시 기능이 있다.

이 있는 쉬운 방법을"무효"기능을 받지 않고 연결/컴파일하는 오류/경?최고 싶을 재정의할 수 있는 기능을 컴파일하고 연결하는 여분의 파일 또는 두 가지보다 주위에 바이올린 연결하는 옵션 또는 변경하에 실제의 소스 코드 내 프로그램입니다.

도움이 되었습니까?

해결책

위한 경우에만 귀하의원 캡처할 수정/통화,간단한 솔루션을 함께 넣어 헤더파일(intercept.h 로):

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

과 구현의 기능은 다음과 같습니다(에 intercept.cintercept.h):

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

다음 확인을 각 소스 파일에 당신이 원하는 곳에 차단하는 전화가있다:

#include "intercept.h"

상단에 있습니다.

그럼,당신이 컴파일하"-DINTERCEPT"모든 파일에 함수 호출이 아닌 실시 하나의 함수를 호출할 수도 있습 실시 하나입니다.

컴파일하지 않고"-DINTERCEPT"을 방지 차단에서 발생합니다.

그것은 조금 더 까다롭고 싶은 경우 차단하는 모든 통화(다만 이러 소스에서)이 일반적으로 수행 가진 동적 하중 및 해결의 실제 기능(과 dlload-dlsym-형 호출)하지만 난 그렇게 생각하지 않는 경우에 필요한 경우.

다른 팁

GCC를 사용하면 Linux에서 사용할 수 있습니다 --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의 GCC에 없습니다.

래퍼 기능을 선언해야합니다 extern "C" 그래도 G ++로 컴파일하는 경우.

사용을 사용하여 기능을 무시할 수 있습니다 LD_PRELOAD 트릭 - 참조 man ld.so. 당신은 당신의 기능과 함께 공유 lib를 컴파일하고 바이너리를 시작합니다 (이진을 수정할 필요도 없습니다!) LD_PRELOAD=mylib.so myprog.

당신의 기능 본문 (공유 lib)에서 당신은 다음과 같이 씁니다.

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의 공유 라이브러리에서 모든 기능을 무시할 수 있으므로 소스가없는 프로그램에서 트릭을 수행 할 수 있습니다. 좋지 않습니까?

GCC를 사용하는 경우 기능을 수행 할 수 있습니다. weak. 저것들 재정의 할 수 있습니다 비유 기능으로 :

test.c:

#include <stdio.h>

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

int main() {
    test();
}

그것은 무엇을합니까?

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

시험 1.c:

#include <stdio.h>

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

그것은 무엇을합니까?

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

안타깝게도 다른 컴파일러에는 효과가 없습니다. 그러나 GCC를 사용하여 컴파일하는 경우 API 구현 파일에 포함하는 것을 자체 파일에 고정 가능한 기능을 포함하는 약한 선언을 가질 수 있습니다.

약한 decls.h:

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

함수 .c:

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

void test(void) { 
    ...
}

... other functions ...

이것의 단점은 작동하지 않는다는 것입니다 전적으로 API 파일에 무언가를하지 않고 (이 세 줄과 약점이 필요). 그러나 일단 변경을 한 후에는 한 파일로 글로벌 정의를 작성하고이를 연결하여 기능을 쉽게 무시할 수 있습니다.

함수를 래핑하거나 교체하여 기존 코드베이스의 동작을 수정하는 것이 종종 바람직합니다. 해당 함수의 소스 코드를 편집하는 것은 실행 가능한 옵션이므로 이는 간단한 프로세스가 될 수 있습니다. 함수의 소스를 편집 할 수없는 경우 (예 : 시스템 C 라이브러리에서 기능을 제공하는 경우) 대체 기술이 필요합니다. 여기서는 UNIX, Windows 및 Macintosh OS X 플랫폼에 대한 이러한 기술을 제시합니다.

이것은 OS X, Linux 및 Windows에서 수행 된 방식을 다루는 훌륭한 PDF입니다.

여기에는 문서화되지 않은 놀라운 트릭이 없습니다 (이것은 놀라운 응답 세트 BTW) ...하지만 멋진 읽기입니다.

Daniel S. Myers와 Adam L. Bazinet의 Windows, Unix 및 Macintosh OS X Platforms (2004)에서 임의의 기능을 가로 채기.

당신은 할 수 있습니다 대체 위치에서 직접 PDF를 다운로드하십시오 (중복성).

그리고 마지막으로, 이전 두 출처가 어떻게 든 화염으로 내려 가면 Google 검색 결과는 다음과 같습니다.

함수 포인터를 글로벌 변수로 정의 할 수 있습니다. 발신자 구문은 변경되지 않습니다. 프로그램이 시작되면 일부 명령 줄 플래그 또는 환경 변수가 로깅을 활성화하도록 설정되어 있는지 확인한 다음 기능 포인터의 원래 값을 저장하고 로깅 함수로 바꾸십시오. 특별한 "로깅 활성화"빌드가 필요하지 않습니다. 사용자는 "현장에서"로깅을 활성화 할 수 있습니다.

발신자의 소스 코드를 수정할 수 있어야하지만 Callee는 아닙니다 (따라서 타사 라이브러리를 호출 할 때 작동합니다).

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에 연결되어 있으며, 호출을 인터셉트하고 라이브러리 #1에서 재정의 된 버전을 호출합니다.

여기서 링크 주문에 매우주의를 기울이지 않으면 작동하지 않습니다.

@Johannes Schaub의 답변을 바탕으로 소유하지 않는 코드에 적합한 솔루션.

별명 약하게 정의 된 함수로 재정의하려는 함수를 한 다음 스스로 상환합니다.

재정의 .H

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

foo.c

function foo() { return 1234; }

재정의 .c

function foo() { return 5678; }

사용 패턴 별 변수 값 컴파일러 플래그를 추가하려면 makefile에서 -include override.h.

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

따로 : 아마도 당신도 사용할 수도 있습니다 -D 'foo(x) __attribute__((weak))foo(x)' 매크로를 정의합니다.

파일을 상환과 함께 컴파일하고 연결하십시오 (override.c).

  • 이를 통해 코드를 수정하지 않고도 모든 소스 파일에서 단일 함수를 재정의 할 수 있습니다.

  • 단점은 재정의하려는 각 파일에 대해 별도의 헤더 파일을 사용해야한다는 것입니다.

당신이 사용할 수 있는 공유 라이브러리(Unix)또는 DLL(윈도우)이 작업을 수행 할 수 있습니다뿐만 아니라(할 수 있고 성능 저하).을 변경할 수 있습 DLL/그래서 얻을 로드(한 버전에 대한 디버깅,하나의 버전에 대한 비 debug).

나는 비슷한 일이 과거에(지 않을 달성하기 위해 무엇을 달성하기 위해 노력하고있다,그러나 기본적인 전제로 동일하다)및 그 밖으로 일했습니다.

[편집 기반으로 OP 코멘트]

에 사실은 이유 중의 하나를 원 무시 기능이기 때문에 나 용의자들은 다르게 행동에 다양한 운영체제 시스템입니다.

다음과 같은 두 가지 일반적인 방법으로(나)의 처리는 공유 라이브러리 dll 방법이나 쓰기 다른 구현하는 당신은 링크에 대한합니다.

모두 솔루션(shared libs 또는 다른 연결)야 foo_linux.c foo_osx.c foo_win32.c(또는 더 좋은 방법은 리눅스/foo.c,osx/foo.c 와 win32/foo.c)다음의 컴파일과 링크에 적절한 하나입니다.

찾고 있다면 모두 다른 코드를 위해 다양한 플랫폼과 디버그 대 릴리스 아일 수 있는 공유 라이브러리 DLL 솔루션으로 대부분의 유연합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top