1

我正在寻找一种方法来重新定义一组 POSIX 函数,然后通过调用原始函数来结束重新定义。这个想法是我正在尝试创建一个层,该层可以根据哪个“配置文件”处于活动状态来限制可以调用哪些 OS 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++ 库的情况下允许这种功能?

4

7 回答 7

3
  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 平台上截取任意函数

于 2013-05-02T15:41:23.360 回答
0

如果使用一些最新的 GCC(例如 4.7 或更新版本),您还可以在MELT中编写 GCC插件GCC 扩展来替换对您自己的. 这可能需要你做一些工作(可能是几天,而不是几个小时),但在编译器内部工作,在 GCC 编译器的内部表示(Gimple)上工作具有巨大的优势。因此,即使在内联等之后也会完成。而且由于您扩展了编译器,您可以根据需要调整其行为。strcpymystrcpy

MELT 是一种扩展 GCC 的领域特定语言。它专为此类任务而设计。

于 2013-05-02T15:45:19.343 回答
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;

为此,您不能包含或使用 using namespace std;

于 2013-05-02T16:18:11.603 回答
0

您无法避免调用这些函数。

一个C++程序可以做它想做的任何事情,它可以有一些代码strcpy从 libc 加载符号并运行它。如果恶意开发者想要调用该函数,您将无法避免。为此,您需要在某些特殊环境中(在沙箱或虚拟机中)运行 C++ 代码,但恐怕这种技术不可用。

如果您信任开发人员,并且您只是在寻找一种方法来提醒他们不要调用某些函数,那么可能会有一些解决方案。

一种解决方案可能是避免使用#includelibc 头文件(如cstring),并且只包含您自己的头文件,其中您只声明了所需的函数。

另一种解决方案可能是查看已编译的可执行文件以找出调用了哪些函数,或者查看LD_PRELOAD重新定义(并因此覆盖)标准函数以使其在运行时打印警告的库。

于 2013-05-02T15:56:49.427 回答
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);
}
于 2013-05-02T15:31:50.453 回答
0

不,不能在 C++ 中完成。您想要的更类似于 LISP(或衍生)语言,您可以在其中获取现有函数的插槽并“在适当位置覆盖它”,可能会退回到原始实现。

于 2013-05-02T15:26:00.597 回答
0

在你的MY_String.h

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

那么所有不以 std:: 为前缀的 strcpys 都将使用你的。如果您真的想禁止它们,请在找到使用它的人时使用 grep 并随身携带霰弹枪。

于 2013-05-02T15:36:29.980 回答