使用C ++和GCC,我可以声明一个在内存中使用特定地址的外部变量吗? 像
这样的东西int key __attribute__((__at(0x9000)));
AFAIK此特定选项仅适用于嵌入式系统。如果在x86平台上有这样的选项,我该如何使用它?
答案 0 :(得分:11)
简易选项:
定义
int * const key = (int *)0x9000;
并在其他地方引用*key
(或使用引用)。
无指针选项:
所有 externs都有特定的地址!这些地址可能在链接时才知道,但最终必须得到解决。如果您声明extern int key;
,那么您必须在链接时提供符号key
的地址。这可以使用链接描述文件(参见Using ld)或链接器命令行,使用--defsym
选项完成。
如果运行gcc,您可以使用-Xlinker
标志将选项传递给链接器。在您的示例中,
gcc -o outfile -Xlinker --defsym -Xlinker key=0x9000 sourcefile.c
由此编制的以下程序输出0x9000
。
#include <stdio.h>
extern int key;
int main(void) {
printf("%p\n", &key);
return 0;
}
如果你想要在某个内存区域中拥有一组变量,那么更合适的方法可能是使用Nikolai建议的输出节,也许与自定义ld
脚本结合使用。
答案 1 :(得分:4)
我在GCC文档中找不到此属性。它对于通用程序没有意义,因为许多现代系统提供address space layout randomization。我想,你可以要求的最好的是将变量放入特定的部分,如
int init_data __attribute__ ((section ("INITDATA")));
另外,如果您知道变量的 [虚拟]地址,为什么不通过指针访问:
int* pkey = ( int* )0x9000;
*pkey = 0xdeadbeef;
答案 2 :(得分:3)
你可以使用一个宏:
#define KEY (*(int*)0x9000)
以便对KEY
的任何写入都写入该内存位置,并且对KEY
的任何读取都从该内存位置读取。
如果该内存位置可能在您的控件之外发生更改(例如,如果它代表硬件寄存器或某种内存映射I / O),那么您应该声明它volatile
:
#define KEY (*(volatile int *)0x9000)
这会强制编译器在每次读取时重新读取内存中的值,并在每次写入时将其重写回内存,而不是将其缓存在寄存器中。
答案 3 :(得分:1)
由于您使用c ++标记了此内容,因此您可以轻松使用placement new。哪个好又便携:
// an object type T at address 0x9000
T* t = new(reinterpret_cast<void*>(0x9000)) T;
显然,这不是全局的,因为new
可以在函数之外使用。但是你可以很容易地拥有一个你尽可能早地调用的函数来以这种方式初始化一些全局变量。
答案 4 :(得分:0)
它不适用于桌面应用程序(对于设备驱动器中的内存映射I / O,它是有意义的)因为内存是在x86平台上的MMU上虚拟化的。所以你不知道虚拟空间中某个物理地址是什么。
除了测试内存I / O或其他一些hack之外,还有什么用例?在用户空间中的x86上,每个内存单元都是等效的...要访问变量,请使用他们的名称dlsym()
是Linux下的朋友,Windows下的GetProcAddr()
。 AFAIK没有预见到自己指定地址。
即使为共享库或DLL提供所谓的首选加载地址也无济于事,因为如果与其他共享库重叠,它可以重新定位到另一个地方。现代操作系统中的地址随机化功能使其几乎无法预测(避免可重现的缓冲区溢出攻击的目标是什么)
答案 5 :(得分:0)
我认为IAR的C编译器使用看起来像
的非标准扩展char foo @ 0x9000;
为此,至少对于他们的MSP430编译器。我从来没有将GCC用于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
答案 6 :(得分:-3)
没有。现代桌面操作系统使用虚拟内存,这意味着无论您拥有什么地址都没有意义,直到您将其提供给操作系统,使特定的内存地址毫无价值。在desktop / x86上没有办法也没有优势。