Générez des binaires Arm64X
Vous pouvez générer des binaires Arm64X, également connus sous le nom de fichiers PE Arm64X, pour supporter le chargement d’un seul binaire à la fois dans les processus x64/Arm64EC et Arm64.
Génération d’un binaire Arm64X à partir d’un projet Visual Studio
Pour activer la génération de binaires Arm64X, les pages de propriétés de la configuration Arm64EC ont une nouvelle propriété appelée « Construire le projet comme ARM64X », connue sous le nom de BuildAsX
dans le fichier du projet.
Lorsqu’un utilisateur génère un projet, Visual Studio compile normalement pour Arm64EC puis lie les sorties en un binaire Arm64EC. Lorsque BuildAsX
est paramétré sur true
, Visual Studio compilera à la place pour Arm64EC et Arm64. L’étape de liaison Arm64EC est ensuite utilisée pour lier les deux ensemble en un seul binaire Arm64X. Le répertoire de sortie pour ce binaire Arm64X sera celui qui est défini sous la configuration Arm64EC
Pour que BuildAsX
fonctionne correctement, l’utilisateur doit avoir une configuration Arm64 existante, en plus de la configuration Arm64EC. Les configurations Arm64 et Arm64EC doivent avoir le même runtime C et la même bibliothèque standard C++ (par exemple, les deux définissent /MT). Pour éviter les inefficacités de construction, telles que la construction de projets Arm64 complets plutôt que juste la compilation, toutes les références directes et indirectes du projet doivent avoir BuildAsX
réglé sur vrai (True).
Le système de génération suppose que les configurations Arm64 et Arm64EC ont le même nom. Si les configurations Arm64 et Arm64EC ont des noms différents (comme Debug|ARM64
et MyDebug|ARM64EC
), vous pouvez éditer manuellement le fichier vcxproj ou Directory.Build.props
pour ajouter une propriété ARM64ConfigurationNameForX
à la configuration Arm64EC qui fournit le nom de la configuration Arm64.
Si le binaire Arm64X souhaité est une combinaison de deux projets séparés, l’un en tant qu’Arm64 et l’autre en tant qu’Arm64EC, vous pouvez éditer manuellement le vxcproj du projet Arm64EC pour ajouter une propriété ARM64ProjectForX
et spécifier le chemin vers le projet Arm64. Les deux projets doivent être dans la même solution.
Génération d’une DLL Arm64X avec CMake
Pour générer vos fichiers binaires de projet CMake en tant qu’Arm64X, vous pouvez utiliser n’importe quelle version de CMake qui prend en charge la génération en tant qu’Arm64EC. Le processus implique de créer initialement le projet ciblant Arm64 pour générer les entrées de l’éditeur de liens Arm64. Par la suite, le projet doit être redécodé ciblant Arm64EC, cette fois en combinant les entrées Arm64 et Arm64EC pour former des fichiers binaires Arm64X. Les étapes ci-dessous tirent parti de l’utilisation de CMakePresets.json.
Vérifiez que vous disposez de présélections de configuration distinctes ciblant Arm64 et Arm64EC. Par exemple :
{ "version": 3, "configurePresets": [ { "name": "windows-base", "hidden": true, "binaryDir": "${sourceDir}/out/build/${presetName}", "installDir": "${sourceDir}/out/install/${presetName}", "cacheVariables": { "CMAKE_C_COMPILER": "cl.exe", "CMAKE_CXX_COMPILER": "cl.exe" }, "generator": "Visual Studio 17 2022", }, { "name": "arm64-debug", "displayName": "arm64 Debug", "inherits": "windows-base", "hidden": true, "architecture": { "value": "arm64", "strategy": "set" }, "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" } }, { "name": "arm64ec-debug", "displayName": "arm64ec Debug", "inherits": "windows-base", "hidden": true, "architecture": { "value": "arm64ec", "strategy": "set" }, "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" } } ] }
Ajoutez deux nouvelles configurations qui héritent des présélections Arm64 et Arm64EC que vous avez ci-dessus. Définissez
BUILD_AS_ARM64X
la valeurARM64EC
dans la configuration qui hérite d’Arm64EC etBUILD_AS_ARM64X
deARM64
l’autre. Ces variables seront utilisées pour indiquer que les builds de ces deux présélections font partie d’Arm64X.{ "name": "arm64-debug-x", "displayName": "arm64 Debug (arm64x)", "inherits": "arm64-debug", "cacheVariables": { "BUILD_AS_ARM64X": "ARM64" }, { "name": "arm64ec-debug-x", "displayName": "arm64ec Debug (arm64x)", "inherits": "arm64ec-debug", "cacheVariables": { "BUILD_AS_ARM64X": "ARM64EC" }
Ajoutez un nouveau fichier .cmake à votre projet CMake appelé
arm64x.cmake
. Copiez l’extrait de code ci-dessous dans le nouveau fichier .cmake.# directory where the link.rsp file generated during arm64 build will be stored set(arm64ReproDir "${CMAKE_CURRENT_SOURCE_DIR}/repros") # This function reads in the content of the rsp file outputted from arm64 build for a target. Then passes the arm64 libs, objs and def file to the linker using /machine:arm64x to combine them with the arm64ec counterparts and create an arm64x binary. function(set_arm64_dependencies n) set(REPRO_FILE "${arm64ReproDir}/${n}.rsp") file(STRINGS "${REPRO_FILE}" ARM64_OBJS REGEX obj\"$) file(STRINGS "${REPRO_FILE}" ARM64_DEF REGEX def\"$) file(STRINGS "${REPRO_FILE}" ARM64_LIBS REGEX lib\"$) string(REPLACE "\"" ";" ARM64_OBJS "${ARM64_OBJS}") string(REPLACE "\"" ";" ARM64_LIBS "${ARM64_LIBS}") string(REPLACE "\"" ";" ARM64_DEF "${ARM64_DEF}") string(REPLACE "/def:" "/defArm64Native:" ARM64_DEF "${ARM64_DEF}") target_sources(${n} PRIVATE ${ARM64_OBJS}) target_link_options(${n} PRIVATE /machine:arm64x "${ARM64_DEF}" "${ARM64_LIBS}") endfunction() # During the arm64 build, create link.rsp files that containes the absolute path to the inputs passed to the linker (objs, def files, libs). if("${BUILD_AS_ARM64X}" STREQUAL "ARM64") add_custom_target(mkdirs ALL COMMAND cmd /c (if not exist \"${arm64ReproDir}/\" mkdir \"${arm64ReproDir}\" )) foreach (n ${ARM64X_TARGETS}) add_dependencies(${n} mkdirs) # tell the linker to produce this special rsp file that has absolute paths to its inputs target_link_options(${n} PRIVATE "/LINKREPROFULLPATHRSP:${arm64ReproDir}/${n}.rsp") endforeach() # During the ARM64EC build, modify the link step appropriately to produce an arm64x binary elseif("${BUILD_AS_ARM64X}" STREQUAL "ARM64EC") foreach (n ${ARM64X_TARGETS}) set_arm64_dependencies(${n}) endforeach() endif()
/LINKREPROFULLPATHRSP est pris en charge uniquement si vous créez l’éditeur de liens MSVC à partir de Visual Studio 17.11 ou version ultérieure.
Si vous devez utiliser un éditeur de liens plus ancien, copiez plutôt l’extrait de code ci-dessous. Cet itinéraire utilise un indicateur /LINK_REPRO plus ancien. L’utilisation de l’itinéraire /LINK_REPRO entraîne un temps de génération global plus lent en raison de la copie de fichiers et présente des problèmes connus lors de l’utilisation du générateur Ninja.
# directory where the link_repro directories for each arm64x target will be created during arm64 build.
set(arm64ReproDir "${CMAKE_CURRENT_SOURCE_DIR}/repros")
# This function globs the linker input files that was copied into a repro_directory for each target during arm64 build. Then it passes the arm64 libs, objs and def file to the linker using /machine:arm64x to combine them with the arm64ec counterparts and create an arm64x binary.
function(set_arm64_dependencies n)
set(ARM64_LIBS)
set(ARM64_OBJS)
set(ARM64_DEF)
set(REPRO_PATH "${arm64ReproDir}/${n}")
if(NOT EXISTS "${REPRO_PATH}")
set(REPRO_PATH "${arm64ReproDir}/${n}_temp")
endif()
file(GLOB ARM64_OBJS "${REPRO_PATH}/*.obj")
file(GLOB ARM64_DEF "${REPRO_PATH}/*.def")
file(GLOB ARM64_LIBS "${REPRO_PATH}/*.LIB")
if(NOT "${ARM64_DEF}" STREQUAL "")
set(ARM64_DEF "/defArm64Native:${ARM64_DEF}")
endif()
target_sources(${n} PRIVATE ${ARM64_OBJS})
target_link_options(${n} PRIVATE /machine:arm64x "${ARM64_DEF}" "${ARM64_LIBS}")
endfunction()
# During the arm64 build, pass the /link_repro flag to linker so it knows to copy into a directory, all the file inputs needed by the linker for arm64 build (objs, def files, libs).
# extra logic added to deal with rebuilds and avoiding overwriting directories.
if("${BUILD_AS_ARM64X}" STREQUAL "ARM64")
foreach (n ${ARM64X_TARGETS})
add_custom_target(mkdirs_${n} ALL COMMAND cmd /c (if exist \"${arm64ReproDir}/${n}_temp/\" rmdir /s /q \"${arm64ReproDir}/${n}_temp\") && mkdir \"${arm64ReproDir}/${n}_temp\" )
add_dependencies(${n} mkdirs_${n})
target_link_options(${n} PRIVATE "/LINKREPRO:${arm64ReproDir}/${n}_temp")
add_custom_target(${n}_checkRepro ALL COMMAND cmd /c if exist \"${n}_temp/*.obj\" if exist \"${n}\" rmdir /s /q \"${n}\" 2>nul && if not exist \"${n}\" ren \"${n}_temp\" \"${n}\" WORKING_DIRECTORY ${arm64ReproDir})
add_dependencies(${n}_checkRepro ${n})
endforeach()
# During the ARM64EC build, modify the link step appropriately to produce an arm64x binary
elseif("${BUILD_AS_ARM64X}" STREQUAL "ARM64EC")
foreach (n ${ARM64X_TARGETS})
set_arm64_dependencies(${n})
endforeach()
endif()
En bas du fichier de niveau
CMakeLists.txt
supérieur dans votre projet, ajoutez l’extrait de code ci-dessous. Veillez à remplacer le contenu des crochets angle par des valeurs réelles. Cela utilisera learm64x.cmake
fichier que vous venez de créer ci-dessus.if(DEFINED BUILD_AS_ARM64X) set(ARM64X_TARGETS <Targets you want to Build as ARM64X>) include("<directory location of the arm64x.cmake file>/arm64x.cmake") endif()
Générez votre projet CMake à l’aide de la présélection Arm64X compatible Arm64 (arm64-debug-x).
Générez votre projet CMake à l’aide de la présélection Arm64X compatible Arm64EC (arm64ec-debug-x). La ou les dll finales contenues dans le répertoire de sortie de cette build seront des fichiers binaires Arm64X.
Créer un DLL de transfert pur Arm64X
Un DLL de transfert pur Arm64X est un petit DLL Arm64X qui transfère les API à des DLL séparées en fonction de leur type :
Les API Arm64 sont transférées à une DLL Arm64.
les API x64 sont transférées à un DLL x64 ou Arm64EC.
Un transfert pur Arm64X accorde les avantages de l’utilisation d’un binaire Arm64X même en cas de difficultés avec la création d’un binaire Arm64X fusionné contenant tout le code Arm64EC et Arm64. Vous pouvez en apprendre davantage concernant les DLL de transfert pur Arm64X dans la page de vue d’ensemble des fichiers PE Arm64X.
Vous pouvez créer un transfert pur Arm64X à partir de la commande développeur Arm64 en suivant la procédure ci-dessous. Le transfert pur Arm64X résultant acheminera les appels x64 vers foo_x64.DLL
et les appels Arm64 vers foo_arm64.DLL
.
Créez des fichiers
OBJ
vides qui seront utilisés plus tard par l’éditeur de liens pour créer le transfert pur. Ceux-ci sont vides car le transfert pur ne contient pas de code Pour cela, créez un fichier vide. Pour l’exemple ci-dessous, nous avons nommé le fichier empty.cpp. Des fichiersOBJ
vides sont ensuite créés en utilisantcl
, avec un pour Arm64 (empty_arm64.obj
) et un pour Arm64EC (empty_x64.obj
) :cl /c /Foempty_arm64.obj empty.cpp cl /c /arm64EC /Foempty_x64.obj empty.cpp
Si le message d’erreur « cl : Command line warning D9002 : ignoring unknown option "-arm64EC" » apparaît, le compilateur incorrect est utilisé. Pour résoudre ce problème, passez à l’invite de commandes du développeur Arm64.
Créez des fichiers
DEF
pour x64 et Arm64. Ces fichiers énumèrent toutes les exportations d’API du DLL et indiquent au chargeur le nom du DLL qui peut répondre à ces appels API.foo_x64.def
:EXPORTS MyAPI1 = foo_x64.MyAPI1 MyAPI2 = foo_x64.MyAPI2
foo_arm64.def
:EXPORTS MyAPI1 = foo_arm64.MyAPI1 MyAPI2 = foo_arm64.MyAPI2
Vous pouvez ensuite utiliser
link
pour créer des fichiers d’importationLIB
pour x64 et Arm64 :link /lib /machine:x64 /def:foo_x64.def /out:foo_x64.lib link /lib /machine:arm64 /def:foo_arm64.def /out:foo_arm64.lib
Liez les fichiers
OBJ
vides et importezLIB
en utilisant l’indicateur/MACHINE:ARM64X
pour produire le DLL de transfert pur Arm6X :link /dll /noentry /machine:arm64x /defArm64Native:foo_arm64.def /def:foo_x64.def empty_arm64.obj empty_x64.obj /out:foo.dll foo_arm64.lib foo_x64.lib
Le foo.dll
résultant peut être chargé dans un processus Arm64 ou x64/Arm64EC. Quand un processus Arm64 charge foo.dll
, le système d’exploitation chargera immédiatement foo_arm64.dll
à sa place et tout appel API sera géré par foo_arm64.dll
.