C测试变量是否在只读部分

时间:2016-01-29 20:11:08

标签: c gcc macros

我想编写一个低级日志记录功能,如下所示:

DO_DBG("some string", val1, val2)

出于性能原因,我想要它做的是将指针存储到字符串而不是字符串的副本。这假设字符串是只读文字。为了防止人们不得不调试调试器,如果DO_DBG的第一个参数位于代码与文本等的可写部分中,编译器可能会抱怨会很好。我想知道是否有一个机制可以做到这一点存在。 (我正在使用gcc 4.9.1,ld 2.24)。

5 个答案:

答案 0 :(得分:8)

您可以使用自动文字字符串连接:

#define DO_DBG(s, a, b) _DO_DBG("" s, a, b)

将您的真实宏实现为_DO_DBG

答案 1 :(得分:2)

您可以设置SIGSEGV处理程序,然后尝试执行以下操作:

s[0] = s[0];

如果处理程序被触发,则意味着该变量位于只读段中,因此您可以保存指针。如果没有,你可以抱怨(或只是复制一下字符串)。

您需要声明字符串volatile,以便编译器不会优化分配。

答案 2 :(得分:0)

或者你可以使用stringify宏构造:

#define DO_DBG(s, a, b) DO_DBG_INTERNAL(# s, a, b)

并调用您的函数DO_DGB_INTERNAL 所以人们会这样做:

DO_DBG(some string, val1, val2)

PS,很可能是一个可变函数和宏在这里是一个好主意。

答案 3 :(得分:0)

您的函数获取指向字符串开头的指针。如果您的操作系统允许它,并且编译器以这种方式设置它,则可以在只读存储器区域中分配常量字符串(如示例中所示)。如果您的编译器是智能的,它将存储一个出现多次的字符串常量的副本。它甚至可能会发现某些字符串从未被使用过,而根本就没有存储它们。

除了上述内容之外,除非您的程序依赖于未定义的行为(在常量字符串上写入),否则您应该无法确定它是否在只读内存中。您可以比较字符串的地址,看它们是否只是一个副本,但就是这样。

在任何合理的 C实现中(即,一个不会过时的方式只是为了让你搞砸了),你将看不到(或者至多是如果字符串是只读,读写,一个或多个,那么性能差异非常小。

如果您使用char *参数编写函数,并将所述指针存储起来(不是复制到指向的字符串),那么 总是会返回相同的字符串(除非你的环境真的很奇怪。)

答案 4 :(得分:0)

这是基于SIGSEGV处理程序的测试,用于检查指向的内存是否映射为只读:

#define _GNU_SOURCE
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <setjmp.h>

static sigjmp_buf label;
static void segv_hndlr(int Sig) { siglongjmp(label,1); } //safe in this ctx
_Bool is_ro(char volatile *X)
{
    _Bool r=0;
    struct sigaction old;
    X[0]; //fault now if the target isn't writable
    if(sigsetjmp(label,1))
        { r=1; goto out; }
    sigaction(SIGSEGV,&(struct sigaction){.sa_handler=segv_hndlr}, &old);
    X[0]=X[0];
out: sigaction(SIGSEGV,&old, NULL);
    return r;
}

//example:
int main()
{
    #define PR_BOOL(X) printf(#X"=%d\n", X)
    char bar[]="bar";
    char *baz = strdup("baz");
    static int const static_ro_int = 42;
    int const auto_ro_int = 42;
    PR_BOOL(is_ro("foo")); //1
    PR_BOOL(is_ro(bar)); //0
    PR_BOOL(is_ro(baz)); //0
    PR_BOOL(is_ro((void*)&static_ro_int)); //1
    PR_BOOL(is_ro((void*)&auto_ro_int)); //0
}