Если для вашего источника требуется только захват / изменение вызовов, самым простым решением является объединение файла заголовка (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-
), но я не думаю, что это необходимо в вашем случае.
Вы можете переопределить функцию, используя трюк LD_PRELOAD
- см. man ld.so
. Вы компилируете shared lib с помощью своей функции и запускаете двоичный файл (вам даже не нужно изменять двоичный файл!), Например LD_PRELOAD=mylib.so myprog
.
В теле вашей функции (в общей папке) вы пишете как this:
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, без изменения / перекомпиляции программы, чтобы вы могли сделать трюк в программах, для которых у вас нет источника. Разве это не приятно?
Вы можете определить указатель функции как глобальную переменную. Синтаксис вызывающих не изменился. Когда ваша программа запускается, она может проверить, установлен ли флажок командной строки или переменная среды для включения ведения журнала, затем сохраните исходное значение указателя функции и замените его функцией ведения журнала. Вам не понадобится специальная сборка с включенным протоколированием. Пользователи могут включить ведение журнала «в поле».
Вам нужно будет иметь возможность изменять исходный код вызывающего абонента, но не вызываемый (так что это будет работать при вызове сторонних библиотек).
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();
}
С помощью 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()
перенаправляются на вашу функцию обертки (во время соединения). Этот очень полезный флаг, однако, отсутствует в gcc в Mac OS X.
Не забудьте объявить функцию-обертку с помощью extern "C"
, если вы компилируете с g ++, хотя.
--wrap
.
– Daryl Spitzer
6 January 2011 в 21:04
extern char *__real_getObjectName(object *anObject)
.
– Brendan
13 May 2016 в 03:13
Если вы используете 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!
test1.c:
#include <stdio.h>
void test(void) {
printf("overridden!\n");
}
Что он делает?
$ gcc test1.c test.c
$ ./a.out
overridden!
К сожалению, это не сработает для других компиляторов. Но вы можете иметь слабые объявления, которые содержат переопределяемые функции в собственном файле, помещая только включение в файлы реализации API, если вы компилируете с использованием GCC:
weakdecls.h:
__attribute__((weak)) void test(void);
... other weak function declarations ...
functions.c:
/* for GCC, these will become weak definitions */
#ifdef __GNUC__
#include "weakdecls.h"
#endif
void test(void) {
...
}
... other functions ...
Недостатком этого является то, что он не работает полностью , не делая ничего с файлами api (для этих трех строк и слабых делений) , Но как только вы сделали это изменение, функции можно легко переопределить, записав глобальное определение в один файл и связав это.
Также существует сложный способ сделать это в компоновщике с участием двух библиотек-заглушек.
Библиотека # 1 связана с библиотекой хоста и предоставляет переопределенный символ под другим именем.
Библиотека # 2 связана с библиотекой # 1, перехватывает вызов и вызывает переопределенную версию в библиотеке # 1.
Будьте очень осторожны с порядками ссылок здесь или не будет работать.
Часто желательно изменить поведение существующих базовых кодов путем переноса или замены функций. При редактировании исходного кода эти функции являются жизнеспособным вариантом, это может быть прямолинейный процесс. Если источник функций не может быть отредактирован (например, если функции предоставлены библиотекой системы C), тогда требуются альтернативные методы. Здесь мы представляем такие методы для платформ UNIX, Windows и Macintosh OS X.
blockquote>Это отличный PDF-материал, описывающий, как это было сделано в OS X, Linux и Windows.
У этого нет никаких удивительных трюков, которые не были зарегистрированы здесь (это удивительный набор ответов BTW) ... но это приятное чтение.
Вы можете загрузить PDF непосредственно из альтернативного местоположения (для избыточности) .
И, наконец, если предыдущие два источника каким-то образом опустились в пламя, вот результат поиска Google для него .
Вы могли бы использовать общую библиотеку (Unix) или DLL (Windows), чтобы это сделать (это было бы немного штрафом за производительность). Затем вы можете изменить DLL / так, чтобы он загружался (одна версия для отладки, одна версия для не-отладки).
Я делал подобное в прошлом (не для достижения того, что вы пытаетесь но основная идея - одна и та же), и это получилось хорошо.
[Редактировать на основе комментария OP]
На самом деле одна из причин, по которым я хочу переопределить функции связаны с тем, что я подозреваю, что они ведут себя по-разному в разных операционных системах.
blockquote>Существует два общих способа (что я знаю) об этом: общий путь lib / dll или запись различных реализаций с которыми вы ссылаетесь.
Для обоих решений (общие библиотеки или разные ссылки) у вас будет foo_linux.c, foo_osx.c, foo_win32.c (или лучший способ - linux / foo.c, osx / foo.c и win32 /foo.c), а затем скомпилировать и связать с соответствующим.
Если вы ищете оба разных кода для разных платформ И debug -vs-release, я бы, вероятно, был склонен идти с общим lib / DLL, поскольку он является наиболее гибким.
Основываясь на ответе @Johannes Schaub с решением, подходящим для кода, которым вы не владеете.
Псевдоним функции, которую вы хотите переопределить, в слабо определенную функцию, а затем переопределите ее самостоятельно.
override.h
#define foo(x) __attribute__((weak))foo(x)
foo.c
function foo() { return 1234; }
override.c
function foo() { return 5678; }
Используйте pattern-
%foo.o: ALL_CFLAGS += -include override.h
Рядом: Возможно, вы также можете использовать -D 'foo(x) __attribute__((weak))foo(x)'
для определения ваших макросов.
в вашем Makefile, чтобы добавить флаг компилятора -include override.h
.
%foo.o: ALL_CFLAGS += -include override.h
] Скомпилировать и связать файл с вашей повторной реализацией (override.c
).