Забытые проблемы разработки 64-битных программ

201 views

Published on

Хотя история развития 64-битных систем составляет более десятилетия, появление 64-битных версий операционной системы Windows поставило перед разработчиками новые задачи в области разработки и тестирования программных решений. В статье рассмотрены некоторые ошибки связанные с разработкой 64-битного Си/Си++ кода под операционную систему Windows. Объяснены причины, по которым данные ошибки не нашли отражения в статьях, посвященных задачам миграции и неудовлетворительно выявляются большинством статических анализаторов.

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
201
On SlideShare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
1
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Забытые проблемы разработки 64-битных программ

  1. 1. Забытые проблемы разработки 64-битных программАвтор: Андрей КарповДата: 11.08.2007АннотацияХотя история развития 64-битных систем составляет более десятилетия, появление 64-битныхверсий операционной системы Windows поставило перед разработчиками новые задачи вобласти разработки и тестирования программных решений. В статье рассмотрены некоторыеошибки связанные с разработкой 64-битного Си/Си++ кода под операционную систему Windows.Объяснены причины, по которым данные ошибки не нашли отражения в статьях, посвященныхзадачам миграции и неудовлетворительно выявляются большинством статических анализаторов.ВведениеИстория развития 64-битных программных систем не нова и составляет уже более десятилетия [1].В 1991 году был выпущен первый 64-битный микропроцессор MIPS R4000 [2, 3]. С тех пор вфорумах и статьях возникали дискуссии, посвященные переносу программ на 64-битные системы.Началось обсуждение проблем, связанных с разработкой 64-битных программ на языке Си.Обсуждались вопросы о том, какая модель данных лучше, что такое long long и многое другое.Вот, например, интересная подборка сообщений [4] из новостной группы comp.lang.c,посвященная использованию типа long long в языке Си, которая, в свою очередь, была связана споявлением 64-битных систем.Одним из наиболее распространенных и чувствительных к изменению размерности типов данныхявляется язык Си. Из-за его низкоуровневых свойств следует постоянно контролироватькорректность программы на этом языке, переносимой на новую платформу. Естественно, что припоявлении 64-битных систем разработчики по всему миру вновь столкнулись с задачамиобеспечения совместимости старого исходного кода с новыми системами. Одним из косвенныхсвидетельств сложности проблем миграции является большое количество моделей данных,которые постоянно следует учитывать. Модель данных - это соотношение размеров базовыхтипов в языке программирования. На рисунке 1 показаны размерность типов в различныхмоделях данных, на которые мы в дальнейшем будем ссылаться.
  2. 2. Рисунок 1. Модели данных.Существующие публикации и инструменты в сфере верификации64-битных приложенийКонечно, это был не первый этап смены разрядности. Достаточно вспомнить переход с 16-битныхсистем на 32-битные. Естественно, накопленный опыт оказал свое положительное воздействие наэтапе перехода на 64-битные системы.Но переход на 64-битные системы имел свои нюансы, в результате чего появилась серияисследований и публикаций по данным вопросам, например [5, 6, 7].В основном, авторами того времени выделялись ошибки следующих типов: 1. Упаковка указателей в типы меньшей размерности. Например, помещение указателя в тип int на системе с моделью данных LP64 приведет к обрезанию значения указателя и невозможности его использования в дальнейшем. 2. Использование магических констант. Опасность заключается в использовании таких чисел как 4, 32, 0x80000000 и ряда других вместо специализированных констант или использования оператора sizeof(). 3. Некорректные операции сдвига, не учитывающие увеличение размерности ряда типов. 4. Использование некорректных объединений или структур без учета выравнивания на системах с различной разрядностью. 5. Ошибки работы с битовыми полями. 6. Некорректные арифметические выражения. Пример:int x = 100000, y = 100000, z = 100000;long long s = x * y * x;
  3. 3. Кроме ошибок, перечисленных в списке, также рассматривались и некоторые другие, болеередкие ошибки.На основе проведенных исследований вопроса верификации 64-битного кода были предложенырешения, обеспечивающие диагностику опасных конструкций. Например, такую проверкуреализовали в статических анализаторах Gimpel Software PC-Lint (http://www.gimpel.com) иParasoft C++test (http://www.parasoft.com).Возникает вопрос. Если 64-битные системы существуют так давно, существуют статьи,посвященные данной тематике, и даже программные инструменты, обеспечивающие контрольопасных конструкций в коде, так стоит ли возвращаться к этому вопросу?К сожалению да - стоит! Причиной тому служит прогресс, произошедший за эти годы в областиинформационных технологий. А актуальность данного вопроса связана с быстрымраспространением 64-битных версий операционной системы Windows.Существующая информационная поддержка и инструменты в области разработки 64-битныхтехнологий устарели и нуждаются в существенной переработке. Но Вы возразите, что в Интернетеможно найти множество современных статей (2005-2007г), посвященных вопросам разработки 64-битных приложений на языке Си/Си++. К сожалению, на практике они являются не более чемпересказом старых статей применительно к новой 64-битной версии Windows, без учета ееспецифики и произошедших изменений технологий.Неосвещенные проблемы разработки 64-битных программНачнем по порядку. Авторы новых статей не учитывают огромный объем памяти, который сталдоступен современным приложениям. Конечно, указатели были 64-битными еще в стародавниевремена, но вот использовать таким программам массивы размером в несколько гигабайт недоводилось. В результате, как в старых, так и в новых статьях выпал целый пласт ошибок,связанный с ошибками индексации больших массивов. Практически невозможно найти в статьяхописание ошибки, подобной следующей:for (int x = 0; x != width; ++x) for (int y = 0; y != height; ++y) for (int z = 0; z != depth; ++z) BigArray[z * width * height + y * width + x] = InitValue;В этом примере, выражение "z * width * height + y * width + x", используемое для адресации,имеет тип int, а, следовательно, данный код будет некорректен на массивах, содержащих более 2GB элементов. На 64-битных системах для безопасной индексации к большим массивам следуетиспользовать типы ptrdiff_t, size_t или производные от них. Отсутствие описания такого видаошибки в статьях объясняется очень просто. Во времена их написания машины с объемом памяти,позволяющим хранить такие массивы, были практически не доступны. Сейчас же это становитсярядовой задачей в программировании, и с большим удивлением можно наблюдать, как код,верой и правдой служивший многие годы, вдруг перестает корректно работать при использованиибольших массивов данных на 64-битных системах.
  4. 4. Другой пласт практически неосвещенных проблем, представлен ошибками, связанными свозможностями и особенностями языка Си++. Почему так произошло, тоже достаточнообъяснимо. Во время внедрения первых 64-битных систем язык Си++ для них не существовал илион был не распространен. Поэтому, практически все статьи посвящены проблемам в областиязыка Си. Современные авторы заменили название Си на Си/Си++, но нового ничего не добавили.Но отсутствие в статьях описания ошибок, специфичных для Си++, не означает, что их нет.Существуют ошибки, проявляющие себя при переносе программ на 64-битные системы. Онисвязанны с виртуальными функциями, исключениями, перегруженными функциями и так далее.Более подробно с такими ошибками можно ознакомиться в статье [8]. Приведем простой пример,связанный с использованием виртуальных функций:class CWinApp { ... virtual void WinHelp(DWORD_PTR dwData, UINT nCmd);};class CSampleApp : public CWinApp { ... virtual void WinHelp(DWORD dwData, UINT nCmd);};Проследим жизненный цикл разработки некоторого приложения. Пусть первоначально оноразрабатывалось под Microsoft Visual C++ 6.0, когда функция WinHelp в классе CWinApp имеласледующий прототип:virtual void WinHelp(DWORD dwData, UINT nCmd = HELP_CONTEXT);Совершенно верно было осуществить перекрытие виртуальной функции в классе CSampleApp, какпоказано в примере. Затем проект был перенесен в Microsoft Visual C++ 2005, где прототипфункции в классе CWinApp претерпел изменения, заключающиеся в смене типа DWORD на типDWORD_PTR. На 32-битной системе программа продолжит совершенно корректно работать, таккак здесь типы DWORD и DWORD_PTR совпадают. Неприятности проявят себя при компиляцииданного кода под 64-битную платформу. Получатся две функции с одинаковыми именами, но сразличными параметрами, в результате чего перестанет вызываться пользовательский код.Помимо особенностей разработки 64-битных программ с точки зрения языка Си++, существуют идругие тонкие моменты. Например, особенности, связанные с архитектурой 64-битной версииWindows. Хочется заранее предупредить разработчиков о потенциальных проблемах ипорекомендовать уделить большее внимание тестированию 64-битного программногообеспечения [9].Теперь вернемся к методам верификации исходного кода программы с использованиемстатических анализаторов. Я думаю, вы уже угадали, что здесь тоже не все так хорошо, каккажется. Несмотря на заявленную поддержку диагностирования особенностей 64-битного кода,эта поддержка на данный момент не удовлетворяет необходимым требованиям. Причина
  5. 5. заключается в том, что диагностические правила были созданы по все тем же статьям, неучитывающим специфику языка Си++ или обработку больших массивов данных, превышающих 2GB.Для Windows-разработчиков дело обстоит еще хуже. Основные статические анализаторырассчитаны на диагностику 64-битных ошибок для модели данных LP64, в то время как в Windowsиспользуется модель данных LLP64 [10]. Обусловлено это тем, что 64-битные версии Windowsмолоды, а ранее 64-битные системы были представлены Unix-подобными системами с модельюданных LP64.В качестве примера рассмотрим диагностическое сообщение 3264bit_IntToLongPointerCast (port-10), генерируемое анализатором Parasoft C++test:int *intPointer;long *longPointer;longPointer = (long *)intPointer; //-ERR port-10C++test предполагает, что с точки зрения модели LP64 данная конструкция будет некорректна. Нов рамках модели данных, принятой в Windows, данная конструкция будет безопасна.Рекомендации по верификации 64-битных программХорошо, - скажете Вы, - проблемы разработки 64-битных версий программ действительноактуальны. Но как найти все эти ошибки?Исчерпывающий ответ дать невозможно, но можно привести ряд рекомендаций, которые в суммепозволят обеспечить безопасную миграцию на 64-битные системы и обеспечить необходимыйуровень надежности: • Ознакомьте Ваших коллег, связанных с разработкой 64-битых приложений, со следующими статьями: [7, 8, 9, 10, 11, 12, 13, 14, 15]. • Ознакомьте Ваших коллег с методологией статического анализа кода: [16, 17, 18]. Статическая верификация кода - один из лучших способов поиска такого рода ошибок. Она позволяет убедиться в работоспособности даже тех частей кода, работу которых на больших объемах данных сложно смоделировать в реальности, например, при использовании методологии юнит-тестов. • Разработчикам будет полезно познакомиться со статическими анализаторами, такими как Parasoft C++test (www.parasoft.com), Gimpel Software PC-lint (www.gimpel.com), Abraxas Software CodeCheck (www.abxsoft.com). • Для разработчиков Windows-приложений особенно полезно будет знакомство со специализированным статическим анализатором Viva64 (http://www.viva64.com), рассчитанным на модель данных LLP64 [19]. • Усовершенствуйте систему юнит-тестирования, включив в набор тестов обработку больших массивов данных. Более подробно с необходимостью тестирования на большом объеме данных можно познакомиться в статье [9], а также узнать, как лучше организовать такое тестирование. • Провести тщательно ручное тестирование перенесенного кода на реальных больших задачах, использующих возможности 64-битных систем. Смена архитектуры слишком
  6. 6. существенное изменение, чтобы полностью положиться на автоматизированные системы тестирования.Библиографический список 1. John R. Mashey, The Long Road to 64 Bits. http://www.viva64.com/go.php?url=20 2. Wikipedia: MIPS architecture. http://www.viva64.com/go.php?url=21 3. John R. Mashey, 64 bit processors: history and rationale. http://www.viva64.com/go.php?url=22 4. John R. Mashey, The 64-bit integer type "long long": arguments and history. http://www.viva64.com/go.php?url=23 5. 64-bit and Data Size Neutrality. http://www.viva64.com/go.php?url=6 6. 64-Bit Programming Models: Why LP64? http://www.viva64.com/go.php?url=24 7. Transitioning C and C++ programs to the 64-bit data model. 8. Andrey Karpov, Evgeniy Ryzhkov. 20 issues of porting C++ code on the 64-bit platform. http://www.viva64.com/art-1-1-1958348565.html 9. Andrey Karpov. Evgeniy Ryzhkov. Problems of testing 64-bit applications. http://www.viva64.com/art-1-1-929024801.html 10. The Old New Thing: Why did the Win64 team choose the LLP64 model? http://www.viva64.com/go.php?url=25 11. Brad Martin, Anita Rettinger, and Jasmit Singh. Multiplatform Porting to 64 Bits. http://www.viva64.com/go.php?url=26 12. Migrating 32-bit Managed Code to 64-bit. http://www.viva64.com/go.php?url=27 13. Matt Pietrek. Everything You Need To Know To Start Programming 64-Bit Windows Systems. http://www.viva64.com/go.php?url=28 14. Microsoft Game Technology Group. 64-bit programming for Game Developers. http://www.viva64.com/go.php?url=29 15. John Paul Mueller. 24 Considerations for Moving Your Application to a 64-bit Platform. http://www.viva64.com/go.php?url=30 16. Wikipedia: Static code analysis. http://www.viva64.com/go.php?url=31 17. Sergei Sokolov. Bulletproofing C++ Code. http://www.viva64.com/go.php?url=32 18. Walter W. Schilling, Jr. and Mansoor Alam. Integrate Static Analysis Into a Software Development Process. http://www.viva64.com/go.php?url=33 19. Evgeniy Ryzhkov. Viva64: what is it and for whom is it meant? http://viva64.com/art-1-1-2081052208.html

×