학습
Arm64X 바이너리 빌드
Arm64X PE 파일이라고도 하는 Arm64X 이진 파일을 빌드하여 x64/Arm64EC 및 Arm64 프로세스 모두에 단일 이진 파일을 로드할 수 있습니다.
Arm64X 이진 파일을 빌드할 수 있도록 Arm64EC 구성의 속성 페이지에는 프로젝트 파일에서와 같이 BuildAsX
로 알려진 새로운 'ARM64X 프로젝트 빌드' 속성이 있습니다.
사용자가 프로젝트를 빌드할 때 Visual Studio는 일반적으로 Arm64EC용으로 컴파일한 다음 출력을 Arm64EC 이진 파일에 연결합니다. BuildAsX
이(가) true
(으)로 설정되면 Visual Studio가 Arm64EC 및 Arm64 둘 다에 대해 컴파일됩니다. 그런 다음 Arm64EC 링크 단계를 사용하여 둘 다 단일 Arm64X 이진 파일에 연결합니다. 이 Arm64X 이진 파일에 대한 출력 디렉터리는 Arm64EC 구성에서 출력 디렉터리가 설정된 모든 디렉터리입니다.
BuildAsX
이(가) 올바르게 작동하려면 사용자에게 Arm64EC 구성 외에도 기존 Arm64 구성이 있어야 합니다. Arm64 및 Arm64EC 구성에는 동일한 C 런타임 및 C++ 표준 라이브러리(예: 둘 다 /MT로 설정되어야 함)가 있어야 합니다. 컴파일이 아닌 전체 Arm64 프로젝트 빌드와 같은 빌드 비효율성을 방지하려면 프로젝트의 모든 직접 및 간접 참조에서 BuildAsX
을(를) true로 설정해야 합니다.
빌드 시스템에서 Arm64 및 Arm64EC 구성의 이름이 동일하다고 가정합니다. Arm64 및 Arm64EC 구성의 이름이 다른 경우(예: Debug|ARM64
및 MyDebug|ARM64EC
) vcxproj 또는 Directory.Build.props
파일을 수동으로 편집하여 Arm64 구성의 이름을 제공하는 Arm64EC 구성ARM64ConfigurationNameForX
에 속성을 추가할 수 있습니다.
원하는 Arm64X 이진 파일이 Arm64와 Arm64EC와 같은 두 개의 개별 프로젝트의 조합인 경우 Arm64EC 프로젝트의 vxcproj를 수동으로 편집하여 ARM64ProjectForX
속성을 추가하고 Arm64 프로젝트의 경로를 지정할 수 있습니다. 두 프로젝트는 동일한 솔루션에 있어야 합니다.
CMake 프로젝트 이진 파일을 Arm64X로 빌드하려면 Arm64EC로 빌드를 지원하는 CMake 버전을 사용할 수 있습니다. 이 프로세스에는 처음에 Arm64를 대상으로 하는 프로젝트를 빌드하여 Arm64 링커 입력을 생성하는 작업이 포함됩니다. 그 후 Arm64EC를 대상으로 프로젝트를 다시 빌드해야 하며, 이번에는 Arm64 및 Arm64EC 입력을 결합하여 Arm64X 이진 파일을 형성합니다. 아래 단계에서는 CMakePresets.json 사용을 활용합니다.
Arm64 및 Arm64EC를 대상으로 하는 별도의 구성 사전 설정이 있는지 확인합니다. 예시:
{ "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" } } ] }
위에 있는 Arm64 및 Arm64EC 사전 설정에서 상속되는 두 개의 새 구성을 추가합니다.
ARM64EC
Arm64EC에서 상속되는 구성에서BUILD_AS_ARM64X
다른 구성으로ARM64
설정합니다BUILD_AS_ARM64X
. 이러한 변수는 이러한 두 사전 설정의 빌드가 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" }
라는
arm64x.cmake
CMake 프로젝트에 새 .cmake 파일을 추가합니다. 아래 코드 조각을 새 .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 는 Visual Studio 17.11 이상의 MSVC 링커를 사용하여 빌드하는 경우에만 지원됩니다.
이전 링커를 사용해야 하는 경우 아래 코드 조각을 대신 복사합니다. 이 경로는 이전 플래그 /LINK_REPRO 사용합니다. /LINK_REPRO 경로를 사용하면 파일 복사로 인해 전체 빌드 시간이 느려지고 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()
프로젝트의 최상위
CMakeLists.txt
파일 아래쪽에 아래 코드 조각을 추가합니다. 꺾쇠 괄호의 내용을 실제 값으로 대체해야 합니다. 이렇게 하면 위에서 방금 만든 파일이 사용됩니다arm64x.cmake
.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()
Arm64X 사용 Arm64 사전 설정(arm64-debug-x)을 사용하여 CMake 프로젝트를 빌드합니다.
Arm64X 사용 Arm64EC 사전 설정(arm64ec-debug-x)을 사용하여 CMake 프로젝트를 빌드합니다. 이 빌드의 출력 디렉터리에 포함된 최종 dll은 Arm64X 이진 파일입니다.
Arm64X 순수 전달자 DLL은 유형에 따라 API를 개별 DLL로 전달하는 작은 Arm64X DLL입니다.
Arm64 API는 Arm64 DLL로 전달됩니다.
x64 API는 x64 또는 Arm64EC DLL로 전달됩니다.
Arm64X 순수 전달자를 사용하면 모든 Arm64EC 및 Arm64 코드를 포함하는 병합된 Arm64X 이진 파일을 빌드하는 데 문제가 있는 경우에도 Arm64X 이진 파일을 사용할 수 있습니다. Arm64X PE 파일 개요 페이지에서 Arm64X 순수 전달자 DLL에 대해 자세히 알아봅니다.
아래 단계에 따라 Arm64 개발자 명령 프롬프트에서 Arm64X 순수 전달자를 빌드할 수 있습니다. 결과로, Arm64X 순수 전달자는 x64 호출을 foo_x64.DLL
(으)로 라우팅하며, Arm64 호출을 foo_arm64.DLL
(으)로 라우팅합니다.
나중에 링커에서 순수 전달자를 만드는 데 사용할 빈
OBJ
파일을 만듭니다. 순수 전달자에 코드가 없으므로 비어 있습니다. 이렇게 하려면 빈 파일을 생성하십시오. 아래 예제에서는 파일 이름을 empty.cpp로 지정했습니다. 그러면cl
을(를) 사용했을 때 빈OBJ
파일이 생성되며, 하나는 Arm64(empty_arm64.obj
)용 파일이고 다른 하나는 Arm64EC(empty_x64.obj
)가 됩니다.cl /c /Foempty_arm64.obj empty.cpp cl /c /arm64EC /Foempty_x64.obj empty.cpp
'cl: 명령줄 경고 D9002 : 알 수 없는 옵션 '-arm64EC'를 무시합니다.'라는 오류 메시지가 나타나면 잘못된 컴파일러가 사용되고 있는 것입니다. 이 문제를 해결하려면 Arm64 개발자 명령 프롬프트로 전환하세요.
x64 및 Arm64 모두에 대한
DEF
파일을 만듭니다. 이러한 파일은 DLL의 모든 API 내보내기를 나열하고, 로더를 해당 API 호출을 수행할 수 있는 DLL의 이름을 가리킵니다.foo_x64.def
:EXPORTS MyAPI1 = foo_x64.MyAPI1 MyAPI2 = foo_x64.MyAPI2
foo_arm64.def
:EXPORTS MyAPI1 = foo_arm64.MyAPI1 MyAPI2 = foo_arm64.MyAPI2
그런 다음
link
을(를) 사용하여 x64 및 Arm64 모두에 대한LIB
가져오기 파일을 만드는 데 사용할 수 있습니다.link /lib /machine:x64 /def:foo_x64.def /out:foo_x64.lib link /lib /machine:arm64 /def:foo_arm64.def /out:foo_arm64.lib
/MACHINE:ARM64X
플래그를 사용하여 빈OBJ
및LIB
파일을 연결하고 파일을 가져와 Arm6X 순수 전달자 DLL을 생성합니다.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
은(는) Arm64 또는 x64/Arm64EC 프로세스로 로드할 수 있습니다. Arm64 프로세스가 foo.dll
을(를) 로드하면 운영 체제가 즉시 foo_arm64.dll
을(를) 해당 위치에 로드하고 모든 API 호출이 foo_arm64.dll
에서 처리됩니다.
Windows on Arm 피드백
Windows on Arm은(는) 오픈 소스 프로젝트입니다. 다음 링크를 선택하여 피드백을 제공해 주세요.