[О блоге] [наверх] [пред] [2022-05-04 17:12:14+03:00] [e2185b7733f55709666d1f4f8a59adcf0eed4506]
Темы: [hate]

amd64, x86_64, x64, intel64

https://en.wikipedia.org/wiki/X86-64
https://sourceware.org/binutils/docs/as/i386_002dISA.html
Ужасно бесит что буквально за пять минут можно встретить в разном софте
разные названия 64-бит x86 архитектуры. Лично для меня amd64 это то что
я пишу и в BSD встречаю. x86_64 это так в GNU/Linux мире принято (просто
создаётся впечатление). А x64... это нечто яростно выбешивающее меня.
Intel64 не встречал прежде, до чтения статьи на Wikipedia.

Оказывается... AMD64 и Intel64 вполне себе разные ISA! Судя по
Wikipedia, всякие штуки типа: не отличающегося на практике (хотя это и
undefined behaviour) значения, формата микрокода, ограничения вне 32-бит
режима -- выглядят как не существенными на практике для пользователя. Но
другие отличия вроде бы выглядят кардинально и серьёзно. Что же получается?
Что на самом деле нет одного amd64 (пускай и с расширениями, типа AVX)?
Есть действительно разные наборы команд и поведения для AMD и Intel
процессоров? Почему же я не встречаю intel64, а только amd64? Или
программистам приходится жутко страдать и делать CPU detection и
удовлетворять оба этих процессора? Или это как-раз то самое: мы работаем
на AMD64, но поддерживаем чётко-заданный-список-CPU? Звучит всё страшно
пугающе несовместимо.

А ещё, бывает, находятся особо умные, кто пишет "IA64", где-то, видимо,
предварительно увидев "IA32". Вот только IA64 это Itanium и совершенно
другая и несовместимая штука.

    [оставить комментарий]
    комментарий 0:
    From: kmeaw
    Date: 2022-05-05 21:27:51Z
    
    > Почему же я не встречаю intel64, а только amd64?
    
    Потому что AMD придумала набор инструкций для своего процессора
    Athlon 64 с микроархитектурой K8, как новую версию K7 (Athlon XP),
    значимыми отличаями которой были перенос контроллера памяти на кристалл
    и те самые 64-битные расширения набора инструкций, названные AMD64.
    
    > Есть действительно разные наборы команд и поведения для AMD и Intel
    > процессоров?
    
    На практике вне системного софта сложно столкнуться с разницей. Из
    забавного вспомнил, что процессоры Intel и AMD отличаются поведением при
    переполнении счётчика инструкций (EIP) - AMD генерирует исключение,
    тогда как Intel просто продолжает выполнять код с (около)нулевого
    адреса. Из-за этой разницы получилось взломать Xbox для запуска Linux
    без необходимости вставлять в загрузчик проприетарный код или
    использования взломанных ключей шифрования (что могло привести к
    нарушению DMCA).
    
    Ещё была проблема с инструкцией SYSRET на процессорах Intel. Эта
    инструкция предназначена для возврата из системного вызова и копирует
    адрес из регистра RCX в RIP, одновременно переключая сегмент кода. Но
    если адрес оказался неканоничным (в диапозоне 0x0000_8000_0000_0000 …
    0xFFFF_7FFF_FFFF_FFFF, иными словами, у которого биты 47…63 не равны
    друг-другу), то будет сгенерировано исключение. Но на процессоре AMD это
    происходит после выхода из привилегированного контекста, а на некоторых
    процессорах Intel - после, что позволяет переписать одно слово в стеке.
    А поскольку обычно SYSRET вызывают из ядра в тот момент, когда уже пора
    обратно переключаться в пользовательский процесс, то у последнего есть
    возможность непосредственно перед SYSCALL использовать произвольный
    указатель на вершину стека просто загрузив его в регистр RSP, который
    уже будет восстановлен к моменту вызова SYSRET, контролируя таким
    образом адрес переписываемого слова. Пришлось и во FreeBSD, и в Linux, и
    в Windows добавлять проверку адреса на каноничность перед SYSRET на
    процессорах Intel.
    
    Другая проблема появилась в процессорах Intel с переносом контроллера
    прерываний APIC внутрь процессора (примерно в момент появления Pentium с
    рабочим напряжением в 3.3 волта). Для настройки контроллера
    использовались memory-mapped регистры по адресу 0xFEE0_0000…0xFEE0_1000.
    Чтобы не сломать совместимость со старыми процессорами на платформах,
    где APIC нет, Intel добавили возможность двигать это четырёхкилобайтное
    окошко в другое место адресного пространства с помощью model-specific
    registers, позволяя производителю оборудования избежать конфликта с
    каким-нибудь другим устройством на шине. А потом на 20 лет все про это
    забыли.
    
    Но есть и другая история, без которой проблемы бы не возникло. Чтобы
    обойти конкурентов, Intel ещё раньше выпустила процессор 386SL,
    предназначенный для ноутбуков - его отличительной особенностью было
    сниженное энергопотребление. Тогда впервые появился новый режим работы
    процессора - SMM, позволяющий прошивке загрузить код и спрятать его от
    ОС. Так, например, можно реализовать уход процессора в сон, когда
    прошивка платформы определяет, что сейчас пользователь ничего полезного
    не делает, без модификации и добавления каких-либо драйверов в ОС. В
    дальнейшем этот режим использовался для ещё более изощрённых хаков -
    эмуляции PS/2-клавиатуры для работы с USB-клавиатурой в DOS.
    
    Вся суть режима SMM в том, что из системной памяти вытаскивается
    небольшой кусочек, который называется SMRAM, и доступ к этому кусочку
    памяти есть только из режима SMM, в который можно перейти по
    специальному прерыванию SMI, которое вызывается оборудованием
    (USB-контроллером, таймером, контроллером памяти или питания).
    Операционная система никак не может потрогать SMRAM, если прошивка
    правильно настроила lock bits в соответствующих регистрах.
    
    Оказалось, что APIC находится настолько глубоко в процессорах Intel, что
    проверка "не происходит ли обращение к memory-mapped регистрам APIC"
    происходит даже в режиме SMM, даже при доступе к SMRAM. Что позволяет
    злоумышленнику подвинуть это окошко, чтобы перекрыть важные структуры
    данных, лежащие в SMRAM регистрами APIC, таким образом влияя на control
    flow. Эта проблема починена начиная с Sandy Bridge возбуждением
    исключения, если эти два региона памяти перекрываются.
    
    Из того, с чем можно столкнуться прямо сейчас на практике, в голову
    приходят только то, как устроены performance counters и model-specific
    registers.
    
    До amd64 когда-то сталкивался с особенностью процессоров Transmeta, в
    которых не была реализована на тот момент недокументированная инструкция
    CMOVxx, из-за чего i686-бинарники ломались - приходилось патчить ядро,
    добавляя в его обработчик исключения #UD (invalid opcode) эмулятор. А
    процессоры National Semiconductors Geode не поддерживают инструкцию
    endbr32, которую вставляет GCC для защиты кода от Spectre. Процессоры
    Intel начиная с Pentium Pro считают это документированным NOP, а Geode
    сваливаются с SIGILL.
    
    > приходится жутко страдать и делать CPU detection
    
    Разработчики системного ПО стараются помочь с этим. Например, свежие
    версии glibc умеют грузить библиотеки по CPU-зависимому пути, позволяя
    положить, например, в /usr/lib64/glibc-hwcaps/x86-64-v4 версии
    библиотек, использующие AVX512.
    
    Но всё становится совсем сложно, когда появляются ассиметричные
    многоядерные процессоры - у разных ядер могут быть разные instruction
    sets. Мне пока неизвестно о наличии в мейнстримных ОС такого
    планировщика, который бы умел мигрировать процессы с одного ядра на
    другое, учитывая особенности сборки каждой запущенной задачи.
    
    Несколько лет назад компилятор от Intel был замечен в "нечестном"
    поведении - в момент запуска программы проверялся CPUID, и если там не
    "GenuineIntel", то использовалась не самая оптимальная версия для
    некоторых builtins, даже если текущий процессор был способен исполнить
    её ожидаемым образом. Что приводило к дополнительным очкам для Intel в
    некоторых тестах производительности.
    
    > Или это как-раз то самое: мы работаем на AMD64, но поддерживаем
    > чётко-заданный-список-CPU?
    
    Если у разработчика прикладной программы нет цели выжать последние
    проценты производительности из CPU, то можно не думать обо всём этом и
    использовать то подмножество инструкций, которое работает и на AMD, и на
    Intel. Разница уже не столь велика, как раньше на i686 - тогда указание
    конкретных микроархитектур в gcc -march= давало куда более заментный
    прирост производительности, чем сейчас.
    
    комментарий 1:
    From: Sergey Matveev
    Date: 2022-05-06 08:20:38Z
    
    *** kmeaw [2022-05-06 00:24]: [...]
    Очень любопытно было почитать это! Спасибо.
    
    >До amd64 когда-то сталкивался с особенностью процессоров Transmeta, в
    >которых не была реализована на тот момент недокументированная инструкция CMOVxx
    
    Про это помню, помню. Сам на практике не встречался, но читал.
    
    >Но всё становится совсем сложно, когда появляются ассиметричные
    >многоядерные процессоры - у разных ядер могут быть разные instruction
    >sets.
    
    Ого, не слышал про такое. Точнее я слышал, с выходом Apple M1, о ядрах
    разной эффективности, но не о том, что могут быть ещё и разные инструкции.
    Задавался ещё и вопросом а современные GNU/Linux, BSD, whatever учитывают
    ли в своих планировщиках особенности разноэффектных ядер.
    
    >Несколько лет назад компилятор от Intel был замечен в "нечестном"
    
    Ага, тоже помню :-)
    
    >Если у разработчика прикладной программы нет цели выжать последние
    >проценты производительности из CPU, то можно не думать обо всём этом и
    >использовать то подмножество инструкций, которое работает и на AMD, и на
    >Intel. Разница уже не столь велика, как раньше на i686 - тогда указание
    >конкретных микроархитектур в gcc -march= давало куда более заментный
    >прирост производительности, чем сейчас.
    
    Ok, ясно, спасибо. Прирост помню был ощутимый для мультимедиа, когда
    комплировать MPlayer для K6-2 с 3DNow!.