特定地址的外部变量

使用C ++和GCC,我可以声明一个在内存中使用特定地址的extern变量吗? 就像是

int key __attribute__((__at(0x9000))); 

AFAIK此特定选项仅适用于嵌入式系统。 如果在x86平台上有这样的选项,我该如何使用它?

简单的选择:

限定

 int * const key = (int *)0x9000; 

并在别处引用*key (或使用参考)。

无指针选项:

所有的外部都有特定的地址! 这些地址可能在链接时才知道,但最终必须得到解决。 如果你声明extern int key; 那么你必须在链接时提供符号key的地址。 这可以使用链接描述文件(请参阅使用ld )或链接器命令行使用--defsym选项完成。

如果运行gcc,您可以使用-Xlinker标志将选项传递给链接器。 在你的例子中,

 gcc -o outfile -Xlinker --defsym -Xlinker key=0x9000 sourcefile.c 

由此编译的以下程序输出0x9000

 #include  extern int key; int main(void) { printf("%p\n", &key); return 0; } 

如果你想要在某个内存区域中拥有一组变量,那么更合适的方法可能是使用Nikolai建议的输出节,也许与自定义ld脚本结合使用。

我在GCC docs中找不到这个属性。 它对于通用程序没有意义,因为许多现代系统提供地址空间布局随机化 。 我想,你可以要求的最好的是将变量放入特定的部分,如

 int init_data __attribute__ ((section ("INITDATA"))); 

此外,如果您知道变量的[虚拟]地址,为什么不通过指针访问:

 int* pkey = ( int* )0x9000; *pkey = 0xdeadbeef; 

你可以使用一个宏:

 #define KEY (*(int*)0x9000) 

因此,对KEY任何写入都会写入该内存位置,而对KEY任何读取都会从该内存位置读取。

如果该内存位置可能在您的控件之外发生更改(例如,如果它代表硬件寄存器或某种内存映射I / O),那么您应该将其声明为volatile

 #define KEY (*(volatile int *)0x9000) 

这将强制编译器在每次读取时重新读取内存中的值,并在每次写入时将其重写回内存,而不是将其缓存在寄存器中。

由于您使用c ++标记了此内容,因此您可以轻松使用placement new。 哪个好又便携:

 // an object type T at address 0x9000 T* t = new(reinterpret_cast(0x9000)) T; 

显然,这不是全局的,因为new可以在函数之外使用。 但是你可以很容易地拥有一个你尽可能早地调用的函数来以这种方式初始化一些全局变量。

它不适用于桌面应用程序(对于设备驱动器中的内存映射I / O,它是有意义的)因为内存是在x86平台上的MMU上虚拟化的。 因此,您不会知道虚拟空间中的某个物理地址。

测试内存I / O或其他一些黑客的用例是什么? 在用户空间的x86上,每个内存单元都是等价的…要访问变量,请使用它们的名称dlsym()是Linux下的朋友,Windows下的GetProcAddr() 。 AFAIK无法预见自己指定地址。

即使为共享的lib或dll提供所谓的首选加载地址也无济于事,因为如果与其他共享库重叠,它可以重新定位到另一个地方。 现代操作系统中的地址随机化function使其几乎无法预测(避免可重现的缓冲区溢出攻击的目标是什么)

我认为IAR的C编译器使用看起来像的非标准扩展

 char foo @ 0x9000; 

为此,至少对于他们的MSP430编译器。 我从来没有将MCC用于MSP430,但我认为它可能也支持它,以便它可以实现与IAR编译器的源兼容性。

如果你想做一些可以在所有编译器上运行的东西而不用搞乱连接器,你必须先做一点努力。

 #define CONST_ADDR_VAR( type, name, address ) type *const name##_addr =(type *)address 

在为每个要指定的变量调用之后,您还需要执行#define var(* var_addr)

如果这可以与之前的宏结合使用会很好,但在宏中定义宏不是标准的。 不过,我认为有一种方法可以用GCC的预处理器来实现。

如果你想跟你的链接器闲聊,那么你可能会找到一种方法来告诉它这些变量的存在位置,并在C程序中将它们用作extern

您也可以使用GCC的__attribute__((section( ... ) ))来执行此操作,但您可能最终需要为要指定其地址的每个变量使用不同的部分。 还有一些其他的事情似乎有点令人困惑,它需要你告诉链接器无论如何这些部分。

http://www.ohse.de/uwe/articles/gcc-attributes.html#var-section

没有。现代桌面操作系统使用虚拟内存,这意味着无论你拥有什么地址都没有意义,直到你把它交给操作系统,使特定的内存地址变得毫无价值。 在desktop / x86上没有办法也没有优势。