如何可靠地创建标记对象结束的符号?

时间:2018-08-02 19:42:58

标签: c linker clang

我正在一个项目中,我需要在C中创建许多全局变量的地址范围(不可能使用C ++),在Clang中使用 。对于完整类型的符号,这很容易以符合标准的方式进行:

typedef struct range {
    void* begin;
    void* end;
} range;

extern int foo;
range foo_range = { &(&foo)[0], &(&foo)[1] };

但是正如我所说,它之所以有效是因为C编译器静态知道foo的大小,因此它能够将&(&foo)[1]解析为foo + 4个字节(假设sizeof(int)为4 , 当然)。这不适用于不完整类型的符号:

struct incomplete;
struct trailing_array {
    int count;
    int elements[];
};

extern int foo[];
extern struct incomplete bar;
extern struct trailing_array baz;

range foo_range = { &(&foo)[0], &(&foo)[1] };
// error: foo has incomplete type

range bar_range = { &(&bar)[0], &(&bar)[1] };
// error: bar has incomplete type

range bar_range = { &(&baz)[0], &(&baz)[1] };
// this one compiles, but the range excludes the elements array

但是,对我来说,更多地描述这些符号不是问题。例如,我可以轻松添加元数据:

// foo.h
extern int foo[];
extern size_t foo_size;

// foo.c
int foo[] = {1,2,3};
size_t foo_size = sizeof(foo);

除了对foo.c以外的引用无济于事,因为foo_size不是编译时常量,因此这行不通:

range foo_range = { &foo, (void*)&foo + foo_size };
// error: foo_size not a compile-time constant

将要工作的工作是获得符号的地址,该符号的地址恰好在我的对象结束处。例如,如果我用以下汇编代码定义foo

_foo:
    .long 1
    .long 2
    .long 3
_foo_end:

然后,在我的C代码中,我可以拥有:

extern int foo[];
extern int foo_end;
range foo_range = { &foo, &foo_end };

这有效地解决了我的问题。

但是,尽管我可以灵活地添加符号,但不能灵活地将每个全局声明重写为文件级汇编语句。所以,我的问题是:使用clang,与我最接近的是什么?

  • 我知道我可以使用小节(因为链接程序在小节中使用了开始和结束符号),但是每个全局变量一个小节可能会导致过度杀伤。
  • 我知道我不能仅在要获取其范围的全局变量之后立即获取变量的地址,因为已知在某些情况下编译器会对全局变量进行重新排序。 li>

我专门使用Apple的链接器,但是如果您有适用于GNU ld / gold或lld的解决方案,我仍然会采用它,看看是否也可以在这里使用它。

1 个答案:

答案 0 :(得分:0)

嗯,如果要在另一个翻译单元中定义它,则没有真正的方法。如果需要,可以包含一个types.h文件,其中包含内容

struct incomplete {
    char data[SIZE];
}

SIZE是您想要的任何整数,并且对每个全局变量都执行相同的操作。但是,这将与将来的定义相交。真的,您必须选择

#define INCOMPLETE_SIZE 5

然后将其用于range = { &bar, (void*)&bar + INCOMPLETE_SIZE }

“不完整类型”只是用于正确描述如何解析的一些标准术语

struct A {
     A* ptr;
}

据我所知,它们并没有真正被其他人使用。

我也不推荐使用&(&foo)[0], &(&foo)[1]作为获取一系列指针的方法,因为它非常深奥/难以阅读。首选&foo, &foo + 1。您可以通过执行bar来了解如何将其变成&bar, (void*)&bar + SIZE的解决方案,其中SIZE是您必须在代码中的某处指定的常量(可以通过声明并使用sizeof / &foo+1解决方案,或使用SIZE定义#define