стандартный способ объединения статических объектов

#c #gcc #llvm

#c #gcc #llvm

Вопрос:

Я хочу объединить некоторые объекты со статической связью, без чередования каких-либо других данных. Объекты определены в отдельных исходных файлах. Порядок не имеет значения, если они являются смежными (за исключением минимального / стандартного заполнения).

В gcc я могу достичь этого с помощью __attribute__((section("mydata"))) , но AFAIK это GCC специфично, и его поддерживают только многие компиляторы (gcc, llvm и несколько других, но, я думаю, не msvc), для многих целей (elf да, в других я не уверен, сомневаюсь в этом).

Q1: существует ли стандартный способ достижения этого?

Q2: GCC гарантирует ли это, что объекты будут смежными с заполнением? т.е. объекты могут быть перечислены линейным сканированием с постоянным шагом.

Q3: есть ли разница в поведении между GCC и другими компиляторами, которые поддерживают этот атрибут?

Q4: какие целевые объекты и основные компиляторы не поддерживали бы это?

Ответ №1:

Звучит так, как будто вы пытаетесь создать независимый от платформы код (отсюда и необходимость в стандартном механизме), и хотя у вас есть доступ к исходному коду, вы хотите внести в него лишь ограниченные изменения. Предлагаемое вами решение предполагает доступ к структурам данных, определенным в нескольких единицах преобразования, как если бы они были элементами одного массива.

Q1. Ни в стандарте C, ни в стандарте C не содержится понятия разделов компоновщика, поэтому нет по-настоящему независимого от платформы способа указания имен выходных разделов.

Вам нужно будет проверить наличие каждого компилятора, на который вы хотите настроить таргетинг. Вы уже нашли заклинание для GCC и Clang. В Visual C есть #pragma section и __declspec(allocate) , которые вместе могут достичь того же результата.

Q2. GCC использует компоновщик платформы для сборки исполняемого файла. Предполагая среду GNU, тогда этим компоновщиком будет binutils ld или gold . С помощью этих компоновщиков каждая секция вывода является непрерывной, и выравнивание секций ввода внутри секции вывода может быть указано или разрешено по умолчанию. Согласно документации, значение по умолчанию будет работать для вас при условии, что все ваши объектные файлы были скомпилированы с использованием одной и той же версии GCC, используя одни и те же параметры компилятора. Различные параметры компилятора могут приводить к различным выравниваниям по умолчанию.

В3. Что касается различий в поведении на платформах: ваше использование компоновщика довольно необычно. Вам следует тщательно протестировать каждую платформу, на которую вы ориентируетесь. Я бы предложил создать небольшой двоичный файл, который объединяет объектные файлы так, как вы хотите, и (программно) проверяет, доступны ли структуры данных так, как вы хотите.

Q4. Похоже, ваш подход будет работать для GCC / Clang / binutils в Linux и для Visual C / LINK в Windows. Потребовалось бы немало усилий, чтобы изучить idosyncracies каждой платформы, но я думаю, вы могли бы заставить свой подход работать практически на любой платформе после 1995 года с настоящей клавиатурой. У вас могут возникнуть трудности на старых машинах и небольших встроенных системах. Вероятно, вы сможете запустить это на Android. Я не уверен насчет iOS.

В качестве общего совета, доступ к структурам данных, предполагая, что они являются смежными по границам единиц преобразования, является необычным. Скорее всего, вы столкнетесь со странными проблемами, и вам будет трудно получить помощь. Вот несколько альтернативных механизмов, которые могут дать вам то, что вы хотите, с меньшими затратами для каждой платформы:

  1. Соберите исходные файлы вместе перед компиляцией и скомпилируйте их как единое целое. Если это невозможно сделать вручную, рассмотрите возможность использования препроцессора или создания небольшого скрипта на Perl или Python, который сделает это за вас во время сборки.

  2. Во время выполнения динамически выделите непрерывный раздел памяти и скопируйте в него структуры данных. Если структуры данных небольшие, это не потребует много дополнительной памяти. Если структуры данных большие, память, содержащая исходные структуры, будет выгружена, и это не займет много дополнительной памяти.

  3. Используйте косвенное обращение: предоставьте вашей программе доступ к структурам данных через массив указателей на данные.