Инструменты пользователя

Инструменты сайта


build32:notes

Сборка 32-битных приложений в 2024++ году

Потребовалось вдруг собрать DLL к довольно старому 32-битному приложению. И это оказалось для меня нетривиальной задачей.

Попытка сборки DLL в QtCreator

У меня стоит QtCreator с компилятором MinGW64. В нем была собрана и отлажена необходимая DLL в 64-битном формате, на чистом C++, без всяких "потрохов" от Qt.

Судя по википедии, разработчики MinGW в свое время разругались с разработчиками версии под 64-битную архитектуру, поэтому ответвление MinGW64 стало самостоятельным продуктом, а не влилось в пакет MinGW. MinGW64 более полно поддерживает 64-битный режим, как и 32-битный. Поэтому рекомендуют выбирать именно его.

Компилятор MinGW64 должен собирать как 64-битные приложения, так и 32-х битные. Т.е. для сборки 32-разрядной версии DLL ожидалось, что достаточно будет сделать 32-разрядный "Kit" в "Manage Kits" QtCreator и собрать его так-же, как 64 разрядную версию. Для этого в Kit пришлось:

  • клонировать текущий 64-разрядный Kit
  • подключить к нему клонированный текущий компилятор, выбрать в нем ABI - Custom и 32-бит.
  • … и попробовать много еще чего, пока Kit не стал доступен для выбора в проекте DLL.

Но после сборки оказалось DLL собралась все-равно 64 битная, несмотря на все старания и попытки перебора настроек.

Попытка сборки DLL через CMake

Если в Kit выбрать версию "Qt: None", то блокируется возможность использовать штатную систему сборки qMake. (На сколько это я сейчас помню… , но это не точно) Поэтому пришла идея использовать сборщик CMake. К тому же, современный QtCreator уже содержит CMake в своем составе.

Оказалось, что у меня так-же установлен еще один CMake, где-то глобально вне QtCreator. Отсюда возникли непонятки какой собственной CMake будет запускаться, если делать сборку через командную строку. К счастью, видимо CMake от Qt прописан я путях PATH раньше, чем второй вариант, поэтому у меня вызывался штатный CMake от QtCreator.

Сборка через CMake 64-разрядной версии DLL прошла успешно, и предстояло "всего лишь" попросить компилятор сделать 32 разрядную сборку ключами "-m32".

set_target_properties(NT_Spectrm PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")

Но…, это не сработало. Возможно мой компилятор более не поддерживает эти ключи, или по каким-то своим причинам все-равно выбирает 64-bit версию. Ведь CMake всё конфигурит сам, и не понятно, что он там выбирает под капотом. По строкам в файле CMakeCache.txt было видно, что он выбирает все те-же утилиты из пакета QtCreator.

Возникла идея поставить другой компилятор, заведомо поддерживающий 32-разрядный режим, и собрать DLL все всякого QtCreator. Для выбора компилятора в CMake необходимо указать файл CMAKE_TOOLCHAIN_FILE.

Этот файл должен описывать каким инструментарием будет пользоваться Cmake, вместо того, что он там выбрал сам при конфигурации.

# the name of the target operating system
set(CMAKE_SYSTEM_NAME Windows)

# which compilers to use for C and C++
set(CMAKE_C_COMPILER   i586-mingw32msvc-gcc)
set(CMAKE_CXX_COMPILER i586-mingw32msvc-g++)

# where is the target environment located
set(CMAKE_FIND_ROOT_PATH  /usr/i586-mingw32msvc
    /home/alex/mingw-install)

# adjust the default behavior of the FIND_XXX() commands:
# search programs in the host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)

# search headers and libraries in the target environment
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

Люди как-то так и делают, Habr: Как мы переводили проект на CMake:

cmake -G "Ninja" \
      --toolchain=path_to/toolchainXX_file.cmake ..
cmake --build .

Попытка сборки DLL в MSYS2

По отзывам в Internet, есть такой пакет MSYS2 который призван упростить сборку проектов разными инструментами (Build Toolchain). Toolchain это не просто утилиты компилятор и линкер, Toolchain включает:

  • набор утилит которые вызывает компилятор и линкер для сборки проекта (binutils)
  • набор библиотек. Например стандартные библиотеки C и C++.
  • набор заголовочных файлов к системе для которой идет сборка
  • и прочее

Вместе с установкой MSYS2 ставится пакетный менеджер Pacman (видимо аналог PIP в Python). С его помощью уже можно поставить любой Toolchain для сборки из поддерживаемых.

Соответственно, я снова поставил компилятор MinWG64 через Pacman, и попытался подключить этот компилятор через CMAKE_TOOLCHAIN_FILE для своей сборки через CMake. Но опять ничего не вышло. Не вспомню конкретно в чем заключалась проблема, но четко помню ощущения, что "руки походу ветвятся у меня не из того места". (Скорее всего Qt-шный CMake делал что-то свое, или какие-то запчасти не находились)

И тут, попалась статья которая все расставила по местам: Установка и использование MSYS2 и Mingw-w64 под Windows. После установки MSYS2 в системе появляется несколько изолированных терминалов со своими изолированными настройками.

В моем случае, мне необходимо было собрать DLL под 32-бита с помощью MinGW64. Соответственно, я запускаю терминал MSYS2 MinGW 32bit:

  • ставлю toolchain для сборки
    pacman -S --needed mingw-w64-i686-toolchain
  • ставлю cmake для сборки
    pacman -S mingw-w64-i686-cmake
  • и затем в этом же терминале запускаю сборку cmake
  cd path_to/build_32
  cmake -G "MinGW Makefiles" ..
  cmake --build .

УРА! Запускается правильный CMake (не Qt-шный) и сам генерит все необходимое для сборки под выбранную платформу - MSYS2 MinGW 32bit. DLL готова!

Интересно, что если запустить терминал MSYS2 MinGW 64bit, то вызов g++ или cmake в нем показывают, что такие пакеты не установлены. Соответственно, для сборки 64-разрядной версии надо поставить в этом терминале -mingw-w64-x86_64-toolchain и mingw-w64-x86_64-cmake.

В терминале сборки так-же можно узнать дополнительную информацию о полученной DLL, используем утилиты file и ldd:

$> file libNT_Spectrm.dll
libNT_Spectrm.dll: PE32 executable (DLL) (console) Intel 80386, for MS Windows, 18 sections

$> ldd libNT_Spectrm.dll
        ntdll.dll => /c/Windows/SYSTEM32/ntdll.dll (0x7fffae5f0000)
        KERNEL32.DLL => /c/Windows/System32/KERNEL32.DLL (0x7fffadce0000)
        KERNELBASE.dll => /c/Windows/System32/KERNELBASE.dll (0x7fffabff0000)
        msvcrt.dll => /c/Windows/System32/msvcrt.dll (0x7fffad890000)
        libNT_Spectrm.dll => /c/path.../libNT_Spectrm.dll (0x63990000)

Для примера, вот используемый cmake (возможно здесь есть что-то лишнее):

cmake_minimum_required(VERSION 3.14)

project(NT_Spectrm LANGUAGES CXX)

set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

set (CMAKE_CXX_FLAGS "-static-libstdc++ -Wall -m32 -s")
set(CMAKE_BUILD_TYPE Release)

message("Compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}")

add_library(NT_Spectrm SHARED
  ../../SM_Dll/app_common.cpp
  ../../SM_Dll/app_lin_pos.cpp
  ../../SM_Dll/app_rot_gratings.cpp
  ../../SM_Dll/app_rot_items.cpp
  ../../SM_Dll/app_sel_desel.cpp
  ../../SM_Dll/app_targ_pos.cpp
  ../../SM_Dll/app_wave_len.cpp
  ../../SM_Dll/board_protocol.cpp
  ../../SM_Dll/coro_calibr.cpp
  ../../SM_Dll/coro_init_parse.cpp
  ../../SM_Dll/coro_moving.cpp
  ../../SM_Dll/ctrl_thread.cpp
  ../../SM_Dll/hidapi/nt_hidapi.cpp
  ../../SM_Dll/logger.cpp
  ../../SM_Dll/sm_dll.cpp
  ../../SM_Dll/storage.cpp
  ../../SM_Dll/wl_math.cpp

  ../../SM_Dll/sm_dll.h
)

# Path to headers
target_include_directories(NT_Spectrm PRIVATE
  ${CMAKE_SOURCE_DIR}/../../SM_Dll/
  ${CMAKE_SOURCE_DIR}/../../SM_Dll/hidapi/
)

find_library(HIDAPI_LIBRARY NAMES hidapi.lib PATHS ${CMAKE_SOURCE_DIR}/../../HID_Api/x86/)
message("HidAPi Lib: ${HIDAPI_LIBRARY}")

target_link_libraries(NT_Spectrm PRIVATE ${HIDAPI_LIBRARY})
target_compile_definitions(NT_Spectrm PRIVATE SM_DLL_LIBRARY)

Ключ -s удаляет из библиотеки отладочные символы (strip symbols).

В моем случае, при подключении DLL к вызывающему приложению так-же потребовалось скопировать из C:\msys64\mingw32 библиотеки libwinpthread-1.dll и libgcc_s_dw2-1.dll. Старое приложение написано на Delphi и при вызове LoadLibrary() отладчик показывал, что моя библиотека и используемые в ней DLL загружаются, а потом сразу же выгружаются. В итоге LoadLibrary() возвращает 0. Используя TDump.exe необходимо посмотреть какие дополнительные DLL используются в моей DLL и скопировать их к вызывающему приложению.

Stack Alignment

build32/notes.txt · Последнее изменение: 2024/01/23 16:01 — vasco