【发布时间】:2018-08-02 19:42:58
【问题描述】:
我正在做一个项目,我需要创建许多全局变量的地址范围用 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 最接近的方法是什么?
- 我知道我可以使用节(因为链接器为节创建了开始和结束符号),但是每个全局变量一个节就太过分了。
- 我知道我不能在我想要获取其范围的全局变量之后立即获取变量的地址,因为已知编译器在某些情况下会重新排序全局变量。李>
我专门使用 Apple 的链接器,但如果您有适用于 GNU ld/gold 或 lld 的解决方案,我仍然会采用它,看看我是否也可以让它在这里工作。
【问题讨论】:
-
int elements[]不是扩展,是flexible array member(在 C99 中引入) -
@KeineLust 感谢您提供信息!
-
我认为有必要提供更多有关您正在尝试做的事情的信息。
-
除了一些链接器魔术(如您所说的部分)之外,我看不到任何解决方案。否则就不需要“不完整类型”的概念。
-
(void*)&foo + foo_size是一个问题,因为它试图在void *上进行指针数学运算。(char*)&foo + foo_size会更有意义。