如何获得一个指向MSVC中的二进制部分的指针?

时间:2022-09-01 19:52:02

I'm writing some code which stores some data structures in a special named binary section. These are all instances of the same struct which are scattered across many C files and are not within scope of each other. By placing them all in the named section I can iterate over all of them.

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

In GCC, I use _attribute_((section(...)) plus some specially named extern pointers which are magically filled in by the linker. Here's a trivial example:

在GCC中,我使用_attribute_(())加上一些特殊命名的外部指针,它们被链接器神奇地填充了。这是一个简单的例子:

#include <stdio.h>

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;
}

I'm trying to figure out how to do this in MSVC but I'm drawing a blank. I see from the compiler documentation that I can declare the section using __pragma(section(...)) and declare data to be in that section with __declspec(allocate(...)) but I can't see how I can get a pointer to the start and end of the section at runtime.

我想知道如何在MSVC中做这个但我画的是空白。我从编译器文档中看到,我可以使用__pragma(…)来声明这个部分,并将数据声明到那个带有__declspec的部分中(分配(…)),但是我不知道如何在运行时得到一个指向开始和结束部分的指针。

I've seen some examples on the web related to doing _attribute_((constructor)) in MSVC, but it seems like hacking specific to CRT and not a general way to get a pointer to the beginning/end of a section. Anyone have any ideas?

在MSVC中,我在web上看到了与do _attribute_((构造函数))相关的一些例子,但它似乎是特定于CRT的黑客行为,而不是一种将指针指向某个部分的开始/结束的一般方法。谁有什么好主意吗?

3 个解决方案

#1


4  

There is also a way to do this with out using an assembly file.

还有一种方法可以通过使用程序集文件来实现这一点。

#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;

The first 3 line defines the segments. These define the sections and take the place of the assembly file. Unlike the data_seg pragma, the section pragma only create the section. The __declspec(allocate()) lines tell the compiler to put the item in that segment.

前3行定义了段。这些定义了分段并取代了程序集文件的位置。与data_seg pragma不同,pragma节只创建section。__declspec(分配())行告诉编译器将该项目放到该段中。

From the microsoft page: The order here is important. Section names must be 8 characters or less. The sections with the same name before the $ are merged into one section. The order that they are merged is determined by sorting the characters after the $.

从微软页面:这里的订单很重要。部分名称必须为8个字符或更少。在$被合并成一个部分之前使用相同名称的部分。它们合并的顺序是通过在$之后对字符进行排序来确定的。

Another important point to remember are sections are 0 padded to 256 bytes. The START and END pointers will NOT be directly before and after as you would expect.

另一个要记住的重点是section是0 padd到256字节。开始和结束指针不会像您预期的那样直接在之前和之后。

If you setup your table to be pointers to functions or other none NULL values, it should be easy to skip NULL entries before and after the table, due to the section padding

如果将表设置为指向函数的指针或其他没有空值的指针,那么应该很容易在表之前和之后跳过空项,因为部分填充。

See this msdn page for more details

有关更多细节,请参见此msdn页面。

#2


3  

First of all, you'll need to create an ASM-file containing all the sections you are interested (for ex., section.asm):

首先,您需要创建一个包含所有您感兴趣的部分的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

Next, in your code you can use the following:

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

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

This gives such a MAP-file:

这就给出了这样一个映射文件:

 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

So, as you can see it, the section with the name .init$u is placed between .init$a and .init$z and this gives you ability to get the pointer to the begin of the data via __InitSectionStart symbol and to the end of data via __InitSectionEnd symbol.

因此,正如你所看到的,名称为.init$u的部分被放置在.init$a和。init$z之间,这使您能够通过__InitSectionStart符号和通过__InitSectionEnd符号获取数据的开头的指针。

#3


0  

I was experimenting here a bit and tried to implement the version without an assembly file, however was struggling with the random number of padding bytes between the sections, which makes it almost impossible to find the start of the .init$u section part if content isn't just pointers or other simple items that could be checked for NULL or some other known pattern. Whether padding is inserted seems to correlate with the use of debug option Zi. When given, padding is inserted, without, all sections appear exactly in the way one would like to have them.

我尝试这一点,并试图实现的版本没有一个汇编文件,不过是在部分之间的填充字节的随机数,这使得它几乎不可能找到.init u美元部分的开始部分如果内容不仅仅是指针或其他简单的物品,可以检查NULL或其他一些已知的模式。是否插入填充似乎与调试选项Zi的使用有关。在给定的情况下,填充是插入的,没有,所有的部分都以一个人想要的方式出现。

#1


4  

There is also a way to do this with out using an assembly file.

还有一种方法可以通过使用程序集文件来实现这一点。

#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;

The first 3 line defines the segments. These define the sections and take the place of the assembly file. Unlike the data_seg pragma, the section pragma only create the section. The __declspec(allocate()) lines tell the compiler to put the item in that segment.

前3行定义了段。这些定义了分段并取代了程序集文件的位置。与data_seg pragma不同,pragma节只创建section。__declspec(分配())行告诉编译器将该项目放到该段中。

From the microsoft page: The order here is important. Section names must be 8 characters or less. The sections with the same name before the $ are merged into one section. The order that they are merged is determined by sorting the characters after the $.

从微软页面:这里的订单很重要。部分名称必须为8个字符或更少。在$被合并成一个部分之前使用相同名称的部分。它们合并的顺序是通过在$之后对字符进行排序来确定的。

Another important point to remember are sections are 0 padded to 256 bytes. The START and END pointers will NOT be directly before and after as you would expect.

另一个要记住的重点是section是0 padd到256字节。开始和结束指针不会像您预期的那样直接在之前和之后。

If you setup your table to be pointers to functions or other none NULL values, it should be easy to skip NULL entries before and after the table, due to the section padding

如果将表设置为指向函数的指针或其他没有空值的指针,那么应该很容易在表之前和之后跳过空项,因为部分填充。

See this msdn page for more details

有关更多细节,请参见此msdn页面。

#2


3  

First of all, you'll need to create an ASM-file containing all the sections you are interested (for ex., section.asm):

首先,您需要创建一个包含所有您感兴趣的部分的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

Next, in your code you can use the following:

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

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

This gives such a MAP-file:

这就给出了这样一个映射文件:

 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

So, as you can see it, the section with the name .init$u is placed between .init$a and .init$z and this gives you ability to get the pointer to the begin of the data via __InitSectionStart symbol and to the end of data via __InitSectionEnd symbol.

因此,正如你所看到的,名称为.init$u的部分被放置在.init$a和。init$z之间,这使您能够通过__InitSectionStart符号和通过__InitSectionEnd符号获取数据的开头的指针。

#3


0  

I was experimenting here a bit and tried to implement the version without an assembly file, however was struggling with the random number of padding bytes between the sections, which makes it almost impossible to find the start of the .init$u section part if content isn't just pointers or other simple items that could be checked for NULL or some other known pattern. Whether padding is inserted seems to correlate with the use of debug option Zi. When given, padding is inserted, without, all sections appear exactly in the way one would like to have them.

我尝试这一点,并试图实现的版本没有一个汇编文件,不过是在部分之间的填充字节的随机数,这使得它几乎不可能找到.init u美元部分的开始部分如果内容不仅仅是指针或其他简单的物品,可以检查NULL或其他一些已知的模式。是否插入填充似乎与调试选项Zi的使用有关。在给定的情况下,填充是插入的,没有,所有的部分都以一个人想要的方式出现。