переопределение — Как обойти стандартную функцию C ++ при сохранении ее функциональности

Я ищу способ переопределить набор функций 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 ++?

1

Решение

  1. Вы можете написать препроцессор, который изменяет вызовы стандартной подпрограммы на вызовы вашей собственной подпрограммы. Такой препроцессор может быть сложным, в зависимости от того, нужно ли вам распознать полную грамматику C ++, чтобы различать вызовы, используя пространства имен и т. Д., Или вы можете избежать случайного распознавания вызовов.

  2. Вы можете связать с вашей собственной библиотекой, создав перемещаемый объектный модуль с разделенными разрешенными именами. Ваша библиотека будет содержать процедуры со стандартными именами, такими как strcpy, которые выполняют любой код, который вы хотите, и называть другие имена, такие как Mystrcpy, Созданный этим объектный модуль затем связывается со второй библиотекой и со стандартной библиотекой. Вторая библиотека содержит подпрограммы с этими именами, такие как Mystrcpy, которые называют оригинальные имена библиотек strcpy, Детали для этого, конечно, зависят от вашего компоновщика. Цель состоит в том, чтобы иметь такую ​​цепочку: Исходные кодовые вызовы strcpy, Это разрешено до версии strcpy в первой библиотеке. Эта версия призывает Mystrcpy, Mystrcpy вызывает стандартную библиотеку strcpy,

  3. Вы можете скомпилировать сборку и отредактировать имена в сборке, чтобы вызывать ваши подпрограммы вместо стандартных подпрограмм библиотеки.

  4. В некоторых системах вы можете использовать dlsym и другие функции, определенные в <dlfcn.h> загрузить динамическую библиотеку, которая содержит стандартные реализации, и вызвать их через указатели, возвращаемые dlsym вместо обычных имен в исходном коде.

  5. Линкер GCC имеет --wrap переключатель, который разрешает звонки foo к вашей рутине __wrap_foo и разрешает звонки __real_foo (который вы бы использовали в своей реализации) к реальному foo,

Смотрите также Перехват произвольных функций на платформах Windows, UNIX и Macintosh OS X.

3

Другие решения

Нет, не может быть сделано в C ++. То, что вам нужно, больше похоже на язык LISP (или его производный), где вы можете взять слот для существующей функции и «переопределить ее на месте», что потенциально может привести к возврату к исходной реализации.

0

Типичный способ сделать это в 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);
}
0

В вашем MY_String.h:

... blah blah
using mynamespace::strcpy;
#endif // header guard or maybe not there if using pragma

тогда все strcpys без префикса std :: будут использовать ваши. Если вы ДЕЙСТВИТЕЛЬНО хотите запретить их, возьмите с собой дробовик, когда найдете человека, который его использовал.

0

Если вы используете какой-то недавний GCC (например, версию 4.7 или новее), вы также можете написать GCC плагин или Расширение GCC в ПЛАВИТЬСЯ заменить каждый звонок strcpy к себе mystrcpy, Это, вероятно, займет у вас некоторую работу (возможно, дни, а не часы), но имеет огромное преимущество для работы внутри компилятора над внутренними представлениями компилятора GCC (Gimple). Так что это будет сделано даже после встраивания и т. Д. И так как вы расширяете компилятор, вы можете адаптировать его поведение к тому, что вы хотите.

MELT — это предметно-ориентированный язык для расширения GCC. Он предназначен для таких задач.

0

Вы не можете избежать вызова этих функций.

C++ Программа может делать все, что захочет, может иметь некоторый код, который загружает strcpy символ из libc и запускает его. Если злонамеренный разработчик захочет вызвать эту функцию, у вас нет способа ее избежать. Для этого вам необходимо запустить код C ++ в какой-то специальной среде (в песочнице или на виртуальной машине), но, боюсь, такая технология недоступна.

Если вы доверяете разработчикам и просто ищете способ напомнить им, что они не должны вызывать определенные функции, тогда может быть какое-то решение.

Одним из решений может быть избежание #include заголовки libc (например, cstring), и включайте только свои собственные заголовочные файлы, в которых вы объявили только нужные функции.

Другим решением может быть поиск скомпилированного исполняемого файла, чтобы выяснить, какие функции вызываются, или LD_PRELOAD библиотека, которая переопределяет (и, следовательно, переопределяет) стандартные функции, чтобы заставить их печатать предупреждение во время выполнения.

0

Вот как бы вы изменили 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;

0
По вопросам рекламы [email protected]