Почему сегментацию нельзя полностью отключить?

Согласно AMD manual сегментация не может быть отключена. Мой вопрос, почему, почему это невозможно? Еще вопрос, там написано, что 64-битная отключает, что это значит? Сегментация полностью отключена в 64-битном режиме?

Руководство AMD: https://s7.postimg.cc/hk15o6swr/Capture.png


person Keet Kate    schedule 13.04.2018    source источник
comment
Сегментация по-прежнему используется в качестве механизма для процессора x86-64, чтобы узнать, работать ли в 32-битном или 64-битном режиме. (Бит L в дескрипторе сегмента, для которого вы установили CS. x86-64_Changes) Таким образом, x86-64 переключается между длинным режимом и режимом совместимости с помощью jmp far для нового сегмента кода или с помощью iret или других вещей, которые изменяют CS:RIP, а не только RIP. Вместо того, чтобы изобретать новый механизм для этого, они просто использовали существующий материал сегмента, потому что ЦП все еще должен поддерживать его для устаревшего режима.   -  person Peter Cordes    schedule 13.04.2018
comment
Связано: stackoverflow.com/a/50407135/7194773   -  person Lewis Kelsey    schedule 21.02.2021


Ответы (1)


Вступление

В 64-битном режиме всякий раз, когда ненулевой селектор сегмента загружается в любой из сегментных регистров, процессор автоматически загружает соответствующий дескриптор сегмента в скрытую часть сегментного регистра, как и в защищенном режиме/режиме совместимости. Однако дескрипторы сегментов, выбранные селекторами DS, ES или SS, полностью игнорируются. Также игнорируются поля ограничений и атрибутов дескрипторов сегментов, выбранных селекторами FS и GS.

Руководство Intel V3 3.4.4:

Поскольку регистры сегментов ES, DS и SS не используются в 64-битном режиме, их поля (база, лимит и атрибут) в регистрах дескриптора сегмента игнорируются. Некоторые формы инструкций по загрузке сегмента также недействительны (например, LDS, POP ES). Вычисления адресов, которые ссылаются на сегменты ES, DS или SS, обрабатываются так, как если бы основание сегмента было равно нулю.

...

В 64-битном режиме доступ к памяти с использованием переопределений FS-сегментов и GS-сегментов не проверяется на ограничение времени выполнения и не подвергается проверке атрибутов.

Помимо этого, предполагается, что базовый адрес каждого из этих сегментов равен 0, а длина равна 264. Однако некоторые части дескрипторов сегментов, выбранные с помощью селекторов CS, FS или GS, по-прежнему действуют. В частности, используются базовые адреса FS и GS, указанные в их соответствующих дескрипторах.

Руководство Intel V3 3.4.4:

Когда переопределения сегментов FS и GS используются в 64-битном режиме, их соответствующие базовые адреса используются при расчете линейного адреса.

Кроме того, используются следующие поля дескриптора CS: D (бит по умолчанию), L (64-битный бит подрежима), AVL (биты ОС), P (текущий бит), DPL (биты уровня привилегий), S (системный бит), D/C (бит данных/кода) и C (бит соответствия). Обратите внимание, что базовый адрес CS зафиксирован на 0, а длины CS, FS и GS зафиксированы на 264. Как указал Питер в своем комментарии, биты L и D дескриптора CS необходимы для возможности переключения между различными подрежимами длинного режима. Другие активные поля CS также полезны. Поддержка разных базовых адресов для FS и GS полезна для таких вещей, как локальное хранилище потока.

Руководство Intel V3 5.2.1:

Сегменты кода продолжают существовать в 64-битном режиме, даже несмотря на то, что для вычислений адресов основание сегмента считается нулевым. Некоторое содержимое дескриптора сегмента кода (CS) (поля базового адреса и лимита) игнорируется; остальные поля функционируют нормально (кроме читаемого бита в поле типа).

Дескрипторы и селекторы сегментов кода необходимы в режиме IA-32e для установки режима работы процессора и уровня привилегий выполнения.

Я думаю, что и читаемый бит, и доступный бит игнорируются в 64-битном режиме. Эти атрибуты заменяются соответствующими атрибутами в структурах страниц. Хотя я не мог найти нигде в руководстве Intel, где говорится, что доступный бит игнорируется. Но в руководстве AMD это четко указано.

Проверки пределов таблицы дескрипторов по-прежнему выполняются.

Руководство Intel V3 5.3.1:

В 64-битном режиме процессор не выполняет проверку ограничений времени выполнения для кода или сегментов данных. Однако процессор проверяет пределы таблицы дескрипторов.

Таким образом, можно сказать, что сегментация полностью отключена для сегментов DS, ES и SS. Но не совсем для остальных трех сегментов. Вот что означает segmentation cannot be completely disabled.

Руководство Intel V2 говорит об обратном

Цитирую описание инструкции POP.

Исключения 64-битного режима

#GP(0) Если адрес памяти имеет неканоническую форму.
#SS(0) Если адрес стека имеет неканоническую форму.
#GP(селектор) Если дескриптор находится вне ограничение таблицы дескрипторов.
Если загружается регистр FS или GS, а сегмент, на который он указывает, не является сегментом данных или читаемого кода.
Если регистр FS или GS загружается, и указанный сегмент является сегментом данных или несоответствующим кодом, но и RPL, и CPL больше, чем DPL.
#AC(0) Если во время выравнивания делается невыровненная ссылка на память проверка включена.
#PF(fault-code) Если происходит ошибка страницы.
#NP Если загружается регистр FS или GS, а сегмент, на который он указывает, помечен как отсутствующий.
#UD Если используется префикс LOCK.

Обратите внимание, что POP для DS, ES, SS недействительны в 64-битном режиме и нет POP CS. Вот почему он говорит только о FS и GS. Хотя это подразумевает, что атрибуты дескрипторов, выбранных FS и GS, не игнорируются полностью.

Точно так же в описании инструкции MOV говорится:

Исключения 64-битного режима

#GP(0)
Если адрес памяти имеет неканоническую форму.
Если сделана попытка загрузить регистр SS с селектором сегмента NULL, когда CPL = 3.
Если сделана попытка загрузить регистр SS с селектором сегмента NULL, когда CPL ‹ 3 и CPL ≠ RPL.
#GP(селектор)
Если индекс селектора сегмента выходит за пределы таблицы дескрипторов. Если доступ к памяти для таблицы дескрипторов неканонический.
Если загружается регистр SS, а RPL селектора сегмента и DPL дескриптора сегмента не равны CPL.
Если загружается регистр SS а сегмент, на который указывает, является недоступным для записи сегментом данных.
Если загружается регистр DS, ES, FS или GS, а сегмент, на который указывает, не является сегментом данных или читаемого кода.< br> Если загружается регистр DS, ES, FS или GS, а указанный сегмент является сегментом данных или несоответствующим кодом, но и RPL, и CPL больше, чем DPL.< br> #SS(0) Если адрес стека имеет неканоническую форму.
#SS(селектор) Если регистр SS загружается, а сегмент, на который он указывает, помечен как отсутствующий.
#PF(fault-code) Если происходит сбой страницы.
#AC(0) Если включена проверка выравнивания и выполняется обращение к невыровненной памяти во время текущего vilege level равен 3.
#UD При попытке загрузки регистра CS. Если используется префикс LOCK.

Но обратите внимание, что #NP здесь не встречается! Это предполагает, что текущий бит (P) проверяется только для FS, GS, CS и SS, но не для DS и ES. (Но я думаю, что бит P проверяется для всех сегментов.) Эти цитаты также предполагают, что часть RPL селектора любого сегментного регистра также используется.

Селектор нулевого сегмента

Селектор нулевого сегмента — это селектор, значение которого равно 0x0000, 0x0001, 0x0002 или 0x0003. Для процессора все эти значения всегда имеют одинаковый эффект. Все они выбирают один и тот же дескриптор, запись 0 GDT.

Селектор нулевого сегмента не может быть загружен в CS в любом режиме, использующем сегментацию (включая 64-битный режим), потому что CS всегда должен содержать фактический селектор. Попытка сделать это генерирует исключение GP.

Селектор нулевого сегмента может быть загружен в SS в 64-битном режиме (в отличие от других режимов), но только в определенных ситуациях. Дополнительные сведения см. в части «Исключение общей защиты (#GP)» руководства Intel V3 6.15.

Селектор нулевого сегмента можно загрузить в DS, ES, GS и FS.

Руководство Intel V3 5.4.1.1:

В 64-битном режиме процессор не выполняет проверку селекторов сегментов NULL во время выполнения. Процессор не вызывает ошибку #GP при попытке доступа к памяти, где регистр сегмента, на который ссылаются, имеет селектор сегмента NULL.

Я нахожу это очень интересным, как я объясню позже. (Мне также кажется странным, что в главе 3, посвященной сегментации, этого не говорится).

Мне не совсем понятно, загружает ли процессор нулевой дескриптор из памяти в невидимую часть сегментного регистра при загрузке его нулевым селектором.

Руководство Intel V3 3.4.2:

Первая запись GDT не используется процессором.

Означает ли это, что процессор не будет загружать нулевой дескриптор? Или, возможно, это просто означает, что содержимое дескриптора не используется. Позже в 3.4.4 говорится:

Чтобы настроить режим совместимости для приложения, инструкции по загрузке сегментов (MOV в Sreg, POP Sreg) нормально работают в 64-битном режиме. Запись считывается из таблицы системных дескрипторов (GDT или LDT) и загружается в скрытую часть сегментного регистра. База регистра дескриптора, лимит и атрибутивные поля загружены. Однако содержимое селектора сегментов данных и стека, а также регистров дескриптора игнорируется.

Описание инструкции POP из Intel Manual V2 гласит:

64-БИТНЫЙ РЕЖИМ

ЕСЛИ FS или GS загружаются с селектором NULL;
THEN
SegmentRegister ← селектор сегмента;
SegmentRegister ← дескриптор сегмента;
FI;

Описание инструкции MOV из Intel Manual V2 гласит:

ЕСЛИ DS, ES, FS или GS загружаются с селектором NULL
ТО
SegmentRegister ← селектор сегмента;
SegmentRegister ← дескриптор сегмента;
FI;

Это говорит о том, что нулевой дескриптор действительно загружается, но его содержимое игнорируется. Ядро Linux определяет нулевой дескриптор, чтобы все биты были равны нулю. Я читал во многих статьях и учебниках, что это обязательно. Однако Коллинз говорит, что в этом нет необходимости:

Первая запись в глобальной таблице дескрипторов (GDT) называется нулевым дескриптором. Дескриптор NULL уникален для GDT, так как он имеет TI=0 и INDEX=0. В большинстве печатных документов указано, что эта запись в таблице дескрипторов должна быть равна 0. Даже Intel несколько двусмысленно относится к этому вопросу, никогда не говоря, для чего ее НЕЛЬЗЯ использовать. Intel заявляет, что процессор никогда не ссылается на 0-ю запись в таблице дескрипторов.

Насколько я знаю, Intel не накладывает никаких ограничений на содержимое нулевого дескриптора. Так что я думаю, что Коллинз прав.

Чем интересна 5.4.1.1?

Потому что это означает, что можно использовать DS, ES, GS и GS для хранения любой из констант 0x0000, 0x0001, 0x0002 или 0x0003 в 64-битном режиме. Гарантируется, что GDT содержит по крайней мере нулевой дескриптор, поэтому проверка ограничения таблицы дескрипторов будет пройдена (это может быть не так с другими селекторами). Кроме того, все ссылки на любой из этих сегментов по-прежнему будут выполняться успешно. Инструкцию MOV можно использовать для перемещения значения из сегментного регистра в GPR и последующего выполнения над ним операции.

Руководство AMD

Быть написанным.

person Hadi Brais    schedule 13.04.2018
comment
Можно ли сделать недопустимое или доступное только для чтения описание сегмента? Что, если ds относится к этому? Или вы действительно можете использовать ds в качестве (медленного) 16-битного рабочего регистра для произвольных значений в длинном режиме? - person Peter Cordes; 15.04.2018
comment
@PeterCordes Вы можете использовать регистры сегментов DS, ES или SS в качестве чистых регистров в 64-разрядной версии, но есть определенные ограничения, которые сделают использование этого очень трудным. Во-первых, каждый раз, когда значение загружается в любой из этих регистров, ЦП по-прежнему обращается к выбранному 8-байтовому дескриптору и загружает его в невидимую часть сегментного регистра. Однако содержимое дескриптора по-прежнему игнорируется. Это необходимо для поддержки переключения режимов. Это увеличивает производительность... - person Hadi Brais; 15.04.2018
comment
... Во-вторых, селекторы DS, ES или SS должны по-прежнему выбирать дескриптор с допустимым битом Present (P=1) или нулевой дескриптор сегмента (индекс 0 в GDT) (в дескрипторе нулевого сегмента P установлен на 0, насколько это круто?). В противном случае генерируется исключение отсутствия сегмента #NP. В-третьих, выбранный дескриптор должен находиться в пределах пределов GDT или LDT (проверки пределов таблицы дескрипторов по-прежнему выполняются). В противном случае #GP будет брошено вам прямо в лицо... - person Hadi Brais; 15.04.2018
comment
... Любые поля дескриптора, не используемые в 64-битном режиме (включая базу и лимит всех дескрипторов, кроме дескриптора нулевого сегмента я думаю), буквально игнорируются, что означает, что вы сохраняете в этих полях все, что хотите (конечно, пока вы не переключитесь на другой режим). - person Hadi Brais; 15.04.2018
comment
Безусловно, в длинном режиме существует 16-битный защищенный подрежим совместимости. - person Hadi Brais; 15.04.2018
comment
Возможно, я упустил другие побочные эффекты или ограничения на использование DS, ES и SS в качестве временных регистров. К сожалению, руководство Intel не очень точное. Так что трудно быть уверенным. Может быть, @MichaelPetch знает больше. - person Hadi Brais; 15.04.2018
comment
Ах, я забыл, что существует 16-битный защищенный/совместимый режим. Интересно, интересно, поддерживает ли Linux это вообще (для тестирования 16-битных функций кода-гольфа или чего-то еще). Подавляющему большинству 16-битного кода требуется 16-битный реальный режим (или vm86), от которого отказался длинный режим. - person Peter Cordes; 15.04.2018
comment
@PeterCordes См. Руководство Intel, том 3, раздел 3.4.4, для получения частичной информации о том, как работает сегментация в 64-битном режиме. У AMD есть действующий патент, посвященный тому, как должна работать сегментация в 64-битном режиме. Но то, что там написано, не соответствует тому, что написано в руководстве Intel, что заставляет задуматься о том, что написано в текущем руководстве AMD. Было бы очень забавно узнать, что это несовместимо с обоими. смешной. - person Hadi Brais; 15.04.2018
comment
re: DS/ES/SS: В вашем первом предложении должно быть сказано, что ... должны присутствовать (P=1), но другие поля игнорируются. Полное игнорирование — слишком сильное описание. - person Peter Cordes; 15.04.2018
comment
@PeterCordes Несколько месяцев назад я провел небольшое исследование о том, поддерживают ли и как Windows, Linux и macOS 16-битный защищенный или реальный режим. Я нашел много неверной информации и бессмысленных дискуссий по этому поводу, что ввергло меня в депрессию на пару дней. - person Hadi Brais; 15.04.2018
comment
@PeterCordes Кстати, использование сегментных регистров в качестве дополнительных регистров распределителем регистров компилятора может быть хорошей идеей. Я не видел ничего подобного раньше. Я также пытался найти некоторые исследовательские работы, которые предлагают или изучают эту идею, но ничего не нашел. Думаю, это был бы интересный проект. - person Hadi Brais; 15.04.2018
comment
Исправлено полное игнорирование. - person Hadi Brais; 15.04.2018
comment
Использование их в качестве дополнительных временных регистров маловероятно для современных x86, за исключением очень особых случаев (например, отключенный кеш). В противном случае кэш L1d работает быстро, а большинство инструкций поддерживают операнды-источники в памяти, поэтому хранение нескольких значений, предназначенных в основном для чтения, в памяти стека лучше, чем повторное чтение из регистров сегментов при необходимости. Тем не менее, чтение регистров сегментов является дешевым, поэтому их можно использовать для значений, предназначенных в основном для чтения, даже если для копирования значения в регистр GP требуется операция ALU. (например, Conroe/Merom занимает 1 мкп с пропускной способностью 1/такт для mov eax,ds. Агнер говорит, что он работает на p2 (под нагрузкой), хотя?) - person Peter Cordes; 15.04.2018
comment
Протестировано на SKL: mov ecx, ds (8c d9, без префикса размера операнда) составляет 2 моп: 1 для порта 1, один для p0156. - person Peter Cordes; 15.04.2018
comment
@PeterCordes под p0156 вы имеете в виду любой из p0, p1, p5, p6? Но что это за 2 мкп? Выпускаются ли они вместе в одном и том же цикле для p0 и p1? - person Hadi Brais; 15.04.2018
comment
Да, я имею в виду, что другой uop может работать на любом порту ALU. Я не измерял задержку, но, скорее всего, они зависимы. (Измерение задержки затруднено, потому что я не могу придумать, как сформировать цепочку зависимостей, которая включает mov из регистра сегмента, не включая также mov в регистр сегмента, поэтому я не смог бы отделить общая задержка туда/обратно.Может быть, в 32-битном режиме или с помощью fs я мог бы измерить задержку mov-to-Sr с этим и нагрузкой?Другой вариант был бы после остановки конвейера, чтобы победить OoO exec. Или, может быть, сериализующий lfence? Непросто.) - person Peter Cordes; 15.04.2018