Подтвердить что ты не робот

Почему адрес 0x400000 выбран как начало текстового сегмента в x86_64 ABI?

В этом документе на стр. 27 говорится, что текстовый сегмент начинается с 0x400000. Почему именно этот адрес был выбран? Есть ли причина для этого? Тот же адрес выбирается в GNU ld на Linux:

$ ld -verbose | grep -i text-segment
  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS;

Это удивительно, потому что этот адрес больше в 32-разрядных исполняемых файлах x86:

$ ld -verbose | grep -i text-segment
  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x08048000)); . = SEGMENT_START("text-segment", 0x08048000) + SIZEOF_HEADERS;

Я читаю этот вопрос, в котором обсуждается, почему был выбран адрес 0x080xxxxx для i386, но это не объясняет изменения в x86_64. Трудно найти любое объяснение по этому вопросу. Кто-нибудь знает?

4b9b3361

Ответ 1

Нижняя строка: некоторые технические ограничения, которые amd64 использует при использовании больших адресов, предлагают выделить нижнее 2GiB адресного пространства для кода и данных для повышения эффективности. Таким образом, стек был удален из этого диапазона.


В i386 ABI 1

  • Стек
  • находится перед кодом, растущим от чуть меньше 0x8048000 вниз. Что обеспечивает "чуть более 128 МБ для стека и около 2 ГБ для текста и данных "(стр. 3-22).
  • Динамические сегменты начинаются с 0x80000000 (2GiB),
  • а ядро ​​занимает "зарезервированную область" наверху, которую спецификация допускает до 1GiB, начиная не менее 0xC0000000 (стр. 3-21) (что обычно делает).
  • Основной программе не требуется независимость от положения.
  • Реализация не требуется для поиска нулевого указателя (стр. 3-21), но разумно ожидать, что для этого будет зарезервировано некоторое пространство стека выше 128MiB (которое равно 288KiB).

amd64 (чей ABI сформулирован как поправка к i386 one (стр. 9)) имеет значительно большую (48-битное) адресное пространство, но большинство инструкций принимают только 32-битные непосредственные операнды (которые включают прямые адреса и смещения в инструкциях перехода), требующие большей работы и менее эффективного кода (особенно при учете взаимозависимости инструкций) для обработки больших значений. Меры по устранению этих ограничений обобщены авторами, введя несколько "кодовых моделей", которые они рекомендуют использовать, чтобы "позволить компилятору генерировать лучший код". (стр. 33)

  • В частности, первая из них "Малая модель кода" предлагает использовать адреса "в диапазоне от 0 до 2 31 -2 24 -1 или от 0x00000000 до 0x7effffff", что позволяет использовать очень эффективные относительные ссылки и итерацию массива. Это 1.98GiB, что более чем достаточно для многих программ.
  • "Модель среднего кода" основана на предыдущем, разбивая данные на "быструю" часть под указанной границей и "более медленную" оставшуюся часть, для которой требуется специальная инструкция для доступа. Пока код остается под границей.
  • И только "большая" модель не делает предположений о размерах, требуя от компилятора "использовать инструкцию movabs, как в среде модель кода, даже для работы с адресами внутри текстового раздела. Кроме того, непрямые ветки необходимы при ветвлении на адреса, чьи смещение от текущего указателя инструкции неизвестно ". Они продолжают предлагать разбиение базы кода на несколько разделяемых библиотек, поскольку эти меры не применяются для относительных ссылок с смещениями, которые, как известно, находятся в пределах границ (как указано в" Малозависимый код модель ").

Таким образом, стек перемещался в разделенное пространство библиотеки (0x80000000000, 128GiB), потому что его адреса никогда не являются непосредственными операндами, всегда ссылаются либо косвенно, либо с помощью lea/mov из другой ссылки, применяются относительные ограничения смещения.


Вышеприведенное объясняет, почему адрес загрузки был перенесен на более низкий адрес. Теперь, почему это было перенесено на 0x400000 (4MiB)? Здесь я пришел пустым, поэтому, обобщая то, что я прочитал в спецификациях ABI, могу только догадываться, что он чувствовал себя "как раз":

  • Он достаточно велик, чтобы уловить любое вероятное неправильное смещение структуры, что позволяет использовать более крупные единицы данных, которые amd64 работает, но достаточно мала, чтобы не тратить значительную часть начального 2GiB адресного пространства.
  • Он равен наибольшему размеру практической страницы на сегодняшний день и является кратным всем другим размерам виртуальной памяти, о которых можно подумать.

1 Обратите внимание, что фактические x32 Linuxes отклоняются от этого макета больше и больше со временем. Но мы говорим о спецификации ABI здесь, поскольку форма amd64 формально основана на ней, а не на любом производном макете (см. Его параграф для цитирования).