Я пишу код, который хранит некоторые структуры данных в специальном именованном двоичном разделе. Это все экземпляры одной и той же структуры, разбросанные по многим файлам C и не входящие в область действия друг друга. Поместив их все в именованный раздел, я могу перебрать их все.
В GCC я использую _attribute_((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(...)) но я не вижу, как я могу получить указатель на начало и конец раздела во время выполнения.
Я видел несколько примеров в Интернете, связанных с выполнением _attribute_((constructor)) в MSVC, но это похоже на взлом, специфичный для CRT, а не на общий способ получить указатель на начало /конец раздела. У кого-нибудь есть идеи?
Ответ 1 · рейтинг 9 · shimpossible
Существует также способ сделать это без использования файла сборки.
#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 · рейтинг 5 · Ilya Matveychikov
Прежде всего, вам нужно создать 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.
Ответ 3 · don
Здесь я немного поэкспериментировал и попытался реализовать версию без файла сборки, однако столкнулся со случайным количеством байтов заполнения между разделами, из-за чего почти невозможно найти начало части раздела .init$u, если содержимое это не просто указатели или другие простые элементы, которые можно проверить на NULL или какой-либо другой известный шаблон. Вставлено ли заполнение, по-видимому, коррелирует с использованием параметра отладки Zi. При задании отступы вставляются, без них все разделы отображаются именно так, как хотелось бы.