Delen via


Binaire Arm64X-bestanden bouwen

U kunt binaire Arm64X-bestanden, ook wel Arm64X PE-bestanden genoemd, bouwen ter ondersteuning van het laden van één binair bestand in zowel x64/Arm64EC- als Arm64-processen.

Een Arm64X-binair bestand bouwen vanuit een Visual Studio-project

Als u Arm64X-binaries wilt bouwen, heeft de eigenschapspagina van de Arm64EC-configuratie een nieuwe eigenschap 'Project bouwen als ARM64X', ook wel bekend als BuildAsX in het projectbestand.

Eigenschappenpagina voor een Arm64EC-configuratie met de optie Build Project als ARM64X

Wanneer u een project bouwt, compileert Visual Studio normaal gesproken voor Arm64EC en koppelt de uitvoer vervolgens aan een binaire Arm64EC-bestand. Wanneer u BuildAsX op true instelt, compileert Visual Studio zowel voor Arm64EC als voor Arm64. De arm64EC-koppelingsstap koppelt beide uitvoer aan één binair arm64X-bestand. De uitvoermap voor dit binaire Arm64X-bestand is de uitvoermap die is ingesteld onder de Arm64EC-configuratie.

Voor BuildAsX om correct te werken, moet u een bestaande Arm64-configuratie hebben, naast de Arm64EC-configuratie. De configuraties Arm64 en Arm64EC moeten dezelfde C-runtime en C++-standaardbibliotheek gebruiken (bijvoorbeeld allebei ingesteld op /MT). Om bouwinefficiënties te vermijden, zoals volledige Arm64-projecten bouwen in plaats van alleen compilatie, stel BuildAsX in op waar voor alle directe en indirecte verwijzingen van het project.

Bij het buildsysteem wordt ervan uitgegaan dat de Arm64- en Arm64EC-configuraties dezelfde naam hebben. Als de Arm64- en Arm64EC-configuraties verschillende namen hebben (zoals Debug|ARM64 en MyDebug|ARM64EC), kunt u de vcxproj of Directory.Build.props het bestand handmatig bewerken om een ARM64ConfigurationNameForX eigenschap toe te voegen aan de Arm64EC-configuratie die de naam van de Arm64-configuratie biedt.

Als u wilt dat het binaire bestand Arm64X twee afzonderlijke projecten combineert, één als Arm64 en één als Arm64EC, kunt u de vxcproj van het Arm64EC-project handmatig bewerken om een ARM64ProjectForX eigenschap toe te voegen en het pad naar het Arm64-project op te geven. De twee projecten moeten zich in dezelfde oplossing bevinden.

Een Arm64X DLL bouwen met CMake

Als u binaire bestanden van uw CMake-project wilt bouwen als Arm64X, gebruikt u elke versie van CMake die ondersteuning biedt voor bouwen als Arm64EC. Bouw eerst het project dat is gericht op Arm64 om de Arm64-linkerinvoer te genereren. Bouw vervolgens opnieuw het project dat gericht is op Arm64EC, waarbij de Arm64- en Arm64EC-invoer worden gecombineerd om binaire Arm64X-bestanden te vormen. De volgende stappen laten zien hoe uCMakePresets.jsonkunt gebruiken.

  1. Zorg ervoor dat u afzonderlijke configuratie-voorinstellingen hebt die gericht zijn op Arm64 en Arm64EC. Voorbeeld:

     {
       "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"
           }
         }
       ]
     }
    
  2. Voeg twee nieuwe configuraties toe die overnemen van de vooraf ingestelde arm64- en Arm64EC-instellingen die u in de vorige stap hebt gemaakt. Stel BUILD_AS_ARM64X in op ARM64EC in de configuratie die wordt overgenomen van Arm64EC en stel BUILD_AS_ARM64X in op ARM64 in de andere. Deze variabelen geven aan dat de builds van deze twee voorinstellingen deel uitmaken van 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"
           }
           }
    
  3. Voeg een nieuw CMAKE-bestand toe aan uw CMake-project met de naam arm64x.cmake. Kopieer het volgende fragment naar het nieuwe CMAKE-bestand.

     # 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 wordt alleen ondersteund als u bouwt met behulp van de MSVC-linker van Visual Studio 17.11 of hoger.

Als u een oudere linker wilt gebruiken, kopieert u in plaats daarvan het volgende codefragment. Deze route maakt gebruik van een oudere vlag /LINK_REPRO. Het gebruik van de /LINK_REPRO route resulteert in een tragere algehele buildtijd vanwege het kopiëren van bestanden en heeft bekende problemen bij het gebruik van ninjagenerator.

# 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()
  1. Voeg onder aan het bestand op het hoogste niveau CMakeLists.txt in uw project het volgende fragment toe. Zorg ervoor dat u de inhoud van de hoekige haken vervangt door werkelijke waarden. Deze stap verbruikt het arm64x.cmake bestand dat u zojuist hebt gemaakt.

     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()
    
  2. Bouw uw CMake-project door de Arm64X-geschikte arm64-voorinstelling (arm64-debug-x) te gebruiken.

  3. Bouw uw CMake-project met behulp van de arm64X-voorinstelling Arm64EC (arm64ec-debug-x). De uiteindelijke DLL's in de uitvoermap voor deze build zijn binaire Arm64X-bestanden.

Een Arm64X pure forwarder DLL bouwen

Een Arm64X pure forwarder DLL is een kleine Arm64X DLL waarmee API's worden doorgestuurd om DLL's te scheiden, afhankelijk van hun type:

  • Arm64-API's worden doorgestuurd naar een Arm64-DLL.

  • x64 API's worden doorgestuurd naar een x64- of Arm64EC-DLL.

Een pure Arm64X-doorstuurserver maakt het gebruik van een Arm64X-binair mogelijk, zelfs als er problemen zijn met het bouwen van een samengevoegd arm64X-binair bestand met alle Arm64EC- en Arm64-code. Zie Arm64X PE-bestanden voor meer informatie.

U kunt een pure Arm64X-doorstuurserver bouwen vanaf de Opdrachtprompt van de Arm64-ontwikkelaar door de onderstaande stappen te volgen. De resulterende Arm64X pure doorstuurserver routeert x64-aanroepen naar foo_x64.DLL en Arm64-aanroepen naar foo_arm64.DLL.

  1. Maak lege OBJ bestanden die door de linker worden gebruikt om de pure doorstuurserver te maken. Deze bestanden zijn leeg omdat de pure doorstuurserver geen code bevat. Als u deze bestanden wilt maken, maakt u een leeg bestand. In het volgende voorbeeld heeft het bestand de naam empty.cpp. Gebruik cl dit om lege OBJ bestanden te maken, met één voor Arm64 (empty_arm64.obj) en één voor Arm64EC (empty_x64.obj):

    cl /c /Foempty_arm64.obj empty.cpp
    cl /c /arm64EC /Foempty_x64.obj empty.cpp
    

    Als u het foutbericht 'cl: opdrachtregelwaarschuwing D9002: onbekende optie '-arm64EC' negeert, gebruikt u de verkeerde compiler. U kunt dit probleem oplossen door over te schakelen naar de Arm64 Developer-opdrachtprompt.

  2. Maak DEF bestanden voor zowel x64 als Arm64. Deze bestanden bevatten alle API-exports van het DLL-bestand en wijzen het laadprogramma aan op de naam van het DLL-bestand dat aan deze API-aanroepen kan voldoen.

    foo_x64.def:

    EXPORTS
        MyAPI1  =  foo_x64.MyAPI1
        MyAPI2  =  foo_x64.MyAPI2
    

    foo_arm64.def:

    EXPORTS
        MyAPI1  =  foo_arm64.MyAPI1
        MyAPI2  =  foo_arm64.MyAPI2
    
  3. Gebruik linkLIB om importbestanden te maken voor zowel x64 als 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
    
  4. Koppel de lege OBJ bestanden en importeer LIB bestanden met behulp van de vlag /MACHINE:ARM64X om de Pure Forwarder DLL van Arm6X te produceren:

    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
    

De resulterende foo.dll kan worden geladen in een Arm64- of een x64/Arm64EC-proces. Wanneer een Arm64-proces foo.dll laadt, laadt het besturingssysteem onmiddellijk foo_arm64.dll ter vervanging ervan, en verwerkt foo_arm64.dll alle API-aanroepen.