7 шагов по переносу программы на64-битную системуАвтор: Андрей КарповДата: 19.04.2009АннотацияВ статье рассмотрены основны...
чем IA-64. Особенно это относится к программному обеспечению для рынка персональныхкомпьютеров, который почти на 100 проце...
2. Шаг второй. Выясните, нужен ли вам 64-битный вариант вашегопродуктаНачать освоение 64-битных систем следует с вопроса "...
откликнулись с большой задержкой на появление 64-битных программ, что заставило рядклиентов искать другие инструменты для ...
Конечно, перечислить все, что может понадобиться для проекта здесь невозможно, но все-такипредложу список, который поможет...
Таблица N2. Возможности различных редакций Visual Studio 20083.2. Наличие 64-битных компьютеров под управлением 64-битныхо...
3.5. Модернизация методологии тестированияСущественная переработка методологии тестирования, модернизация юнит-тестов,испо...
Рисунок 1. Запуск менеджера конфигурацийВ менеджере конфигураций выбираем поддержку новой платформе (рисунок N2):         ...
Рисунок 3. Выбираем x64 в качестве платформы и берем за основу конфигурацию Win32Добавление новой конфигурации завершено, ...
Если вам повезет, то дополнительно заниматься настройкой 64-битного проекта необходимостине будет. Но это сильно зависит о...
DWORD         32 / 32         32-битный беззнаковый тип. Объявлен в WinDef.h как:typedef                              unsi...
битном режиме определенные участки кода будут некорректны. При компиляции 64-битного кодаэти предупреждения будут выданы к...
ret = __fread(buf, size, count, fp);          FUNLOCKFILE(fp);          return (ret);}Функция __fread возвращает тип size_...
Рисунок 5. A - Корректная установка 31-ого бита в 32-битном коде; B,C - Ошибка установки 32-                   ого бита на...
Рисунок 6. Ошибка установки 31-ого бита на 64-битной системе6.4. Магические числаМного неприятностей могут доставить магич...
Таблица N4. Основные магические значения, опасные при переносе приложений с 32-битной на                                 6...
6.5. Ошибки использования 32-битных переменных в качестве индексовВ программах обрабатывающих большие объемы данных могут ...
Для исправления кода необходимо использовать такие типы, как ptrdiff_t и size_t.6.6. Ошибки, связанные с изменением типов ...
Рисунок 8. Не корректный, но работоспособный 32-битный кодОшибка ждет, чтобы проявить себя в 64-битной системе, где размер...
Рисунок 9. Ошибка проявляет себя в 64-битном коде6.7. Диагностика скрытых ошибокПримеры подобных 64-битных ошибок можно пр...
самом начальном этапе, так и в дальнейшей разработке 64-битных решений. Статические анализпредупредит и научит программист...
Рисунок 10. Окно настроек программы Parallel Inspector перед запуском приложения.Последнее. Не забудьте добавить тесты, пр...
8. Mike Becker. Accessing 32-bit DLLs from 64-bit code. http://www.viva64.com/go.php?url=2099. Eric Palmer. How to use all...
7 шагов по переносу программы на 64-битную систему
7 шагов по переносу программы на 64-битную систему
Upcoming SlideShare
Loading in …5
×

7 шагов по переносу программы на 64-битную систему

929 views

Published on

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

Published in: Technology, News & Politics
  • Be the first to comment

  • Be the first to like this

7 шагов по переносу программы на 64-битную систему

  1. 1. 7 шагов по переносу программы на64-битную системуАвтор: Андрей КарповДата: 19.04.2009АннотацияВ статье рассмотрены основные шаги, обеспечивающие корректный перенос 32-битных Windowsприложений на 64-битные Windows системы. Хотя статья ориентирована на разработчиков,использующих язык Си/Си++ в среде Visual Studio 2005/2008, она будет полезна и другимразработчикам, планирующим перенос своих приложений под 64-битные системы.ВведениеВ статье описаны основные моменты, с которыми сталкиваются разработчики, планирующиемигрировать 32-битные программы на 64-битные системы. Конечно, список рассмотренныхвопросов не полон, но хочется надеяться, что со временем будет предложен расширенныйвариант этой статьи. Автор будет благодарен отзывам, комментариям и вопросам, которыепозволят улучшить информативность этой статьи.1. Шаг первый. 64-битность бывает разной. Давайте разберемсяВ рамках архитектуры вычислительной техники под термином "64-битный" понимают 64-битныецелые и другие типы данных, имеющих размер 64 бита. Под "64-битными" системами могутпониматься 64-битные архитектуры микропроцессоров (например, EM64T, IA-64) или 64-битныеоперационные системы (например, Windows XP Professional x64 Edition) [1].AMD64 (она же x86-64, Intel 64, EM64T, x64) - 64-битная архитектура микропроцессора исоответствующий набор инструкций, разработанные компанией AMD [2]. Этот набор инструкцийбыл лицензирован компанией Intel под названием EM64T (Intel64). Архитектура AMD64представляет собой расширение архитектуры x86 с полной обратной совместимостью.Архитектура получила широкое распространение в качестве базы персональных компьютеров ирабочих станций.IA-64 - 64-битная микропроцессорная архитектура, разработанная совместно компаниями Intel иHewlett Packard [3]. Реализована в микропроцессорах Itanium и Itanium 2 [4]. Архитектураиспользуется в основном в многопроцессорных серверах и кластерных системах.AMD64 и IA-64 это две различные 64-битные архитектуры не совместимые между собой. Поэтомуразработчикам следует сразу решить, необходимо ли поддерживать обе эти архитектуры илитолько одну. В большинстве случаев, если вы не разрабатываете узкоспециализированноепрограммное обеспечение для кластерных систем или не реализуете своювысокопроизводительную СУБД, то с большой вероятностью вам необходимо реализоватьподдержку только архитектуры AMD64, которая получила значительно большее распространение,
  2. 2. чем IA-64. Особенно это относится к программному обеспечению для рынка персональныхкомпьютеров, который почти на 100 процентов занят архитектурой AMD64.Далее в статье мы будем говорить только об архитектуре AMD64 (EM64T, x64), так как ееиспользование сейчас наиболее актуально для разработчиков прикладного программногообеспечения.Говоря о различных архитектурах, следует упомянуть о понятии "Модель данных". Под модельюданных следует понимать соотношения размерностей типов, принятых в рамках средыразработки. Для одной операционной системы могут существовать несколько средств разработки,придерживающихся разных моделей данных. Но обычно преобладает только одна модель,наиболее соответствующая аппаратной и программной среде. Примером может служить 64-битная операционная система Windows, в которой родной моделью данных является LLP64. Нодля совместимости 64-битная система Windows поддерживает исполнение 32-битных программ,которые работают в режиме модели данных ILP32LL. В таблице N1 приведены сведения обосновных моделях данных. Таблица N1. Модели данныхИспользуемая модель данных накладывает отпечаток на процесс разработки 64-битныхприложений, так как в коде программ необходимо учитывать разрядность используемых данных[5].
  3. 3. 2. Шаг второй. Выясните, нужен ли вам 64-битный вариант вашегопродуктаНачать освоение 64-битных систем следует с вопроса "А нужно ли нам пересобрать свой проектдля 64-битной системы?". На этот вопрос надо обязательно дать ответ, но не торопясь, подумав. Содной стороны можно отстать от своих конкурентов, вовремя не предложив 64-битные решения.С другой - можно впустую потратить время на 64-битное приложение, которое не даст никакихконкурентных преимуществ.Перечислим основные факторы, которые помогут сделать вам выбор.2.1. Продолжительность жизненного цикла приложенийНе следует создавать 64-битную версию приложения с коротким жизненным циклом. Благодаряподсистеме WOW64 старые 32-битные приложения достаточно хорошо работают на 64-битныхWindows системах и поэтому делать программу 64-битной, которая через 2 года перестанетподдерживаться, смысла не имеет [6]. Более того, практика показала, что переход на 64-битныеверсии Windows затянулся и возможно большинство ваших пользователей в краткосрочнойперспективе будут использовать только 32-битный вариант вашего программного решения.Если планируется длительное развитие и длительная поддержка программного продукта, тоследует начинать работать над 64-битным вариантом вашего решения. Это можно делатьнеспешно, но учтите, что чем дольше у вас не будет полноценного 64-битного варианта, тембольше сложностей может возникать с поддержкой такого приложения, устанавливаемого на 64-битные версии Windows.2.2. Ресурсоемкость приложенияПерекомпиляция программы для 64-битной системы позволит ей использовать огромные объемыоперативной памяти, а также убыстрит скорость ее работы на 5-15%. Убыстрение на 5-10%произойдет за счет использования архитектурных возможностей 64-битного процессора,например большего количества регистров. Еще 1%-5% прироста скорости обуславливаетсяотсутствием прослойки WOW64, которая транслирует вызовы API между 32-битнымиприложениями и 64-битной операционной системой.Если ваша программа не работает с большими объемами данных (более 2GB) и скорость ееработы не критична, то переход на 64-битную в ближайшее время систему не столь актуален.Кстати, даже простые 32-битные приложения, могут получить преимущество от их запуска в 64-битной среде. Вы, наверное, знаете, что программа собранная с ключом/LARGEADDRESSAWARE:YES может выделять до 3-х гигабайт памяти, если 32-битная операционнаясистема Windows запущена с ключом /3gb. Эта же 32-битная программа, запущенная на 64-битнойсистеме может выделить почти 4 GB памяти (на практике около 3.5 GB).2.3. Разработка библиотекЕсли вы разрабатываете библиотеки, компоненты или иные элементы, с помощью которыхсторонние разработчики создают свое программное обеспечение, то вы должны проявитьоперативность в создании 64-битного варианта своей продукции. В противном случае, вашиклиенты, заинтересованные в выпуске 64-битных версий, будут вынуждены искатьальтернативные решения. Например, некоторые разработчики программно-аппаратной защиты
  4. 4. откликнулись с большой задержкой на появление 64-битных программ, что заставило рядклиентов искать другие инструменты для защиты своих программ.Дополнительным преимуществом от выпуска 64-битной версии библиотеки является то, что выможете продавать ее как отдельный продукт. Таким образом, ваши клиенты, желающиесоздавать как 32-битные, так и 64-битные приложения будут вынуждены приобретать 2различные лицензии. Например, такая политика используется компанией Spatial Corporation припродаже библиотеки Spatial ACIS.2.4. Зависимость вашего продукта от сторонних библиотекПрежде чем планировать работу над созданием 64-битной версий вашего продукта выясните,имеются ли 64-битные варианты библиотек и компонентов, которые в нем используются. Такжеузнайте, какова ценовая политика по отношению к 64-битному варианту библиотеки. Все этоможно выяснить, посетив сайт разработчика библиотеки. Если поддержка отсутствует, то заранеепоищите альтернативные решения, поддерживающие 64-битные системы.2.5. Наличие 16-битных приложенийЕсли в ваших решениях все еще присутствуют 16-битные модули, то пора от них избавиться.Работа 16-битных приложений в 64-битных версиях Windows не поддерживается.Здесь следует пояснить один момент, связанный с использованием 16-битных инсталляторов. Онидо сих пор используются для установки некоторых 32-битных приложений. Создан специальныймеханизм, который на лету подменяет ряд наиболее популярных 16-битных инсталляторов наболее новые версии. Это может вызвать неверное мнение, что 16-битные программы по-прежнему работают в 64-битной среде. Помните, это не так.2.6. Наличие кода на ассемблереНе забывайте, что использование большого объема кода на ассемблере, может существенноповысить стоимость создания 64-битной версии приложения.Взвесив все перечисленные факты, все за и против, примите решение, следует ли вам переноситьваш проект на 64-битные системы. И если это так, то давайте пойдем дальше.3. Шаг третий. ИнструментарийЕсли вы приняли решение о разработке 64-битной версии вашего продукта и готовы потратить наэто время, это еще не гарантирует успех. Дело в том, что вы должны обладать всем необходимыминструментарием и здесь могут быть неприятные казусы.Самой простой, но и самой непреодолимой, может стать проблема отсутствия 64-битногокомпилятора. Статья пишется в 2009 году, но все еще нет 64-битного компилятора C++ Builder отCodegear [7]. Его выпуск ожидается только к концу этого года. Невозможно обойти подобнуюпроблему, если конечно но переписать весь проект, например, с использованием Visual Studio. Ноесли с отсутствием 64-битного компилятора все понятно, то другие аналогичные проблемы могутоказаться более скрытными и вылезти уже на этапе работ по переносу проекта на новуюархитектуру. Поэтому, хочется посоветовать заранее провести исследование, существуют ли всенеобходимые компоненты, которые потребуются для реализации 64-битной версии вашегопродукта. Вас могут поджидать неприятные сюрпризы.
  5. 5. Конечно, перечислить все, что может понадобиться для проекта здесь невозможно, но все-такипредложу список, который поможет вам соорентироваться и возможно вспомнить о другихмоментах, которые необходимы для реализации вашего 64-битного проекта:3.1. Наличие 64-битного компилятораСложно что-то еще сказать о важности наличия 64-битного компилятора. Он просто должен быть.Если вы планируете разрабатывать 64-битные приложения с использованием последней версии(на момент написания статьи) Visual Studio 2008, то следующая таблица N2 поможет помочьопределить, какая из редакций Visual Studio вам необходима.
  6. 6. Таблица N2. Возможности различных редакций Visual Studio 20083.2. Наличие 64-битных компьютеров под управлением 64-битныхоперационных системМожно конечно использовать виртуальные машины для запуска 64-битных приложений на 32-битной технике, но это крайне неудобно и не обеспечит необходимого уровня тестовыхиспытаний. Желательно, чтобы в машинах было установлено не менее 4-8 гигабайт оперативнойпамяти.3.3. Наличие 64-битных вариантов всех используемых библиотекЕсли библиотеки представлены в исходных кодах, то должна присутствовать 64-битнаяконфигурация проекта. Самостоятельно заниматься модернизацией библиотеки для ее сборкипод 64-битную систему может быть неблагодарным и сложным занятием, а результат можетоказаться ненадежным и содержащим ошибки. Также вы можете нарушить этим лицензионныесоглашения. Если вы используете библиотеки в виде бинарных модулей, то вы также должныузнать, существуют ли 64-битные модули. Вы не сможете использовать 32-битные DLL внутри 64-битного приложения. Можно создать специальную обвязку через COM, но эта будет отдельнойбольшой, сложной задачей [8]. Также учтите, что приобретение 64-битной версии библиотекиможет стоить дополнительных денег.3.4. Отсутствие встроенного кода на ассемблереVisual C++ не поддерживает 64-битный встроенный ассемблер. Вы должны использовать иливнешний 64-битный ассемблер (например, MASM) или иметь реализацию той жефункциональности на языке Си/Си++ [9].
  7. 7. 3.5. Модернизация методологии тестированияСущественная переработка методологии тестирования, модернизация юнит-тестов,использование новых инструментальных средств. Более подробно об этом будет сказано ниже, ноне забывайте учесть это на этапе оценки временных затрат на миграцию приложения на новуюсистему [10].3.6. Новые данные для тестированияЕсли вы разрабатываете ресурсоемкие приложения, потребляющие большой объем оперативнойпамяти, то вам необходимо позаботиться о пополнении базы тестовых входных данных. Принагрузочном тестировании 64-битных приложений желательно выходить за пределы 4 гигабайтпотребляемой памяти. Многие ошибки могут проявиться только при таких условиях.3.7. Наличие 64-битных систем защитыИспользуемая система защиты, должна поддерживать 64-битные системы в полном необходимомвам объеме. Например, компания Aladdin достаточно быстро выпустила 64-битные драйвера дляподдержки аппаратных ключей Hasp. Но очень долго отсутсвовала система автоматическойзащиты 64-битных бинарных файлов (программа Hasp Envelop). Таким образом, механизм защитыприходилось реализовывать самостоятельно внутри программного кода, что являлосьдополнительной сложной задачей, требующей квалификации и времени. Не забывайте проподобные моменты, связанные с обеспечением защиты, системой обновлений и так далее.3.8. ИнсталляторНеобходимо наличие нового инсталлятора, способного полноценно устанавливать 64-битныеприложения. Хочется здесь сразу предостеречь об одной традиционной ошибке. Это создание 64-битных инсталляторов для установки 32/64-битных программных продуктов. Подготавливая 64-битую версию приложения, разработчики часто хотят довести "64-битность" в нем до абсолюта. Исоздают 64-битный инсталлятор, забывая о том, что у пользователей 32-битной операционнойсистемы такой инсталляционный пакет просто не запустится. Обратим внимание, что незапустится не 32-битное приложение включенное в дистрибутив наряду с 64-битным, а именносам установщик. Ведь если дистрибутив представляет собой 64-битное приложение, то на 32-битной операционной системе он, конечно же, не запустится. Самое обидное в этом то, чтопользователь никак не сможет догадаться, что же происходит. Он просто увидитинсталляционный пакет, который невозможно запустить.4. Шаг четвертый. Настройка проекта в Visual Studio 2005/2008Создание 64-битной конфигурации проекта в Visual Studio 2005/2008 выглядит достаточно просто.Сложности будут подстерегать вас на этапе сборки новой конфигурации и поиска в ней ошибок.Для создания же 64-битной конфигурации достаточно выполнить следующие 4 шага:Запускаем менеджер конфигураций, как показано на рисунке N1:
  8. 8. Рисунок 1. Запуск менеджера конфигурацийВ менеджере конфигураций выбираем поддержку новой платформе (рисунок N2): Рисунок 2. Создание новой конфигурацииВыбираем 64-битную платформу (x64), а в качестве основы выбираем настройки от 32-битнойверсии (рисунок N3). Те настройки, которые влияют на режим сборки среда Visual Studioскорректирует сама.
  9. 9. Рисунок 3. Выбираем x64 в качестве платформы и берем за основу конфигурацию Win32Добавление новой конфигурации завершено, и мы можем выбрать 64-битный вариантконфигурации и приступить к компиляции 64-битного приложения. Выбор 64-битнойконфигурации для сборки показан на рисунке N4. Рисунок 4. Теперь доступна 32-битная и 64-битная конфигурация
  10. 10. Если вам повезет, то дополнительно заниматься настройкой 64-битного проекта необходимостине будет. Но это сильно зависит от проекта, его сложности и количества используемых библиотек.Единственное, что стоит сразу изменить, это размер стека. В случае если в вашем проектеиспользуется стек размером по умолчанию, то есть в 1 мегабайт, то есть смысл задать егоразмером в 2 мегабайта для 64-битной версии. Это не обязательно, но лучше заранееподстраховаться. Если у вас используется размер стека, отличный от размера по умолчанию, тоесть смысл сделать его для 64-битной версии в 2 раза больше. Для этого в настройках проектанайдите и измените параметры Stack Reserve Size и Stack Commit Size.5. Шаг пятый. Компиляция приложенияЗдесь было бы хорошо рассказать о типичных проблемах, возникающих на этапе компиляции 64-битной конфигурации. Рассмотреть, какие проблемы возникают со сторонними библиотеками,рассказать, что компилятор в коде связанного с функциями WInAPI более не допустит помещенияуказателя в тип LONG и вам будет необходимо модернизировать свой код и использовать типLONG_PTG. И многое, многое другое. К сожалению этого так много и ошибки так разнообразны,что нет возможности изложить это в рамках одной статьи и даже, пожалуй, книги. Вам придетсясамим просмотреть все ошибки, которые выдаст компилятор и новые предупреждения, которыхранее не было и в каждом отдельно случае разобраться, как модернизировать код.Частично облегчить жизнь может коллекция ссылок на ресурсы, посвященные разработке 64-битных приложений: http://www.viva64.com/links/64-bit-development/. Коллекция постояннопополняется и автор будет благодарен читателям, если они пришлют ему ссылки на ресурсы,которые, по их мнению, заслуживают внимания.Остановимся здесь только на типах, которые могут представлять интерес для разработчиков примиграции приложений. Эти типы представлены в Таблице N3. Большинство ошибок прикомпиляции будет связано с использование именно этих типов.Тип Размерность Примечание типа на платформе x86 / x64int 32 / 32 Базовый тип. На 64-битных системах остался 32-битным.long 32 / 32 Базовый тип. На 64-битных Windows системах остался 32- битным. Учтите, что в 64-битных Linux системах этот тип был расширен до 64-бит. Не забывайте об этом если разрабатываете код, который должен работать компилироваться для Windows и для Linux систем.size_t 32 / 64 Базовый беззнаковый тип. Размер типа выбирается таким образом, чтобы в него можно было записать максимальный размер теоретически возможного массива. В тип size_t может быть безопасно помещен указатель (исключение составляют указатели на функции классов, но это особенный случай).ptrdiff_t 32 / 64 Аналогичен типу size_t, но является знаковым. Результат выражения, где один указатель вычитается из другого (ptr1- ptr2), как раз будет иметь тип ptrdiff_t.Указатель 32 / 64 Размер указателя напрямую зависит от разрядности платформы. Будьте аккуратны при приведении укзателей к другим типам.__int64 64 / 64 Знаковый 64-битный тип.
  11. 11. DWORD 32 / 32 32-битный беззнаковый тип. Объявлен в WinDef.h как:typedef unsigned long DWORD;DWORDLONG 64 / 64 64-битный беззнаковый тип. Объявлен в WinNT.h как:typedef ULONGLONG DWORDLONG;DWORD_PTR 32 / 64 Беззнаковый тип, в который можно помещать указатель. Объявлен в BaseTsd.h как:typedef ULONG_PTR DWORD_PTR;DWORD32 32 / 32 32-битный беззнаковый тип. Объявлен в BaseTsd.h как:typedef unsigned int DWORD32;DWORD64 64 / 64 64-битный беззнаковый тип. Объявлен в BaseTsd.h как:typedef unsigned __int64 DWORD64;HALF_PTR 16 / 32 Половина указателя. Объявлен в Basetsd.h как:#ifdef _WIN64 typedef int HALF_PTR;#else typedef short HALF_PTR;#endifINT_PTR 32 / 64 Знаковый тип, в который можно помещать указатель. Объявлен в BaseTsd.h как:#if defined(_WIN64) typedef __int64 INT_PTR; #else typedef int INT_PTR;#endifLONG 32 / 32 Знаковый тип, который остался 32-битным. Поэтому во многих случаях теперь следует использовать LONG_PTR. Объявлен в WinNT.h как:typedef long LONG;LONG_PTR 32 / 64 Знаковый тип, в который можно помещать указатель. Объявлен в BaseTsd.h как:#if defined(_WIN64) typedef __int64 LONG_PTR; #else typedef long LONG_PTR;#endifLPARAM 32 / 64 Параметр для посылки сообщений. Объявлен в WinNT.h как:typedef LONG_PTR LPARAM;SIZE_T 32 / 64 Аналог типа size_t. Объявлен в BaseTsd.h как:typedef ULONG_PTR SIZE_T;SSIZE_T 32 / 64 Аналог типа ptrdiff_t. Объявлен в BaseTsd.h как:typedef LONG_PTR SSIZE_T;ULONG_PTR 32 / 64 Беззнаковый тип, в который можно помещать указатель. Объявлен в BaseTsd.h как:#if defined(_WIN64) typedef unsigned __int64 ULONG_PTR;#else typedef unsigned long ULONG_PTR;#endifWORD 16 / 16 Беззнаковый 16-битный тип. Объявлен в WinDef.h как:typedef unsigned short WORD;WPARAM 32 / 64 Параметр для посылки сообщений. Объявлен в WinDef.h как:typedef UINT_PTR WPARAM;Таблица N3. Типы представляющие интерес при переносе 32-битных программ на 64-битыеWindows системы.6. Диагностика скрытых ошибокЕсли вы думаете, что после исправления всех ошибок компиляции будет получено долгожданное64-битное приложение, то придется вас разочаровать. Самое сложное впереди. На этапекомпиляции вами будут исправлены самые явные ошибки, которые смог обнаружить компилятор,которые в основном связаны с невозможностью неявного приведения типов. Но это верхушкаайсберга. Основная часть ошибок скрыта. Эти ошибки с точки зрения абстрактного языка Си++смотрятся безопасно или замаскированы явными приведениями типов. Таких ошибок в несколькораз больше, чем количество ошибок выявленных на этапе компиляции.На ключ /Wp64 надежды возлагать не следует. Это ключ часто преподносится как чудесноесредство поиска 64-битных ошибок. В действительности ключ /Wp64 всего лишь даетвозможность при компиляции 32-битного кода получить некоторые предупреждения, что в 64-
  12. 12. битном режиме определенные участки кода будут некорректны. При компиляции 64-битного кодаэти предупреждения будут выданы компилятором в любом случае. И поэтому при компиляции64-битного приложения ключ /Wp64 игнорируется. И уж тем более этот ключ не поможет в поискескрытых ошибок [11].Рассмотрим несколько примеров скрытых ошибок.6.1. Явное приведение типовСамый простой, но вовсе не самый легкий для обнаружения класс ошибок связан с явнымприведением типов, при которых происходит обрезание значащих бит.Распространенным пример - приведение указателей к 32-битным типам при передачи их вфункции, такие как SendMessage:MyObj* pObj = ...::SendMessage(hwnd, msg, (WORD)x, (DWORD)pObj);Здесь явное приведение типа используется для превращения указателя в числовой тип. Для 32-битной архитектуры приведенный пример корректен, так как последний параметр функцииSendMessage имеет тип LPARAM, который на 32-битной архитектуре совпадает с DWORD. Для 64-битной архитектуре использование DWORD ошибочно и должно быть заменено на LPARAM. ТипLPARAM имеет в зависимости от архитектуры размер 32 или 64 бита.Это простой случай, но часто приведение типа выглядит более изысканно и обнаружить егоиспользуя предупреждения компилятора или поиском по тексту программы невозможно. Явныеприведения типов подавляют диагностику компилятора, поскольку они именно и предназначены,чтобы сказать компилятору что приведение типов корректно и программист взял на себяответственность за безопасность кода. Явный поиск тоже не поможет. Типы могут быть нестандартные имена (заданные программистом через typedef), а способов осуществить явноеприведение типов тоже не мало. Для надежной диагностики подобных ошибок необходимоиспользовать только специальный инструментарий, такой как анализаторы Viva64 или PC-Lint.6.2. Неявное приведение типовСледующий пример связан уже с неявным приведением типа, при котором также происходитпотеря значащих бит. Код функции fread осуществляет чтение из файла, но некорректен припопытке чтения более 2 гигабайт данных на 64-битной системе.size_t __fread(void * __restrict buf, size_t size, size_t count, FILE * __restrict fp);size_tfread(void * __restrict buf, size_t size, size_t count, FILE * __restrict fp){ int ret; FLOCKFILE(fp);
  13. 13. ret = __fread(buf, size, count, fp); FUNLOCKFILE(fp); return (ret);}Функция __fread возвращает тип size_t, но для хранения количества прочитанных байтиспользуется тип int. В результате при больших объемах читаемых данных функция может вернутьне то количество байт, которое на самом деле будет прочитано.Вы можете сказать, что это безграмотный код начинающих, что о таком приведении типа сообщиткомпилятор и что вообще такой код легко найти и поправить. Это теоретически. А практически вреальной жизни с большими проектами все может обстоять иначе. Этот пример взят из исходногокода FreeBSD. Ошибка была поправлена только в декабре 2008 года! Это притом, что первая(экспериментальная) 64-битная версия FreeBSD вышла еще в июне 2003 года.Вот исходный код до исправления:http://www.viva64.com/go.php?url=503А вот исправленный вариант (декабрь 2008) года:http://www.viva64.com/go.php?url=5046.3. Работа с битами, сдвигиЛегко сделать ошибку в коде, работающем с отдельными битами. Следующий тип ошибки связанс операциями сдвига. Рассмотрим пример:ptrdiff_t SetBitN(ptrdiff_t value, unsigned bitNum) { ptrdiff_t mask = 1 << bitNum; return value | mask;}Приведенный код работоспособен на 32-битной архитектуре и позволяет выставлять бит сномерами от 0 до 31 в единицу. После переноса программы на 64-битную платформу возникнетнеобходимость выставлять биты от 0 до 63. Но данный код никогда не выставит биты, с номерами32-63. Обратите внимание, что "1" имеет тип int и при сдвиге на 32 позиции произойдетпереполнение, как показано на рисунке 5. Получим мы в результате 0 (рисунок 5-B) или 1 (рисунок5-C) зависит от реализации компилятора.
  14. 14. Рисунок 5. A - Корректная установка 31-ого бита в 32-битном коде; B,C - Ошибка установки 32- ого бита на 64-битной системе (два варианта поведения)Для исправления кода необходимо сделать константу "1" того же типа, что и переменная mask:ptrdiff_t mask = ptrdiff_t(1) << bitNum;Заметим также, что неисправленный код приведет еще к одной интересной ошибке. Привыставлении 31 бита на 64-битной системе результатом работы функции будет значение0xffffffff80000000 (см. рисунок 6). Результатом выражения 1 << 31 является отрицательное число -2147483648. Это число представляется в 64-битной целой переменной как 0xffffffff80000000.
  15. 15. Рисунок 6. Ошибка установки 31-ого бита на 64-битной системе6.4. Магические числаМного неприятностей могут доставить магические константы, то есть числа, с помощью которыхзадается размер того или иного типа. Правильным решением является использование для такихцелей операторов sizeof(), но в большой программе вполне может затеряться старый кусочеккода, где твердо были уверены, что размер указателя был 4 байта, а в типе size_t всегда 32 бита.Обычно подобные ошибки выглядят следующим образом:size_t ArraySize = N * 4;size_t *Array = (size_t *)malloc(ArraySize);Основными числами, к которым следует отнестись с осторожностью при переходе на 64-битнуюплатформу приведены в таблице N4.
  16. 16. Таблица N4. Основные магические значения, опасные при переносе приложений с 32-битной на 64-битную платформу
  17. 17. 6.5. Ошибки использования 32-битных переменных в качестве индексовВ программах обрабатывающих большие объемы данных могут встретиться ошибки связанные синдексацией больших массивов или возникнуть вечные циклы. Следующий пример содержитсразу 2 ошибки:const size_t size = ...;char *array = ...;char *end = array + size;for (unsigned i = 0; i != size; ++i){ const int one = 1; end[-i - one] = 0;}Первая ошибка заключается в том, что если размер обрабатываемых данных превысит 4 гигабайта(0xFFFFFFFF), то возможно возникновение вечного цикла, поскольку переменная i имеет типunsigned и никогда не достигнет значения 0xFFFFFFFF. Я специально пишу, что возникновениевозможно, но не обязательно оно произойдет. Это зависит от того, какой код построиткомпилятор. Например, в отладочном (debug) режиме вечный цикл будет присутствовать, а вrelease-коде зацикливание исчезнет, так компилятор примет решение оптимизировать код,используя для счетчика 64-битный регистр и цикл будет корректным. Все это добавляет путаницы,и код который работал вчера, неожиданно может перестать работать на следующий день.Вторая ошибка связана с проходом по массиву от конца к началу для чего используютсяотрицательные значения индексов. Приведенный код работоспособен в 32-битном режиме, нопри его запуске на 64-битной машине на первой же итерации цикла произойдет доступ заграницы массива и программа аварийно завершится. Рассмотрим причину такого поведения.Согласно правилом языка Си++ на 32-битной системе выражение "-i - one" будет вычислятьсяследующим образом (на первом шаге i = 0): 1. Выражение "-i" имеет тип unsigned и имеет значение 0x00000000u. 2. Переменная one будет расширена от типа int до типа unsigned и будет равна 0x00000001u. Примечание: Тип int расширяется (согласно стандарту языка Си++) до типа unsigned, если он участвует в операции, где второй аргумент имеет тип unsigned. 3. Происходи операция вычитания, в котором участвуют два значения типа unsigned и результат выполнения операции равен 0x00000000u - 0x00000001u = 0xFFFFFFFFu. Обратите внимание, что результат имеет беззнаковый тип. 4. На 32-битной системе обращение к массиву по индексу 0xFFFFFFFFu эквивалентно использованию индекса -1. То есть end[0xFFFFFFFFu] является аналогом end[-1]. В результате происходит корректная обработка элемента массива.В 64-битной системе в последнем пункте картина будет иной. Произойдет расширение типаunsigned до знакового ptrdiff_t и индекс массива будет равен 0x00000000FFFFFFFFi64. В результатепроизойдет выход за рамки массива.
  18. 18. Для исправления кода необходимо использовать такие типы, как ptrdiff_t и size_t.6.6. Ошибки, связанные с изменением типов используемых функцийБывают ошибки, в которых, в общем, то никто не виноват, но они от этого не перестают бытьошибками. Представьте, что давным-давно в далекой галактике (в Visual Studio 6.0) былразработан проект, в котором присутствует класс CSampleApp, являющийся наследником отCWinApp. В базовом классе есть виртуальная функция WinHelp. Наследник перекрывает этуфункцию и выполняет необходимые действия. Визуально это представлено на рисунке 7. Рисунок 7. Работоспособный корректный код, который создан в Visual Studio 6.0Затем проект переносится на Visual Studio 2005, где прототип функции WinHelp изменился, ноэтого никто не замечает, так как в 32-битном режиме типы DWORD и DWORD_PTR совпадают ипрограмма продолжает корректно работать (рисунок 8).
  19. 19. Рисунок 8. Не корректный, но работоспособный 32-битный кодОшибка ждет, чтобы проявить себя в 64-битной системе, где размер типов DWORD и DWORD_PTRразличен (рисунок 9). Получается, что в 64-битном режиме классы содержат две РАЗНЫЕ функцииWinHelp, что естественно некорректно. Учтите, что подобные ловушки могут скрываться не тольков MFC, где часть функций изменили типы своих аргументов, но и в коде ваших приложений исторонних библиотек.
  20. 20. Рисунок 9. Ошибка проявляет себя в 64-битном коде6.7. Диагностика скрытых ошибокПримеры подобных 64-битных ошибок можно приводить и приводить. Тем, кто заинтересовалсяподобными ошибками и хочет более подобно узнать о них будет интересна статья "20 ловушекпереноса Си++ - кода на 64-битную платформу" [12].Как видите, этап поиска скрытых ошибок представляет нетривиальную задачу, тем более чтомногие из них будут проявляться нерегулярно или только на больших входных объемах данных.Для диагностики подобных ошибок хорошо подходят статические анализаторы кода, так как онимогут проверять весь код приложения, в не зависимости от входных данных и частотывыполнения его участков в реальных условиях. Использовать статический анализ есть смысл какна этапе переноса приложения на 64-битные платформы, чтобы найти большинство ошибок на
  21. 21. самом начальном этапе, так и в дальнейшей разработке 64-битных решений. Статические анализпредупредит и научит программиста лучше понимать особенности ошибок связанных с 64-битнойархитектурой и писать более эффективный код. Автор статьи является разработчиком одного изтаких специализированных анализаторов кода, носящий название Viva64 [13]. Более подробнопознакомиться с инструментом и скачать демонстрационную версию можно c сайта компанииООО "Системы программной верификации".В качестве справедливости следует сказать, что в таких анализаторах кода как Gimpel PC-Lint иParasoft C++test имеются наборы правил для диагностики 64-битных ошибок. Но, во-первых, этоанализаторы общего назначения и правила диагностики 64-битных ошибок в них представленыслабо. Во-вторых они больше ориентированы на модель данных LP64 используемую в семействеоперационных систем Linux, что снижает их пользу для Windows программ, где используетсямодель данных LLP64 [14].7. Шаг седьмой. Модернизация процесса тестированияОписанный в предыдущем разделе шаг поиска ошибок в программном коде необходимый, нонедостаточный шаг. Ни один метод, в том числе статического анализа кода, не дает полнойгарантии обнаружения всех ошибок и наилучший результат может быть достигнут только прикомбинации различных методик.Если ваша 64-битная программа обрабатывает больший объем данных, чем 32-битная версия тонеобходимо расширить тесты, чтобы включить в них обработку данных объемом более 4 гигабайт.Эта та граница, за которой начинают проявлять себя многие 64-битные ошибки. Времени такиетесты могут занимать на порядок больше и к этому надо быть заранее готовым. Обычно тестыпишут так, чтобы обрабатывать в каждом тесте небольшое количество элементов и тем самымиметь возможность проходить все внутренние юнит-тесты, например? за несколько минут, аавтоматические тесты (например, с использованием AutomatedQA TestComplete) за несколькочасов. Функция сортировки на 32-битной системе, если она сортирует 100 элементов, почти сполной гарантией будет корректна себя вести на 100000 элементах. Но та же функция на 64-битной системе может подвести при попытке обработать 5 миллиардов элементов. Скоростьвыполнения юнит-теста может понизиться в миллионы раз. Не забудьте заложить стоимостьадаптации тестов при освоении 64-битных систем. Одним из решений является разделение юнит-тестов на быстрые (работающие с малым объемом памяти) и медленные, обрабатывающихгигабайты и запускаемые, например, ночью. Автоматизированное тестирование ресурсоемких 64-битных программ можно построить на основе распределенных вычислений.Есть еще одна неприятная новость. Вам вряд ли удастся использовать инструменты, подобныеBoundsChecker для поиска ошибок в ресурсоемких 64-битных программах, поглощающих большойобъем памяти. Дела в колоссальном замедлении тестируемых программ, что делает такой подходкрайне неудобным. Инструмент Parallel Inspector входящий в состав Intel Parallel Studio в режимедиагностики всех ошибок связанных с работой с памятью, замедлит скорость выполненияприложения в среднем в 100 раз (рисунок 10). Вполне есть вероятность, что тестируемыйалгоритм, который работает 10 минут, придется оставлять на ночь и смотреть результаты работытолько на следующий день. При этом я уверен, что Parallel Inspector в режиме поиска ошибокработы с памятью один из самых полезных и удобных инструментов. Просто надо быть готовым кизменению практики диагностики ошибок и закладывать это в планы освоения 64-битных систем.
  22. 22. Рисунок 10. Окно настроек программы Parallel Inspector перед запуском приложения.Последнее. Не забудьте добавить тесты, проверяющие совместимость форматов данных между32-битной и 64-битной версией. Совместимость данных при миграции достаточно частонарушается из-за записи в файлы таких типов как size_t или long (в случае Linux систем).Библиографический список 1. Wikipedia. 64-bit. http://www.viva64.com/go.php?url=203 2. Wikipedia. AMD64. http://www.viva64.com/go.php?url=204 3. Wikipedia. IA-64. http://www.viva64.com/go.php?url=205 4. Wikipedia. Itanium. http://www.viva64.com/go.php?url=206 5. Андрей Карпов. Забытые проблемы разработки 64-битных программ http://www.viva64.com/art-1-1-1609701563.html 6. Wikipedia. WOW64. http://www.viva64.com/go.php?url=207 7. Nick Hodges. The Future of the Delphi Compiler. http://www.viva64.com/go.php?url=208
  23. 23. 8. Mike Becker. Accessing 32-bit DLLs from 64-bit code. http://www.viva64.com/go.php?url=2099. Eric Palmer. How to use all of CPUID for x64 platforms under Microsoft Visual Studio .NET 2005. http://www.viva64.com/go.php?url=21010. Андрей Карпов, Евгений Рыжков. Поиск ловушек в Си/Си++ коде при переносе приложений под 64-битную версию Windows. http://www.viva64.com/art-1-1- 329725213.html11. Андрей Карпов. 64 бита, /Wp64, Visual Studio 2008, Viva64 и все, все, все... http://www.viva64.com/art-1-1-253695945.html12. Андрей Карпов, Евгений Рыжков. 20 ловушек переноса Си++ - кода на 64-битную платформу. http://www.viva64.com/art-1-1-1958348565.html13. Евгений Рыжков. Viva64: что это и для кого? http://viva64.com/art-1-1-2081052208.html14. Андрей Карпов. Сравнение диагностических возможностей анализаторов при проверке 64- битного кода. http://www.viva64.com/art-1-1-1441719613.html

×