-
[어셈블리어] AArch64 어셈블리어임베디드 SW/어셈블리어 2023. 8. 3. 02:35
글의 참고
- https://mariokartwii.com/armv8/
- https://thinkingeek.com/arm-assembler-raspberry-pi/
- https://thinkingeek.com/2016/10/08/exploring-aarch64-assembler-chapter1/
글의 전제
- 내가 글을 쓰다가 궁금한 점은 파란색 볼드체로 표현했다. 나도 모르기 때문에 나중에 알아봐야 할 내용이라는 뜻이다.
- 밑줄로 작성된 글은 좀 더 긴 설명이 필요해서 친 것이다. 그러므로, 밑 줄 처친 글이 이해가 안간다면 링크를 따라서 관련 내용을 공부하자.
- `글의 참조`에서 빨간색 볼드체로 체크된 링크는 이 글을 작성하면 가장 많이 참조한 링크다.
- 이 글은 글의 참조에 있는 링크들을 내 마음대로 번역한 것이다. 심지어 필요없다고 생각된 부분은 생략했음을 알린다.
- Software 환경
타입 버전 OS(리눅스 커널 정보) Linux yohda-800G5M-800G5W 5.4.0-139-generic #156~18.04.1-Ubuntu SMP Wed Jan 25 15:56:22 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux lsb_release -a(리눅스 배포판 정보) No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 18.04.6 LTS
Release: 18.04
Codename: bionicqemu-aarch64 qemu-aarch64 version 2.11.1(Debian 1:2.11+dfsg-1ubuntu7.41)
Copyright (c) 2003-2017 Fabrice Bellard and the QEMU Project developersaarch64-linux-gnu-gcc aarch64-linux-gnu-gcc (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04) 7.5.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
글의 내용
- AArch64는 ARM이 2011년에 발표한 ARMv8 아키텍처의 64-bit mode에서 동작하는 실행 상태를 의미한다. 현재는 스마트폰과 서버에 점차 배포가 되어가고 있다(이 글이 써진 시점이 2016.08). 사실 이미 굉장히 많은 분야에서 사용하고 있다.
1) ARMv8 아키텍처를 지원하는 하드웨어
- 오늘날 ARMv6/ARMv7 아키텍처를 사용하는 single board computers는 쉽게 구할 수 있습니다. 가장 인기있는 선택 중 하나는 `Raspberry Pi`입니다.
- 반면 ARMv8의 64비트 모드를 지원하는 single board computers는 흔하지 않지만, 요즘에는 서서히 대중화되고 있습니다.
Raspberry Pi 3에는 64비트 시스템을 실행할 수 있는 Cortex-A53 가 내장되어 있다. 하지만 Raspberry Foundation(Raspbian)에서 제공하는 소프트웨어 시스템(Raspbian)은 32비트 전용이다. 라즈베리파이 공식 입장은 64비트 시스템에 대한 계획은 없다고 한다.
SUSE에는 Raspberry Pi 3에서 실행할 수 있는 64비트 버전의 OpenSUSE 배포판이 있습니다. Archlinux에는 RPi3에 설치할 수 있는 64비트 배포판도 있습니다.2) 64비트 하드웨어가 없을 경우
- AArch64 지원 하드웨어가 없어도 우리는 ARM64 프로그래밍을 할 수 가 있다. 왜?
우리에겐 `cross-toolchain`과 `QEMU`가 있으니께~
- 일단 QEMU와 AArch64 cross-toolchain을 설치하자.
$ sudo apt-get install qemu-user gcc-aarch64-linux-gnu
- 일단 C로 작성된 "Hello world"를 실행할 수 있는지 테스트해보자. 다음 내용으로 hello.c 파일을 만듭니다.
#include <stdio.h> int main(int argc, char *argv[]) { printf("Hello AArch64!\n"); return 0; }
- 이제 이전에 설치한 AArch64용 크로스 컴파일러로 위의 파일을 컴파일한다(-static 플래그가 중요함).
$ aarch64-linux-gnu-gcc -static -o hello hello.c
- AArch64 binary 인지 확인해보자.
yohda@yohda-800G5M-800G5W:~/workspace/SW/Exploring-AArch64-assembler$ file hello hello: ELF 64-bit LSB executable, ARM aarch64, version 1 (GNU/Linux), statically linked, for GNU/Linux 3.7.0, BuildID[sha1]=e5111eb8e87ee9c87a7545629bab22c0c521c9ce, not stripped
- 실행하려고 하면 아래와 같은 에러가 발생할 수 있다.
$ ./hello -bash: ./hello: No such file or directory
- aarch64-linux-gnu-gcc를 통해서 만들어진 바이너리는 윈칙적으로 aarch64 환경에서 동작하는 컴퓨터 위에서 실행해야 한다. 결국 우리의 노트북은 인텔 CPU일 확률이 99%이니, qemu를 통해서 해당 프로그램을 실행해야 된다.
yohda@yohda-800G5M-800G5W:~/workspace/SW/Exploring-AArch64-assembler$ qemu-aarch64 ./hello Hello AArch64!
- 주의점은 `-static` 옵션을 사용하는 경우, 항시 qemu-aarch64를 통해서 프로그램을 실행해야 한다.
3) 첫 번째 AArch64 어셈블리어 프로그램
- 오류 코드 2를 반환하는 매우 간단한 프로그램을 작성해 봅시다.
// first.s .text .globl main main: mov w0, #2 ret
- 위 코드를 일단 내 환경에서는 아래와 같이 빌드를 해야 한다.
aarch64-linux-gnu-gcc -c first.s aarch64-linux-gnu-gcc -static -o first first.o
- `qeme-aarch64 ./first`를 실행하고 반환값을 확인해보자.
$ qemu-aarch64 ./first $ echo $? 2
- 이제 코드를 한 번 분석해 봅시다.
// first.s .text
- 1행 : 어셈블리 파일에서 주석은 `//`을 사용할 수 있다.
-2행 : `.text`는 어셈블러 지시어(assembler directive)중에 하나이다. 이 지시어의 기능은 `이 지시어가 선언된 지점 아래부터는 모두 코드(명령어) 영역이라는 의미`이다. 참고로 데이터는 .data 지시어 뒤에 온다.
.globl main
- `.global` 또한 어셈블러 지시어중에 하나이다. 이 지시어 뒤에 오는 심볼(symbol)은 전역 심볼(global symbol)이 되는 것을 의미한다. 이것은 컴파일의 최종단계에서 프로그램이 구성될 때, 이 프로그램을 시작하기 위해 C 라이브러리에 필요한 global `main` symbol을 가질 것이라는 것을 의미한다(`global` directive에 대한 내용은 아래 `참고` 확인).
main: mov w0, #2 // w0 ← 2 ret // return
- `main:`이 우리 프로그램의 시작점이다.
- 6행 : 위에서 언급한 것은 global `main` symbol에 대한 label이다. 이건 쉽게 함수를 생각하면 된다. 위쪽에 함수를 선언했으면(.global main), 이 부분은 실제 함수의 정의하는 부분이라고 생각하면 될 것 같다.
- 7행 : 2라는 상수값을 w0 레지스터에 넣는다.
- 8행 : main을 종료한다.
- 프로그램이 종료되면, w0 레지스터의 값이 이전에 실행됬던 프로그램의 오류 여부를 결정한다. 아래 코드를 보자.
#include <stdio.h> int main(int argc, char *argv[]) { printf("Hello AArch64!\n"); return 0; // mov w0, #0 }
- 위의 main() 함수에서 `return 0` == `mov w0, #0`고 같다고 보면 된다. 그럼 이제 Shell에서 `echo $?`를 치면 2가 출력되는 것을 확인할 수 있다.