Как получить указатель на двоичный раздел в MSVC? - PullRequest
11 голосов
/ 28 сентября 2010

Я пишу некоторый код, который хранит некоторые структуры данных в специальном именованном двоичном разделе. Это все экземпляры одной и той же структуры, которые разбросаны по многим файлам C и не входят в область видимости друг друга. Поместив их все в именованный раздел, я могу перебрать их все.

В GCC я использую атрибут * _ ((section (...)) плюс некоторые специально названные внешние указатели, которые магически заполняются компоновщиком. Вот тривиальный пример:

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

Я пытаюсь выяснить, как это сделать в MSVC, но я рисую пробел. Из документации компилятора я вижу, что могу объявить раздел с помощью __pragma (section (...)) и объявить данные, которые будут в этом разделе, с помощью __declspec (allocate (...)), но я не вижу, как получить указатель на начало и конец раздела во время выполнения.

Я видел в Интернете несколько примеров, связанных с выполнением атрибута _ _ ((конструктор)) в MSVC, но это похоже на взлом, специфичный для CRT, а не общий способ получить указатель на начало / конец раздела. У кого-нибудь есть идеи?

Ответы [ 4 ]

7 голосов
/ 09 февраля 2013

Существует также способ сделать это, используя файл сборки.

#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, прагма section создает только раздел. Строки __declspec (allocate ()) сообщают компилятору поместить элемент в этот сегмент.

Со страницы Microsoft: Порядок здесь важен. Названия разделов должны быть не более 8 символов. Разделы с одинаковыми именами перед $ объединяются в один раздел. Порядок их объединения определяется путем сортировки символов после символа $.

Еще один важный момент, о котором следует помнить, это разделы, заполненные от 0 до 256 байтов. Указатели START и END НЕ будут непосредственно до и после, как вы ожидаете.

Если вы установите в своей таблице указатели на функции или другие значения, не равные NULL, будет легко пропустить записи NULL до и после таблицы из-за заполнения раздела

Подробнее см. на этой странице MSDN

4 голосов
/ 07 мая 2011

Прежде всего, вам нужно создать 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.

0 голосов
/ 28 мая 2019

ML64 позволяет значительно снизить монтажный шум:

public foo_start
public foo_stop

.code foo$a
foo_start:

.code foo$z
foo_stop:

end
0 голосов
/ 03 июля 2013

Я немного экспериментировал здесь и пытался реализовать версию без файла сборки, однако боролся со случайным количеством байтов заполнения между разделами, что делает почти невозможным найти начало раздела .init $ u часть, если содержимое не просто указатели или другие простые элементы, которые можно проверить на NULL или какой-либо другой известный шаблон. То, вставлено ли заполнение, похоже, связано с использованием опции отладки Zi. Когда задано, вставка вставляется без, все секции отображаются точно так, как хотелось бы.

...