SlideShare ist ein Scribd-Unternehmen logo
1 von 6
Downloaden Sie, um offline zu lesen
Забытые проблемы разработки 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 показаны размерность типов в различных
моделях данных, на которые мы в дальнейшем будем ссылаться.
Рисунок 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;
Кроме ошибок, перечисленных в списке, также рассматривались и некоторые другие, более
редкие ошибки.

На основе проведенных исследований вопроса верификации 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, а, следовательно, данный код будет некорректен на массивах, содержащих более 2
GB элементов. На 64-битных системах для безопасной индексации к большим массивам следует
использовать типы ptrdiff_t, size_t или производные от них. Отсутствие описания такого вида
ошибки в статьях объясняется очень просто. Во времена их написания машины с объемом памяти,
позволяющим хранить такие массивы, были практически не доступны. Сейчас же это становится
рядовой задачей в программировании, и с большим удивлением можно наблюдать, как код,
верой и правдой служивший многие годы, вдруг перестает корректно работать при использовании
больших массивов данных на 64-битных системах.
Другой пласт практически неосвещенных проблем, представлен ошибками, связанными с
возможностями и особенностями языка Си++. Почему так произошло, тоже достаточно
объяснимо. Во время внедрения первых 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-битного кода,
эта поддержка на данный момент не удовлетворяет необходимым требованиям. Причина
заключается в том, что диагностические правила были созданы по все тем же статьям, не
учитывающим специфику языка Си++ или обработку больших массивов данных, превышающих 2
GB.

Для 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-10

C++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-битных систем. Смена архитектуры слишком
существенное изменение, чтобы полностью положиться на автоматизированные системы
     тестирования.


Библиографический список
  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

Weitere ähnliche Inhalte

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

Ähnlich wie Забытые проблемы разработки 64-битных программ (20)

7 шагов по переносу программы на 64-битную систему
7 шагов по переносу программы на 64-битную систему7 шагов по переносу программы на 64-битную систему
7 шагов по переносу программы на 64-битную систему
 
Особенности разработки 64-битных приложений
Особенности разработки 64-битных приложенийОсобенности разработки 64-битных приложений
Особенности разработки 64-битных приложений
 
Сравнение диагностических возможностей анализаторов при проверке 64-битного кода
Сравнение диагностических возможностей анализаторов при проверке 64-битного кодаСравнение диагностических возможностей анализаторов при проверке 64-битного кода
Сравнение диагностических возможностей анализаторов при проверке 64-битного кода
 
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
 
Безопасность 64-битного кода
Безопасность 64-битного кодаБезопасность 64-битного кода
Безопасность 64-битного кода
 
20 ловушек переноса Си++ - кода на 64-битную платформу
20 ловушек переноса Си++ - кода на 64-битную платформу20 ловушек переноса Си++ - кода на 64-битную платформу
20 ловушек переноса Си++ - кода на 64-битную платформу
 
Урок 19. Паттерн 11. Сериализация и обмен данными
Урок 19. Паттерн 11. Сериализация и обмен даннымиУрок 19. Паттерн 11. Сериализация и обмен данными
Урок 19. Паттерн 11. Сериализация и обмен данными
 
Урок 5. Сборка 64-битного приложения
Урок 5. Сборка 64-битного приложенияУрок 5. Сборка 64-битного приложения
Урок 5. Сборка 64-битного приложения
 
PVS-Studio, решение для разработки современных ресурсоемких приложений
PVS-Studio, решение для разработки современных ресурсоемких приложенийPVS-Studio, решение для разработки современных ресурсоемких приложений
PVS-Studio, решение для разработки современных ресурсоемких приложений
 
Оптимизация 64-битных программ
Оптимизация 64-битных программОптимизация 64-битных программ
Оптимизация 64-битных программ
 
Разработка статического анализатора кода для обнаружения ошибок переноса прог...
Разработка статического анализатора кода для обнаружения ошибок переноса прог...Разработка статического анализатора кода для обнаружения ошибок переноса прог...
Разработка статического анализатора кода для обнаружения ошибок переноса прог...
 
Реклама PVS-Studio - статический анализ кода на языке Си и Си++
Реклама PVS-Studio - статический анализ кода на языке Си и Си++Реклама PVS-Studio - статический анализ кода на языке Си и Си++
Реклама PVS-Studio - статический анализ кода на языке Си и Си++
 
Урок 27. Особенности создания инсталляторов для 64-битного окружения
Урок 27. Особенности создания инсталляторов для 64-битного окруженияУрок 27. Особенности создания инсталляторов для 64-битного окружения
Урок 27. Особенности создания инсталляторов для 64-битного окружения
 
Коллекция примеров 64-битных ошибок в реальных программах
Коллекция примеров 64-битных ошибок в реальных программахКоллекция примеров 64-битных ошибок в реальных программах
Коллекция примеров 64-битных ошибок в реальных программах
 
Сущность библиотеки анализа кода VivaCore
Сущность библиотеки анализа кода VivaCoreСущность библиотеки анализа кода VivaCore
Сущность библиотеки анализа кода VivaCore
 
Урок 8. Статический анализ для выявления 64-битных ошибок
Урок 8. Статический анализ для выявления 64-битных ошибокУрок 8. Статический анализ для выявления 64-битных ошибок
Урок 8. Статический анализ для выявления 64-битных ошибок
 
Поиск ловушек в Си/Си++ коде при переносе приложений под 64-битную версию Win...
Поиск ловушек в Си/Си++ коде при переносе приложений под 64-битную версию Win...Поиск ловушек в Си/Си++ коде при переносе приложений под 64-битную версию Win...
Поиск ловушек в Си/Си++ коде при переносе приложений под 64-битную версию Win...
 
Правила статического анализа кода для диагностики потенциально опасных констр...
Правила статического анализа кода для диагностики потенциально опасных констр...Правила статического анализа кода для диагностики потенциально опасных констр...
Правила статического анализа кода для диагностики потенциально опасных констр...
 
Проблемы тестирования 64-битных приложений
Проблемы тестирования 64-битных приложенийПроблемы тестирования 64-битных приложений
Проблемы тестирования 64-битных приложений
 
Урок 26. Оптимизация 64-битных программ
Урок 26. Оптимизация 64-битных программУрок 26. Оптимизация 64-битных программ
Урок 26. Оптимизация 64-битных программ
 

Mehr von Tatyanazaxarova

Использование анализатора PVS-Studio в процессе инкрементальной сборки в Micr...
Использование анализатора PVS-Studio в процессе инкрементальной сборки в Micr...Использование анализатора PVS-Studio в процессе инкрементальной сборки в Micr...
Использование анализатора PVS-Studio в процессе инкрементальной сборки в Micr...
Tatyanazaxarova
 

Mehr von Tatyanazaxarova (20)

Урок 25. Практическое знакомство с паттернами 64-битных ошибок
Урок 25. Практическое знакомство с паттернами 64-битных ошибокУрок 25. Практическое знакомство с паттернами 64-битных ошибок
Урок 25. Практическое знакомство с паттернами 64-битных ошибок
 
Урок 24. Фантомные ошибки
Урок 24. Фантомные ошибкиУрок 24. Фантомные ошибки
Урок 24. Фантомные ошибки
 
Урок 23. Паттерн 15. Рост размеров структур
Урок 23. Паттерн 15. Рост размеров структурУрок 23. Паттерн 15. Рост размеров структур
Урок 23. Паттерн 15. Рост размеров структур
 
Урок 21. Паттерн 13. Выравнивание данных
Урок 21. Паттерн 13. Выравнивание данныхУрок 21. Паттерн 13. Выравнивание данных
Урок 21. Паттерн 13. Выравнивание данных
 
Урок 20. Паттерн 12. Исключения
Урок 20. Паттерн 12. ИсключенияУрок 20. Паттерн 12. Исключения
Урок 20. Паттерн 12. Исключения
 
Урок 17. Паттерн 9. Смешанная арифметика
Урок 17. Паттерн 9. Смешанная арифметикаУрок 17. Паттерн 9. Смешанная арифметика
Урок 17. Паттерн 9. Смешанная арифметика
 
Урок 16. Паттерн 8. Memsize-типы в объединениях
Урок 16. Паттерн 8. Memsize-типы в объединенияхУрок 16. Паттерн 8. Memsize-типы в объединениях
Урок 16. Паттерн 8. Memsize-типы в объединениях
 
Урок 15. Паттерн 7. Упаковка указателей
Урок 15. Паттерн 7. Упаковка указателейУрок 15. Паттерн 7. Упаковка указателей
Урок 15. Паттерн 7. Упаковка указателей
 
Урок 13. Паттерн 5. Адресная арифметика
Урок 13. Паттерн 5. Адресная арифметикаУрок 13. Паттерн 5. Адресная арифметика
Урок 13. Паттерн 5. Адресная арифметика
 
Урок 11. Паттерн 3. Операции сдвига
Урок 11. Паттерн 3. Операции сдвигаУрок 11. Паттерн 3. Операции сдвига
Урок 11. Паттерн 3. Операции сдвига
 
Урок 10. Паттерн 2. Функции с переменным количеством аргументов
Урок 10. Паттерн 2. Функции с переменным количеством аргументовУрок 10. Паттерн 2. Функции с переменным количеством аргументов
Урок 10. Паттерн 2. Функции с переменным количеством аргументов
 
Урок 9. Паттерн 1. Магические числа
Урок 9. Паттерн 1. Магические числаУрок 9. Паттерн 1. Магические числа
Урок 9. Паттерн 1. Магические числа
 
Урок 7. Проблемы выявления 64-битных ошибок
Урок 7. Проблемы выявления 64-битных ошибокУрок 7. Проблемы выявления 64-битных ошибок
Урок 7. Проблемы выявления 64-битных ошибок
 
Урок 4. Создание 64-битной конфигурации
Урок 4. Создание 64-битной конфигурацииУрок 4. Создание 64-битной конфигурации
Урок 4. Создание 64-битной конфигурации
 
Статический анализ Си++ кода
Статический анализ Си++ кодаСтатический анализ Си++ кода
Статический анализ Си++ кода
 
PVS-Studio
PVS-Studio PVS-Studio
PVS-Studio
 
PVS-Studio научился следить за тем, как вы программируете
PVS-Studio научился следить за тем, как вы программируетеPVS-Studio научился следить за тем, как вы программируете
PVS-Studio научился следить за тем, как вы программируете
 
Пояснения к статье про Copy-Paste
Пояснения к статье про Copy-PasteПояснения к статье про Copy-Paste
Пояснения к статье про Copy-Paste
 
Использование анализатора PVS-Studio в процессе инкрементальной сборки в Micr...
Использование анализатора PVS-Studio в процессе инкрементальной сборки в Micr...Использование анализатора PVS-Studio в процессе инкрементальной сборки в Micr...
Использование анализатора PVS-Studio в процессе инкрементальной сборки в Micr...
 
Статический анализ и ROI
Статический анализ и ROIСтатический анализ и ROI
Статический анализ и ROI
 

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

  • 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. Рисунок 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. Кроме ошибок, перечисленных в списке, также рассматривались и некоторые другие, более редкие ошибки. На основе проведенных исследований вопроса верификации 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, а, следовательно, данный код будет некорректен на массивах, содержащих более 2 GB элементов. На 64-битных системах для безопасной индексации к большим массивам следует использовать типы ptrdiff_t, size_t или производные от них. Отсутствие описания такого вида ошибки в статьях объясняется очень просто. Во времена их написания машины с объемом памяти, позволяющим хранить такие массивы, были практически не доступны. Сейчас же это становится рядовой задачей в программировании, и с большим удивлением можно наблюдать, как код, верой и правдой служивший многие годы, вдруг перестает корректно работать при использовании больших массивов данных на 64-битных системах.
  • 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. заключается в том, что диагностические правила были созданы по все тем же статьям, не учитывающим специфику языка Си++ или обработку больших массивов данных, превышающих 2 GB. Для 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-10 C++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. существенное изменение, чтобы полностью положиться на автоматизированные системы тестирования. Библиографический список 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