Criar binários do Arm64X
Você pode criar binários Arm64X, também conhecidos como arquivos Arm64X PE, para suportar o carregamento de um único binário em processos x64/Arm64EC e Arm64.
Para habilitar a criação de binários Arm64X, as páginas de propriedades da configuração Arm64EC têm uma nova propriedade "Build Project as ARM64X", conhecida como BuildAsX
no arquivo de projeto.
Quando um usuário cria um projeto, o Visual Studio normalmente compila para Arm64EC e, em seguida, vincula as saídas em um binário Arm64EC. Quando BuildAsX
estiver definido como true
, o Visual Studio compilará para Arm64EC e Arm64. A etapa de link Arm64EC é então usada para vincular ambos em um único binário Arm64X. O diretório de saída para este binário Arm64X será qualquer que seja o diretório de saída definido sob a configuração Arm64EC.
Para BuildAsX
funcionar corretamente, o usuário deve ter uma configuração Arm64 existente, além da configuração Arm64EC. As configurações Arm64 e Arm64EC devem ter o mesmo tempo de execução C e biblioteca padrão C++ (por exemplo, ambos definidos /MT). Para evitar ineficiências de construção, como a construção de projetos Arm64 completos em vez de apenas compilação, todas as referências diretas e indiretas do projeto devem ter BuildAsX
sido definidas como verdadeiras.
O sistema de compilação assume que as configurações Arm64 e Arm64EC têm o mesmo nome. Se as configurações Arm64 e Arm64EC tiverem nomes diferentes (como Debug|ARM64
e MyDebug|ARM64EC
), você poderá editar manualmente o vcxproj ou Directory.Build.props
arquivo para adicionar uma ARM64ConfigurationNameForX
propriedade à configuração Arm64EC que forneça o nome da configuração Arm64.
Se o binário Arm64X desejado for uma combinação de dois projetos separados, um como Arm64 e outro como Arm64EC, você poderá editar manualmente o vxcproj do projeto Arm64EC para adicionar uma ARM64ProjectForX
propriedade e especificar o caminho para o projeto Arm64. Os dois projetos devem estar na mesma solução.
Para criar os binários do projeto do CMake como Arm64X, você pode usar qualquer versão do CMake que dê suporte à compilação como Arm64EC. O processo envolve a criação inicial do projeto direcionado ao Arm64 para gerar as entradas do vinculador Arm64. Posteriormente, o projeto deve ser construído novamente visando Arm64EC, desta vez combinando as entradas Arm64 e Arm64EC para formar binários Arm64X. As etapas abaixo aproveitam o uso de CMakePresets.json.
Certifique-se de ter predefinições de configuração separadas direcionadas a Arm64 e Arm64EC. Por exemplo:
{ "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" } } ] }
Adicione duas novas configurações que herdam as predefinições Arm64 e Arm64EC que você tem acima. Defina
BUILD_AS_ARM64X
comoARM64EC
na configuração que herda de Arm64EC eBUILD_AS_ARM64X
comoARM64
na outra. Essas variáveis serão usadas para indicar que as compilações dessas duas predefinições fazem parte do 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" }
Adicione um novo arquivo .cmake ao seu projeto CMake chamado
arm64x.cmake
. Copie o snippet abaixo para o novo arquivo .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 só terá suporte se você estiver criando usando o vinculador MSVC do Visual Studio 17.11 ou posterior.
Se você precisar usar um vinculador mais antigo, copie o snippet abaixo. Essa rota usa um sinalizador /LINK_REPRO mais antigo. Usar a rota /LINK_REPRO resultará em um tempo de compilação geral mais lento devido à cópia de arquivos e tem problemas conhecidos ao usar o gerador 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()
Na parte inferior do arquivo de nível
CMakeLists.txt
superior do seu projeto, adicione o snippet abaixo. Certifique-se de substituir o conteúdo dos colchetes angulares por valores reais. Isso consumirá oarm64x.cmake
arquivo que você acabou de criar acima.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()
Crie seu projeto CMake usando a predefinição Arm64 habilitada para Arm64X (arm64-debug-x).
Crie seu projeto CMake usando a predefinição Arm64EC habilitada para Arm64X (arm64ec-debug-x). As dlls finais contidas no diretório de saída para este build serão binários Arm64X.
Uma DLL de encaminhador puro Arm64X é uma pequena DLL Arm64X que encaminha APIs para separar DLLs dependendo de seu tipo:
As APIs do Arm64 são encaminhadas para uma DLL do Arm64.
APIs x64 são encaminhadas para uma DLL x64 ou Arm64EC.
Um encaminhador puro Arm64X permite as vantagens de usar um binário Arm64X, mesmo que haja desafios com a construção de um binário Arm64X mesclado contendo todo o código Arm64EC e Arm64. Saiba mais sobre as DLLs de encaminhador puro Arm64X na página de visão geral dos arquivos Arm64X PE.
Você pode criar um encaminhador puro Arm64X a partir do prompt de comando do desenvolvedor Arm64 seguindo as etapas abaixo. O encaminhador puro Arm64X resultante roteará chamadas x64 para e chamadas Arm64 para foo_x64.DLL
foo_arm64.DLL
.
Crie arquivos vazios
OBJ
que serão usados posteriormente pelo vinculador para criar o encaminhador puro. Eles estão vazios, pois o encaminhador puro não tem código. Para fazer isso, crie um arquivo vazio. Para o exemplo abaixo, nomeamos o arquivo vazio.cpp. Arquivos vaziosOBJ
são então criados usandocl
, com um para Arm64 () e um para Arm64EC (empty_arm64.obj
empty_x64.obj
):cl /c /Foempty_arm64.obj empty.cpp cl /c /arm64EC /Foempty_x64.obj empty.cpp
Se a mensagem de erro "cl : aviso de linha de comando D9002 : ignorando a opção desconhecida '-arm64EC'" for exibida, o compilador incorreto está sendo usado. Para resolver isso, mude para o Prompt de Comando do Desenvolvedor Arm64.
Crie
DEF
arquivos para x64 e Arm64. Esses arquivos enumeram todas as exportações de API da DLL e aponta o carregador para o nome da DLL que pode atender a essas chamadas de 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
Em seguida, você pode usar
link
para criarLIB
arquivos de importação para x64 e 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
Vincule os arquivos vazios
OBJ
e de importaçãoLIB
usando o sinalizador para produzir a DLL do encaminhador/MACHINE:ARM64X
puro 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
O resultado foo.dll
pode ser carregado em um processo Arm64 ou x64/Arm64EC. Quando um processo Arm64 é foo.dll
carregado, o sistema operacional será carregado foo_arm64.dll
imediatamente em seu lugar e quaisquer chamadas de API serão tratadas pelo foo_arm64.dll
.
Comentários do Windows on Arm
O Windows on Arm é um projeto código aberto. Selecione um link para fornecer comentários: