Статья объясняет идеологию мультисегментных приложений для
<ow:link name="PalmOS" href="ow.asp?PalmOS" date="2003-05-13T18:59:32+01:00">Palm OS</ow:link>
с использованием GCC.
В информатике существует малоизвестный термин ABI - Application Binary Interface. Этим термином называют набор особенностей компьютерной архитектуры, которые нужно учитывать при создании приложения. ABI может накладываться процессором: направление роста стека, размер элемента данных в стеке, порядок байт в слове итд. Также ABI может накладываться операционной системой. Тогда в ABI входит способ вызова системного API, специфика использования адресного пространства и прочие нюансы. Изменения, которые вносятся в GCC для компиляции под
<ow:link name="PalmOS" href="ow.asp?PalmOS" date="2003-05-13T18:59:32+01:00">Palm OS</ow:link>
, нужны именно для соответствия
<ow:link name="PalmOS" href="ow.asp?PalmOS" date="2003-05-13T18:59:32+01:00">Palm OS</ow:link>
ABI.
Что такое приложение с точки зрения
<ow:link name="PalmOS" href="ow.asp?PalmOS" date="2003-05-13T18:59:32+01:00">Palm OS</ow:link>
? Приложение - это база ресурсов. Четыре ресурса из этой базы используются для исполнения программы.
Это ресурс 'code' #1, в котором собственно и хранится исполняемый код, который запускается с нулевого байта ресурса. Также из ресурса 'code' #0 берется размеры области глобальных данных. Из ресурса 'pref' #0 берется размер стека. И ресурс 'data' #0 используется для инициализации глобальных данных. Все! До всего остального содержимого базы приложения
<ow:link name="PalmOS" href="ow.asp?PalmOS" date="2003-05-13T18:59:32+01:00">Palm OS</ow:link>
дела нет. Подробности можно почитать здесь:
<a href="http://www.linuxmafia.com/pub/palmos/development/prc-format.html" class="external" target="_blank">http://www.linuxmafia.com/pub/palmos/development/prc-format.html</a>
Такой способ позволяет запускать программы практически "на месте", выделяя только память под стек и глобальные переменные.
Какие ограничения накладываются на простое односегментное приложение?
Размер ресурса 'code' #0 ограничен 64К. Это ограничение было у
<ow:link name="PalmOS" href="ow.asp?PalmOS" date="2003-05-13T18:59:32+01:00">Palm OS</ow:link>
включая версию 4 и у Хотсинка до версии 5 включительно. Условное ограничение сегмента GCC в 32К лечится заменой скрипта для линкера на text_64k.
</li>
<li>Ограничение смещения у команды вызова процедуры в 32К. То есть, если сегмент больше 32К, то функция из начала не может вызвать функцию в конце сегмента. Это можно обойти путем перемещения вызываемых функций "поближе" к вызываемым или созданием функций-заглушек в середине сегмента.</li>
<li>Ограничение размера глобальных данных программы. Общий объем глобальных данных (в сумме инициализированных и неинициализированных) должен укладываться в 64К. Замечу, что константные инициализированные данные помещаются в сегмент кода.</li>
</ul>
Что усложнится при попытке добавить новый сегмент?
Самая большая проблема - межсегментный вызов процедуры. В случае одного сегмента расстояние между процедурами постоянно и код вызова не меняется после компиляции. В случае межсегментного вызова ресурсы могут располагаться в произвольных участках памяти и смещение нужно корректировать.
Замечу, что в односегментном приложении также происходит коррекция области данных. В нижележащем примере значение переменной pfn будет скорректировано. Это возможно, поскольку сегмент данных находится в динамической памяти. Копировать же все сегменты в динамическую память для коррекции неразумно и расточительно.
<pre class="code">
void foo(void){
}
void *pfn = foo;
</pre>
Все (оба
<img src="ow/images/icons/emoticon-wink.gif" width="14" height="12" alt="" />
) производители компиляторов для
<ow:link name="PalmOS" href="ow.asp?PalmOS" date="2003-05-13T18:59:32+01:00">Palm OS</ow:link>
предлагают свои поддержки мультисегментности.
Gcc предлагает следующий способ:
<li>Всем функциям приписывается атрибут с именем сегмента. Функции помещаются в соответствующие сегменты единственного получаемого ELF-файла.</li>
<li>В области глобальных переменных хранятся адреса всех сегментов</li>
<li>При вызове функции из другого семента происходит сложение смещения функции от начала сегмента к адресу сегмента из глобальной памяти</li>
</ul>
В чем ограничения такого метода:
<li>Использование глобальных данных. Это обозначает, что при вызове программы без глобальных переменных, вызов функций из других сегментов попросту невозможен. При попытке вызвать произойдет обращение к несуществующей области глобалных данных и Fatal Alert неизбежен.</li>
<li>Этот способ никак не расширяет область глобальных данных.</li>
<li>Проблемы с доступом к константным данным. Константные данные могут оказаться в другом сегменте и тебовать наличия глобальных данных.</li>
<li>Неявные функции и методы класса. C++ богат на неявную генерацию неявного кода. Следует контролировать куда будет помещен код темплейтов и не инлайновых инлайнов.</li>
</ul>
Подробное описание мультисегментной архитектуры GCC здесь:
<a href="http://prc-tools.sourceforge.net/doc/prc-tools_3.html" class="external" target="_blank">http://prc-tools.sourceforge.net/doc/prc-tools_3.html</a>
Существует альтернативный способ создания мультисегментных приложений с помощью утилиты multilink. Смотри описание здесь:
<a href="http://www.djw.org/product/palm/multilink/" class="external" target="_blank">http://www.djw.org/product/palm/multilink/</a>
Multilink использует другую идею:
<li>Исходные файлы компилируются как обычно. Не нужно указывать сегменты в исходном коде</li>
<li>Утилита получает на вход объектные файлы и необязательный список разбиения функций по сегментам.</li>
<li>Утилита разбивает все функции по сегментам по списку и добивает оставшиеся до указанного размера сегментов.</li>
<li>Если функция foo из сегмента segA вызывает bar из сегмента srgB, то в segA добавляется заглушка для bar, вызывающая bar.</li>
<li>Для вычисления адреса bar используются данные, которые хранятся в feature memory. Тем самым наличие глобальных данных необязательно.</li>
<li>Полученные сегменты добавляются в программу.</li>
</ul>
У этого решения есть один большой минус - невозможен доступ к константным данным из другого сегмента.
</ow:body>