임베디드를 좋아하는 조금 특이한 개발자?

RaspberryPI Assembly Language: 1. 기본 구조 본문

Embedded/Raspberry PI

RaspberryPI Assembly Language: 1. 기본 구조

Gordon_ 2022. 9. 9. 03:01

개발 환경

H/W : raspberryPI 3 B+

OS : raspbian 4.19

 


 

https://www.youtube.com/watch?v=FV6P5eRmMh8&list=PLc7W4b0WHTAXkCy3RUAO0Eqx52ALTLUdq&index=3  

 

현재 이 글은 위 영상을 제 나름대로 해석하고 참고하여 작성하였습니다. 더 자세한 정보를 원한다면 위 영상을 보는 것을 추천합니다.

 

 

  대부분의 언어를 배우게 되면 하나의 관례처럼 Hello world를 출력하는 프로그램을 작성합니다. 처음 어셈블리를 배우는 사람으로서 이번에 어셈블리 언어로 HelloWorld를 출력하고 기본적인 어셈블리의 실행과정을 살펴보겠습니다. 

 

.global _start

.section .text
_start:
    mov     r7, #1
    mov     r0, #0
    swi     0

위의 코드는 C언어로 바꾸면 아래와 같은 코드와 같습니다.

int main(void){
    return 0;
}

  _start라는 이름의 라벨은 C언어의 main함수와 같습니다.  어셈블리 언어에서는 라벨은 분기를 할 위치를 지정하기 위해 사용됩니다. 그래서 C언어에서 if, while 과 같은 분기문 또는 반복문을 할떄 또는 함수의 이름 처럼 사용합니다. ARM 어셈블리 언어는 _start 라벨 부터 시작되기 때문에 라벨을 명시하여야 하며 외부에서 분기가 가능하도록 .global _start로 명시하여 줍니다.

  만약 라벨의 이름이 다르거나 .global로 _start를 명시 하지 않는 다면 

warning: cannot find entry symbol _start; defaulting to 000100b8

와 같은 메시지를 볼 수 있습니다.

 

    mov     r7, #1
    mov     r0, #0
    swi     0

  위 코드는 return 0;과 같은 역할을 하고 있습니다. 하지만 더 정확하게 말하자면 exit(0);과 같은 역할을 한다고 할 수 있  습니다. return은 함수가 종료 되는 것이고 exit()함수는 프로세스의 종료을 수행하게 됩니다. 

 

어셈블리 언어에서 프로세스 종료는 System Call를 통해 수행하게 됩니다. System Call은 프로세스의 생성과 종료 그리고 파일 접근등 유저 모드에서 수행할 수 없는 기능들을 커널 모드에서 수행 할 수 있도록 하는 함수입니다.  윈도우와 CPU 아키텍쳐에 따라 다르므로 참고해야 하는 System Call Table을 잘 확인 해야 합니다.

https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md#arm-32_bit_EABI 

 

Chromium OS Docs - Linux System Call Table

Linux System Call Table These are the system call numbers (NR) and their corresponding symbolic names. These vary significantly across architectures/ABIs, both in mappings and in actual name. This is a quick reference for people debugging things (e.g. secc

chromium.googlesource.com

 

 

이번 환경은 Raspberry PI 3 B+이기에 ARM 아키텍쳐이며 Raspbian은 리눅스 환경이므로 해당 테이블을 찾아야 합니다.

 

System call Table

위 System call Table를 확인 해보니 프로그램의 종료을 하는 System call인 exit이 2번째 줄에 보입니다. 

즉, exit를 수행하는 System call를 호출하기 위해서는 r7에는 0x01라는 값을 할당하고 r0에는 에러 코드를 할당해야 한다는 것을 알 수 있습니다. 그럼 라인 별로 의미를 살펴보겠습니다.

 

"mov r7, #1"은 r7 레지스터에 10진수 1라는 값의 정수를 저장하는 코드입니다.

"mov r0, #0"은 r0 레지스터에 10진수 0라는 값의 정수를 저장하는 코드입니다.

"swi 0" System call를 호출하는 코드입니다.


실행 방법

컴파일러는 이미 Raspbian에 설치 되어 있으므로 굳이 따로 설치할 필요는 없습니다.

저는 위의 코드를 "hello.asm"에 작성하였습니다.

as hello.asm -o hello.o
gcc -nostdlib hello.o -o hello.elf

먼저 as를 통해 object file를 만들고 gcc를 통해 실행 파일을 만들수 있습니다.

실행 결과

프로그램을 수행하면 아무것도 나타나지 않습니다. 하지만 "echo $?" 명령어를 통해 마지막 프로그램의 에러코드를 확인 할 수 있습니다.(메인 함수의 return 값, 여기서는 exit System Call을 수행시 r0에 저장되 있던 정수)

 

만약 r0의 값을 2로 바꾸어 보겠습니다.

.global _start

.section .text
_start:
    mov     r7, #1
    mov     r0, #2
    swi     0

r0의 값이 2일때 실행결과

"swi 0"를 통해 시스템 콜을 호출할 때 r0값이 출력되는 것을 확인 할 수 있습니다.

 


특이 사항

Warning: end of file not at end of a line; newline inserted

이 메세지는 실행 결과에 영향을 미치지 않습니다. 그래도 보기 싫다면 코드이 마지막에 엔터를 하여 빈줄을 추가해 주세요.