Я ищу способ переопределить набор функций POSIX, но затем завершить переопределение вызовом исходной функции. Идея состоит в том, что я пытаюсь создать слой, который может ограничивать то, какие API-интерфейсы ОС могут вызываться в зависимости от того, какой «профиль» активен. Этот «профиль» определяет, какой набор функций разрешен, и любые не указанные функции не должны использоваться.
Например, если в одном профиле мне не разрешено использовать strcpy, я хотел бы иметь возможность вызвать ошибку времени компиляции (через static_assert) или вывести что-то на экран с надписью «strcpy не разрешен в этом профиле», например ниже:
MY_string.h
#include <string.h>
char *strcpy(char *restrict s1, const char *restrict s2)
{
#if defined(PROFILE_PASS_THROUGH)
printf("strcpy is not allowed in this profile\n");
return strcpy(s1, s2);
#elif defined(PROFILE_ERROR)
static_assesrt(0, "strcpy is not allowed in this profile\n");
return 0;
#else
return strcpy(s1, s2);
#endif
}
Таким образом, в main.cpp я могу использовать MY_string.h
#define PROFILE_PASS_THROUGH
#include "MY_string.h"
int main()
{
char temp1[10];
char temp2[10];
sprintf(temp2, "Testing");
if (0 = strcpy(temp1, temp2))
{
printf("temp1 is %s\n", temp1);
}
return 0;
}
Теперь я понимаю, что код, который я написал выше, не будет компилироваться должным образом из-за переопределения strcpy, но есть ли способ разрешить такую функциональность, не играя с макросами и не создавая мои собственные стандартные библиотеки c и c ++?
Вы можете написать препроцессор, который изменяет вызовы стандартной подпрограммы на вызовы вашей собственной подпрограммы. Такой препроцессор может быть сложным, в зависимости от того, нужно ли вам распознать полную грамматику C ++, чтобы различать вызовы, используя пространства имен и т. Д., Или вы можете избежать случайного распознавания вызовов.
Вы можете связать с вашей собственной библиотекой, создав перемещаемый объектный модуль с разделенными разрешенными именами. Ваша библиотека будет содержать процедуры со стандартными именами, такими как strcpy
, которые выполняют любой код, который вы хотите, и называть другие имена, такие как Mystrcpy
, Созданный этим объектный модуль затем связывается со второй библиотекой и со стандартной библиотекой. Вторая библиотека содержит подпрограммы с этими именами, такие как Mystrcpy
, которые называют оригинальные имена библиотек strcpy
, Детали для этого, конечно, зависят от вашего компоновщика. Цель состоит в том, чтобы иметь такую цепочку: Исходные кодовые вызовы strcpy
, Это разрешено до версии strcpy
в первой библиотеке. Эта версия призывает Mystrcpy
, Mystrcpy
вызывает стандартную библиотеку strcpy
,
Вы можете скомпилировать сборку и отредактировать имена в сборке, чтобы вызывать ваши подпрограммы вместо стандартных подпрограмм библиотеки.
В некоторых системах вы можете использовать dlsym
и другие функции, определенные в <dlfcn.h>
загрузить динамическую библиотеку, которая содержит стандартные реализации, и вызвать их через указатели, возвращаемые dlsym
вместо обычных имен в исходном коде.
Линкер GCC имеет --wrap
переключатель, который разрешает звонки foo
к вашей рутине __wrap_foo
и разрешает звонки __real_foo
(который вы бы использовали в своей реализации) к реальному foo
,
Смотрите также Перехват произвольных функций на платформах Windows, UNIX и Macintosh OS X.
Нет, не может быть сделано в C ++. То, что вам нужно, больше похоже на язык LISP (или его производный), где вы можете взять слот для существующей функции и «переопределить ее на месте», что потенциально может привести к возврату к исходной реализации.
Типичный способ сделать это в Unix — через LD_PRELOAD, пример (Unix) ниже проксирует вызов функции, в частности malloc (полный пример):
/**
* malloc() direct call
*/
inline void * libc_malloc(size_t size)
{
typedef void* (*malloc_func_t)(size_t);
static malloc_func_t malloc_func = (malloc_func_t) dlsym(RTLD_NEXT, "malloc");
return malloc_func(size);
}
В вашем MY_String.h
:
... blah blah
using mynamespace::strcpy;
#endif // header guard or maybe not there if using pragma
тогда все strcpys без префикса std :: будут использовать ваши. Если вы ДЕЙСТВИТЕЛЬНО хотите запретить их, возьмите с собой дробовик, когда найдете человека, который его использовал.
Если вы используете какой-то недавний GCC (например, версию 4.7 или новее), вы также можете написать GCC плагин или Расширение GCC в ПЛАВИТЬСЯ заменить каждый звонок strcpy
к себе mystrcpy
, Это, вероятно, займет у вас некоторую работу (возможно, дни, а не часы), но имеет огромное преимущество для работы внутри компилятора над внутренними представлениями компилятора GCC (Gimple). Так что это будет сделано даже после встраивания и т. Д. И так как вы расширяете компилятор, вы можете адаптировать его поведение к тому, что вы хотите.
MELT — это предметно-ориентированный язык для расширения GCC. Он предназначен для таких задач.
Вы не можете избежать вызова этих функций.
C++
Программа может делать все, что захочет, может иметь некоторый код, который загружает strcpy
символ из libc и запускает его. Если злонамеренный разработчик захочет вызвать эту функцию, у вас нет способа ее избежать. Для этого вам необходимо запустить код C ++ в какой-то специальной среде (в песочнице или на виртуальной машине), но, боюсь, такая технология недоступна.
Если вы доверяете разработчикам и просто ищете способ напомнить им, что они не должны вызывать определенные функции, тогда может быть какое-то решение.
Одним из решений может быть избежание #include
заголовки libc (например, cstring
), и включайте только свои собственные заголовочные файлы, в которых вы объявили только нужные функции.
Другим решением может быть поиск скомпилированного исполняемого файла, чтобы выяснить, какие функции вызываются, или LD_PRELOAD
библиотека, которая переопределяет (и, следовательно, переопределяет) стандартные функции, чтобы заставить их печатать предупреждение во время выполнения.
Вот как бы вы изменили MY_string.h
#include <cstring>
namespace my_functions{
char *strcpy(char *s1, const char *s2)
{
#if defined(PROFILE_PASS_THROUGH)
printf("strcpy is not allowed in this profile\n");
return std::strcpy(s1, s2);
#elif defined(PROFILE_ERROR)
static_assert(0, "strcpy is not allowed in this profile\n");
return 0;
#else
return std::strcpy(s1, s2);
#endif
}
}
using namespace my_functions;
Чтобы это работало, вы не можете включить или использовать пространство имен std;