Как получить указатель на двоичный раздел в MSVC?
Я пишу код, который хранит некоторые структуры данных в специальном двоичном разделе. Это все экземпляры одной и той же структуры, которые разбросаны по многим файлам C и не находятся в пределах друг от друга. Поместив их все в именованный раздел, я могу перебрать их все.
В GCC я использую _attribute _ ((раздел (...)) плюс некоторые специально названные указатели extern, которые магически заполнены компоновщиком. Здесь тривиальный пример:
#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 (...)), но я не вижу, как я могу получить указатель на начало и конец раздела во время выполнения.
Я видел несколько примеров в Интернете, связанных с выполнением _attribute _ ((constructor)) в MSVC, но он похож на хакерство, специфичное для CRT, а не на общий способ получить указатель на начало/конец раздела. У кого-нибудь есть идеи?
Ответы
Ответ 1
Есть также способ сделать это без использования файла сборки.
#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 для более подробной информации
Ответ 2
Прежде всего, вам нужно создать 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 [email protected]@3HA 10005000 dllmain.obj
0003:00000000 ___InitSectionStart 10005000 section.obj
0003:00000004 [email protected]@3HA 10005004 dllmain.obj
0003:00000008 ___InitSectionEnd 10005008 section.obj
Итак, как вы можете видеть, раздел с именем .init$u
помещается между .init$a
и .init$z
, и это дает вам возможность получить указатель на начало данных с помощью символа __InitSectionStart
и до конца данных с помощью символа __InitSectionEnd
.
Ответ 3
Я немного экспериментировал и пытался реализовать версию без файла сборки, однако боролся со случайным количеством байтов заполнения между разделами, что делает невозможным найти начало секции .init $u part, если контент - это не просто указатели или другие простые элементы, которые можно проверить на NULL или какой-либо другой известный шаблон.
Является ли добавление вставленным, похоже, коррелирует с использованием опции отладки Zi. Когда задано, добавляется дополнение, без, все разделы отображаются точно так, как хотелось бы иметь их.
Ответ 4
ML64 позволяет снизить шум при сборке:
public foo_start
public foo_stop
.code foo$a
foo_start:
.code foo$z
foo_stop:
end