Arm64X PE 파일이라고도 하는 Arm64X 이진 파일을 빌드하여 x64/Arm64EC 및 Arm64 프로세스 모두에 단일 이진 파일을 로드할 수 있습니다.
Visual Studio 프로젝트에서 Arm64X 바이너리 빌드하기
Arm64X 이진 파일을 빌드할 수 있도록 Arm64EC 구성의 속성 페이지에는 프로젝트 파일에서와 같이 BuildAsX로 알려진 새로운 'ARM64X 프로젝트 빌드' 속성이 있습니다.
프로젝트를 빌드할 때 Visual Studio는 일반적으로 Arm64EC용으로 컴파일한 다음 출력을 Arm64EC 이진 파일에 연결합니다. 설정 BuildAsXtrue하면 Visual Studio는 Arm64EC 및 Arm64 모두에 대해 컴파일됩니다. Arm64EC 링크 단계는 두 출력을 단일 Arm64X 이진 파일로 연결합니다. 이 Arm64X 이진 파일에 대한 출력 디렉터리는 Arm64EC 구성에서 설정된 출력 디렉터리입니다.
BuildAsX 올바르게 작동하려면 Arm64EC 구성 외에도 기존 Arm64 구성이 있어야 합니다. Arm64 및 Arm64EC 구성은 동일한 C 런타임 및 C++ 표준 라이브러리를 사용해야 합니다(예: 둘 다 /MT로 설정됨). 컴파일이 아닌 전체 Arm64 프로젝트 빌드와 같은 빌드 비효율성을 방지하려면 프로젝트의 모든 직접 및 간접 참조에 대해 true로 설정합니다 BuildAsX .
빌드 시스템에서 Arm64 및 Arm64EC 구성의 이름이 동일하다고 가정합니다. Arm64 및 Arm64EC 구성의 이름이 다른 경우(예: Debug|ARM64 및 MyDebug|ARM64EC) vcxproj 또는 Directory.Build.props파일을 수동으로 편집하여 Arm64 구성의 이름을 제공하는 Arm64EC 구성ARM64ConfigurationNameForX에 속성을 추가할 수 있습니다.
Arm64X 이진 파일이 Arm64와 Arm64EC로 각각 다른 두 개의 프로젝트를 결합하도록 하려면 Arm64EC 프로젝트의 vxcproj를 수동으로 편집하여 속성을 추가하고 ARM64ProjectForX Arm64 프로젝트의 경로를 지정할 수 있습니다. 두 프로젝트는 동일한 솔루션에 있어야 합니다.
CMake를 사용하여 Arm64X DLL 빌드
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 사전 설정에서 상속되는 두 개의 새 구성을 추가합니다.
BUILD_AS_ARM64X를 Arm64EC에서 상속되는 구성에서 설정하고,ARM64EC를 다른 구성에서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.cmakeCMake 프로젝트에 새 .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 만들기
Arm64X 순수 전달자 DLL은 유형에 따라 API를 개별 DLL로 전달하는 작은 Arm64X DLL입니다.
Arm64 API는 Arm64 DLL로 전달됩니다.
x64 API는 x64 또는 Arm64EC DLL로 전달됩니다.
Arm64X 순수 전달자를 사용하면 모든 Arm64EC 및 Arm64 코드를 포함하는 병합된 Arm64X 이진 파일을 빌드하는 데 문제가 있는 경우에도 Arm64X 이진 파일을 사용할 수 있습니다. 자세한 내용은 Arm64X PE 파일을 참조하세요.
아래 단계에 따라 Arm64 개발자 명령 프롬프트에서 Arm64X 순수 전달자를 빌드할 수 있습니다. 결과 Arm64X 순수 전달자는 x64 호출을 foo_x64.DLL로, Arm64 호출을 foo_arm64.DLL로 라우팅합니다.
링커가 순수 전달자를 만드는 데 사용하는 빈
OBJ파일을 만듭니다. 순수 전달자에 코드가 없기 때문에 이러한 파일은 비어 있습니다. 이러한 파일을 만들려면 빈 파일을 만듭니다. 다음 예제에서 파일 이름은 empty.cpp. 빈cl파일을 생성하여 하나는 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.MyAPI2foo_arm64.def:EXPORTS MyAPI1 = foo_arm64.MyAPI1 MyAPI2 = foo_arm64.MyAPI2x64 및 Arm64용 가져오기 파일을 만들기 위해
link을(를) 사용합니다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을 로드하고, foo_arm64.dll가 모든 API 호출을 처리합니다.
Windows on Arm