Erstellen von Arm64X-Binärdateien
Erstellen Sie Arm64X-Binärdateien, auch als Arm64X PE-Dateien bezeichnet, um das Laden einer einzelnen Binärdatei in x64/Arm64EC- und Arm64-Prozesse zu unterstützen.
Erstellen einer Arm64X-Binärdatei aus einem Visual Studio-Projekt
Um das Erstellen von Arm64X-Binärdateien zu ermöglichen, verfügt die Seite „Property“ der Arm64EC-Konfiguration über die neue Eigenschaft „Build Project as ARM64X“, die in der Projektdatei als BuildAsX
bezeichnet wird.
Normalerweise würde Visual Studio beim Erstellen eines Projekts eine Kompilierung für Arm64EC durchführen und dann die Ausgaben mit einer Arm64EC-Binärdatei verknüpfen. Wenn BuildAsX
auf true
festgelegt ist, kompiliert Visual Studio stattdessen sowohl für Arm64EC als auch für . Im Arm64EC-Verknüpfungsschritt werden dann beide mit einer einzelnen Arm64X-Binärdatei verknüpft. Das Ausgabeverzeichnis für diese Arm64X-Binärdatei entspricht dem Ausgabeverzeichnis, das unter der Arm64EC-Konfiguration festgelegt ist.
Damit BuildAsX
ordnungsgemäß funktioniert, muss der Benutzer zusätzlich zur Arm64EC-Konfiguration über eine vorhandene Arm64-Konfiguration verfügen. Die Arm64- und Arm64EC-Konfigurationen müssen dieselbe C-Runtime- und C++-Standardbibliothek aufweisen (z. B. beides auf /MT) festgelegt. Um Ineffizienzen zu vermeiden, z. B. das Erstellen vollständiger Arm64-Projekte und nicht nur die Kompilierung, sollte bei allen direkten und indirekten Verweise des Projekts die Option BuildAsX
auf „true“ festgelegt sein.
Das Buildsystem geht davon aus, dass die Konfigurationen Arm64 und Arm64EC denselben Namen haben. Wenn die Arm64- und Arm64EC-Konfigurationen unterschiedliche Namen haben (wie Debug|ARM64
und MyDebug|ARM64EC
), können Sie vcxproj oder die Datei Directory.Build.props
manuell bearbeiten, um der Arm64EC-Konfiguration eine ARM64ConfigurationNameForX
-Eigenschaft hinzuzufügen, die den Namen der Arm64-Konfiguration bereitstellt.
Wenn die gewünschte Arm64X-Binärdatei eine Kombination aus zwei separaten Projekten ist, eines davon Arm64 und und das andere Arm64EC, können Sie vxcproj des Arm64EC-Projekts manuell bearbeiten, um eine ARM64ProjectForX
-Eigenschaft hinzuzufügen und den Pfad zum Arm64-Projekt anzugeben. Die beiden Projekte müssen sich in derselben Lösung befinden.
Erstellen einer Arm64X-DLL mit CMake
Um Ihre CMake-Projektbinärdateien als Arm64X zu erstellen, können Sie jede Version von CMake verwenden, die das Erstellen als Arm64EC unterstützt. Der Prozess umfasst zunächst die Erstellung des Projekts für Arm64, um die Arm64-Linkereingaben zu generieren. Anschließend sollte das Projekt erneut auf Arm64EC ausgerichtet werden, diesmal mit der Kombination der Arm64- und Arm64EC-Eingaben, um Arm64X-Binärdateien zu bilden. Die folgenden Schritte nutzen die Verwendung von CMakePresets.json.
Stellen Sie sicher, dass Sie über separate Konfigurationsvoreinstellungen für Arm64 und Arm64EC verfügen. Zum Beispiel:
{ "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" } } ] }
Fügen Sie zwei neue Konfigurationen hinzu, die von den oben genannten Voreinstellungen "Arm64" und "Arm64EC" erben. Legen Sie diesen
ARM64EC
WertBUILD_AS_ARM64X
in der Konfiguration fest, die von Arm64EC undBUILD_AS_ARM64X
ARM64
in der anderen Datei erbt. Diese Variablen werden verwendet, um zu kennzeichnen, dass die Builds aus diesen beiden Voreinstellungen Teil von Arm64X sind.{ "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" }
Fügen Sie ihrem CMake-Projekt eine neue CMAKE-Datei hinzu, die aufgerufen wird
arm64x.cmake
. Kopieren Sie den folgenden Codeausschnitt in die neue CMAKE-Datei.# 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 wird nur unterstützt, wenn Sie den MSVC-Linker aus Visual Studio 17.11 oder höher erstellen.
Wenn Sie einen älteren Linker verwenden müssen, kopieren Sie stattdessen den folgenden Codeausschnitt. Diese Route verwendet ein älteres Flag /LINK_REPRO. Die Verwendung der Route /LINK_REPRO führt zu einer langsameren Gesamtbuildzeit aufgrund des Kopierens von Dateien und hat bekannte Probleme bei der Verwendung des Ninja-Generators.
# 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()
Fügen Sie unten in der Datei auf oberster Ebene
CMakeLists.txt
in Ihrem Projekt den folgenden Codeausschnitt hinzu. Achten Sie darauf, den Inhalt der winkeligen Klammern durch tatsächliche Werte zu ersetzen. Dadurch wird die Datei verwendet, diearm64x.cmake
Sie soeben erstellt haben.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()
Erstellen Sie Ihr CMake-Projekt mit der Arm64X-fähigen Arm64-Voreinstellung (arm64-debug-x).
Erstellen Sie Ihr CMake-Projekt mit der Arm64X-fähigen Arm64EC-Voreinstellung (arm64ec-debug-x). Die endgültigen DLL-Dateien, die im Ausgabeverzeichnis für diesen Build enthalten sind, sind Arm64X-Binärdateien.
Erstellen einer reinen Arm64X-Weiterleitungs-DLL
Eine reine Arm64X-Weiterleitungs-DLL ist eine kleine Arm64X-DLL, die APIs je nach Typ an separate DLLs weiterleitet:
Arm64-APIs werden an eine Arm64-DLL weitergeleitet.
x64-APIs werden an eine x64- oder Arm64EC-DLL weitergeleitet.
Eine reine Arm64X-Weiterleitung ermöglicht die Vorteile der Verwendung einer Arm64X-Binärdatei, auch wenn es Herausforderungen beim Erstellen einer zusammengeführten Arm64X-Binärdatei mit dem gesamten Arm64EC- und Arm64-Code gibt. Erfahren Sie mehr über reine Arm64X-Weiterleitungs-DLLs auf der Übersichtsseite für Arm64X PE-Dateien.
Sie können eine reine Arm64X-Weiterleitung über die Arm64-Entwickler-Eingabeaufforderung erstellen, indem Sie die nachfolgenden Schritte befolgen. Die daraus resultierende reine Arm64X-Weiterleitung leitet x64-Aufrufe an foo_x64.DLL
und Arm64-Aufrufe an foo_arm64.DLL
weiter.
Erstellen Sie leere
OBJ
-Dateien, die später vom Linker zum Erstellen der reinen Weiterleitung verwendet werden. Diese sind leer, da die reine Weiterleitungen keinen Code enthält. Erstellen Sie dazu eine leere Datei. Im folgenden Beispiel wurde die Datei empty.cpp benannt. LeereOBJ
-Dateien werden dann mitcl
erstellt, einmal für Arm64 (empty_arm64.obj
) und einmal für Arm64EC (empty_x64.obj
):cl /c /Foempty_arm64.obj empty.cpp cl /c /arm64EC /Foempty_x64.obj empty.cpp
Wenn die Fehlermeldung „cl : Command line warning D9002 : ignoring unknown option '-arm64EC“ angezeigt wird, wird der falsche Compiler verwendet. Um dies zu beheben, wechseln Sie zur Arm64 Developer-Eingabeaufforderung.
Erstellen Sie
DEF
-Dateien für x64 und Arm64. Diese Dateien enumerieren alle API-Exporte der DLL und verweisen das Ladeprogramm auf den Namen der DLL, die diese API-Aufrufe erfüllen kann.foo_x64.def
:EXPORTS MyAPI1 = foo_x64.MyAPI1 MyAPI2 = foo_x64.MyAPI2
foo_arm64.def
:EXPORTS MyAPI1 = foo_arm64.MyAPI1 MyAPI2 = foo_arm64.MyAPI2
Anschließend können Sie
link
verwenden, umLIB
-Importdateien für x64 und Arm64 zu erstellen:link /lib /machine:x64 /def:foo_x64.def /out:foo_x64.lib link /lib /machine:arm64 /def:foo_arm64.def /out:foo_arm64.lib
Verknüpfen Sie die leeren
OBJ
-Dateien, und importieren SieLIB
-Dateien mithilfe des Flags/MACHINE:ARM64X
, um die reine Arm6X-Weiterleitungs-DLL zu erzeugen: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
Die daraus resultierende foo.dll
kann entweder in einen Arm64- oder einen x64/Arm64EC-Prozess geladen werden. Wenn ein Arm64-Prozess foo.dll
lädt, lädt das Betriebssystem stattdessen sofort foo_arm64.dll
, und alle API-Aufrufe werden von foo_arm64.dll
verarbeitet.
Windows on Arm