Assembly
Intel x86 Architecture ( windows ) 연산 방향 ←
AT&T Architecture ( *-NIX계열 ) 연산 방향 →( * gdb가 아니라 objdump에서 확인해야 함 )
Inline Assembly
1
2
3
4
5
6
7
8
9
int input = 3;
int output = 0;
\_\_asm\_\_ \_\_volatile\_\_(
"mov %1, %%eax \n\t"
"add $1, %%eax \n\t"
"mov %%eax, %0 \n\t"
: "=r" (output)
: "r" (input)
: "eax"); // clobber
- output / input / clobber를 사용하는 경우(Extended asm), asm에서 레지스터를 사용할 때 반드시
%%
로 적어주어야 한다. 반대로 Basic asm을 사용하는 경우 반드시%
로 적어주어야 한다. - output / input 구분 없이 좌측부터 순서 대로
%0, %1, ...
으로 번호가 매겨지기 때문에 asm 내에서 접근할 때는 이 번호로 접근하면 된다. - eax의 경우 clobber로 명시해주지 않아도 잘 동작하는 것 같기는 하다.
output / input Constraints
1
2
3
4
5
6
7
8
9
10
11
12
m - 메모리를 이용해 전달
r - 레지스터를 이용해 전달
+---+--------------------+
| r | Register(s) |
+---+--------------------+
| a | %eax, %ax, %al |
| b | %ebx, %bx, %bl |
| c | %ecx, %cx, %cl |
| d | %edx, %dx, %dl |
| S | %esi, %si |
| D | %edi, %di |
+---+--------------------+
clobber
output / input에 지정되어 있지 않지만 assembly 내에서 레지스터 등을 사용해야 하는 이유로 값이 변경되는 경우 여기에 명시해 주어야 한다. 명시해주지 않으면 컴파일러가 해당 레지스터 등의 값이 변경되었는지 아닌지 체크하지 않아 이상하게 동작할 수 있음.
etc
"instruction \n\t"
\n
만 쓰거나, ;
을 써도 명령어로 인식이 되기는 되는데, .s
파일에서 보기 안좋다.
\_\_asm\__
( )
안의 내용이 인라인 어셈블리로 작성된다는 것을 명시한다. ANSI 표준은 \_\_asm\__
이므로 이를 사용하는 것이 좋지만 gcc에서는 asm
만 써도 잘 동작하며 kernel에서도 asm volatile
을 사용한다.
\_\_volatile\__
컴파일러가 최적화 하지 않고 인라인 어셈블리로 작성한 어셈블리 그대로 둔다.
\__declspec(naked)
VC++에서만 지원하고 gcc에서는 지원하지 않는 키워드다. \_\_volatile\_\_
은 \_\_asm\_\_
에 붙어 적용 범위가 \_\_asm\_\_
이고 \__declspec(naked)
는 함수에 붙어 적용 범위가 함수라는 차이점이 있다. gcc에 비슷한 기능의 \_\_attribute\__((naked))
가 있기는 하지만 ARM 등만 지원하고 x86은 지원하지 않는다.
instructions
1
2
3
"push $0x00000000 \n\t"
"push $0x61626364 \n\t"
"call 0x5d0ba0e7 \n\t"
call
에는 $
를 사용하지 않는다.
1
retn 0x0005
0x0005
를 리턴한다는게 아니라, stack을 5
만큼 정리한다는 것임에 유의.
1
db "/asdf", 0x00
1byte 씩 직접 쓰는 명령어. 위처럼 문자열을 적는 경우 아스키 코드가 들어가며 ,
로 hex값을 직접 넣어줄 수 있다. 쓰는 단위에 따라 dw, dd, dq...
명령어도 있다.
1
xchg %ebx,%edi
두 레지스터의 값을 교환한다.
1
sar eax, 1
shift arithmetic right로, C의 >>
와 동일하다. 산술 연산이기 때문에 채워지는 bit가 MSB에 의존한 값이라 음수에 사용해도 /2
한 효과를 보장한다.
pointer
1
2
3
jmp \*%eax
jmp (%eax)
jmp \*(%eax)
모두 기계어 코드가 다르다. 연산 결과도 다를 것 같은데, 아직 확인안해봤다. windows에서는 \*
연산자를 못본 것 같고, linux에서만 확인했다.
mov & lea
mov :
Load Valuelea :
Load Effective Address lea와 mov는 어떻게 사용하느냐에 따라 똑같이 동작할 수 있다. lea의 두번째 operand에는 레지스터만 올 수 있다.
1
mov %esp, %eax
esp의 값을 eax에 저장. eax에 stack 주소가 들어가며 esp와 같은 곳을 가리키게 된다.
1
mov %esp, (%eax)
esp의 값을 eax가 가리키고 있는 곳에 저장. 즉 eax는 주소값을 저장하고 있어야 하며 어딘가를 가리키고 있어야 한다.
1
mov (%esp), %eax
esp가 가리키고 있는 곳의 값을 eax에 저장. 가장 일반적인 사용일 듯.
1
lea 0x4(%esp), %eax
esp의 값에 4를 더한 값을 eax에 저장. 결국 eax에 저장되는 것은 주소다.
1
mov 0x4(%esp), %eax
esp의 값에 4를 더한 값이 가리키는 곳의 값을 eax에 저장. 결국 eax에 저장되는 것은 data다.