20 ловушек переноса си кода на 64 битную платформу


Содержание

20 ловушек переноса си кода на 64 битную платформу

Андрей Карпов, Евгений Рыжков. 20 ловушек переноса Си++ — кода на 64-битную платформу
Аннотация. Рассмотрены программные ошибки, проявляющие себя при переносе Си++ — кода с 32-битных платформ на 64-битные платформы. Приведены примеры некорректного кода и способы его исправления. Перечислены методики и средства анализа кода, позволяющие диагностировать обсуждаемые ошибки.
Замечание. На сайте RSDN.ru лежит старый вариант этой статьи, содержащий много ошибок и неточностей. По приведенной ссылке расположен более свежий вариант.

Андрей Карпов Проблемы тестирования 64-битных приложений
Аннотация. В статье рассмотрен ряд вопросов связанных с тестированием 64-битного программного обеспечения. Обозначены сложности, с которыми может столкнуться разработчик ресурсоемких 64-битных приложений, и пути их преодоления.

Евгений Рыжков. Проблемы 64-битного кода на примерах
Аннотация. При переносе 32-битного программного обеспечения на 64-битные системы в коде приложений, написанных на языке Си++, могут проявляться отсутствующие ранее ошибки. Причина этого кроется в изменении базовых типов данных (а точнее отношений между ними) на новой аппаратной платформе. В статье приводится примеры ошибок в коде, приводящие к неработоспособности Си++ программ при переносе их в среду Windows X64.

Андрей Карпов. 64 бита для Си++ программистов: от /Wp64 к Viva64
Аннотация. Развитие рынка 64-битных решений поставило новые задачи в области их верификации и тестирования. В статье говорится об одном из таких инструментов — Viva64. Это lint-подобный статический анализатор Си++ кода, предназначенный специально для выявления ошибок, связанных с особенностями 64-битных платформ. Освещены предпосылки для создания данного анализатора и отражена его связь с режимом «Detect 64-Bit Portability Issues» в Си++ компиляторе Visual Studio 2005.

Евгений Рыжков Viva64: что это и для кого?
Аннотация. Одним из возможных решений для поиска ошибок при переносе кода является использование программ специального класса – статических анализаторов кода. Представителем данной группы программ и является Viva64. Viva64 – это анализатор кода, который выявляет в приложениях, написанных на языках программирования Си и Си++, потенциальные проблемы переноса кода.

Андрей Карпов, Евгений Рыжков Разработка ресурсоемких приложений в среде Visual C++.
Аннотация. Статья познакомит разработчиков прикладного программного обеспечения с задачами, которые ставит перед ним массовое внедрение многоядерных 64-битных вычислительных систем, знаменующих революционное увеличение вычислительной мощности, доступное рядовому пользователю. Будут рассмотрены вопросы эффективного использования аппаратных ресурсов для решения повседневных прикладных задач в рамках операционной системы Windows x64.

Андрей Карпов 64 бита, /Wp64, Visual Studio 2008, Viva64 и все, все, все.
Аннотация. Данная статья ставит своей задачей ответить на ряд вопросов, касающихся безопасного переноса Си/Си++ кода на 64-битные системы. Статья написана как ответ на часто обсуждаемую в форумах тематику, связанную с использованием ключа /Wp64 и инструмента Viva64.

20 ловушек переноса си кода на 64 битную платформу

Профиль
Группа: Завсегдатай
Сообщений: 2069
Регистрация: 3.11.2005
Где: Красноярск

Репутация: 1
Всего: 17

Код
unsigned __int64 *arr_64;
int var;

arr_64[i] |= 1

Цитата
warning C4334: ‘
char count_str[20];
unsigned __int64 sdf = 1

Может есть оператор для 64-битного сдвига? Я в MSDN-е такого не нашёл.

Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154

Lazin
Дата 5.5.2009, 09:46 (ссылка) | (нет голосов) Загрузка .
Код
unsigned __int64 sdf = 1ui64

Профиль
Группа: Завсегдатай
Сообщений: 2069
Регистрация: 3.11.2005
Где: Красноярск

Репутация: 1
Всего: 17

Lazin, а что такой префикс можно использовать? У меня компилятор не понял.

Но я уже понял, что сдвигать надо тоже 64-битную переменную:

ksili
Дата 5.5.2009, 09:53 (ссылка) | (нет голосов) Загрузка .
Код
arr_64[i] |= (unsigned __int64)1

Добавлено через 6 минут и 21 секунду

Цитата(Lazin @ 5.5.2009, 13:46 )
1ui64 | (нет голосов) Загрузка .

Профиль
Группа: Участник
Сообщений: 117
Регистрация: 7.11.2007
Где: Тула

Репутация: 10
Всего: 16

5. Операции сдвига

Операции сдвига при невнимательном использовании могут принести много неприятностей во время перехода от 32-битной к 64-битной системе. Начнем с примера функции, выставляющей в переменной типа memsize, указанный вами бит в 1:

Код
ptrdiff_t SetBitN(ptrdiff_t value, unsigned bitNum) <
ptrdiff_t mask = 1

Приведенный код работоспособен на 32-битной архитектуре и позволяет выставлять биты с номерами от 0 до 31. После переноса программы на 64-битную платформу возникнет необходимость выставлять биты от 0 до 63. Как Вы думаете, какое значение вернет следующий вызов функции SetBitN(0, 32)? Если Вы думаете, что 0x100000000, то авторы рады, что не зря подготовили эту статью. Вы получите 0.

Обратите внимание, что «1» имеет тип int и при сдвиге на 32 позиции произойдет переполнение, как показано на рисунке 2.

Рисунок 2. Вычисление выражения «ptrdiff_t mask = 1 ptrdiff_t mask = ptrdiff_t(1)

Код
ptrdiff_t mask = CONST3264(1)

Еще один вопрос. Чему будет равен результат вызова неисправленной функции SetBitN(0, 31)? Правильный ответ 0xffffffff80000000. Результатом выражения 1 Google

Дата 12.11.2020, 13:54 (ссылка)
  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе — для этого существует «Центр Помощи».
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

Перенос кода на 64-битную платформу

Вроде всё понятно, кроме одного: «##i64».
В поисковике найти не удалось, не понимает он таких языковых конструкций.

П.с. Кстати, почему-то, в результате у меня получается одно и тоже. Хотя, как я понял из текста, битовое представление числа должно корректироваться в зависимости от системы.

Правила форума «С++:Общие вопросы»
10.03.2012, 00:34

Перенос Java-кода на платформу Android
Всем доброе утро! Есть неплохой код. Но мне оказалось этого мало и хочу перенести на платформу.

Перенос кода
Подскажите пожалуйста в чём проблема? имею код: func.cpp #ifndef LAB4_H #define.

перенос кода из VB
Подскажите, как перенести данный код из VB в Builder? myhead = createobject(‘eapi.head’) .

Перенос кода
Подскажите кто-нибудь каким лучше текстовым редактором пользоваться, чтобы при переносе кода.

20 ловушек переноса си кода на 64 битную платформу


Здравствуйте. Я являюсь автором и соавтором нескольких статей, посвященных разработке статических анализаторов и вопросом разработки 64-битных приложений. Поскольку на нашем сайте www.Viva64.com пока отсутствует раздел русских статей, то я решил дать ссылки на них в нескольких форумах. Надеюсь, они будут интересны и полезны.

Андрей Карпов, Евгений Рыжков. 20 ловушек переноса Си++ — кода на 64-битную платформу.
Аннотация. Рассмотрены программные ошибки, проявляющие себя при переносе Си++ — кода с 32-битных платформ на 64-битные платформы. Приведены примеры некорректного кода и способы его исправления. Перечислены методики и средства анализа кода, позволяющие диагностировать обсуждаемые ошибки.

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

Андрей Карпов, Евгений Рыжков. Статический анализ кода для верификации 64-битных приложений.
Аннотация. В результате появления на рынке персональных компьютеров 64-битных процессоров перед разработчиками программ возникает задача переноса старых 32-битных приложений на новую платформу. После такого переноса кода приложение может вести себя некорректно. В статье рассматривается вопрос разработки и применения статического анализатора кода для проверки правильности таких приложений. Приводятся проблемы, возникающие в приложениях после перекомпиляции для 64-битных систем, а также правила, по которым выполняется проверка кода.
Тематика: Статический анализ кода, разработка статических анализаторов, Си/Си++, 64-бит

Евгений Рыжков. Viva64: разработка 64-битных приложений.
Аннотация. Viva64 – это инструмент для поиска ошибок и возможных проблем в С/C++ коде при переносе приложений на 64-битную платформу. Сейчас, когда необходимость выпуска 64-битных версий программного обеспечения осознали почти все разработчики, как никогда необходимо убедиться в качестве разрабатываемого продукта. И если традиционная 32-битная версия Вашего приложения протестирована на должном уровне, то перенос ее на 64-битную платформу может выявить неожиданные проблемы. Обзору проблем переноса приложений посвящены отдельные статьи, а в данном документе мы расскажем о том, как обеспечить необходимый уровень качества кода с помощью статического анализатора Viva64.

Андрей Карпов. Установка PC-Lint и его использование в Visual Studio 2005.
Аннотация. Статья посвящена первому знакомству со статическим анализатором Си++-кода PC-Lint версии 8.0. Описан процесс установки инструмента и его первоначальной настройки.

Андрей Карпов. Проблемы тестирования 64-битных приложений.
Аннотация. В статье рассмотрен ряд вопросов связанных с тестированием 64-битного программного обеспечения. Обозначены сложности, с которыми может столкнуться разработчик ресурсоемких 64-битных приложений, и пути их преодоления.

Евгений Рыжков. Viva64: что это и для кого?
Аннотация. Viva64 (www.Viva64.com) — это инструмент для разработчиков программ. С его помощью можно упростить перенос кода с 32-битных Windows-систем на 64-битные.

Андрей Карпов. 64 бита для Си++ программистов: от /Wp64 к Viva64.
Аннотация. Развитие рынка 64-битных решений поставило новые задачи в области их верификации и тестирования. В статье говорится об одном из таких инструментов — Viva64. Это lint-подобный статический анализатор Си/Си++ кода, предназначенный специально для выявления ошибок, связанных с особенностями 64-битных платформ. Освещены предпосылки для создания данного анализатора и отражена его связь с режимом «Detect 64-Bit Portability Issues» в Си++ компиляторе Visual Studio 2005.

Андрей Карпов, Евгений Рыжков. Сущность библиотеки анализа кода VivaCore.
Аннотация. Статья знакомит разработчиков с библиотекой VivaCore, предпосылками ее создания, возможностями, структурой и областями применения. Данная статья была написана параллельно с разработкой библиотеки VivaCore и поэтому отдельные детали ее конечной реализации могут отличаться от описанных здесь свойств. Но это не помешает разработчикам познакомиться с общими принципами работы библиотеки, механизмами анализа и обработки текстов программ на языке Си и Си++.

Андрей Карпов. Использование библиотеки анализа кода OpenC++: модификации, улучшения, исправления ошибок.
Аннотация. Статья представляет интерес для разработчиков, использующих или планирующих использовать библиотеку OpenC++ (OpenCxx). Автор рассказывает о своем опыте улучшения библиотеки OpenC++ и модификации библиотеки для решения специализированных задач.

Евгений Рыжков. Проблемы 64-битного кода на примерах.
Аннотация. При переносе 32-битного программного обеспечения на 64-битные системы в коде приложений, написанных на языке Си++, могут проявляться отсутствующие ранее ошибки. Причина этого кроется в изменении базовых типов данных (а точнее отношений между ними) на новой аппаратной платформе. В статье приводится примеры ошибок в коде, приводящие к неработоспособности Си++ программ при переносе их в среду Windows X64.

Андрей Карпов, Евгений Рыжков. Краткое описание библиотеки анализа кода VivaCore.
Описание. Наша команда, занимаясь исследованиями в области статического анализа и работая над созданием инструмента Viva64, пришла к выводу, что большая часть разработанных структур и алгоритмов может быть выделена в библиотеку и использована сторонними разработчиками для создания новых программных продуктов. Эта библиотека получила название — VivaCore.

Евгений Рыжков. Разработка драйверов для 64-битных версий Windows.
Аннотация. При разработке драйверов для 64-битных систем нужно учесть некоторые особенности, игнорирование которых впоследствии может привести к долгим попыткам понять, почему те или иные вещи работают не так, как раньше. MSDN достаточно полно освещает вопрос разработки 64-битных драйверов. Однако, иногда информация оказывается слишком разрозненной. Поэтому в данной статье представлен обзор основных проблем и направлений для дальнейшего изучения вопроса.

Андрей Карпов, Евгений Рыжков. Разработка ресурсоемких приложений в среде Visual C++.
Аннотация. Статья познакомит разработчиков прикладного программного обеспечения с задачами, которые ставит перед ним массовое внедрение многоядерных 64-битных вычислительных систем, знаменующих революционное увеличение вычислительной мощности, доступное рядовому пользователю. Будут рассмотрены вопросы эффективного использования аппаратных ресурсов для решения повседневных прикладных задач в рамках операционной системы Windows x64.

Андрей Карпов. 64 бита, /Wp64, Visual Studio 2008, Viva64 и все, все, все.
Аннотация. Данная статья ставит своей задачей ответить на ряд вопросов, касающихся безопасного переноса Си/Си++ кода на 64-битные системы. Статья написана как ответ на часто обсуждаемую в форумах тематику, связанную с использованием ключа /Wp64 и инструмента Viva64.

Андрей Карпов, Евгений Рыжков. Преимущества использования Viva64.
Аннотация. Цель данной рекламной статьи — показать преимущества от использования инструмента Viva64 в жизненном цикле разработки программного обеспечения.

Владимир Елесин. Перенос приложений на 64-битные платформы, или: «Не говори гоп, пока не перепрыгнешь:.»
Аннотация. 64-битные системы появились больше десяти лет назад, однако близко знакомиться с ними мы начали относительно недавно, когда они вышли на рынок массовых компьютеров. Все больше производителей программного обеспечения говорят о необходимости поддержки таких систем. Если раньше 64-битные процессоры были широко распространены, в основном, в сфере сложных и длительных вычислений — численного моделирования процессов гидро- и газодинамики, механики деформируемого твердого тела, экологии и молекулярной химии и т.д., а также обслуживания сверхбольших баз данных, то сегодня системы на их основе уже можно видеть в качестве типовых рабочих станций. Насколько же велика необходимость перевода своих приложений на 64-битную платформу? И если решение о переходе принято, то каким образом это можно сделать с наименьшими временными и материальными затратами? Давайте посмотрим.

Евгений Рыжков. Особенности разработки 64-битных приложений.
Аннотация. В данной статье перечислен ряд проблем, с которыми сталкиваются программисты после того, как перекомпилируют свои приложения для работы на 64-битных системах.

Алексей Колосов. Применение статического анализа при разработке программ.
Аннотация. Статический анализ — это способ проверки исходного кода программы на корректность. Процесс статического анализа состоит из трех этапов. Сначала анализируемый код разбивается на лексемы — константы, идентификаторы, и т. д. Эта операция выполняется лексером. Затем лексемы передаются синтаксическому анализатору, который выстраивает по этим лексемам дерево кода. Наконец, проводится статический анализ построенного дерева. В данной обзорной статье приведено описание трех методов статического анализа: анализ с обходом дерева кода, анализ потока данных и анализ потока данных с выбором путей.

Андрей Карпов, Евгений Рыжков. Применение технологии статического анализа кода при разработке параллельных программ.
Аннотация. В статье рассматривается вопрос применения статических анализаторов кода в современных процессах разработки параллельных программ. Появившись в 70-80-х годах как дополнение к компиляторам, статические анализаторы перестали пользоваться популярностью у разработчиков в 90-х годах. Вероятно, причиной этого стало повышение качества диагностики ошибок компиляторами. Однако, в 2000-х годах интерес к статическим анализаторам кода вновь начал расти. Это объясняется тем, что были созданы новые статические анализаторы кода, которые начали выявлять достаточно сложные ошибки в программах. Если статические анализаторы прошлого позволяли, например, обнаружить использование неинициализированной переменной, то современные статические анализаторы подходят к тому, чтобы обнаруживать небезопасный доступ к данным из нескольких потоков. Современным направлением развития статических анализаторов стало их применение для диагностики ошибок в параллельных программах. В работе рассмотрены ситуации, в которых применение таких инструментов позволяет существенно упростить создание параллельных программных решений.

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

Written on 24 Июня 2013 .

ОГЛАВЛЕНИЕ

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

Оглавление

Аннотация
Введение
1. Разбор разных 64-битных режимов
2. Выяснить, нужна ли 64-битная версия продукта

2.1. Длительность жизненного цикла приложений
2.2. Ресурсоемкость приложения
2.3. Разработка библиотек
2.4. Зависимость продукта от сторонних библиотек
2.5. Использование 16-битных приложений
2.6. Код на ассемблере

3.1. 64-битный компилятор
3.2. 64-битные компьютеры под управлением 64-битных операционных систем
3.3. 64-битные версии всех используемых библиотек
3.4. Отсутствие встроенного кода на ассемблере
3.5. Обновление методики тестирования
3.6. Новые данные для тестирования
3.7. 64-битные системы защиты
3.8. Установочный пакет

4. Настройка проекта в Visual Studio 2005/2008
5. Компиляция приложения
6. Выявление скрытых ошибок

6.1. Явное преобразование типа
6.2. Неявное преобразование типа
6.3. Биты и сдвиги
6.4. Магические числа
6.5. Ошибки, касающиеся использования 32-битных переменных в качестве индексов
6.6. Ошибки, касающиеся изменения типов используемых функций
6.7. Выявление скрытых ошибок

7. Обновление процесса тестирования

Аннотация

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

Введение

Статья описывает основные проблемы, стоящие перед разработчиками, планирующими перенести 32-битные программы на 64-битные системы. Конечно, список рассмотренных проблем не исчерпывающий, но в дальнейшем будет создан более подробный список. Автор будет рад получить отклики, комментарии и вопросы, помогающие повысить информативность статьи.

1. Разбор разных 64-битных режимов

В рамках архитектуры компьютера под термином «64-битный» понимаются 64-битные целые числа и иные типы данных размером 64 бит. Под «64-битными» системами могут пониматься 64-битные архитектуры микропроцессора (например, EM64T, IA-64) или 64-битная операционная система (например, Windows XP x64 профессиональная версия).

AMD64 (или x86-64, Intel 64, EM64T, x64) — 64-битная архитектура микропроцессора и соответствующая система команд, разработанные компанией AMD. Эта система команд была лицензирована компанией Intel под именем EM64T (Intel64). Архитектура AMD64 является расширением архитектуры x86 с полной обратной совместимостью. Архитектура стала распространенной в качестве основного компонента персональных компьютеров и рабочих станций.

IA-64 — 64- битная архитектура микропроцессора, разработанная совместно компаниями Intel и Hewlett Packard. Она реализована в микропроцессорах Itanium и Itanium 2. Архитектура в основном используется в многопроцессорных серверах и кластерных системах.

AMD64 и IA-64 – две разные 64-битные архитектуры, несовместимые друг с другом. Поэтому разработчики должны сразу решить, нужна ли им поддержка обеих архитектур или лишь одной из них. Большей частью, если вы не разрабатываете узкоспециализированное программное обеспечение (ПО) для кластерных систем или не реализуете свою собственную высокопроизводительную СУБД, вам понадобиться реализовать поддержку только архитектуры AMD64, гораздо более популярной, чем IA-64. Это особенно касается ПО для рынка ПК, почти на 100% занятого архитектурой AMD64.
Далее в статье рассматривается только архитектура AMD64 (EM64T, x64), поскольку сейчас она наиболее актуальна для разработчиков прикладного ПО.

Говоря о разных архитектурах, надо упомянуть понятие «модель данных». Под моделью данных понимаются соответствия между размерами типов, принятых внутри среды разработки. Может иметься несколько средств разработки, придерживающихся разных типов данных для одной операционной системы. Но обычно преобладает только одна модель, максимально соответствующая аппаратно-программной среде. Таким примером является 64-битная Windows, исходная модель данных которой — LLP64. Но ради совместимости 64-битная Windows поддерживает выполнение 32-битных программ, работающих в режиме модели данных ILP32LL. Таблица 1 дает сведения об основных моделях данных.

Таблица 1. Модели данных.

Используемая модель данных влияет на процесс разработки 64-битных приложений, так как надо учитывать размеры данных, используемых в программном коде.

2. Нужна ли 64-битная версия продукта?

Вы должны начинать изучать 64-битные системы с вопроса: «Действительно ли мне нужно переделывать проект для 64-битной системы?» Отвечайте на этот вопрос неспешно, обдуманно. С одной стороны, вы можете отстать от конкурентов, если не предлагаете 64-битные решения. С другой стороны, вы можете напрасно потерять время, разрабатывая 64-битное приложение, не дающие никаких конкурентных преимуществ.

Ниже перечислены основные факторы, которые помогут принять решение.

2.1. Длительность жизненного цикла приложений

Не надо создавать 64-битную версию приложения с коротким жизненным циклом. Благодаря подсистеме WOW64 старые 32-битные приложения успешно работают в 64-битных системах Windows, и оттого нет смысла делать программу 64-битной, если через 2 года она перестанет поддерживаться. Более того, практика показывает, что перенос на 64-битные версии Windows был отложен, и вероятно, большинство ваших пользователей будут использовать только 32-битную версию вашего программного решения в ближайшие годы.


Если вы планируете длительную разработку и поддержку программного продукта, начинайте разрабатывать 64-битную версию вашего решения. Вы можете делать это неспешно, но помните, что чем дольше вы не имеете полную 64-битную версию, тем с большими трудностями вы столкнетесь при поддержке этого приложения, установленного на 64-битных версиях Windows.

2.2. Ресурсоемкость приложения

Перекомпиляция программы для 64-битной системы позволяет ей использовать оперативную память большего объема и ускоряет ее работу на 5-15%. Увеличение на 5-10% будет получено из-за использования возможностей 64-битной архитектуры процессора, например, большего количества регистров. Остальное увеличение скорости на 1-5% объясняется отсутствием уровня WOW64, передающего вызовы API между 32-битными приложениями и 64-битной операционной системой.

Если ваша программа не работает с большими объемами данных (более 2Гб) и скорость ее работы не критична, перенос в 64-битную систему не срочен в ближайшее время.

Кстати, даже простые 32-битные приложения могут получить преимущество при запуске в 64-битной среде. Программа, собранная с ключом /LARGEADDRESSAWARE:YES, может выделять до 3 Гб памяти, если 32-битная Windows запущена с ключом /3gb. Эта же 32-битная программа, запущенная в 64-битной системе, может выделить около 4 Гб памяти (на практике около 3.5 Гб).

2.3. Разработка библиотек

Если вы разрабатываете библиотеки, компоненты или иные элементы, с помощью которых сторонние разработчики создают свое ПО, то вы должны действовать быстро при создании 64-битной версии вашего продукта. Иначе вашим клиентам, заинтересованным в выпуске 64-битных версий, придется искать альтернативные решения. Например, некоторые разработчики программно-аппаратной защиты с большим опозданием отреагировали на появление 64-битных программ, что заставило некоторых клиентов искать другие средства для защиты своих программ.

Дополнительное преимущество выпуска 64-битной версии библиотеки состоит в том, что ее можно продавать как отдельный продукт. При этом вашим клиентам, желающим создать 32-битное и 64-битное приложения, придется купить 2 разные лицензии. Например, такую политику использует Пространственная корпорация при продаже библиотеки Пространственный 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. Его выпуск ожидается к концу текущего года. Эту проблему невозможно обойти, только если переписать весь проект с использованием, например, Visual Studio. Но если все ясно с отсутствием 64-битного компилятора, другие подобные проблемы могут быть менее явными и возникать лишь на этапе переноса проекта на новую архитектуру. Оттого следует заранее выяснить, имеются ли все нужные компоненты, требуемые для реализации 64-битной версии вашего продукта, иначе вы можете столкнуться с неприятными неожиданностями.

Разумеется, здесь невозможно перечислить все, что вам может понадобиться для проекта, но будет продолжен список, помогающий вам сориентироваться и вспомнить прочие вещи, необходимые для реализации вашего 64-битного проекта:

3.1. 64-битный компилятор

Иметь 64-битный компилятор надо обязательно.

Если вы планируете разрабатывать 64-битные приложения с использованием последней (на момент написания статьи) версии Visual Studio 2008, следующая таблица 2 поможет понять, какой из выпусков Visual Studio вам нужен.

Таблица 2. Возможности разных выпусков Visual Studio 2008.

3.2. 64-битные компьютеры под управлением 64-битных операционных систем

Можно использовать виртуальные машины для запуска 64-битных приложений на 32-битных компьютерах, но это крайне неудобно и не обеспечивает необходимый уровень тестирования. Желательно, чтобы машины имели не менее 4-8 Гб оперативной памяти.

3.3. 64-битные версии всех используемых библиотек

Если библиотеки представлены в исходных кодах, то должна иметься 64-битная конфигурация проекта. Самостоятельно обновление библиотеки для 64-битной системы являются трудной задачей, и результат может быть ненадежным и содержать ошибки. Также вы можете нарушить лицензионные соглашения такими действиями. Если вы используете библиотеки в виде двоичных единиц, то вы также должны выяснить, существуют ли 64-битные единицы. Нельзя использовать 32-битную DLL (динамически подключаемая библиотека) в 64-битном приложении. Можно создать специальную связь с помощью COM (компонентная модель объектов), но это будет отдельной большой и тяжелой задачей. Так вам может понадобиться потратить лишние деньги на покупку 64-битной версии библиотеки.

3.4. Отсутствие встроенного кода на ассемблере

Visual C++ не поддерживает 64-битный встроенный ассемблер. Вы должны использовать внешний 64-битный ассемблер (например, MASM) или иметь реализацию с такой же функциональностью на C/C++.

3.5. Обновление методики тестирования

Означает серьезную переработку методики тестирования, обновление тестов модулей и использование новых инструментов. Подробно это рассмотрено далее, но не забывайте учесть это на этапе оценки временных затрат на перенос приложения в новую систему.

3.6. Новые данные для тестирования

Если вы разрабатываете ресурсоемкие приложения, использующие большие объемы оперативной памяти, вы должны пополнить базу входных данных для испытаний. При нагрузочном тестировании 64-битных приложений желательно превышать пределы 4 Гб используемой памяти. Многие ошибки происходят именно( лишь) в таких условиях.

3.7. 64-битные системы защиты

Используемая система защиты должна полностью поддерживать 64-битные системы. Например, компания Aladdin довольно быстро выпустила 64-битные драйверы для поддержки аппаратных ключей Hasp. Но долго не было никакой системы автоматической защиты 64-битных двоичных файлов (программа обертка Hasp). Следовательно, механизм защиты приходилось реализовывать вручную в программном коде, что было трудной задачей, требующей профессионализма и времени. Не забывайте о таких вещах, касающихся защиты, системы обновления и т.д.

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 шага:

Запустите диспетчер конфигураций, как показано на рисунке 1:

Рисунок 1. Запуск диспетчера конфигураций

В диспетчере конфигураций выберите поддержку новой платформы (рисунок 2):

Рисунок 2. Создание новой конфигурации

Выберите 64-битную платформу (x64) и в качестве основы – настройки из 32-битной версии (рисунок 3). Настройки, влияющие на режим сборки, будут автоматически исправлены Visual Studio.

Рисунок 3. Выберите x64 в качестве платформы и используйте конфигурацию Win32 в качестве основы

Добавление новой конфигурации завершено, и теперь вы можете выбрать 64-битную версию конфигурации и начать компилировать 64-битное приложение. Выбор 64-битной конфигурации для сборки показан на рисунке 4.

Рисунок 4. Теперь доступны 32-битная и 64-битная конфигурации

Если вам повезет, то вам не понадобится дополнительно настраивать 64-битный проект. Но это сильно зависит от проекта, его сложности и числа используемых библиотек. Вы должны изменить лишь размер стека. Если размер стека в вашем проекте задан по умолчанию, то есть 1 Мб, вы должны определить его как 2 Мб для 64-битной версии. Это не обязательно, но лучше подстраховаться заранее. Если вы используете размер, отличный от стандартного, то следует увеличить его вдвое для 64-битной версии. Чтобы сделать это, найдите и измените параметры «Размер резерва стека» и «Размер фиксации стека» в настройках проекта.


5. Компиляция приложения

Далее изложены типичные проблемы, возникающие на этапе компиляции 64-битной конфигурации, разобраны проблемы, возникающие в сторонних библиотеках, рассказано, что в коде, относящемся к функциям WinAPI, компилятор не позволяет помещать указатель в тип LONG, и вам придется обновить ваш код и использовать тип LONG_PTG. И это далеко не все. К сожалению, есть так много проблем, и ошибки столь разнообразны, что все их нельзя описать в одной статье даже книге. Вам придется самому изучать все ошибки, показываемые компилятором, и все отсутствовавшие ранее новые предупреждения, и в каждом конкретном случае выяснять, как надо исправить код.

Следующий список ссылок на ресурсы, посвященные разработке 64-битных приложений, может отчасти помочь вам: http://www.viva64.com/links/64-bit-development/. Список непрерывно дополняется, и автор будет рад, если читатели отправят ему ссылки на достойные внимания ресурсы.

Ниже описаны типы, представляющие интерес для разработчиков при переносе приложений. Эти типы показаны в таблице 3. Большинство ошибок перекомпиляции связано с использованием данных типов.

Тип

Размер типа на платформе x 32 / x 64

Примечание

Базовый тип . В 64-битных системах остается 32-битным.

Базовый тип . В 64-битных системах Windows остается 32-битным. Помните, что в 64-битных системах Linux этот тип был увеличен до 64-битного. Не забывайте об этом, если разрабатываете код, который должен компилироваться для систем Windows и Linux .

Базовый беззнаковый тип . Размер типа выбирается так, чтобы в него можно было записать массив теоретически возможного максимального размера. Можно безопасно поместить указатель в тип size _ t (исключая указатели на функции класса, являющиеся особым случаем).

Знаковый тип, похожий на size _ t . Результат выражения, в котором один указатель вычитается из другого ( ptr 1- ptr 2), имеет тип ptrdiff _ t .

Размер указателя прямо зависит от размера платформы. Будьте внимательны при преобразовании указателей в другие типы.

Знаковый 64- битный тип .

32- битный беззнаковый тип . В WinDef.h определен как : typedef unsigned( беззнаковый ) long( длинный ) DWORD;

64- битный беззнаковый тип . В WinNT.h определен как : typedef ULONGLONG DWORDLONG;

Беззнаковый тип, в который можно поместить указатель. В BaseTsd . h определен как: typedef ULONG _ PTR DWORD _ PTR ;

32- битный беззнаковый тип . В BaseTsd.h определен как : typedef unsigned int( целый ) DWORD32;

64- битный беззнаковый тип . В BaseTsd.h определен как : typedef unsigned __int64 DWORD64;

Половина указателя . В Basetsd.h определен как : #ifdef _WIN64 typedef int HALF_PTR;#else typedef short HALF_PTR;#endif

Знаковый тип, в который можно поместить указатель. В BaseTsd.h определен как : #if defined(_WIN64) typedef __int64 INT_PTR; #else typedef int INT_PTR;#endif

Знаковый тип , оставшийся 32- битным . Оттого теперь зачастую должен использоваться LONG _ PTR . В WinNT.h определен как : typedef long LONG;

Знаковый тип, в который можно поместить указатель. В BaseTsd.h определен как : #if defined(_WIN64) typedef __int64 LONG_PTR; #else typedef long LONG_PTR;#endif

Параметр для отправки сообщений . В WinNT.h определен как : typedef LONG_PTR LPARAM;

Аналог типа size_t. В BaseTsd.h определен как : typedef ULONG_PTR SIZE_T;

Аналог типа ptrdiff_t. В BaseTsd.h определен как : typedef LONG_PTR SSIZE_T;

Беззнаковый тип, в который можно поместить указатель. В BaseTsd.h определен как : #if defined(_WIN64) typedef unsigned __int64 ULONG_PTR;#else typedef unsigned long ULONG_PTR;#endif

Беззнаковый 16- битный тип . In WinDef.h определен как : typedef unsigned short( короткий ) WORD;

Параметр для отправки сообщений . В WinDef.h определен как : typedef UINT_PTR WPARAM;

Таблица 3. Типы, на которые надо обращать внимание при переносе 32-битных программ на 64-битные системы Windows.

6. Выявление скрытых ошибок

Если вы считаете, что после исправления всех ошибок компиляции получите долгожданное 64-битное приложение, то ошибаетесь. Самое трудное еще впереди. На этапе компиляции исправляются самые явные ошибки, которые компилятор смог обнаружить, и которые в основном связаны с невозможностью неявного преобразования типов. Но это лишь малая часть проблемы. Большинство ошибок скрыто. С позиции абстрактного языка C++ эти ошибки кажутся безопасными и скрыты под явными преобразованиями типов. Число таких ошибок намного превышает число ошибок, выявленных на этапе компиляции.

Не возлагайте надежды на ключ /Wp64. Этот ключ часто представляется как чудесное средство поиска 64-битных ошибок. На практике ключ /Wp64 позволяет получить некоторые предупреждения, касающиеся неправильности некоторых участков кода в 64-битном режиме при компиляции 32-битного кода. При компиляции 64-битного кода эти предупреждения показываются в любом случае. И поэтому ключ /Wp64 игнорируется при компиляции 64-битного приложения. Данный ключ не поможет в поиске скрытых ошибок.

Рассмотрим несколько примеров скрытых ошибок.

6.1. Явное преобразование типа

Простейший, но отнюдь не самый легкий для выявления класс ошибок связан с явными преобразованиями типов, когда обрезаются значимые биты. Известный пример – преобразование указателей в 32-битные типы при передаче их в такие функции, как SendMessage(послать сообщение):

Здесь явное преобразование типа используется для преобразования указателя в числовой тип. Для 32-битной архитектуры этот пример правилен, так как последний параметр функции SendMessage имеет тип LPARAM, совпадающий с DWORD в 32-битной архитектуре. Для 64-битной архитектуры DWORD неправилен и должен быть заменен на LPARAM. Тип LPARAM имеет размеры 32 или 64 бита в зависимости от архитектуры.

Это простой случай, но преобразование типа часто кажется более сложным, и его нельзя обнаружить с помощью предупреждений компилятора или поиска по тексту программы. Явные преобразования типов подавляют выявление ошибок компилятором, так как они предназначены для этой самой цели – сообщать компилятору, что преобразование типа верное, и что программист отвечает за безопасность кода. Явный поиск также не поможет. Типы могут иметь нестандартные имена (определенные программистом с помощью typedef), и велико количество методов, выполняющих явное преобразование типов. Для надежного обнаружения таких ошибок используйте только специальный инструментарий, такой как анализаторы Viva64 или PC-Lint.

6.2. Неявное преобразование типа

Следующий пример касается неявного преобразования типа, когда значимые биты также теряются. Код функции fread производит чтение из файла, но работает неверно, когда пытается считать более 2 ГБ в 64-битной системе.

Функция __fread возвращает тип size_t, но тип int используется для хранения количества считанных байтов. В результате при больших размерах считанных данных функция может вернуть неверное количество байтов.

Можно сказать, что это безграмотный код для начинающих, что компилятор сообщил о таком преобразовании типа, и что на самом деле данный код легко найти и исправить. Это в теории. На практике в крупных проектах все иначе. Этот пример взят из исходного кода FreeBSD. Ошибка была исправлена лишь в декабре 2008! Заметьте, что первая (пробная) 64-битная версия FreeBSD была выпущена в июне 2003.

Исходный код до исправления:

Исправленный вариант кода (декабрь 2008):

6.3. Биты и сдвиги

Легко сделать ошибку в коде при работе с отдельными битами. Следующий тип ошибки относится к операциям сдвига.

Этот код успешно работает в 32-битной архитектуре и позволяет установить биты с номерами 0 — 31 на единицу. После переноса программы на 64-битную платформу вам понадобится установить биты от 0 до 63. Но этот код никогда не установит биты 32-63. Обратите внимание, что «1» имеет тип int, и когда совершается сдвиг на 32 разряда, происходит переполнение, как показано на рисунке 5. Будет ли получен 0 (рисунок 5-B) или 1 (рисунок 5-C) в результате – зависит от реализации компилятора.

Рисунок 5. A – Правильная установка 32-го бита в 32-битном коде; B,C – ошибка установки 32-го бита в 64-битной системе (два способа поведения)

Чтобы исправить код, надо сделать константу «1» такого же типа, что и переменная mask(маска):

Также обратите внимание, что неверный код вызывает еще одну ошибку. При установке 31 бита в 64-битной системе результатом функции будет значение 0xffffffff80000000 (смотрите рисунок 6). Результатом выражения 1 в 32-битной системе вычисляется следующим образом: (на первом шаге i = 0):
• выражение ‘-i’ имеет беззнаковый тип и значение 0x00000000u.
• переменная ‘one’ будет увеличена с типа ‘int’ до беззнакового типа и будет равняться 0x00000001u. Примечание: тип int увеличивается (согласно стандарту C++) до беззнакового типа, если участвует в операции, где второй аргумент имеет беззнаковый тип.


Совершается операция вычитания, в которой участвуют два значения беззнакового типа, и результат операции равен 0x00000000u — 0x00000001u = 0xFFFFFFFFu. Результат имеет беззнаковый тип.

В 32-битной системе обращение к массиву по индексу 0xFFFFFFFFu тождественно использованию индекса -1. То есть конец[0xFFFFFFFFu] является аналогом конец[-1]. В результате элементы массива обрабатываются правильно.
В 64-битной системе ситуация будет отличаться в последнем пункте. Беззнаковый тип будет увеличен до знакового типа ptfdiff_t, и индекс массива будет равняться 0x00000000FFFFFFFFi64. В результате произойдет переполнение.
Чтобы исправить код, надо использовать типы 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).

Рисунок 8. Неправильный, но эффективный 32-битный код.

Ошибка возникает в 64-битной системе, где размеры типов DWORD и DWORD_PTR различаются (рисунок 9). В 64-битном режиме классы кажутся содержащими две разные функции WinHelp, что неверно. Помните, что такие ловушки могут скрываться не только в MFC(библиотека базовых классов Microsoft), в которой изменились типы аргументов ряда функций, но и в коде ваших приложений и сторонних библиотек.

Рисунок 9. Ошибка возникает в 64-битном коде

6.7. Выявление скрытых ошибок

Есть много примеров таких 64-битных ошибок. Те, кто интересуется данной темой и хочет узнать больше о таких ошибках, смотрите статью (

  • Вперёд >
  • При копировании материалов наличие активной индексируемой ссылки на сайт обязательно.

    20 ловушек переноса си кода на 64 битную платформу

    Группа: Главные администраторы
    Сообщений: 14349
    Регистрация: 12.10.2007
    Из: Twilight Zone
    Пользователь №: 1

    Недавно наша команда завершила миграцию на 64-битную платформу довольного большого проекта (9 млн строк кода, 300Mb исходников). Проект занял полтора года. Хотя в силу NDA мы не можем привести название проекта, надеемся, что наш опыт окажется полезен другим разработчикам.

    Об авторах
    Многие знают нас как авторов статического анализатора кода PVS-Studio. Это действительно основная наша деятельность. Однако кроме этого мы еще принимаем участие в сторонних проектах в качестве команды экспертов. Мы называем это «продажа экспертизы». Недавно мы публиковали отчет о работе над кодом Unreal Engine 4. Сегодня время очередного отчета о проделанной работе в рамках продажи нашей экспертизы.

    «Ага, значит дела с PVS-Studio у них идут совсем плохо!», — может воскликнуть читатель, который следит за нашей деятельностью. Спешим огорчить охочую до сенсаций публику. Участие в таких проектах очень важно для нашей команды, но совершенно по другой причине. Таким образом мы можем сами более активно пользоваться нашим анализатором кода в реальной жизни, нежели просто при его разработке. Реальное применение анализатора в коммерческих проектах, над которыми трудятся десятки или даже сотни программистов, дает потрясающий опыт команде PVS-Studio. Мы видим, как применяется наш инструмент, какие возникают сложности и что необходимо изменить или хотя бы просто улучшить в нашем продукте.

    Поэтому мы и дальше планируем участвовать в таких проектах в рамках продажи нашей экспертизы. Пишите, если у вас есть подходящий для нас проект. А пока рады представить отчет о миграции кода на 64-битную платформу.

    Введение, или в чем проблема? Рамки проекта и размер команды
    На первый взгляд с темой миграции кода на платформу x64 все уже понятно. Проверенная временем статья «Коллекция примеров 64-битных ошибок в реальных программах» была написана нами в 2010 году. Наш курс «Уроки разработки 64-битных приложений на языке Си/Си++» — в 2012 году. Казалось бы, читай, делай как написано, и все будет хорошо. Зачем же понадобилось заказчику обращаться к сторонней организации (к нам), и почему даже мы потратили на проект полтора года? Ведь если мы делаем в рамках PVS-Studio анализ 64-битных проблем, то вроде бы должны разбираться в теме? Конечно мы разбираемся, и это была главная причина того, что заказчик обратился к нам. Но почему у заказчика вообще появилась мысль обратиться к кому-то по поводу 64-битной миграции?

    Давайте сначала опишем проект и заказчика. Так как NDA запрещает говорить напрямую, то приведем лишь количественные характеристики. Проекту, над которым мы работали, порядка 20 лет. В настоящее время над ним ежедневно работают несколько десятков разработчиков. Клиенты — крупные компании, продажи единичны, так как продукт очень нишевый и узкоспециализированный.

    Ну и самое главное — это размер. 9 млн строк кода, 300Mb исходного кода, тысяча проектов в решении (.sln) это ОЧЕНЬ много. Платформа — Windows only. Но даже с таким проектом 64-битная миграция вроде бы должна быть понятной. Для того, чтобы перевести такой проект на x64 надо всего лишь:

    • на несколько месяцев полностью остановить разработку;
    • быстренько заменить типы данных на 64-битные;
    • проверить, что все корректно работает после замены;
    • можно возвращаться к разработке.

    Почему первым пунктом указано «полностью остановить разработку»? Да потому что для 64-битной миграции нужна, естественно, замена некоторых типов данных на 64-битные. Если в проекте такого размера создать отдельную ветку и внести там все необходимые правки, то объединить код (выполнить merge) будет невозможно! Не забывайте об объеме проекта и нескольких десятках программистов, которые ежедневно пишут новый код.

    В силу бизнес-ограничений заказчик не мог остановить процесс разработки. Его клиентам постоянно нужны новые релизы, исправления проблем, специальные возможности и т.п. Остановить разработку в таких условиях означает остановить бизнес. Поэтому заказчик стал искать команду, которая может выполнить миграцию без остановки процесса разработки. Такой командой стали мы, так как наша компетенция в 64-битной разработке подтверждается анализатором кода PVS-Studio и статьями по данной тематике.

    Мы выполнили миграцию за полтора года. С нашей стороны в проекте первые полгода принимали участие два человека, затем еще год уже четыре человека. Почему так долго? Первые полгода два человека занимались настройкой инфраструктуры, знакомством с проектом, проверкой конкретных способов миграции. Затем, через полгода, когда задача стала уже более конкретной, к проекту подключились еще люди и уже 4 человека за год выполнили миграцию.

    Как перенести проект на 64-битную систему?
    Перенос проекта на 64-битную платформу по большому счёту заключается в следующих двух шагах:

    1. Создание 64-битной конфигурации, получение 64-битных версий сторонних библиотек и сборка проекта.
    2. Исправление кода, который приводит к ошибкам в 64-битной версии. Этот пункт почти полностью сводится к тому, что нужно заменить 32-битные типы на memsize-типы в коде программы.

    Напомним, что под memsize-типами понимают типы переменной размерности. Такие типы имеют размер 4 байта на 32-битной системе и 8 байт на 64-битной.

    Портирование большого и активно развивающегося проекта не должно мешать текущей разработке, поэтому мы предприняли следующие меры. Во-первых, мы делали все наши правки в отдельной ветке, чтобы не ломать основную сборку. Когда очередной набор наших изменений был готов и протестирован, мы объединяли наши изменения с основной веткой. А во-вторых, мы не стали менять жёстко 32-битные типы на memsize-типы. Мы ввели свои типы и делали замену на них. Это было сделано для того, чтобы избежать потенциальных проблем, таких как, например, вызов другой реализации перегруженной функции, а также, чтобы иметь возможность быстро откатить наши изменения. Типы были введены приблизительно таким образом:

    #if defined(_M_IX86)
    typedef long MyLong;
    typedef unsigned long MyULong;
    #elif defined(_M_X64)
    typedef ptrdiff_t MyLong;
    typedef size_t MyULong;
    #else
    #error «Unsupported build platform»
    #endif

    Хотим еще раз подчеркнуть, что мы меняли типы не на size_t/ptrdiff_t и им подобные типы, а на свои собственные типы данных. Это дало большую гибкость и возможность легко отследить те места, которые уже портированы, от тех, где пока «не ступала нога человека».

    Возможные подходы к миграции: их плюсы и минусы, в чем мы ошиблись
    Первая идея портирования проекта была следующей: сначала заменить все 32-битные типы на memsize-типы за исключением тех мест, где 32-битные типы было необходимо оставить (например, это структуры, представляющие собой форматы данных, функции, обрабатывающие такие структуры), а потом уже привести проект в рабочее состояние. Мы решили сделать так для того, чтобы сразу устранить как можно больше 64-битных ошибок и сделать это за один проход, а потом доправить все оставшиеся предупреждения компилятора и PVS-Studio. Хотя такой способ работает для небольших проектов, в нашем случае он не подошёл. Во-первых, замена типов занимала слишком много времени и приводила к большому количеству изменений. А во-вторых, как мы ни старались делать это аккуратно, мы, тем не менее, правили по ошибке структуры с форматами данных. В результате, когда мы закончили работать над первой частью проектов и запустили приложение, мы не смогли загрузить предустановленные шаблоны интерфейсов, так как они были бинарными.

    Итак, первый план предполагал следующую последовательность действий.

    1. Создание 64-битной конфигурации.
    2. Компиляция.
    3. Замена большинства 32-битных типов на 64-битные (вернее на memsize-типы).
    4. Линковка со сторонними библиотеками.
    5. Запуск приложения.
    6. Правка оставшихся предупреждений компилятора.
    7. Правка оставшихся 64-битных ошибок, которые выявит анализатор PVS-Studio.

    И этот план был признан неудачным. Мы выполнили первые пять пунктов, и все наши изменения в исходном коде пришлось откатить. Мы потратили впустую несколько месяцев работы.

    Теперь мы решили сначала как можно быстрее получить рабочую 64-битную версию приложения, а потом уже поправить явные 64-битные ошибки. Наш план теперь исключал массовую замену типов и предполагал правку только явных 64-битных ошибок:

    1. Создание 64-битной конфигурации.
    2. Компиляция.
    3. Линковка со сторонними библиотеками.
    4. Запуск приложения.
    5. Правка предупреждений компилятора.
    6. Правка самых приоритетных 64-битных ошибок, которые выявит анализатор PVS-Studio.

    На этот раз мы получили первую рабочую версию приложения значительно быстрее, в том числе и потому что сторонние библиотеки уже были собраны, и шаблоны интерфейсов загрузились правильно. Надо сказать, что приложение в основном работало достаточно стабильно, что нас удивило. Мы нашли всего несколько падений при первом тестировании.

    Дальше нам предстояло поправить предупреждения компилятора и 64-битные предупреждения PVS-Studio, чтобы устранить найденные и потенциальные падения. Так как общее количество 64-битных предупреждений PVS-Studio исчислялась тысячами, то мы решили исправить только самые основные: неявные преобразования memsize-типов к 32-битным типам (V103, V107, V110), преобразования указателей к 32-битным типам и наоборот (V204, V205), подозрительные цепочки преобразований (V220, V221), приведение в соответствие типов в параметрах виртуальных функций (V301) и замена устаревших функций на новые версии (V303). Описание всех этих диагностик вы можете найти в документации.

    Другими словами, задача этого этапа — исправить все 64-битные сообщения PVS-Studio только первого уровня (level 1). Это наиболее важные диагностики. И для запуска 64-битного приложения все ошибки 64 L1 должны быть исправлены.

    Большинство таких правок сводилось к замене 32-битных типов на memsize-типы, как и при первом подходе. Но в этот раз, в отличие от первого подхода, эти замены носили выборочный и итеративный характер. Это было связано с тем, что правка типов параметров функции тянула за собой правки типов локальных переменных и возвращаемого значения, которые в свою очередь приводили к правке типов параметров других функций. И так до тех пор, пока процесс не сошёлся.

    Ещё один минус этого подхода в сравнении с первым заключается в том, что мы таким образом поправили только основные 64-битные ошибки. Например, типы счётчиков циклов мы не правили. В подавляющем большинстве случаев это было не нужно. И это не приводит к ошибкам, но возможно где-то это нужно было сделать, и такие места мы пропустили и не найдём при нашем подходе. Другими словами, возможно со временем еще кое-что придется поправить.

    При портировании приложения нам также потребовалось получить 64-битные версии сторонних библиотек. В случае библиотек с открытым исходным кодом мы старались собрать их из тех же исходников, из которых были собраны 32-битные версии. Это было связано с тем, что мы хотели сохранить возможные правки в коде сторонних библиотек, если такие были, и также нам нужно было собирать их по возможности в такой же конфигурации, в какой они были собраны для 32-битной версии. Например, некоторые библиотеки были собраны с настройкой не считать wchar_t встроенным типом или с отключенной поддержкой юникода. В таких случаях нам пришлось немного повозиться с параметрами сборки, прежде чем мы смогли понять, почему наш проект не может слинковаться с ними. Какие-то библиотеки не предполагали сборку под 64-битную версию. И в этом случае нам приходилось либо самим конвертировать их, либо скачивать более новую версию с возможностью сборки под 64-битную платформу. В случае коммерческих библиотек мы либо просили докупить 64-битную версию, либо искали замену не поддерживающимся больше библиотекам, как в случае с xaudio.

    Также нам нужно было избавиться от всех ассемблерных вставок, так как в 64-битной версии компилятора Visual C++ ассемблер не поддерживается. В этом случае мы либо использовали intrinsic функции там, где это можно было сделать, либо переписывали код на C++. В некоторых случаях это не приводило к ухудшению производительности, например, если в 32-битном ассемблерном коде использовались 64-битные MMX регистры, то в 64-битной версии у нас и так все регистры 64-битные.

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

    Примеры 64-битных проблем, с которыми мы столкнулись
    Самой распространённой ошибкой при портировании на 64-битную платформу было явное приведение указателей к 32-битным типам, например, к DWORD. В таких случаях решением была замена на memsize-тип. Пример кода:

    MMRESULT m_tmScroll = timeSetEvent(
    GetScrollDelay(), TIMERRESOLUTION, TimerProc,
    (DWORD)this, TIME_CALLBACK_FUNCTION);

    Также проявились ошибки при изменении параметров виртуальных функций в базовом классе. Например, у CWnd::OnTimer(UINT_PTR nIDEvent) тип параметра поменялся с UINT на UINT_PTR с появлением 64-битной версии Windows, и соответственно во всех наследниках в нашем проекте нам тоже надо было выполнить эту замену. Пример кода:

    class CConversionDlg : public CDialog <
    .
    public:
    afx_msg void OnTimer(UINT nIDEvent);
    .
    >

    Некоторые WinAPI фукции поддерживают работу с большими объёмами данных, как, например, CreateFileMapping и MapViewOfFile. И мы адаптировали код соответствующим образом:


    sharedMemory_ = ::CreateFileMapping(
    INVALID_HANDLE_VALUE, // specify shared memory file
    pSecurityAttributes, //NULL, // security attributes
    PAGE_READWRITE, // sharing
    NULL, // high-order DWORD of the file size
    sharedMemorySize, // low-order DWORD of the file size
    sharedMemoryName_.c_str());

    #if defined(_M_IX86)
    DWORD sharedMemorySizeHigh = 0;
    DWORD sharedMemorySizeLow = sharedMemorySize;
    #elif defined(_M_X64)
    ULARGE_INTEGER converter;
    converter.QuadPart = sharedMemorySize;
    DWORD sharedMemorySizeHigh = converter.HighPart;
    DWORD sharedMemorySizeLow = converter.LowPart;
    #else
    #error «Unsuported build platform»
    #endif
    sharedMemory_ = ::CreateFileMapping(
    INVALID_HANDLE_VALUE, // specify shared memory file
    pSecurityAttributes, //NULL, // security attributes
    PAGE_READWRITE, // sharing
    sharedMemorySizeHigh, // high-order DWORD of the file size
    sharedMemorySizeLow, // low-order DWORD of the file size
    sharedMemoryName_.c_str());

    Ещё в проекте нашлись места использования функций, которые в 64-битной версии считаются устаревшими и должны быть заменены на соответствующие новые реализации. Например, GetWindowLong/SetWindowLong следует заменить на GetWindowLongPtr/SetWindowLongPtr.

    PVS-Studio находит все приведённые примеры и другие виды 64-битных ошибок.

    Роль статического анализатора PVS-Studio при 64-битной миграции
    Часть потенциальных ошибок при миграции на 64-битную платформу находит компилятор и выдаёт соответствующие предупреждения. PVS-Studio лучше справляется с этой задачей, так как инструмент изначально разрабатывался с целью находить все такие ошибки. Более подробно о том, какие 64-битные ошибки находит PVS-Studio и не находят компилятор и статический анализатор Visual Studio, можно прочитать в статье «64-битный код в 2015 году: что нового в диагностике возможных проблем?».

    Хочется обратить внимание ещё на один важный момент. Регулярно используя статический анализатор, мы могли постоянно наблюдать, как исчезают старые, а иногда добавляются новые 64-битные ошибки. Ведь код постоянно правят десятки программистов. И иногда они могут ошибиться и внести 64-битную ошибку в проект, который уже адаптирован к x64. Если бы не статический анализ, было бы невозможно сказать, сколько ошибок исправлено, сколько внесено, и на каком этапе мы сейчас находимся. Благодаря PVS-Studio мы строили графики, которые помогали нам иметь представление о прогрессе. Но это уже тема для отдельной статьи.

    Заключение
    Для того, чтобы 64-битная миграция вашего проекта прошла как можно более спокойно, последовательность шагов должна быть следующей:

    1. Изучить теорию (например, наши статьи).
    2. Найти все 64-битные библиотеки, которые используются в проекте.
    3. Максимально быстро собрать 64-битную версию, которая компилируется и линкуется.
    4. Исправить все 64-битные сообщения первого уровня анализатора PVS-Studio (64 L1).

    Что почитать про 64-битную миграцию?

    Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: How to Port a 9 Million Code Line Project to 64 bits?.

    Rakafon’s blog

    2 апр. 2009 г.

    Организация доступа к 32-bit DLL из 64-bit приложения.

    Перенос 32-битных Windows приложений на 64-битную машину может быть весьма проблематичным, если у вас есть 32-разрядные библиотеки DLL, которые вы не можете переписать и портировать для 64-bit. Майк Беккер покажет вам, как можно получить доступ к 32-bit DLL из 64-битного кода с помощью встроенных механизмов IPC.

    64-разрядные технологии Microsoft впервые появились в Windows Server 2003 для процессоров Itanium 2 (архитектура ихвестна как «IA64») а также для eXtended CPUs (архитектура известна как «x64»). 64-bit технология имеет много преимуществ, но также поднимает новые вопросы для разработчиков программного обеспечения. Например, вам может понадобиться необходимость доступа к существующим 32-разрядным библиотекам DLL из 64-битного процесса.

    Главное преимущество технологии 64-бит состоит в способности адресовать до 8 ТБ памяти, против максимальных 2 Гб для 32-битных процессов. Как результат, 64-разрядная технология позволяет проводить операции с большим объёмом данных в оперативной памяти без необходимости временного сброса памяти на жётский диск. Это может значительно повысить производительность и открыть новые алгоритмы обработки данных базирующихся на очень больших доступных объёмов оперативной памяти. Как бы то ни было аргументы для миграции существующего программного обеспечения на 64-битную платформу имеют место быть.

    Многие приложения, написанные с помощью C/C++ могут быть легко портированы под 64-битную платформу, особенно если они написаны в виде монолитного модуля. Иногда достаточно просто пересобрать исходные коды с использованием x64/IA64 компилятора. Однако уже опубликованное или базирующееся на модулях ПО может вызвать проблемы.

    Конфликт: 64-bit против 32-bit

    Основная проблема миграции возникает при необходимости портирования 32-разрядных программных компонентов, которые не могут быть пересобраны, возможно, потому что исходный код потерян, к нему нет доступа или одна из зависимостей этого модуля не можeт быть перенесена на 64-bit платформу.

    32-битное ПО по-прежнему поддерживается на 64-битной платформе. Так 32-битные процессы могут выполняться внутри Windows WOW64-подсистемы, которая является частью всех 64-битных систем Windows. Однако 64-разрядный процесс не может загружать 32-разрядные модули в своё адресное пространство, также и 32-разрядные процессы не могут загружать 64-разрядные модули в своё адресное пространство. Единственный способ общения между 32-битными и 64-битными модулями возможен путём межпроцессного взаимодействия (IPC). Другими словами, 32-разрядные и 64-разрядные процессы могут обмениваться данными с использованием IPC-механизмов, например такие как out-of-proc COM, сокеты, сообщения Windows или MMF (Memory mapped files).

    Например, 32-битный программный продукт содержит основной модуль WeatherReport (см. рисунок выше), который внутри обращается к DLL WeatherStationControl. Пока основной модуль и DLL являются 32-разрядными, продукт может работать как на 32-битных, так и 64-битных платформах (внутри WOW64). Если основной модуль и DLL переносятся на 64-битную платформу, то они могут работать в рамках 64-битных процессов. Однако, если только основной модуль переносится на 64-bit, он не сможет загружать 32-разрядные DLL.

    Лучшим способом переноса такого продукта на 64-битную платформу является миграция как основного модуля, так и всех его зависимостей, но если хоть одна зависимая DLL не может быть перенесена, то продукт не может быть загружен в 64-битном процессе, следовательно приложение работать не будет.

    Решение: суррогатный процесс

    Эта проблема может быть решена путем загрузки зависимой 32-битной DLL в отдельном пространстве 32-разрядного процесса. Основной модуль, работая в качестве 64-битного процесса, может получить доступ к зависимым DLL’ям через границы процессов, используя IPC (Смотри MSDN reference).

    64-битный процесс может получить доступ к 32-разрядной DLL используя механизмы межпроцессного взаимодействия. То бишь 32-разрядные DLL загружаются в отдельный 32-битный суррогатный процесс, и тогда 64-битное приложение использует встроенные механизмы IPC для того, чтобы обмениваться данными 32-битным процессом.

    Это решение требует дополнительной работы, например необходимо создать 32-разрядный суррогатный процесс, который загрузит 32-разрядные DLL и предоставит API для работы с ними. Кроме того, некоторые изменения будет необходимо стелать со стороны 64-битного клиента, т.к. клиент будет вынужден использовать методы IPC вместо непосредственного доступа к 32-разрядной DLL. Стоит отметить, что, в крайнем случае, эта дополнительная работа может быть сравнима с работой, которую необходимо выполнить при разработке 64-разрядной версию 32-битной DLL с нуля.

    Одним из возможных путей сокращения этих расходов является реализация 64-разрядной DLL обертки, которая предоставит те же функции, что и оригинальная 32-битная DLL. Эта обёртка уже внутри себя скроет детали IPC вызовов оригинальной 32-битной DLL, загруженной в 32-битный суррогатный процесс.

    64-битная обёртка (WeatherStationControl64.DLL) экспортирует тот же интерфейс, что и оригинальная 32-битная DLL (WeatherStationControl.DLL), то бишь предоставляет главному модулю WeatherReport те же сервисы без необходимости внесения изменений в код модуля WeatherReport.

    Основные затраты этого решения связаны с реализацией суррогатного процесса, загружающего 32-битную DLL и реализаццей 64-битной DLL оболочки. Фактически затраты будут зависеть от того, какая из IPC технологий будет использоваться для обмена данными между 64-битным и 32-битным процессами.

    Использование COM для межпроцессного взаимодействия

    Один из самых популярных методов IPC — это DCOM (Distributed COM). Первоначально разработанная для распределенных систем, DCOM по-прежнему поддерживается как на 64-битных платформах Windows. Модули COM могут быть собраны как 32-разрядные так и 64-разрядные. Единственным ограничением является то, что 64-битные и 32-битные модули не могут находиться в одном и том же процессе, следовательно они должны взаимодействовать через границы процессов. Это делается с помощью out-of-process (OOP) COM компонентов, следующим образом:

    1. Создаём 32-битный COM-Сервер, который загрузит 32-битную DLL и опубликует 32-bit DLL интерфейс как COM-интерфейс, реализованный внутри через делегирование к API исходной 32-битной DLL.
    2. Конфигурируем этот COM-Сервер для out-of-proc загрузки любым способом создания COM+ приложений (используя dllhost.exe в качестве суррогата).
      Также можно реализовать этот COM-компонент как специальный COM-Сервер EXE, используя ATL COM Server в качестве хостящего процесса.
      Можно также помеcтить этот COM-компонент внутрь Win32-сервиса.
    3. Создаём 64-разрядную DLL оболочку, реализующую тот же интерфейс, как оригинальная 32-битная DLL. Обёртка будет импортировать COM-интерфейс из COM-объекта, созданного выше, и транслировать каждый API вызов в обращение к COM-интерфейсу, передавая параметры вызова и получая возвращаемые значения.

    32-разрядная DLL (WeatherStationControl.DLL) используется СОМ-объектом (WeatherStationWrapper), который предоставляет интерфейс 32-битной DLL в качестве COM-интерфейса. 64-битная DLL обёртка (WeatherStationControl64.DLL) делает вызовы COM-интерфейса, которые уже делегируют всю работу оригинальным API вызовам исходной 32-битой DLL. Основной процесс (WeatherReport) использует интерфейс, предоставляемый 64-разрядной DLL обёрткой, но на самом деле работа выполняется в оригинальной 32-битной DLL.

    Это решение должно быть значительно дешевле, чем создание 64-разрядную версию 32-битных DLL с нуля. Библиотека ATL, поддерживающаяся Visual Studio, вкупе со всеми своими визардами и готовыми фрагментами кода, также должны помочь снизить затраты миграции за счет экономии времени и снижения вероятности ошибок.

    Последствия

    Однако существует ряд вещей, которые вам все еще нужно иметь в виду:

    1. Выравнивание
      Выравнивание данных в памяти отличается в 32-bit и 64-bit процессах. Это означает, что более сложные пользовательские структуры данных могут быть сериализованы 32-разрядным процессом иначе, чем ожидается в 64-битном проыцессе, и наоборот. Microsoft Windows Platform SDK включает документацию о различиях в соответствие памяти данными между 32-битной и 64-битных процессов.
    2. Типы данных
      В большинстве случаев, 64-разрядная Windows использует те же типы данных, как 32-битной версии. Различия в основном в указателях, которые 32-битные в 32-битной версий Windows и 64-битные в 64-битных Windows.
      Указатель полученных данных типов, таких как HANDLE и HWND также различны между 32-битной и 64-разрядных версиях. Windows позволяет вести единый базовый код для 32-разрядных версиях и 64-битного программного обеспечения, предлагая полиморфные типы данных, которые имеют различную длину в зависимости от целевой платформы, например INT_PTR представляет целое с таким же размером, как и указатель. Любая переменная этого типа будет целым числом, которое составляет 32 бита на 32-битной платформе и 64 бита на 64-битной платформе.
    3. COM инициализации
      Вы можете получить доступ к СОМ-объекту, если он был успешно инициализирован. Функция COM API CoInitialize() должна вызываться для каждого потока, который собирается работать с COM-объектом, т.е. делать вызовы COM-интерфейсов, также должна вызываться CoUninitialize() перед завершением потока.(см. MSDN). Это правило должно строго соблюдаться, если основной процесс вызывает оригинальные 32-битные DLL из разных потоков выполнения.
    4. Безопасность
      При использовании out-of-proc, экземпляры COM-объектов находятся в отдельном процессе, независимо от того используете вы стандартный суррогатной процесс, EXE COM-Сервер или Win32 сервис. Это может означать, что вызовы 32-битной DLL могут произойти в другом контексте безопасности, чем у основного процесса, особенно если основной процесс интенсивно использует имперсонирование. Если это так, вы можете настроить собственные параметры доступа для out-of-proc компонента, или осуществлять внутреннее имперсонирование внутри COM-объекта.
    5. Производительность
      IPC-решение почти наверняка будет медленнее, чем прямые вызовы DLL. Маршалинг данных за границы процессов, автоматическое преобразование данных между 32 и 64 бит, WOW64 особенности, задержки инстанцирования экземпляров COM-объектов — всё это будет влиять на производительность. Однако есть много методов оптимизации, которые можно использовать, таких как COM pooling, кэширование данных внутри DLL-оболочки, реализация критичных к производительности интерфейсов в 64-битной DLL, и так далее.
    6. Перенаправление
      Подсистема WOW64 отвечает за поддержку 32-битных модулей на 64-битных Windows. Чтобы избежать нежелательных коллизий между 32-битным и 64-битным ПО, особенно при доступе к файловой системы и реестру, WOW64 изолирует 32-разрядные модули, используя механизм, называемый перенаправление (см. MSDN).
      Например, для 64-битного процесса при получении пути системной папки возвращается %WINDOWS%\System32, а для 32-разрядного процесса возвращается %WINDOWS%\SysWOW64.
      Путь к папке исполняемого файла будет «Program Files» для 64-битного процесса, но для 32-разрядного процесса это будет «Program Files (x86)».
      Раздел реестра HKEY_LOCAL_MACHINE\Software содержит настройки и данные для 64-разрядных процессов, а ключ HKEY_LOCAL_MACHINE\Software\WOW6432Node содержит настройки и данные для 32-разрядных процессов.
      Это перенаправление активируется автоматически, когда программные модули пытаются получить предопределённые системные пути или ключи реестра.
    7. Модули ядра
      Предложенное здесь решение работает для 32-разрядных DLL, использующихся в user mode, но не работает с 32-битными драйверами. Это происходит, потому что 32-битные модули ядра не могут быть использованы на 64-битной платформе, без исключений или обходных путей. Если ваш продукт включает в себя любой модуль уровня ядра, таких как драйвер устройства, то единственно возможный путь миграции — это портировать модуль ядра на 64-битную платформу.
    8. Установка.
      Использование out-of-proc COM-компонента требует изменений в процедуре установки вашего ПО, т.к. COM-компонент должен быть установлен и зарегистрирован в системе. Как уже говорилось выше в пункте Безопасность, для этого может потребоваться настройка специальных параметров доступа для COM-компонента.

    Тыкните сюда дабы скачать пример кода

    20 ловушек переноса Си++ — кода на 64-битную платформу

    Published on
    12-Jun-2015

    • 1. 20 ++ — 64- : , : 01.03.2007 , ++ — 32- 64- . . , . 64- . , , . » 64- «, 64-. » 64- /++», 64- Viva64. , 32- 64- . , ++, , . 64- Viva64, 64- . , , 64-, , . , . . ( , ) , . 64- . , 64- — . , 64- . , 64- . , .

    2. , 64- . : , 64- ; , ( ); ; 64- . : 32- 64- ; 64- ; 32- 64- ; 64- . , . , . 64-. , (. N1). — — (32- (64-) )ptrdiff_t 32 64 , . . , -1 .size_t32 64 . sizeof(). .intptr_t, uintptr_t, 32 64 , SIZE_T, SSIZE_T, .INT_PTR,DWORD_PTR, time_t 32 64 . N1. . «memsize» . memsize- , 32- 64-. memsize-: size_t, ptrdiff_t, , intptr_t, INT_PTR, DWORD_PTR. , . N2 , . 3. ILP32 LP64 LLP64 ILP64char8 88 8short 1616 1616int 3232 3264long3264 3264long long 6464 6464size_t3264 6464pointer 3264 6464 N2. 32- 64- , , ILP32, LP64 LLP64. : 64- Linux (LP64) Windows (LLP64) long. , long, unsigned long, ptrdiff_t,size_t. , 64-.1. , , , . , . , , . , , , , . 64- . , . . , . , 64-, :unsigned char *array[50];unsigned char size = sizeof(array);32-bit system: sizeof(array) = 20064-bit system: sizeof(array) = 4002. printf, scanf : 4. 1) const char *inval , pointer); , size_t unsigned 64-. , value >UINT_MAX. , 32. 64- . , 64-. ++. . . , printf cout, sprintf boost::format std::stringstream. , sscanf, , . :// PR_SIZET on Win64 = «I»// PR_SIZET on Win32 = «»// PR_SIZET on Linux64 = «l»// . size_t u;scanf(«%» PR_SIZET «u», &u);3. , . 64- , , . N3 , . 4 32 0x7fffffff 32- . 5. 32- .0x80000000 32- . 32- .0xffffffff 32- . -1 . N3. , 32- 64- . . sizeof(), , . , . :1) size_t ArraySize = N * 4;intptr_t *Array = (intptr_t *)malloc(ArraySize);2) size_t values[ARRAY_SIZE];memset(values, ARRAY_SIZE * 4, 0);3) size_t n, newexp;n = n >> (32 — newexp); , , 4 . sizeof():1) size_t ArraySize = N * sizeof(intptr_t);intptr_t *Array = (intptr_t *)malloc(ArraySize);2) size_t values[ARRAY_SIZE];memset(values, ARRAY_SIZE * sizeof(size_t), 0);memset(values, sizeof(values), 0); //preferred alternative3) size_t n, newexp;n = n >> (CHAR_BIT * sizeof(n) — newexp); . size_t, 4 . 32- :// constant 1111..110000const size_t M = 0xFFFFFFF0u; 64- . , 6. . , , , #ifdef .#ifdef _WIN64#define CONST3264(a) (a##i64)#else#define CONST3264(a)(a)#endifconst size_t M =

    CONST3264(0xFu); «-1», «0xffffffff». 64- -1. , 0xffffffff :#define INVAL 64- . 7. , . — , -1.#define INVAL >

    сложность разработки под 64 bit

    дисклаймер: писал монго на асме,С и тп, так что вопрос не совсем нубский.

    всегда думал, что собрать прогу под 64 битную платформу — это просто поменять кое-где в коде типа вида small int на long (по идее автоматом должно компилиться)


    а на практике — Flash 64bit нет вроде до сих пор. вон тут рядом удивляются, что хром так быстро под 64 бит написали.

    в чем проблема?
    в чем такое кардинальное отличие 32 от 64?

    Re: сложность разработки под 64 bit

    Скорее всего, общение с ОС. Впрочем, мне тоже интересно послушать граблепроходцев.

    Re: сложность разработки под 64 bit

    кстати, в эту же степь можно отнести вопрос, почему до сих пор в гимпе нет поддержки 16 цвета? в чем проблема поменять тип с int на long int в структурах, где хранятся массивы цветовых пространств.

    подозреваю, что все намного сложнее, но не могу понять что именно ((

    Re: сложность разработки под 64 bit

    Граблей очень много (если с самого начала не удосужиться подумать чуть-чуть головой). Например, использование типов нефиксированного размера (т.е., например, long int вместо int32_t) там, где явно нужны, к примеру, 32битные целые, тем более, в передаче в бинарном виде по сети/записи в файл/et cetera. Неаккуратная работа со спецификаторами printf/scanf. И так далее, и тому подобное.

    Основная проблема в том, что большинство проблем очень сложно обнаружить.

    Re: сложность разработки под 64 bit

    > Впрочем, мне тоже интересно послушать граблепроходцев.
    если самого начала юзать как написали выше переопределенный int8, uint8, int16, uint32 и т.п. то граблей в x86_64 нет.
    мне гораздо больше неприятностей доставило big/little engian.

    Re: сложность разработки под 64 bit

    добавлю что, чем изобретать велосипед лучше юзать кроссплатформенный тулкит, и по тем правилам как они же рекомендуют.

    Re: сложность разработки под 64 bit

    >это просто поменять кое-где в коде типа вида small int на long

    Так это ж еще найти надо где менять.

    Пример:
    void foo(int n) < . >
    .
    long n;
    foo(n);

    соберется на обоих платформах без проблем, а нормально работать будет только на 32.

    Re: сложность разработки под 64 bit

    Re: сложность разработки под 64 bit

    > Граблей очень много (если с самого начала не удосужиться подумать чуть-чуть головой). Например, использование типов нефиксированного размера (т.е., например, long int вместо int32_t) там, где явно нужны, к примеру, 32битные целые, тем более, в передаче в бинарном виде по сети/записи в файл/et cetera. Неаккуратная работа со спецификаторами printf/scanf. И так далее, и тому подобное.

    низкий уровень профессионализма конкретных разработчиков мы не рассматриваем. вопрос сугубо технический и с этой точки зрения разница между i386 и amd64 сугубо минорная.

    если же кто-то вдруг считает, что его int всегда и везде будет 32bit или что он будет всегда в LE/BE ордере — это его личные сексуальные трудности. но платформа то в этом не виновата, правда?

    Re: сложность разработки под 64 bit

    Re: сложность разработки под 64 bit

    Кстати, ЕМНИП в венде граблей чуть меньше, так как там из стандартных типов распухли только void* и size_t.

    Re: сложность разработки под 64 bit

    Для большинства языков от перехода с x86 на x86-64 будет только полезно.

    Никаких особых сложностей с переходом x86 x86-64 при программировании на C нет. Если программист не криворукий, то все будет нормально.

    Re: сложность разработки под 64 bit

    Для обычных программ — только типы поменять, а вот для flash и chromium — переписывать генератор машинных инструкций из байткода под новый асм, т.е. не так и мало.

    Re: сложность разработки под 64 bit

    > Для обычных программ — только типы поменять

    Для *правильно* написанных программ — только перекомпилировать.

    Re: сложность разработки под 64 bit

    > низкий уровень профессионализма конкретных разработчиков мы не рассматриваем.

    Наоборот, мы не рассматриваем сферических программистов, переносящих свой сферический софт на сферический amd64 в вакууме. 8))

    Вопрос был — какие могут быть проблемы, что флеш под x86_64 так долго появлялся, например. Да, проблема только одна — криворукость. Но эта проблема как-то уж черезчур общая. 8))

    > но платформа то в этом не виновата, правда?

    На платформу никто и не ругается. Более того, перенос на любую другую архитектуру ещё никому не вредил. В лучшем случае поднимает ЧСВ авторов (какие мы крутые, просто перекомпилировали — а оно прекрасно заработало на 113битной архитектуре с mixed endian! 8)) ), в худшем — помогает избавиться от кучи wtf’ов в коде.

    Re: сложность разработки под 64 bit

    > Да, проблема только одна — криворукость. Но эта проблема как-то уж черезчур общая. 8))

    Она часто встречается. Вот примеры для сравнения: Linux перетащили на amd64 довольно шустро, хотя там кода немало и завязан на архитектуру не меньше, чем тот же флеш. Можно ещё NetBSD вспомнить. А некоторые дрова под тот же Linux до сих пор прибиты гвоздями к i386.

    Re: сложность разработки под 64 bit

    Сложностей полно. И на продукте PVS-Studio (старое название Viva64) для поиска 64-битных ошибок в программах мы зарабатываем свои деньги.

    Вот несколько тематических статей о проблемах миграции 32-битного кода на 64-битные системы:

    20 ловушек переноса Си++ — кода на 64-битную платформу — http://www.viva64.com/art-1-1-1958348565.html

    7 шагов по переносу программы на 64-битную систему — http://www.viva64.com/art-1-1-1148261225.html


    Re: сложность разработки под 64 bit

    > Сложностей полно. И на продукте PVS-Studio (старое название Viva64) для поиска 64-битных ошибок в программах мы зарабатываем свои деньги.

    Некоторые умудряются заработать денег на магических наклейках для сотовых телефонов, защищающих мозг от излучения.

    Re: сложность разработки под 64 bit

    Re: сложность разработки под 64 bit

    > Для обычных программ — только типы поменять, а вот для flash и chromium — переписывать генератор машинных инструкций из байткода под новый асм, т.е. не так и мало.

    А зачем его вообще переписывать?

    Re: сложность разработки под 64 bit

    На самом деле, если всё начать писать с нуля, то особых проблем быть не должно, но это малореально. В основном, при разработке чего-либо, для реализации конкретных деталей используются уже имеющиеся наработки, которые не всегда являются универсальными в данном контексте, не говоря уже о переписывании имеющихся версий для удовлетворения меняющимся требованиям. Вообщем-то это проблемы кривого и недальновидного проектирования.

    Re: сложность разработки под 64 bit

    >хром на 95% это webkit а остаток косметика от гугла. Так вот webkit в равной степени отлично собирается как под 32 так и под 64 бита. А «так быстро» гугл написал как раз вот ту самую косметику по кнопачкам которой тыкают пользователи.

    Там есть JIT-движок жабоскрипта V8 который девелоперы не хотят портировать на x86-64 до тех пор пока x86-64 не станет десктопным мейнстримом везде включая виндовс.

    Re: сложность разработки под 64 bit

    >Кстати, ЕМНИП в венде граблей чуть меньше, так как там из стандартных типов распухли только void* и size_t.

    Ну вообще-то размер стандартных типов языка C (int,long, etc) зависит от разрядности системы независимо от OS.

    Re: сложность разработки под 64 bit

    > Вопрос был — какие могут быть проблемы, что флеш под x86_64 так долго появлялся, например. Да, проблема только одна — криворукость. Но эта проблема как-то уж черезчур общая. 8))

    конкретно по флешу ответ скорее прост: он никому нафик не нужен на 64х битных архитектурах и AMD64 в том числе. ну совсем не нужен. даже на i386 линуксе то факт, что флеш вообще появился — это уже серьёзный прогресс. количество десктопных пользователей линуксы и так мизерно. теми же отчаянными, которые по какой-то причине гоняют десктоп на AMD64 можно смело пренебречь и вынести за пределы погрешности. овчинка совершенно не стоит выделки. вне зависимости от того, сложно портировать флеш с i386 на AMD64 или не очень.

    Re: сложность разработки под 64 bit

    > Ну вообще-то размер стандартных типов языка C (int,long, etc) зависит от разрядности системы независимо от OS.

    Я чуть выше запостил ссылку на IBM dW. Там есть табличка «Table 1. 32-bit and 64-bit data models» и в ней три разных набора размеров для стандартных типов на 64х-разрядной системе. Если не ошибаюсь, в линуксе используется LP64, а в виндоусе — LLP64.

    Плюс ко всему, размеры большинства «стандартных» типов WinAPI (названия которых написаны обычно в блондинко-стиле =)) одинаковы в Win32 и Win64.

    Зато в линуксе, при условия правильного написания кода, проще получить преимущества 64х-разрядной системы.

    Re: сложность разработки под 64 bit

    Ух как всё сложно в виндах :). Юниксы в этом плане проще.

    Re: сложность разработки под 64 bit

    >> Для обычных программ — только типы поменять, а вот для flash и chromium — переписывать генератор машинных инструкций из байткода под новый асм, т.е. не так и мало.

    >А зачем его вообще переписывать?

    Уговорил, дописывать. Там же генерятся машинные инструкции, и для x64 они другие, регистров больше, т.е. V8 — это же фактически компилятор, и ему совсем не пофиг для какой платформы генерить код. AFAIK бинарной совместимости между записью машинных инструкций x86 и x64 нет.

    Re: сложность разработки под 64 bit

    > Flash 64bit нет вроде до сих пор.

    Сто лет как есть. Сложностей никаких нет, просто кому-то лень собрать и запустить свой софт на 64х битах.

    Re: сложность разработки под 64 bit

    > Уговорил, дописывать. Там же генерятся машинные инструкции, и для x64 они другие, регистров больше,

    Формат упаковки инструкций остался практически таким же. Опкоды такие же. 32-битный код будет спокойно работать на 64 битах.

    Re: сложность разработки под 64 bit

    Не знал, спасибо за инфу. А с поинтерами проблема будет(не полностью заполненные регистры)?

    Re: сложность разработки под 64 bit

    > Не знал, спасибо за инфу. А с поинтерами проблема будет(не полностью заполненные регистры)?

    32-битный код в 32-битном сегменте будет работать в compatibility mode: формат инструкций старый, адресное пространство ограничено 4 гб.

    Re: сложность разработки под 64 bit

    > Она часто встречается.

    Она часто встречается как причина любой проблемы. 8)) Так что лучше подняться на уровень выше, и описать последствия криворукости, которые непосредственно и приведут к проблемам в переносе на другую архитектуру.

    Re: сложность разработки под 64 bit

    > Ну вообще-то размер стандартных типов языка C (int,long, etc) зависит от разрядности системы независимо от OS.

    Какая новость для меня. Номер параграфа в C99 не подскажете, где такое написано?

    Re: сложность разработки под 64 bit

    Re: сложность разработки под 64 bit

    >> Ну вообще-то размер стандартных типов языка C (int,long, etc) зависит от разрядности системы независимо от OS.


    > Какая новость для меня. Номер параграфа в C99 не подскажете, где такое написано?

    5.2.4.2.1, внимательно читать слова minimum/maximum.

    Не знаю про «зависит независимо», но размер типов не определён. Есть соглашения ILP32, LP64 итп. Это и есть самые популярные грабли. Можно найти кучу быдлокода с приведением указателей к int (вместо intptr_t, если очень уж хочется) и наоборот.

    Re: сложность разработки под 64 bit

    > 5.2.4.2.1, внимательно читать слова minimum/maximum.

    Ну дык ента. Где там сказано, что «размер стандартных типов языка C (int,long, etc) зависит от разрядности системы независимо от OS»? Там несколько не про то. Там про то, что гарантируется минимальный размер, а реальный может быть больше в зависимости от реализации, нет? Под рукой только драфт.

    Re: сложность разработки под 64 bit

    >Там про то, что гарантируется минимальный размер

    По моему стандартом гарантируется только то что sizeof(char) ★★★ ( 20.08.09 19:28:56 )

    Re: сложность разработки под 64 bit

    > По моему стандартом гарантируется только то что sizeof(char) ★ ( 20.08.09 19:30:25 )

    Re: сложность разработки под 64 bit

    > В C99 ввели минимальные размеры.

    А максимальные — нет. Поэтому int будет хотя бы 16-разрядный, но может оказаться и 32-, и 64-разрядным. Это из реальной жизни. Если он вдруг окажется килобитным, это тоже не выйдет за рамки стандарта :)

    Re: сложность разработки под 64 bit

    >Какая новость для меня. Номер параграфа в C99 не подскажете, где такое написано?

    A ‘‘plain’’ int object has the natural size suggested by the architecture of the execution environment (large enough to contain any value in the range INT_MIN to INT_MAX as defined in the header
    ).

    Вообще по идее создателей языка int соответствует разрядности процессора.

    Re: сложность разработки под 64 bit

    >> В C99 ввели минимальные размеры.

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

    Аннотация
    Введение
    1. Шаг первый. 64-битность бывает разной.
    Давайте разберемся
    2. Шаг второй. Выясните, нужен ли вам 64-битный вариант вашего продукта
    2.1. Продолжительность жизненного цикла приложений
    2.2. Ресурсоемкость приложения
    2.3. Разработка библиотек
    2.4. Зависимость вашего продукта от сторонних библиотек
    2.5. Наличие 16-битных приложений
    2.6. Наличие кода на ассемблере
    3. Шаг третий. Инструментарий
    3.1. Наличие 64-битного компилятора
    3.2. Наличие 64-битных компьютеров под управлением 64-битных операционных систем
    3.3. Наличие 64-битных вариантов всех используемых библиотек
    3.4. Отсутствие встроенного кода на ассемблере
    3.5. Модернизация методологии тестирования
    3.6. Новые данные для тестирования
    3.7. Наличие 64-битных систем защиты
    3.8. Инсталлятор
    4. Шаг четвертый. Настройка проекта в Visual Studio 2005/2008
    5. Шаг пятый. Компиляция приложения
    6. Диагностика скрытых ошибок
    6.1. Явное приведение типов
    6.2. Неявное приведение типов
    6.3. Работа с битами, сдвиги
    6.4. Магические числа
    6.5. Ошибки использования 32-битных переменных в качестве индексов
    6.6. Ошибки, связанные с изменением типов используемых функций
    6.7. Диагностика скрытых ошибок
    7. Шаг седьмой. Модернизация процесса тестирования
    Библиографический список

    В статье рассмотрены основные шаги, обеспечивающие корректный перенос 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, которая получила значительно большее распространение, чем IA-64. Особенно это относится к программному обеспечению для рынка персональных компьютеров, который почти на 100 процентов занят архитектурой AMD64.
    Далее в статье мы будем говорить только об архитектуре AMD64 (EM64T, x64), так как ее использование сейчас наиболее актуально для разработчиков прикладного программного обеспечения.
    Говоря о различных архитектурах, следует упомянуть о понятии . Под моделью данных следует понимать соотношения размерностей типов, принятых в рамках среды разработки. Для одной операционной системы могут существовать несколько средств разработки, придерживающихся разных моделей данных. Но обычно преобладает только одна модель, наиболее соответствующая аппаратной и программной среде. Примером может служить 64-битная операционная система Windows, в которой родной моделью данных является LLP64. Но для совместимости 64-битная система Windows поддерживает исполнение 32-битных программ, которые работают в режиме модели данных ILP32LL. В таблице N1 приведены сведения об основных моделях данных.

    Таблица N1. Модели данных
    Используемая модель данных накладывает отпечаток на процесс разработки 64-битных приложений, так как в коде программ необходимо учитывать разрядность используемых данных [5].

    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-битных версий, будут вынуждены искать альтернативные решения. Например, некоторые разработчики программно-аппаратной защиты откликнулись с большой задержкой на появление 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-битной версии вашего продукта. Вас могут поджидать неприятные сюрпризы.
    Конечно, перечислить все, что может понадобиться для проекта здесь невозможно, но все-таки предложу список, который поможет вам соорентироваться и возможно вспомнить о других моментах, которые необходимы для реализации вашего 64-битного проекта:

    3.1. Наличие 64-битного компилятора

    Сложно что-то еще сказать о важности наличия 64-битного компилятора. Он просто должен быть.
    Если вы планируете разрабатывать 64-битные приложения с использованием последней версии (на момент написания статьи) Visual Studio 2008, то следующая таблица N2 поможет помочь определить, какая из редакций Visual Studio вам необходима.

    Таблица N2. Возможности различных редакций Visual Studio 2008

    3.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].

    3.5. Модернизация методологии тестирования

    Существенная переработка методологии тестирования, модернизация юнит-тестов, использование новых инструментальных средств. Более подробно об этом будет сказано ниже, но не забывайте учесть это на этапе оценки временных затрат на миграцию приложения на новую систему [10].

    3.6. Новые данные для тестирования

    Если вы разрабатываете ресурсоемкие приложения, потребляющие большой объем оперативной памяти, то вам необходимо позаботиться о пополнении базы тестовых входных данных. При нагрузочном тестировании 64-битных приложений желательно выходить за пределы 4 гигабайт потребляемой памяти. Многие ошибки могут проявиться только при таких условиях.

    3.7. Наличие 64-битных систем защиты

    Используемая система защиты, должна поддерживать 64-битные системы в полном необходимом вам объеме. Например, компания Aladdin достаточно быстро выпустила 64-битыне драйвера для поддержки аппаратных ключей Hasp. Но очень долго отсутсвовала система автоматической защиты 64-битных бинарных файлов (программа Hasp Envelop). Таким образом, механизм защиты приходилось реализовывать самостоятельно внутри программного кода, что являлось дополнительной сложной задачей, требующей квалификации и времени. Не забывайте про подобные моменты, связанные с обеспечением защиты, системой обновлений и так далее.

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

    4. Шаг четвертый. Настройка проекта в Visual Studio 2005/2008

    Создание 64-битной конфигурации проекта в Visual Studio 2005/2008 выглядит достаточно просто. Сложности будут подстерегать вас на этапе сборки новой конфигурации и поиска в ней ошибок. Для создания же 64-битной конфигурации достаточно выполнить следующие 4 шага:
    Запускаем менеджер конфигураций, как показано на рисунке N1:

    Рисунок 1. Запуск менеджера конфигураций

    В менеджере конфигураций выбираем поддержку новой платформе (рисунок N2):

    Рисунок 2. Создание новой конфигурации

    Выбираем 64-битную платформу (x64), а в качестве основы выбираем настройки от 32-битной версии (рисунок N3). Те настройки, которые влияют на режим сборки среда Visual Studio скорректирует сама.

    Рисунок 3. Выбираем x64 в качестве платформы и берем за основу конфигурацию Win32

    Добавление новой конфигурации завершено, и мы можем выбрать 64-битный вариант конфигурации и приступить к компиляции 64-битного приложения. Выбор 64-битной конфигурации для сборки показан на рисунке N4.

    Рисунок 4. Теперь доступна 32-битная и 64-битная конфигурация

    Если вам повезет, то дополнительно заниматься настройкой 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. Большинство ошибок при компиляции будет связано с использование именно этих типов.

    Таблица N3. Типы представляющие интерес при переносе 32-битных программ на 64-битые Windows системы.

    6. Диагностика скрытых ошибок

    Если вы думаете, что после исправления всех ошибок компиляции будет получено долгожданное 64-битное приложение, то придется вас разочаровать. Самое сложное впереди. На этапе компиляции вами будут исправлены самые явные ошибки, которые смог обнаружить компилятор, которые в основном связаны с невозможностью неявного приведения типов. Но это верхушка айсберга. Основная часть ошибок скрыта. Эти ошибки с точки зрения абстрактного языка Си++ смотрятся безопасно или замаскированы явными приведениями типов. Таких ошибок в несколько раз больше, чем количество ошибок выявленных на этапе компиляции.
    На ключ /Wp64 надежды возлагать не следует. Это ключ часто преподносится как чудесное средство поиска 64-битных ошибок. В действительности ключ /Wp64 всего лишь дает возможность при компиляции 32-битного кода получить некоторые предупреждения, что в 64-битном режиме определенные участки кода будут некорректны. При компиляции 64-битного кода эти предупреждения будут выданы компилятором в любом случае. И поэтому при компиляции 64-битного приложения ключ /Wp64 игнорируется. И уж тем более этот ключ не поможет в поиске скрытых ошибок [11].
    Рассмотрим несколько примеров скрытых ошибок.

    6.1. Явное приведение типов

    Самый простой, но вовсе не самый легкий для обнаружения класс ошибок связан с явным приведением типов, при которых происходит обрезание значащих бит.
    Распространенным пример — приведение указателей к 32-битным типам при передачи их в функции, такие как SendMessage:Здесь явное приведение типа используется для превращения указателя в числовой тип. Для 32-битной архитектуры приведенный пример корректен, так как последний параметр функции SendMessage имеет тип LPARAM, который на 32-битной архитектуре совпадает с DWORD. Для 64-битной архитектуре использование DWORD ошибочно и должно быть заменено на LPARAM. Тип LPARAM имеет в зависимости от архитектуры размер 32 или 64 бита.
    Это простой случай, но часто приведение типа выглядит более изысканно и обнаружить его используя предупреждения компилятора или поиском по тексту программы невозможно. Явные приведения типов подавляют диагностику компилятора, поскольку они именно и предназначены, чтобы сказать компилятору что приведение типов корректно и программист взял на себя ответственность за безопасность кода. Явный поиск тоже не поможет. Типы могут быть не стандартные имена (заданные программистом через typedef), а способов осуществить явное приведение типов тоже не мало. Для надежной диагностики подобных ошибок необходимо использовать только специальный инструментарий, такой как анализаторы Viva64 или PC-Lint.

    6.2. Неявное приведение типов

    Следующий пример связан уже с неявным приведением типа, при котором также происходит потеря значащих бит. Код функции fread осуществляет чтение из файла, но некорректен при попытке чтения более 2 гигабайт данных на 64-битной системе.Функция __fread возвращает тип size_t, но для хранения количества прочитанных байт используется тип int. В результате при больших объемах читаемых данных функция может вернуть не то количество байт, которое на самом деле будет прочитано.
    Вы можете сказать, что это безграмотный код начинающих, что о таком приведении типа сообщит компилятор и что вообще такой код легко найти и поправить. Это теоретически. А практически в реальной жизни с большими проектами все может обстоять иначе. Этот пример взят из исходного кода FreeBSD. Ошибка была поправлена только в декабре 2008 года! Это притом, что первая (экспериментальная) 64-битная версия FreeBSD вышла еще в июне 2003 года.
    Вот исходный код до исправления:
    http://www.freebsd.org/cgi/cvsweb.cgi/src/. read.c?rev=1.14
    А вот исправленный вариант (декабрь 2008) года:
    http://www.freebsd.org/cgi/cvsweb.cgi/src/. read.c?rev=1.15

    6.3. Работа с битами, сдвиги

    Легко сделать ошибку в коде работящем с отдельными битами. Следующий тип ошибки связан с операциями сдвига. Рассмотрим пример:Приведенный код работоспособен на 32-битной архитектуре и позволяет выставлять бит с номерами от 0 до 31 в единицу. После переноса программы на 64-битную платформу возникнет необходимость выставлять биты от 0 до 63. Но данный код никогда не выставит биты, с номерами 32-63. Обратите внимание, что «1» имеет тип int и при сдвиге на 32 позиции произойдет переполнение, как показано на рисунке 5. Получим мы в результате 0 (рисунок 5-B) или 1 (рисунок 5-C) зависит от реализации компилятора.

    Рисунок 5. A — Корректная установка 31-ого бита в 32-битном коде; B,C — Ошибка установки 32-ого бита на 64-битной системе (два варианта поведения)
    Для исправления кода необходимо сделать константу «1» того же типа, что и переменная mask:Заметим также, что неисправленный код приведет еще к одной интересной ошибке. При выставлении 31 бита на 64-битной системе результатом работы функции будет значение 0xffffffff80000000 (см. рисунок 6). Результатом выражения 1 6.4. Магические числа

    Много неприятностей могут доставить магические константы, то есть числа, с помощью которых задается размер того или иного типа. Правильным решением является использование для таких целей операторов sizeof(), но в большой программе вполне может затеряться старый кусочек кода, где твердо были уверены, что размер указателя был 4 байта, а в типе size_t всегда 32 бита. Обычно подобные ошибки выглядят следующим образом:Основными числами, к которым следует отнестись с осторожностью при переходе на 64-битную платформу приведены в таблице N4.

    Таблица N4. Основные магические значения, опасные при переносе приложений с 32-битной на 64-битную платформу

    6.5. Ошибки использования 32-битных переменных в качестве индексов

    В программах обрабатывающих большие объемы данных могут встретиться ошибки связанные с индексацией больших массивов или возникнуть вечные циклы. Следующий пример содержит сразу 2 ошибки:Первая ошибка заключается в том, что если размер обрабатываемых данных превысит 4 гигабайта (0xFFFFFFFF), то возможно возникновение вечного цикла, поскольку переменная ‘i’ имеет тип ‘unsigned’ и никогда не достигнет значения 0xFFFFFFFF. Я специально пишу, что возникновение возможно, но не обязательно оно произойдет. Это зависит от того, какой код построит компилятор. Например, в отладочном (debug) режиме вечный цикл будет присутствовать, а в release-коде зацикливание исчезнет, так компилятор примет решение оптимизировать код, используя для счетчика 64-битный регистр и цикл будет корректным. Все это добавляет путаницы, и код который работал вчера, неожиданно может перестать работать на следующий день.
    Вторая ошибка связана с проходом по массиву от конца к началу для чего используются отрицательные значения индексов. Приведенный код работоспособен в 32-битном режиме, но при его запуске на 64-битной машине на первой же итерации цикла произойдет доступ за границы массива и программа аварийно завершится. Рассмотрим причину такого поведения.

    Согласно правилом языка Си++ на 32-битной системе выражение будет вычисляться следующим образом (на первом шаге i = 0):

    1. Выражение имеет тип 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. В результате произойдет выход за рамки массива.
    Для исправления кода необходимо использовать такие типы, как 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).

    Рисунок 8. Не корректный, но работоспособный 32-битный код

    Ошибка ждет, чтобы проявить себя в 64-битной системе, где размер типов DWORD и DWORD_PTR различен (рисунок 9). Получается, что в 64-битном режиме классы содержат две РАЗНЫЕ функции WinHelp, что естественно некорректно. Учтите, что подобные ловушки могут скрываться не только в MFC, где часть функций изменили типы своих аргументов, но и в коде ваших приложений и сторонних библиотек.

    Рисунок 9. Ошибка проявляет себя в 64-битном коде

    6.7. Диагностика скрытых ошибок

    Примеры подобных 64-битных ошибок можно приводить и приводить. Тем, кто заинтересовался подобными ошибками и хочет более подобно узнать о них будет интересна статья [12].
    Как видите, этап поиска скрытых ошибок представляет нетривиальную задачу, тем более что многие из них будут проявляться нерегулярно или только на больших входных объемах данных. Для диагностики подобных ошибок хорошо подходят статические анализаторы кода, так как они могут проверять весь код приложения, в не зависимости от входных данных и частоты выполнения его участков в реальных условиях. Использовать статический анализ есть смысл как на этапе переноса приложения на 64-битные платформы, чтобы найти большинство ошибок на самом начальном этапе, так и в дальнейшей разработке 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-битных систем.

    Рисунок 10. Окно настроек программы Parallel Inspector перед запуском приложения.

    Последнее. Не забудьте добавить тесты, проверяющие совместимость форматов данных между 32-битной и 64-битной версией. Совместимость данных при миграции достаточно часто нарушается из-за записи в файлы таких типов как size_t или long (в случае Linux систем).

    Илон Маск рекомендует:  Farcalloc   farrealloc работа с дальней кучей
    Понравилась статья? Поделиться с друзьями:
    Кодинг, CSS и SQL