如何在MSVC中获取指向二进制部分的指针?

我正在编写一些代码,它将一些数据结构存储在一个特殊的命名二进制部分中。 这些是同一结构的所有实例,它们分散在许多C文件中,并且不在彼此的范围内。 通过将它们全部放在命名区域中,我可以遍历所有这些区域。

在GCC中,我使用_ attribute _((section(…))加上一些特别命名的extern指针,这些指针由链接器神奇地填充。这是一个简单的例子:

#include  extern int __start___mysection[]; extern int __stop___mysection[]; static int x __attribute__((section("__mysection"))) = 4; static int y __attribute__((section("__mysection"))) = 10; static int z __attribute__((section("__mysection"))) = 22; #define SECTION_SIZE(sect) \ ((size_t)((__stop_##sect - __start_##sect))) int main(void) { size_t sz = SECTION_SIZE(__mysection); int i; printf("Section size is %u\n", sz); for (i=0; i < sz; i++) { printf("%d\n", __start___mysection[i]); } return 0; } 

我试图找出如何在MSVC中做到这一点,但我画了一个空白。 我从编译器文档中看到我可以使用__pragma(section(…))声明该部分,并使用__declspec(allocate(…))声明数据在该部分中,但我看不出我怎么能得到在运行时指向节的开始和结束的指针。

我在Web上看到了一些与在MSVC中执行_ attribute _((构造函数))相关的示例,但它似乎是特定于CRT的黑客攻击,而不是获取指向段的开头/结尾的指针的一般方法。 有人有主意吗?

还有一种方法可以使用汇编文件来完成此操作。

 #pragma section(".init$a") #pragma section(".init$u") #pragma section(".init$z") __declspec(allocate(".init$a")) int InitSectionStart = 0; __declspec(allocate(".init$z")) int InitSectionEnd = 0; __declspec(allocate(".init$u")) int token1 = 0xdeadbeef; __declspec(allocate(".init$u")) int token2 = 0xdeadc0de; 

前3行定义了段。 这些定义了部分并取代了程序集文件。 与data_seg pragma不同,section pragma仅创建节。 __declspec(allocate())行告诉编译器将该项放入该段中。

从微软页面:这里的顺序很重要。 部分名称不得超过8个字符。 $之前具有相同名称的部分合并为一个部分。 它们合并的顺序是通过对$后面的字符进行排序来确定的。

要记住的另一个要点是段填充为256字节。 START和END指针不会像你期望的那样直接在之前和之后。

如果将表设置为指向函数或其他非NULL值的指针,则应该很容易在表之前和之后跳过NULL条目,因为部分填充

有关更多详细信息,请参见此msdn页面

首先,您需要创建一个包含您感兴趣的所有部分的ASM文件(例如,section.asm):

 .686 .model flat PUBLIC C __InitSectionStart PUBLIC C __InitSectionEnd INIT$A SEGMENT DWORD PUBLIC FLAT alias(".init$a") __InitSectionStart EQU $ INIT$A ENDS INIT$Z SEGMENT DWORD PUBLIC FLAT alias(".init$z") __InitSectionEnd EQU $ INIT$Z ENDS END 

接下来,在您的代码中,您可以使用以下内容:

 #pragma data_seg(".init$u") int token1 = 0xdeadbeef; int token2 = 0xdeadc0de; #pragma data_seg() 

这给出了这样一个MAP文件:

  Start Length Name Class 0003:00000000 00000000H .init$a DATA 0003:00000000 00000008H .init$u DATA 0003:00000008 00000000H .init$z DATA Address Publics by Value Rva+Base Lib:Object 0003:00000000 ?token1@@3HA 10005000 dllmain.obj 0003:00000000 ___InitSectionStart 10005000 section.obj 0003:00000004 ?token2@@3HA 10005004 dllmain.obj 0003:00000008 ___InitSectionEnd 10005008 section.obj 

因此,正如您所看到的,名称.init$u部分位于.init$a.init$z ,这使您能够通过__InitSectionStart符号获取指向数据开头的指针,并且通过__InitSectionEnd符号结束数据。

我在这里进行了一些实验并试图在没有汇编文件的情况下实现该版本,但是在部分之间的随机数量的填充字节中苦苦挣扎,这使得几乎不可能找到.init $ u部分的开头如果内容不只是指针或其他可以检查NULL或其他已知模式的简单项。 是否插入填充似乎与使用调试选项Zi相关联。 给定时,插入填充,而不是,所有部分都完全按照想要的方式显示。