-
Assembly대학/시스템소프트웨어 2022. 12. 12. 22:38
레지스터와 명령어에 대해 살펴보자.
(x86 아키텍쳐의 GAS/GNU format에 대해 다룹니다)
- Register
레지스터 설명 %eax 데이터를 저장 %edx 데이터를 저장 %ecx 데이터를 저장 %ebx 데이터를 저장 %esi ? %edi ? %esp stack의 top. 즉 stack frame의 끝을 가리킴 %ebp stack frame의 시작을 가리킴 %eip program counter. 현재 실행중인 라인을 가리킴 레지스터 안에는 기본적으로 메모리의 주소가 저장되어 있다. (e.g. %eax = 0x8048b90)
(%eax)와 같이 레지스터에 괄호를 치는 경우에는 레지스터가 가리키는 메모리주소에 담긴 값을 가리킨다. (e.g. (%eax) = 100 )
상수는 $표시를 앞에 붙여 사용할 수 있다. (e.g. $-100 = -100 )
레지스터는 연산이 가능한데 일반화하면 다음과 같다. (movl에선 D(Rb)만 사용. 나머진 사용하지 않는다)
D(Rb, Ri, S)
이는 Rb + S * Ri + D 번지의 메모리 주소의 값을 의미한다.
- 명령어
movl Src, Dest
Src, Dest에는 상수, 레지스터, 메모리의 값이 올 수 있는데, Src의 값을 Dest에 넣는다는 뜻이다.
단, 메모리 -> 메모리는 불가능하다. (논리상 상수 -> 상수도 불가능)
leal Src, Dest
Src의 메모리 주솟값을 Dest에 넣는다는 뜻이다.
movl과 거의 유사하게 동작한다. 다만 메모리 주소를 이용한 연산이 가능하기 때문에
addl + movl 연산들을 한 줄에 처리할 수 있다.
addl Src, Dest: Dest = Dest + Src
subl Src, Dest: Dest = Dest = Src
imull Src, Dest: Dest = Dest * Src
sall Src, Dest: Dest = Dest << Src (Arithmetic)
sarl Src, Dest: Dest = Dest >> Src (Arithmetic)
shrl Src, Dest: Dest = Dest >> Src (Logical)
xorl Src, Dest: Dest = Dest ^ Src
andl Src, Dest: Dest = Dest & Src
orl Src, Dest: Dest = Dest | Src
incl Dest: Dest = Dest + 1
decl Dest: Dest = Dest - 1
negl Dest: Dest = - Dest
notl Dest: Dest = ~ Dest
예시를 살펴보자.
왼쪽이 c 코드이고, 오른쪽은 스택 프레임에 저장된 c 코드이다.
아래가 위 코드를 어셈블리어로 변환한 코드 중 body에 해당하는 코드이다.
이를 해석한 그림은 다음과 같다.
cmpl Src2, Src1
아래의 명령을 수행하기 전에 single bit register을 채워주는 역할을 한다.
setX Dest
single bit register의 조건에 따라 1(true) / 0(false)를 Dest에 저장한다.
jX Symbol
single bit register의 조건에 따라 Symbol로 점프할지 말지를 정한다.
X 대신에 e, ne, s, ns 등 여러 베리에이션이 생기는데,
각각 equal, signed, greater, less, above, below 와 같은 의미의 앞자를 따왔고, n이 있으면 부정의 의미이다.
역시 예시를 들어보자.
위 c코드를 어셈블리로 변환 후 body부분만 가져오면 아래와 같아진다.
L9: 이후에는 finish부분 어셈블리 코드가 있는데 여기서 ret(리턴)할 때 %eax값이 리턴되게 된다.
즉, body 부분에서 %eax값을 잘 세팅해내야 한다.
해석하면 다음과 같다.
어셈블리 코드를 보면 뭔가 이상하다.
분명 조건문으로 넣었는데 어느새 goto문법으로 번역이 되어있다.
do-while, while 모두 goto문법으로 번역되곤 한다.
따라서 어셈블리 코드를 파악할 때 기존 c코드를 goto문법으로 바꾸고 분석하면 한 결 편해진다.
마지막으로 do-while문의 예시를 봐보자.
참고로 while문을 변환하는 방법은 다음과 같다.
지금까지 기본적인 내용과 예시를 살펴보면서 무시했던 내용이 있다.
body코드만 보면 setup, finish 코드는 어떻게 동작하는가,
%ebp는 뭔데 매번 처음에 나와서 변수를 할당하는가.
이제는 그 원리를 알아보기 위해 stack frame과 그 어셈블리 코드 동작방식을 알아보자.
(이 부분 끝까지 읽고나서 처음부터 글을 다시 읽어보면 한 결 이해가 쉬워집니다)
함수가 호출되면 해당 함수를 호출한 부분의 다음 줄 주소(리턴주소) 함수의 인자값,
지역 변수 등의 정보가 스택에 쌓이게 된다.
이렇게 함수에 쌓인 각 함수의 스택을 스택 프레임이라고 한다.
pushl Src
Src를 스택에 넣는다. 이 때 스택의 크기가 4만큼 증가한다. 따라서 %esp의 값이 4 감소한다.
popl Dest
스택에서 데이터를 빼서 Dest에 넣는다. 이 때 스택의 크기가 4만큼 감소한다. 따라서 %esp의 값이 4 증가한다.
음? 스택이 증가하는데 %esp의 값이 4 감소한다고? 맞다.
앞서 여러번 언급했 듯 메모리 구조상 스택이 증가하는 방향은 아래로 증가한다.
즉, 메모리 주소가 감소하는 방향으로 스택이 커지는 것이다.
call label
label로 점프한다. 단, 점프하기 전에 바로 다음 줄의 명령어를 처리하고 가는데,
다음 줄의 명령어는 반드시 pushl %eip 가 온다.
즉, 리턴될 주소를 스택에 저장하고 점프하는 것이다.
ret
return의 약자로 popl %eip의 명령을 수행하고 %eip의 주소로 점프한다.
즉, 스택에 저장했던 리턴 주소로 점프하는 것이다.
%ebp의 설명이 없었는데, 이 내용을 포함해서 예시를 보며 이해해보자.
스왑하는 6줄이 좀 이해가 안될 수도 있는데 아래의 그림을 참고하면 어느정도 이해가 가능할 것이다.
'대학 > 시스템소프트웨어' 카테고리의 다른 글
Linking (0) 2022.12.11 Memory Management (0) 2022.12.11 Synchronization (0) 2022.12.11 Threads (0) 2022.12.11 Inter-Process Communication (2) 2022.10.23