暂时转义函数范围以在 C 中定义全局符号? (海湾合作委员会)

Temporarily escape function scope to define global symbols in C? (gcc)

在标准 C 中或使用 gcc/gas/binutils/etc 失败时,是否有任何方法可以使用在函数定义主体的语法范围内编写的代码(可能是宏)来定义全局符号?

没有 需要为出现在函数体外部的每个此类符号创建一个单独的宏吗?

为了这个目的,如果我必须明确指定 ELF 部分来放入生成的符号,那很好,如果我必须使用像 .pushsection.popsection 这样的 asm 特性(如果它们可以帮助?)等。任何和所有 gcc 扩展都是公平的游戏。

我想做这件疯狂的事情的原因是跟踪点声明(必须出现在函数体中)可以自动生成跟踪工具可以查找的关联元数据。无需单独预先声明跟踪点,即使其尽可能干燥。

伪代码

void foo(void)
{
    normal_app_code();
    MAGICALLY_DEFINE_A_GLOBAL_VARIABLE_SYMBOL(symboltype, symbolname);
    more_normal_app_code();
}

这样编译的结果就像上面写的一样:

symboltype symbolname;

void foo(void)
{
    normal_app_code();
    some_library_function_that_uses(symbolname);
    more_normal_app_code();
}

上下文

我正在研究增强 systemtap/dtrace 跟踪 API 以支持记录探测参数的数据类型和名称的想法。

我目前的设计要求应用程序作者在顶级(全局)范围内插入额外的宏来定义 stap 运行时将用于发现名称和 arg 类型的探测器的符号,例如人为的:

STAP_PROBE2_ARGNAMES(myprovider, myprobe, foo, bar);
STAP_PROBE2_ARGTYPES(myprovider, myprobe, const char *, MyDataType*);

void something(const char *foo)
{
    MyDataType *bar = get_bar();
    STAP_PROBE2(myprovider, myprobe, foo, bar);
}

这很不方便,而且很容易出错。特别是如果应用程序想要自动生成 STAP_PROBEn(..) 个探测点作为它自己的宏的一部分。

我宁愿在同一站点的跟踪点旁边声明 arg 类型和名称信息,使用预处理器字符串化来捕获 arg 名称(当它们是简单的变量名称标记时)和 __typeof__ 运算符捕获它们的类型名称,例如:

void something(const char *foo)
{
    MyDataType *bar = get_bar();
    STAP_PROBE2_ARGINFO(myprovider, myprobe, foo, bar);
}

或者对于非简单标记表达式参数,例如:

void something(void)
{
    MyDataTypeHolder *barholder = get_barholder();
    STAP_PROBE2_ARGNAMES(myprovider, myprobe,
        something_global->foo, "foo",
        barholder->bar, "bar");
}

STAP_PROBE2_ARGINFO 的宏扩展负责使用适当的字符串化和 _typeof_ 运算符为全局符号 table 中的字符数组生成一对单独的符号,例如伪-ish-c:

#define STAP_PROBE2_ARGINFO(myprovider, myprobe, arg1, arg2) \
    STAP_PROBE2(myprovider, myprobe, (arg1), (arg2)) \
    STAP_PROBE2_ARGTYPES(myprovider, myprobe, (arg1), (arg2))
    STAP_PROBE2_ARGNAMES(myprovider, myprobe, (arg1), (arg2))

#define STAP_PROBE2_ARGTYPES(myprovider, myprobe, argname1, argname2) \
    const char _stapargtypes_#myprovider#_#myprobe[2][] = {#argname1, #argname2};

#define STAP_PROBE2_ARGTYPES(myprovider, myprobe, arg1, arg2) \
    const char _stapargtypes_#myprovider#_#myprobe[2][] = {__typeof__((arg1)), __typeof__((arg2))};

... 和 STAP_PROBE2_ARGNAMES 类似的东西,分别提取和存储显式提供的 arg 名称。

目标是使结果类似于以下的全局声明:

const char _stapargnames_myprovider_myprobe[2][] = {"foo", "bar"};
const char _stapargtypes_myprovider_myprobe[2][] = {"const char *", "MyDataType*"};

并且还在 STAP_PROBE2_ARGINFO(...) 出现的调用点发出探测点本身的常用 asm,就好像它是一个普通的 STAP_PROBE2(...).

疯了吗?

可能吗?

我不明白为什么你不能用 __asm__.pushsection 做到这一点。在 C 中为变量做一个 extern 声明,它在块范围内有效,以便它可以从 C 访问,并将其大小作为整数文字操作数传递给 __asm__。在__asm__里面,你可以定义这个符号,如果你愿意(或者不喜欢),就把它变成.global,并根据传入的大小为它保留space。

另一种可行的方法(虽然略有不同)是

主要的 sudo 代码...但它会给你思路

declare 2 global vars
unsigned char* my_stack;
unsigned long int stack_size=0;


my_function
{
    normal code....

    if(my_stack==NULL)
    {
        my_stack=malloc(size of vars you need to store);
        stack_size=new_size;
    }
    else { my_stack=realloc(my_stack, stack_size + new_vars_size ); }

    my_stack[stack_size+1]=  <-- start pushing your new vars on the stack here

    more normal code....
}

它是全球性的,您可以即时实时进行。你甚至可以把它变成一个函数,让它变得简单和容易。这是一个非常简单的示例,但您可以让它保存变量的名称、变量的类型和值。您还可以使用结构来使其更易于使用……随心所欲。这只是说明了这个想法。

事后看来...对象的链接列表可能是最好的方法。

类似于(更多 sudo 代码):

struct my_var
{
    char name[50];
    int type;
    unsigned char *data;
}

struct my_stack
{
    my_var* previous;
    my_var* current;
    my_var* next;
}