]]> ]]>

Ада-Эльбрус

Реализация языка программирования Ada

В Новосибирском филиале Института точной механики и вычислительной техники АН СССР в 1980х годах группа под руководством Запреева Сергея Степановича реализовала Систему программирования Ада-Эльбрус. Нетипичность разработки — в том, что, как правило, трансляторы разрабатываются для процессора, а в данном случае процессор адаптировался под язык программирования. Языков программирования, под которые можно было бы адаптировать архитектуру команд, было много, но, что касается языка Ада, в своей книге «Языки программирования. Концепции и принципы» (2010) Виталий Шахнович Кауфман пишет:

«Первая [часть книги] посвящена основным абстракциям, используемым в современных ЯП. В качестве основного языка примеров здесь фигурирует ЯП Ada. Он удобен в этой роли потому, что в той или иной форме содержит ответы практически на все технологические проблемы. Другими словами, Ада служит примером «максимального» современного ЯП. «Минимальные» ЯП представлены языками Никлауса Вирта — это Modula-2 и Oberon…»

Инженеры МВК Эльбрус встречались на совещаниях Рабочей группы по реализации языков программирования (РГ-20, РГ РЯП) с разработчиками системы СП Ада-Эльбрус. Из протоколов заседаний:

«С 22 по 24 апреля 1981 г. в Протвино Московской области состоялось совещание РГ РЯП … В докладе Б.А.Бабаяна были изложены основные принципы архитектуры и операционной системы МВК Эльбрус, при этом особое внимание было уделено тем аспектам, которые способствуют реализации трансляторов и систем программирования.»

«С 21 по 26 мая 1984 г. в Львовском госуниверситете проходило 6-е заседание Рабочей группы по реализации языков программирования. … В.М.Пентковский (Москва) в докладе «Требования к среде программирования на языке Ада» сделал обзор требований к системе программирования на языке Ада. … В докладе В.Ю.Волконского и В.М.Пентковского (Москва) «Системная поддержка СП в МВК Эльбрус» В.Ю.Волконский сообщил о динамической и технологической поддержке систем программирования. Отмечалось, что многие функции динамической поддержки реализованы аппаратно, что обеспечивает эффективное исполнение программ. Использование стандартных файлов обеспечивает скорость работы компиляторов. Богатый системный инструментарий применим ко всем языкам высокого уровня. В МВК реализовано многоязыковое взаимодействие, широко используются стандартные пакеты и модули.»

«3 – 6 октября 1988 года в Тбилиси проходила научная конференция «Язык программирования Ада». … Группой представителей НФ ИТМ и ВТ (Чеблаков Б.Г., Разгулин В.Б., Полюдов П.А.) были сделаны три доклада по системе программирования Ада-Эльбрус. … Рыбин С.И. рассказал о методике тестирования диагностики транслятора (вообще, и языка Ада в частности), разрабатываемой в МГУ под руководством Кауфмана В.Ш. Приведены данные по оценке качества и диагностики трех трансляторов Лада, Ада-Эльбрус и венгерская Ада ЕС; из них видно, что качество диагностики Ада-Эльбрус — лучшее из трёх систем.»

Таким образом, адаптацию МВК Эльбрус под языки программирования можно понимать как аппаратную поддержку возможностей языка программирования Ада. ЯП Ада раскрывает все возможности аппаратуры, а другие ЯП — некоторое подмножество.

Сергей Игоревич Рыбин пишет:

«В начале 80‐х Ада привлекла всеобщее внимание среди русских специалистов и ученых в области компьютерных наук. В 1980г. в Москве была образованна русская группа SIGAda, которая активно действовала до конца 80‐х годов. … Были начаты проекты по реализации языка Ада для всех архитектур мейнфреймов, используемых в России, и некоторые из них достигли уровня работающих прототипов. … К сожалению, наши «реформы» конца 80‐х — начала 90‐х годов погубили в том числе и практически все отечественные Ада‐проекты, равно как и исследования в области информационных стандартов.»

Лихая година (выражение президента Ельцина Б.Н., 1992г.) настала и для Ады в России, и для Эльбруса.

Двадцать лет спустя на сайте МЦСТ не осталось и следа славного советского прошлого. Среди поддерживаемых языков программирования Ада официально не значится, но по косвенным признакам можно судить, что прошлые разработки не пропали, и в архитектуре продолжают существовать. Аппаратная поддержка языка программирования Ада переименована в так называемый «Защищённый режим Эльбрус». Вместо Ада-Эльбрус МЦСТ предоставляет транслятор lcc для языка C со специальным ключом -mptr128. -mptr играет роль, похожую на -m в GCC для x86/64: -m32 для 32-разрядного режима, -m64 для 64-разрядного. Так же и в Эльбрус: -mptr32 — 32-разрядный режим, -mptr64 — 64-разрядный режим, а -mptr128 — как бы продолжая эту линию, 128-разрядный режим, он же режим аппаратной поддержки языка Ада, он же «Защищённый режим Эльбрус». По ключевым словам «Эльбрус mptr128» можно искать информацию об этом режиме.

Кауфман В.Ш. описывает ЯП Ada как «максимальный», а ЯП Modula-2 и Oberon — «минимальные». Ему повезло во время своей работы над диагностикой не видеть такого пробивающего дно языка программирования, как C. Хотя C и Pascal появились примерно в одно время, на рубеже 1960х-1970х, и у СССР было время 20 лет до развала, чтобы отзеркалить оба языка в ГОСТ, на Фортран есть ГОСТ, на Паскаль есть ГОСТ, на язык Ада есть ГОСТ, а на язык Си — не было и нет ГОСТа. В СССР товары массового потребления были обязаны соответствовать ГОСТу, и отсутствие ГОСТа на язык программирования Си в некотором роде служило запретом на широкое применение. Система флагов транслятора идеологически устроена так, будто режим аппаратной поддержки языка Ада (также известный как Защищённый режим Эльбрус) является третьим в линейке после 32-разрядного и 64-разрядного режимов, но это уловка (возможно, сделанная не сознательно). То обстоятельство, что Си — более минимальный, чем то, что считалось минимальным в СССР, безусловно требует «подтянуть» входной язык до некоторого уровня, без которого невозможна работа в этом режиме. Проблема в том, что язык Си, подтянутый к языку Ада, перестаёт быть прежним языком Си, и становится неким новым, Эльбрусобезопасным Си или Адаистичным Си. Евангелисты МЦСТ пользуются этой уловкой, чтобы лучше продавать Эльбрусы.

Если потенциальный покупатель исходит из того мнения, что lcc — транслятор языков Си и C++, -mptr128 — всего лишь флаг транслятора, а защищённый режим Эльбрус — просто такой режим работы, то можно впасть в иллюзию, что вот готовые программы, вот готовый процессор, вот транслятор, и всё это будет работать в сборе. Суровая правда, что при переходе между -mptr64 и -mptr128 становится совсем другой Си, под которым без переработки не собирается практически ни одна существующая программа, помешала бы продажам Эльбрус, и это объясняет, почему лица, аффилированные с МЦСТ, выстраивают пропаганду именно таким образом. Таким образом, информацию о СП Ада-Эльбрус и о том, как особенности языков программирования, в первую очередь, языка программирования Ада, отразились в аппаратном устройстве архитектуры Эльбрус, приходится почерпывать из источников, не упоминающих о языке Ада.

Немного суровой правды просачивается через доклад, сделанный в декабре 2017го года. Например, оттуда:

«Библиотеки libmodbusregshelper и libmodbussrv были реализованы на языке Си (переписаны с языка C++ на C)», — то есть, C++ не поддерживается.

«Графические библиотеки на языке С: mesa, x86emul, rfb (подготовлены к переносу в защищенный режим)», — то есть, с графическими приложениями пока довольно туговато. Согласно докладу графическая часть была вынесена на отдельный ПК под управлением Windows 10.

«Был добавлен изначально отсутствующий функционал обработки исключений математических функций с помощью функции matherr. Функция-обработчик может быть определена пользователем для перехвата исключений (аналог try/catch в C++)», — в языке Ада исключения пораньше были лет на 10, чем в C++, и стандарт на язык Ада с самого начала определял исключительные ситуации вроде Numeric_Error, в отличие от не к месту упомянутого C++, где такие ситуации в общем случае не обрабатываются, а исключения есть только пользовательские, явно сделанные throw. Например, Access Violation в Windows и SIGSEGV проецируются в языке Ада на Storage_Error, и любые написанные за все 30 лет программы на языке Ада написаны со знанием этого обстоятельства, а matherr — это библиотечный вызов, который есть не на любой ОС и не в любой библиотеке времени исполнения под выбранную ОС. Учитывая, что matherr где-то всё же есть, это ещё не начало Эльбрусобезопасного Си, но дальше — больше.

В книге «Микропроцессоры и вычислительные комплексы семейства «Эльбрус»» можно прочитать:

«Архитектура «Эльбрус» защищает контекст программного модуля, образованный объектами, которые допускается в нём использовать согласно правилам языка программирования. Это делается путём организации доступа к низкоуровневому представлению объекта в памяти или «регистрах» через «проходную» его дескриптора — служебного слова, содержащего ссылку и информацию об объекте, соответствующую его типу. Доступ возможен только через дескриптор. Аппаратные операции создания и использования дескрипторов специализированы таким образом, что не оставляют возможности непосредственного воздействия на объект.»

«…согласно правилам языка программирования…», — очень интересно, а это правила какого же языка программирования имеются в виду? В языке Ада можно в публичной части пакета написать “type Cursor is private;”, и посторонние модули семантически теряют право посмотреть внутрь типа Cursor, но могут размещать переменные этого типа на стеке, класть в структуры и массивы. Если поддержать этот запрет в аппаратуре, для нормальных программ ничего не меняется. А в языке Си любой typedef «показывает» всему миру содержимое struct, и как понять, какому коду можно обращаться внутрь, а какому — нельзя? Как понять, что такое модуль в языке Си? Единица трансляции (.o) — это уже модуль? Да нет, есть программы и библиотеки, которые рассчитывают на возможность лезть внутрь структуры из разных единиц трансляции, и это как бы модуль, разрешающий другим модулям (в языке Ада есть два способа сделать вложение, а вложенные единицы имеют доступ к приватной части объемлющих пакетов) доступ. Статическая или динамическая библиотека (.so) — это уже модуль? Наверное, но чтобы это понять, требуется хакнуть язык. В обычном языке Си есть некие приватные инклуды и публичные. В обычном языке Си эти инклуды не отличимы. Соответственно, транслятор Адаистичного Си, чтобы протащить эту аппаратную возможность в язык Си, например, должен научиться отличать приватные инклуды от публичных. Например, транслятору Адаистичного Си нужно как-то подсказать: вот в этой директории лежат приватные инклуды, а в этой директории лежат публичные инклуды. И транслятор Адаистичного Си, когда читает приватные инклуды, должен давать права доступа к структурам в них только одному модулю, а когда читает публичные инклуды, должен давать права неограниченные. А во многих реальных библиотеках те и другие инклуды лежат вперемешку, и только скрипты установки избирательно одни файлы устанавливают, а другие — нет. Соответственно, надо поменять систему сборки, чтобы сообщать транслятору, какой заголовочный файл — приватный, а какой — публичный. И, в отличие от языка Ада, по-видимому, непрозрачными могут быть только указатели. Либо Адаистичный Си как-то так синтаксически модифицирован, чтоб можно было объявить структуру, её содержимое все видят, но аппаратура и транслятор запрещают обращения внутрь неё. Класть в другие структуры можно, а читать/писать содержимое — только через соответствующий модуль.

«Устройство преобразования адресных типов оперирует адресными словами. Результат выполнения — также адресное слово, например дескриптор на подмассив исходного массива, метка процедуры и др.», — тоже интересная вещь. Откуда в языке Ада подмассивы, понятно. Там есть недоопределённые (indefinite) типы данных, и самый простой String — как раз такой массив, и если брать средствами языка подстроку, то транслятор по возможности не копирует целиком подстроку, а манипулирует метаданными так, чтобы получить ссылку на подстроку. Это работает и для любых других массивов. В терминах других языков программирования это срезы (slice, span), жирные указатели с длиной. На Эльбрусе указатели тоже получаются жирными, но обычные программы на языке Си совершенно не могут об этом знать! Например, это проявляется так: программа на языке Ада передаёт доступ к массиву на запись другой подпрограмме, и не ко всему массиву, а к подмассиву. Подпрограмма не сможет выйти за рамки подмассива. Если же программа на обычном языке Си пытается делать то же самое, то в общепринятом стиле это делается при помощи передачи двух аргументов: указателя на начало массива и длины. Адресной арифметикой из массива берётся указатель в середину массива (в начало подмассива), затем вычисляется длина подмассива, и программа передаёт два параметра. Поскольку обычные программы ничего не знают про Эльбрус, именно так они и будут написаны. А на Эльбрусе указатели (дескрипторы) — жирные и содержат копию информации о длине массива, но программа на языке Си об этом не знает и будет ориентироваться на длину, переданную числом. Тут есть два отличия от языка Ада. Во-первых, в языке Си допустимо получать при адресной арифметике невалидные указатели. Требуется только, чтоб они были валидные при попытке разыменовать их. То есть, если в программе ошибка, указатель на начало массива может быть невалидным, и в программе на Ада даже вызвать подпрограмму не получилось бы. Кроме того, вычисление длины подмассива — это операция с точки зрения транслятора чисто целочисленная. В результате может получиться любое число, даже такое, которое соответствует выходу за границу объемлющего массива. В языке Ада эта ошибка была бы перехвачена тоже до вызова подпрограммы. В языке Си, так получается, что вызов удался бы. На Эльбрусе подпрограмма получает доступ на запись ко всему массиву, как он был выделен изначально, и имеет возможность что-нибудь перезаписать за границами подмассива, если это всё ещё в границах массива. А если не в границах, то обнаруживается это только в самый последний момент, при попытке обращения. Чтобы с этим бороться, в Адаистичном Си можно ввести концепцию срезов из языка Ада, и таким образом дать возможность манипулировать жирными указателями-дескрипторами. Это получается другой Си, Адаистичный Си, Эльбрусобезопасный Си, под который практически нет программ, в то время, как на языке Ада программ и библиотек явно поболее будет, чем «почти ничего». Программы на Аде расчитаны на проверки, которые вставляет транслятор, а на Эльбрусе часть этих проверок просто переносится в аппаратуру, без изменений исходного кода. Программы на Си придётся «портировать» (а если начистоту — переписывать на другой язык программирования, которым, если уж дело доходит до переписывания, могла бы быть Ада, но в МЦСТ сделали выбор в пользу Адаистичного Си, и скрывать, что это другой язык программирования, под который нет программ и библиотек).

«Возникшие проблемы защиты данных при переиспользовании памяти эффективно разрешены снабжением указателей на память в стеке специальными тегами и введением в указатели и дескрипторы уровня (номера) процедуры в стеке процедур. Этот номер позволяет определить принадлежность указателя к той или иной процедуре и тем самым решить проблему защиты данных», — в языке Ада нельзя без обхода безопасности передать access за пределы области видимости, и вложенность как раз нумеруется. Для программ на языке Си это, конечно, новшество неожиданное.

Но ещё большие неожиданности начинаются при попытке выделить память: «Операционная система должна взять на себя реализацию части функций управления памятью, которые традиционно включались в библиотеки динамической поддержки реализации языков. В первую очередь это касается функций выделения и освобождения памяти под объекты, таких как malloc, realloc, calloc, free…», и вот, почему: написано где-нибудь в коде «X Y = (X) malloc (sizeof (X) * Z);», и с точки зрения обычного Си «sizeof (X) * Z» — это просто число, а в режиме аппаратной поддержки языка Ада это не может быть просто числом. Если программа выделяет память таким образом, то надо полагать, впоследствии программа будет считать что на этом месте возник массив длиной Z из структур типа X. А это значит, надо после приведения типа от (void *) в (X * ) получить жирный указатель с информацией о том, сколько элементов есть в массиве, и какого типа этот массив, во внутреннем тэге, даже разметить выделенную память. Это уже как минимум два отклонения от Си: результат такого malloc — не void, void «съедается», а сразу возвращается X * , и аргумент malloc — это уже не просто число, а дерево AST, которое проверяется по шаблону, и при совпадении шаблона из этого дерева убирается умножение, sizeof — уже не статическая функция, возвращающая число, а нечто, что будет записано во внутренний тег полученного дескриптора. Это уже не Си, а угадайка какая-то, это уже Адаистичный Си. Не все выражения malloc удастся угадать, в отличие от сильно типизированного языка Ада. Не угаданные случаи придётся переписывать. Рано или поздно в большой замысловатой программе придётся дотягивать семантику Си до Ады, прибегая к расширениям языка и нестандартным (с точки зрения Си) функциям. В то время, как на языке Ада можно было бы оставаться в пределах стандарта.

Семантические отличия также касаются обработки переполнения целых чисел. Обычно числа переполняются, и программы на Си пишутся в оптимистичном расчёте, что там, где не надо, числа не переполнятся. А там, где надо, — там переполнятся с прокручиванием через границу (два миллиарда умножить на три равно примерно минус два миллиарда). С точки зрения исходных кодов это неотличимо. В режиме аппаратной поддержки языка программирования Ада транслятор lcc навешивает контроль на все целые числа, но это становится другая крайность. Тогда, наоборот, проблемы возникают в программах, ожидавших, что будет прокрутка. Такие программы придётся переписать на Адаистичный Си. Чтобы была прокрутка, потребуется прибегнуть к нестандартным расширениям языка Си, позволяющим указать, где можно допускать прокрутку, а где — нельзя. В языке Ада этим целям служат модульные типы вроде Unsigned_32, и программы на языке Ада написаны со знанием о существовании этих типов. А если писать новые программы, то информацию о том, как правильно писать на Аде, можно почерпнуть из общедоступного стандарта на язык Ада, а не из труднодоступной документации на Эльбрусобезопасный Си.

Лица, аффилированные с МЦСТ, настаивают на том, что режим поддержки языка Ада — уникален, но это не совсем не так. Кроме Эльбруса, существует достаточно давно CHERI. В Эльбрусе VLIW, в CHERI — RISC на базе MIPS. Давно продаётся. Есть эмулятор на базе QEMU. Имеет своё архитектурное своеобразие: на Эльбрусе есть и дескрипторы модулей, на CHERI есть «способности» (capabilities), которые аппаратно запрещено подделывать. Из дескриптора, содержащего некие разрешённые способности, можно производить дескриптор с меньшим наобором способностей, для этого, опять же, есть некие расширения языка Си или нестандартные функции. Для работы со срезами массивов — нестандартные расширения и/или функции. Причём, если работы над CHERI и над Эльбрусом не координировались, то можно ожидать, что результаты, достигнутые на CHERI, не подойдут к Эльбрусу и наоборот. Везде разные функции, разные расширения языка Си.

Повышением безопасности унаследованного кода на языке Си занимались и для обычных процессоров, и выдающимся проектом является CCured. Так же, как и на Эльбрусе, и на CHERI, приходится расширять язык программирования, и это ещё один, третий, набор расширений, которые требуется применять в достаточно сложных программах. Доклад «CCured in the Real World» упоминает об успешном переписывании на CCured таких программ, как ftpd, OpenSSL, OpenSSH, sendmail, bind. Доклад датируется 2003м годом. Когда код на языке Си называется унаследованным, это подразумевает, нового кода на этих языках не должно производиться.

Но суровая проза жизни — в том, что продолжает производиться. Суровая проза жизни — в том, что безопасность оказалась категорически не востребована, с какой стороны ни посмотреть. На безопасном языке программирования Ада пишут как раньше мало, так и сейчас. Безопасные версии программ, перенесённые на CCured, не вошли дистрибутивы Linux и BSD. Мейнтейнеры Linux бесстыдно продолжают класть в свои дистрибутивы версии программ с критическими уязвимостями, журналисты бесстыдно продолжают называть напрочь проигнорировавший стремление к безопасному коду Linux безопасной операционной системой потому что миллион пытливых глаз. Или макОС, потому что «это же Макинтош». Или «потому что консоль». И всё идёт своим чередом. Как и язык Ада, CCured оказался не востребован, но в отличие от языка Ада, совсем заброшен. Не стал хитом продаж CHERI с киллер-фичей защищённого режима CHERI. Пока срабатывает оруэлловский метод «решения» проблем с безопасностью, ситуация не улучшится.

Как если бы было мало проблем с отнюдь не помогающими журналистами, указанные группы разработчиков, занимающиеся одной и той же проблемой, игнорируют достижения друг друга, не ссылаются друг на друга, постоянно начинают с нуля, поодиночке не достигают весомых результатов и забрасывают. Например, в материалах CCured было бы логично ожидать, что этот инструмент — для унаследованного кода, а новый код нужно писать на языке Ада, но такой рекомендации нет. Тоже следовало бы писать лицам, аффилированным с МЦСТ, тем более, что это именно тот фундамент, на котором стоит Эльбрус. Безумие — рекомендовать писать новый код на Си, пусть даже с аппаратной защитой. Это только для повышения безопасности старого кода! Нельзя новый. Почему в МЦСТ не учитывают опыт CHERI, понять ещё возможно, ведь внезапно выяснится, что Эльбрус не уникален, и у конкурентов не очень-то выстрелило. Но всё же как-то выстрелило, и если игнорировать опыт Ada, CCured, CHERI, а сейчас игнорируется вообще всё, что только можно, то всё случается как в первый раз, или как у золотых рыбок память три секунды, или как в анекдоте про склероз, каждый день новости. Правда ли, что финансово обусловленное невежество лиц, аффилированных с МЦСТ, действительно выгоднее, чем «разглядеть» попытки других групп и воспользоваться плодами их деятельности, сорганизоваться медийно, большой вопрос. Но точно не выгодно оставаться в неведении лицам, не аффилированным с МЦСТ.

Бочка дёгтя про защищённый режим Эльбрус: -mptr128 значит квадрослово, 128 бит. В эти 128 бит должны уместиться: базовый виртуальный адрес, размер, текущий указатель, уровень процедуры в стеке, внутренний тег. То есть, защищённый режим Эльбрус — это обязательно 32-разрядный режим.

Подводя итоги, можно составить список рекомендаций для разработчиков:

Пишите на Аде. Вам не нужен Эльбрус за 200т.р., чтобы проверить переполнение буфера или целочисленное переполнение, это сделает Ада на любом бросовом x86/x64 процессоре. Не переполнится буфер, не затрётся адрес возврата, не перейдёт управление полученным из Сети данным, — для всего этого вам не нужен Intel MPX, Intel SGX, Эльбрус mptr128 или очередная припарка, вокруг которой делается ажиотаж. Писать код на Си — значит, создавать проблему, быть проблемой. И все эти iMPX — попытки героически полумерами решить проблему.

Чисто — не там, где убирают, а там, где не мусорят. Не будьте проблемой, не создавайте проблемы, не мусорите, не пишите на Си. Просто пишите на Аде. И будет чисто. Безопасно. Всегда. Везде. На Эльбрусе доступна трансляция сквозь язык Си средствами AdaMagic. Без аппаратной поддержки, зато можно писать под 64 бита безопасно на Эльбрусе. Со временем можно ожидать, что «угадайка» внутри lcc научится хорошо понимать промежуточный код AdaMagic и проецировать на соответствующие возможности аппаратуры, это был бы самый простой способ возобновить поддержку языка Ада, если СП Ада-Эльбрус утеряна. Так что можно безопасно писать на Аде впрок, а даже если этот «впрок» никак не наступает, всё равно все выигрывают.

Если вы чувствуете, что вас «зацепили» защищённым режимом Эльбрус, но Эльбруса у вас нет и, может быть, никогда и не будет, а раз нет Эльбруса, то и о безопасности можно не думать, с таким наваждением нужно бороться рациональными доводами. Если безопасность — это так здорово, а есть какой-то код на Си, и есть процессора, сильно отличающиеся от Эльбрус по своему устройству, на которых работает программа, то нужно сделать что? Правильно, для начала взять хотя бы CCured. И обезопасить им программу, раз это настолько важно. Лучше, конечно, на Аду переписать, но хотя бы CCured.

mptr128 на Эльбрусе, CCured на остальных платформах, а новый код — так вообще на Аде, да и старый понемногу переписывать на Аду — это самая последовательная линия поведения человека, действительно заботящегося о безопасности. mptr128 только на Эльбрусе, а про CCured и Аду вообще не думать — это не последовательная линия поведения. Это линия поведения человека, которому что-то впродали.


Комментарии

]]>

blog comments powered by Disqus

]]>

Работа программистам