Dela via


Skapa Arm64X-binärfiler

Du kan skapa Arm64X-binärfiler, även kallade Arm64X PE-filer, för att stödja inläsning av en enda binär fil i både x64/Arm64EC- och Arm64-processer.

Skapa en Arm64X-binär fil från ett Visual Studio-projekt

För att aktivera skapandet av Arm64X-binärfiler har egenskapssidorna i Arm64EC-konfigurationen en ny "Build Project as ARM64X"-egenskap, som BuildAsX kallas i projektfilen.

Inställningssida för en Arm64EC-konfiguration där alternativet Skapa projekt som ARM64X visas

När du skapar ett projekt kompilerar Visual Studio normalt för Arm64EC och länkar sedan utdata till en Arm64EC-binär fil. När du anger BuildAsX till true, kompilerar Visual Studio för både Arm64EC och Arm64. Arm64EC-länksteget länkar båda utdata till en enda Arm64X-binär fil. Utdatakatalogen för den här Arm64X-binärfilen är utdatakatalogen som anges under Arm64EC-konfigurationen.

För BuildAsX att fungera korrekt måste du ha en befintlig Arm64-konfiguration utöver Arm64EC-konfigurationen. Arm64- och Arm64EC-konfigurationerna måste använda samma C-körnings- och C++-standardbibliotek (till exempel båda inställda på /MT). För att undvika ineffektivitet i bygget, till exempel att skapa fullständiga Arm64-projekt i stället för bara kompilering, anger du BuildAsX till sant för alla direkta och indirekta referenser till projektet.

Byggsystemet förutsätter att Arm64- och Arm64EC-konfigurationerna har samma namn. Om arm64- och Arm64EC-konfigurationerna har olika namn (till exempel Debug|ARM64 och MyDebug|ARM64EC) kan du manuellt redigera vcxproj eller Directory.Build.props filen för att lägga till en ARM64ConfigurationNameForX egenskap i Arm64EC-konfigurationen som ger namnet på Arm64-konfigurationen.

Om du vill att Arm64X-binärfilen ska kombinera två separata projekt, ett som Arm64 och ett som Arm64EC, kan du manuellt redigera vxcproj för Arm64EC-projektet för att lägga till en ARM64ProjectForX egenskap och ange sökvägen till Arm64-projektet. De två projekten måste finnas i samma lösning.

Skapa en Arm64X DLL med CMake

Om du vill skapa dina CMake-projekt binärfiler som Arm64X använder du valfri version av CMake som har stöd för att skapa som Arm64EC. Börja med att skapa projektet med arm64 som mål för att generera Arm64-länkningsindata. Skapa sedan projektet igen med Arm64EC som mål och kombinera Arm64- och Arm64EC-indata för att bilda Arm64X-binärfiler. Följande steg visar hur du använderCMakePresets.json.

  1. Se till att du har separata konfigurationsförinställningar för Arm64 och Arm64EC. Till exempel:

     {
       "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. Lägg till två nya konfigurationer som ärver från de Arm64- och Arm64EC-förinställningar som du skapade i föregående steg. Ange BUILD_AS_ARM64X till ARM64EC i konfigurationen som ärver från Arm64EC och BUILD_AS_ARM64X till ARM64 i den andra. Dessa variabler innebär att versionerna från dessa två förinställningar är en del av 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. Lägg till en ny .cmake-fil i CMake-projektet med namnet arm64x.cmake. Kopiera följande kodfragment till den nya .cmake-filen.

     # 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 stöds endast om du skapar med hjälp av MSVC-länkaren från Visual Studio 17.11 eller senare.

Om du behöver använda en äldre länkare kopierar du följande kodfragment i stället. Den här vägen använder en äldre flagga /LINK_REPRO. Om du använder /LINK_REPRO-väg resulterar det i en långsammare total byggtid på grund av kopiering av filer och det har kända problem när man använder Ninja-generator.

# 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. Lägg till följande kodfragment längst ned i filen på den översta nivån CMakeLists.txt i projektet. Se till att ersätta innehållet i vinkelparenteserna med faktiska värden. Det här steget använder filen arm64x.cmake som du nyss skapade.

     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. Skapa ditt CMake-projekt med den Arm64X-stödda förinställningen Arm64 (arm64-debug-x).

  3. Skapa ditt CMake-projekt med arm64X-aktiverad Arm64EC-förinställning (arm64ec-debug-x). De sista DLL:erna i utdatakatalogen för den här versionen är Arm64X-binärfiler.

Skapa en ren Arm64X forwarder-DLL-fil

En Arm64X ren vidarebefordrare DLL är en liten Arm64X DLL som vidarebefordrar API:er för att separera DLL:er beroende på typ:

  • Arm64-API:er vidarebefordras till en Arm64 DLL.

  • x64-API:er vidarebefordras till en X64- eller Arm64EC DLL.

En Arm64X ren vidarebefordrare möjliggör fördelarna med att använda en Arm64X-binär fil även om det finns utmaningar med att skapa en sammanslagen Arm64X-binär fil som innehåller all Arm64EC- och Arm64-kod. Mer information finns i Arm64X PE-filer.

Du kan skapa en Arm64X ren vidarebefordrare från kommandotolken för Arm64-utvecklare genom att följa stegen nedan. Den resulterande Arm64X rena vidarebefordraren dirigerar x64-anrop till foo_x64.DLL och Arm64-anrop till foo_arm64.DLL.

  1. Skapa tomma OBJ filer som länkaren använder för att skapa den rena vidarebefordraren. Dessa filer är tomma eftersom den rena vidarebefordraren inte innehåller någon kod. Skapa en tom fil för att skapa dessa filer. I följande exempel heter filen empty.cpp. Använd cl för att skapa tomma OBJ filer med en för Arm64 (empty_arm64.obj) och en för Arm64EC (empty_x64.obj):

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

    Om du ser felmeddelandet "cl : Kommandoradsvarning D9002 : ignorerar det okända alternativet "-arm64EC" använder du fel kompilator. Åtgärda det här problemet genom att växla till kommandotolken för Arm64 Developer.

  2. Skapa DEF filer för både x64 och Arm64. De här filerna visar alla API-exporter av DLL:n och pekar inläsaren på namnet på den DLL som kan uppfylla dessa API-anrop.

    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. Använd link för att skapa LIB importfiler för både x64 och 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. Länka de tomma OBJ filerna och importera LIB filerna med hjälp av flaggan /MACHINE:ARM64X för att skapa arm6X-DLL:n för ren vidarebefordrare:

    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
    

foo.dll Resultatet kan läsas in i antingen en Arm64- eller x64/Arm64EC-process. När en Arm64-process läser in foo.dll, laddar operativsystemet omedelbart in foo_arm64.dll i dess ställe, och eventuella API-anrop hanteras av foo_arm64.dll.