Microsoft MVP성태의 닷넷 이야기
C/C++: 176. C/C++ - ARM64로 포팅할 때 유의할 점 [링크 복사], [링크+제목 복사],
조회: 6279
글쓴 사람
정성태 (seongtaejeong at gmail.com)
홈페이지
첨부 파일
 

(시리즈 글이 4개 있습니다.)
개발 환경 구성: 722. ARM 플랫폼 빌드를 위한 미니 PC(?) - Khadas VIM4
; https://www.sysnet.pe.kr/2/0/13727

개발 환경 구성: 724. ARM + docker 환경에서 .NET 8 설치
; https://www.sysnet.pe.kr/2/0/13732

개발 환경 구성: 726. ARM 플랫폼용 Visual C++ 리눅스 프로젝트 빌드
; https://www.sysnet.pe.kr/2/0/13735

C/C++: 176. C/C++ - ARM64로 포팅할 때 유의할 점
; https://www.sysnet.pe.kr/2/0/13751




C/C++ - ARM64로 포팅할 때 유의할 점

근래에 Visual C++ Linux 프로젝트 하나를 ARM64 환경으로 포팅했는데요, 윈도우 환경에서 Linux 환경으로 포팅할 때보다는 확실히 쉬웠습니다. ^^

우선, 빌드 자체가 기존 소스코드 그대로 ARM64 환경에서 오류 하나 없이 빌드되었기 때문에 다양한 API 사용에 대한 소스코드 수정은 필요치 않았습니다. 하지만, (컴파일 오류보다 더 무서운) 실행 시 예외가 2가지 정도 발생했는데요, 그중 하나는 char 타입의 signed/unsigned 여부였습니다.

가령 아래의 코드로,

#include <iostream>

int main()
{
    char v1 = 216;

    printf("v1: %d, sizeof(char): %zu\n", v1, sizeof(char));
}

x64 환경에서 빌드하면 이렇게 출력이 나오지만,

$ ./bin/x64/Debug/ConsoleApplication2.out
v1: -40, sizeof(char): 1

ARM64 환경에서 빌드하면 (-40이 아닌) 216이 출력됩니다.

$ ./bin/ARM64/Debug/ConsoleApplication2.out
v1: 216, sizeof(char): 1

이에 대해 검색해 보면 ARM64 환경에서 성능상의 이유로 char가 unsigned로 처리된다고 합니다.

is char signed or unsigned by default on iOS?
; https://stackoverflow.com/questions/20576300/is-char-signed-or-unsigned-by-default-on-ios

In most cases, char is unsigned on ARM (for performance reasons) and signed on other platforms.


하지만, 이게 ARM64 환경의 잘못이라기보다는 원래 C 표준 자체가 char 타입에 대해 signed/unsigned를 선택할 수 있도록 했기 때문이라고 합니다.

What causes a char to be signed or unsigned when using gcc?
; https://stackoverflow.com/questions/46463064/what-causes-a-char-to-be-signed-or-unsigned-when-using-gcc

위의 덧글에 나온 C11 표준뿐만 아니라, 근래에 나온 표준 문서인 n3301에도,

n1570 - ISO/IEC 9899:201x
; http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf

n3301 - ISO/IEC 9899:202y
; https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3301.pdf

이렇게 명시하고 있습니다.

6.2.5 Types

20. The three types char, signed char, and unsigned char are collectively called the character types.
The implementation shall define char to have the same range, representation, and behavior as either
signed char or unsigned char.

CHAR_MIN, defined in <limits.h>, will have one of the values 0 or SCHAR_MIN, and this can be used to distinguish the
two options. Irrespective of the choice made, char is a separate type from the other two and is not compatible with either.


따라서, 무심코 char 타입을 signed로 가정한 모든 소스코드에서 버그가 발생할 수 있는데요, 이를 해결하려면 1) 모든 소스코드에 사용한 char 타입을 signed/unsigned를 구분해 명시하거나, 2) 컴파일러 옵션을 사용하여 char의 부호 유무에 대한 기본값을 명시하는 방법으로 해결할 수 있습니다.

당연히, 두 번째 선택이 편하고 안전합니다. gcc의 경우 그에 해당하는 옵션을 제공하는데요,

-fsigned-char 
-funsigned-char

Visual C++ 프로젝트라면 다음과 같이 "Command Line"을 통해 "-fsigned-char"를 직접 명시해 주면 됩니다.

gcc_char_signed_1.png

(위의 화면에서는 "Configuration"을 "Debug"로 설정했는데, 당연히 "Release"도 함께 설정해야 합니다.)




그다음 문제는, x86/x64 빌드를 모두 지원하는 C/C++ 프로젝트에서 발생할 수 있는 유형인데요, 보통 Windows/Linux 환경만을 대상으로 했다면 64비트/32비트 환경만을 구분하면 되기 때문에 전처리 변수를 _AMD64_ 또는 _X86_으로만 사용했을 것입니다.

#if defined(_AMD64_)
    // 64비트 환경의 코드
#else
    // 32비트 환경의 코드
#endif

그나마 "_X86_"으로 했다면 상관없지만, "_AMD64_"로 했다면 위의 소스코드를 ARM64 환경에서 빌드하는 경우 "// 32비트 환경의 코드"가 빌드돼 버그가 발생합니다.

따라서, 이제는 ARM64를 고려하도록 다음과 같이 변경해야 합니다.

#if defined(_AMD64_) || defined(_ARM64_)
    // 64비트 환경의 코드
#else
    // 32비트 환경의 코드
#endif

제 경우에는, 위의 2가지만 수정했더니 ARM64 환경에서도 C/C++ 프로젝트가 잘 동작했습니다.




마지막으로, ARM64의 경우 대체로 Little-Endian을 쓰는 듯하지만, 그래도 원칙은 Bi-Endian을 지원하는 환경이니만큼 혹시 모르니 Endian 체크도 하면 좋을 듯합니다. ^^

#include <cstdio>
#include <limits.h>

bool IsLittleEndian()
{
    int x = 1;
    return *(char*)&x == 1;
}

int main()
{
    signed char v1 = 216;

    printf("v1: %d, sizeof(char): %zu, is little-endian: %d\n", v1, sizeof(char), IsLittleEndian());

    return 0;
}

이 외에, 기타 (다행히 제 경우에는 해당하지 않았지만) 뭔가 빠진 부분이 있을 수도 있는데요, 혹시 이에 대한 경험이 있으신 분들은 ^^ 덧글로 재미있는 썰을 부탁드립니다.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 10/2/2024]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 61  62  63  64  65  66  67  68  69  70  71  72  73  [74]  75  ...
NoWriterDateCnt.TitleFile(s)
12117정성태1/15/202020282디버깅 기술: 159. C# - 디버깅 중인 프로세스를 강제로 다른 디버거에서 연결하는 방법파일 다운로드1
12116정성태1/15/202021024디버깅 기술: 158. Visual Studio로 디버깅 시 sos.dll 확장 명령어를 (비롯한 windbg의 다양한 기능을) 수행하는 방법
12115정성태1/14/202021258디버깅 기술: 157. C# - PEB.ProcessHeap을 이용해 디버깅 중인지 확인하는 방법파일 다운로드1
12114정성태1/13/202022652디버깅 기술: 156. C# - PDB 파일로부터 심벌(Symbol) 및 타입(Type) 정보 열거 [1]파일 다운로드3
12113정성태1/12/202022933오류 유형: 590. Visual C++ 빌드 오류 - fatal error LNK1104: cannot open file 'atls.lib' [1]
12112정성태1/12/202017607오류 유형: 589. PowerShell - 원격 Invoke-Command 실행 시 "WinRM cannot complete the operation" 오류 발생
12111정성태1/12/202021478디버깅 기술: 155. C# - KernelMemoryIO 드라이버를 이용해 실행 프로그램을 숨기는 방법(DKOM: Direct Kernel Object Modification) [16]파일 다운로드1
12110정성태1/11/202021546디버깅 기술: 154. Patch Guard로 인해 블루 스크린(BSOD)가 발생하는 사례 [5]파일 다운로드1
12109정성태1/10/202017768오류 유형: 588. Driver 프로젝트 빌드 오류 - Inf2Cat error -2: "Inf2Cat, signability test failed."
12108정성태1/10/202018753오류 유형: 587. Kernel Driver 시작 시 127(The specified procedure could not be found.) 오류 메시지 발생
12107정성태1/10/202020195.NET Framework: 877. C# - 프로세스의 모든 핸들을 열람 - 두 번째 이야기
12106정성태1/8/202020466VC++: 136. C++ - OSR Driver Loader와 같은 Legacy 커널 드라이버 설치 프로그램 제작 [1]
12105정성태1/8/202018993디버깅 기술: 153. C# - PEB를 조작해 로드된 DLL을 숨기는 방법
12104정성태1/7/202021016DDK: 9. 커널 메모리를 읽고 쓰는 NT Legacy driver와 C# 클라이언트 프로그램 [4]
12103정성태1/7/202024127DDK: 8. Visual Studio 2019 + WDK Legacy Driver 제작- Hello World 예제 [1]파일 다운로드2
12102정성태1/6/202019687디버깅 기술: 152. User 권한(Ring 3)의 프로그램에서 _ETHREAD 주소(및 커널 메모리를 읽을 수 있다면 _EPROCESS 주소) 구하는 방법
12101정성태1/5/202020798.NET Framework: 876. C# - PEB(Process Environment Block)를 통해 로드된 모듈 목록 열람
12100정성태1/3/202018063.NET Framework: 875. .NET 3.5 이하에서 IntPtr.Add 사용
12099정성태1/3/202021011디버깅 기술: 151. Windows 10 - Process Explorer로 확인한 Handle 정보를 windbg에서 조회 [1]
12098정성태1/2/202020746.NET Framework: 874. C# - 커널 구조체의 Offset 값을 하드 코딩하지 않고 사용하는 방법 [3]
12097정성태1/2/202018694디버깅 기술: 150. windbg - Wow64, x86, x64에서의 커널 구조체(예: TEB) 구조체 확인
12096정성태12/30/201920761디버깅 기술: 149. C# - DbgEng.dll을 이용한 간단한 디버거 제작 [1]
12095정성태12/27/201923126VC++: 135. C++ - string_view의 동작 방식
12094정성태12/26/201920938.NET Framework: 873. C# - 코드를 통해 PDB 심벌 파일 다운로드 방법
12093정성태12/26/201920441.NET Framework: 872. C# - 로딩된 Native DLL의 export 함수 목록 출력파일 다운로드1
12092정성태12/25/201918582디버깅 기술: 148. cdb.exe를 이용해 (ntdll.dll 등에 정의된) 커널 구조체 출력하는 방법
... 61  62  63  64  65  66  67  68  69  70  71  72  73  [74]  75  ...