2010-06-12 69 views
3

使用C++和GCC,我可以声明一个在内存中使用特定地址的外部变量吗? 类似于特定地址的外部变量

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

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

回答

10

容易的选择:

定义

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

并且指*key别处(或使用的引用)。

无指针选项:

所有实习医生有具体的地址!这些地址在链接时间之前可能不知道,但最终必须解决。如果您声明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; 
} 

如果你想在某些内存区域变量的集合,更合适的方法可能是使用输出部分由尼古拉的建议,可能与自定义脚本ld结合。

+0

'const'指针的好处。 – 2010-06-13 06:28:04

+1

这就是我一直在寻找的东西。 我正在通过使用DLL注入来逆转程序。在我的DLL代码中,我必须访问属于目标进程的变量。为了在没有指针的情况下访问它们,我需要一些方法给extern变量指定一个特定的地址。谢谢! – AndiNo 2010-06-13 19:11:22

+0

太好了,那么如果你想改变变量地址,出于某种原因,你不需要重新编译你的代码,只能重新链接:) – Artelius 2010-06-20 08:25:56

0

由于内存是通过x86平台上的MMU进行虚拟化的,因此它不适用于桌面应用程序(对于设备驱动器中的内存映射I/O,它可能有意义)。所以你不会知道什么物理地址将在虚拟空间中的某处。

除了测试内存I/O或其他一些破解之外,还会有什么用例呢?在用户空间中的x86上,每个内存单元是等效的...要访问变量,请使用其名称dlsym()是Linux下的朋友,Windows下的朋友GetProcAddr()。 AFAIK没有预见到你自己指定的地址。

即使为共享库或DLL提供所谓的首选加载地址也无济于事,因为在与其他共享库重叠的情况下,它可以重新定位到其他位置。现代操作系统中的地址随机化功能使其几乎不可预知(避免可重现的缓冲区溢出攻击的目标是什么)

4

我在GCC文档中找不到此属性。这对通用程序没有意义,因为许多现代系统提供了address space layout randomization。你可以问,我想最好的,是把一个变量插入一个特定部分,如

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

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

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

你忘记了gcc也经常用于内核编程,这将是非常好的。通常重要的存储器映射设备位于特定位置。 – 2010-06-13 03:10:41

+1

在内核中,只有当物理内存映射到内核虚拟地址空间时,这在引导代码中才有意义。即使那样,你是否真的想将物理地址硬编码到源代码中(而不是偏移到某个可配置的基地址)? – 2010-06-13 04:08:46

+0

它仍然偶尔有用,例如对于其虚拟地址通常是固定的UTCB。对于*简单*内核,没有虚拟内存或简单的线性映射,从虚拟到物理。我能想到的唯一优点是这些符号可以在链接时解析(如我的答案所示),因此您不需要重新编译您的目标文件就可以更改地址。 – Artelius 2010-06-13 05:07:22

-2

号现代桌面操作系统使用虚拟内存,这就意味着你拥有的任何地址是没有意义的,直到你把它交给OS,使得特定的内存地址一文不值。桌面/ x86上没有办法也没有这个优势。

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

1

既然你这个标签使用C++,你可以很容易地使用放置新。这是很好的便携:

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

显然,这不会是一个全球性的,因为new可以在功能之外使用。但是你可以很容易地通过一个函数来尽早初始化一些全局变量。

3

你可以只使用宏:

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

,以便任何写入KEY写入到存储位置,并任意读取到KEY从内存位置读取。

如果(如果它代表一个硬件寄存器或某种类型的内存映射I/O EG)内存位置可以外部改变你的控制,那么你应该把它声明volatile

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

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

+0

如果您不喜欢宏,请在我的答案中使用常量指针。 – Artelius 2010-06-13 05:43:50