$0x10)%rax)(%rax))Imm → M[Imm](ra) → M[R[ra]]Imm(rb) → M[Imm + R[rb]]Imm(rb, ri, s) → M[Imm + R[rb] + R[ri] * s]s는 1, 2, 4, 8만 가능해레지스터 값:
%rax = 0x100,%rdx = 0xC,%rcx = 0x4
| 오퍼랜드 | 의미 설명 | 계산된 주소 | 해당 메모리 값 |
|---|---|---|---|
%rax | 레지스터 자체 값 | - | 0x100 |
0x104 | 절대 주소 | 0x104 | 0xAB |
$0x108 | 즉시 값 (Immediate) | - | 0x108 |
(%rax) | 간접 주소 - rax가 가리키는 주소 | 0x100 | 0xFF |
4(%rax) | rax + 4 | 0x104 | 0xAB |
9(%rax,%rdx) | rax + rdx + 9 | 0x10C | 0x11 |
260(%rcx,%rdx) | 260 + rcx + rdx = 0x104 + 0x4 + 0xC | 0x108 | 0x13 |
0xFC(,%rcx,4) | 0xFC + rcx × 4 = 0xFC + 0x10 | 0x10C | 0xFF |
(%rax,%rdx,4) | rax + rdx × 4 = 0x100 + 0x30 | 0x130 | 0x11 |
mov[bwlq] S, D → D ← Sb, w, l, q는 각각 1, 2, 4, 8 바이트movl은 레지스터 대상이면 상위 4바이트를 0으로 초기화함movl $0x4050, %eax ; 즉시값 → 레지스터
movw %bp, %sp ; 레지스터 → 레지스터
movb (%rdi,%rcx), %al ; 메모리 → 레지스터
movb $-17, (%esp) ; 즉시값 → 메모리
movq %rax, -12(%rbp) ; 레지스터 → 메모리
※ 메모리↔메모리는 직접 복사가 안 되고, 반드시 레지스터를 거쳐야 함
📌 목적: 다양한
mov명령어를 보고 오퍼랜드 형식과 데이터 크기를 파악하는 문제
📍%rsp,%rax,%rdx,%eax,%dx,%dl,%bl등은 전부 64비트 기반 레지스터들
| 명령어 | 동작 설명 |
|---|---|
movl %eax, (%rsp) | %eax의 4바이트 값을 %rsp가 가리키는 메모리에 저장 |
movw (%rax), %dx | %rax가 가리키는 메모리 2바이트를 %dx에 복사 |
movb $0xFF, %bl | 즉시값 0xFF를 1바이트 레지스터 %bl에 저장 |
movb (%rsp,%rdx,4), %dl | 주소 계산: %rsp + 4 × %rdx의 1바이트 값을 %dl에 저장 |
movq (%rdx), %rax | %rdx가 가리키는 메모리 8바이트를 %rax에 복사 |
movw %dx, (%rax) | %dx의 2바이트 값을 %rax가 가리키는 메모리에 저장 |
| 어셈블리 코드 | 오류 내용 설명 |
|---|---|
movb $0xF, (%ebx) | ❌ %ebx는 주소 지정용 레지스터로 사용할 수 없어. (주소 계산에 사용할 수 없는 32비트 레지스터) |
movl %rax, (%rsp) | ❌ 레지스터 크기와 명령어 접미사가 맞지 않아. movl은 32비트인데 %rax는 64비트야. |
movw (%rax), 4(%rsp) | ❌ 메모리 → 메모리 복사는 불가능해! (레지스터를 거쳐야 함) |
movb %al, %sl | ❌ %sl이라는 레지스터는 존재하지 않아. 오타이거나 잘못된 이름이야. |
movl %eax, $0x123 | ❌ 즉시값은 **목적지(오른쪽)**에 올 수 없어. 항상 소스에서만 사용 가능! |
movl %eax, %dx | ❌ 대상 레지스터의 크기가 다름. %eax는 32비트, %dx는 16비트 → 크기 불일치! |
movb %si, 8(%rbp) | ❌ %si는 16비트인데 movb는 1바이트 → 크기 불일치! |
exchange(long *xp, long *yp)를 예시로, 어떻게 데이터가 이동하는지 보여줘.long exchange(long *xp, long *yp) {
long x = *xp;
long y = *yp;
*xp = y;
*yp = x;
return x;
}
movq (%rdi), %rax ; *xp → %rax
movq (%rsi), %rdx ; *yp → %rdx
movq %rdx, (%rdi) ; %rdx → *xp
movq %rax, (%rsi) ; %rax → *yp
ret
※ 함수 인자는 %rdi, %rsi, 반환값은 %rax
📌 변수 선언:
src_t *sp;
dest_t *dp;원하는 연산:
*dp = (dest_t) *sp;C의 형변환과 어셈블리에서의 데이터 이동/확장 방식 연결!
| src_t | dest_t | Instruction | 설명 |
|---|---|---|---|
long | long | movq (%rdi), %rax -> movq %rax, (%rsi) | 8바이트 → 8바이트 그대로 복사 |
char | int | movsbl (%rdi), %eax -> movl %eax, (%rsi) | 1바이트 → 4바이트 부호 확장 |
char | unsigned | movsbl (%rdi), %eax -> movl %eax, (%rsi) | GCC가 char를 부호형으로 간주하여 movsbl 사용 |
unsigned char | long | movzbl (%rdi), %eax -> movq %rax, (%rsi) | 1바이트 → 4바이트 0 확장 → 8바이트로 저장 |
int | char | movl (%rdi), %eax -> movb %al, (%rsi) | 4바이트에서 하위 1바이트만 복사 |
unsigned int | unsigned char | movl (%rdi), %eax -> movb %al, (%rsi) | 하위 1바이트만 저장 |
char | short | movsbw (%rdi), %ax -> movw %ax, (%rsi) | 1바이트 부호 확장 → 2바이트로 저장 |
movs* : sign extension (부호 유지 확장)movz* : zero extension (상위 0으로 채움)movb/movw/movl/movq : 복사 크기 (1/2/4/8 바이트)movs* 또는 movz* 사용movb, movw로 하위 일부만 저장void decode1(long *xp, long *yp, long *zp){
*xp = *yp;
*yp = *zp;
*zp = *xp;
}
위는 정확한 답은 아니다. 착각을 한 부분이 있는데
movq (%rdi), %r8
movq (%rsi), %rcx
movq (%rdx), %rax
이 부분을 함수 호출 시에 각 매개 변수 값을 임시로 복사하여 저장하는 로직이라고 줄 착각했다. 함수 호출 시에 매개 변수의 주소값을 rbp가 가지고 있다가 rsp에 임시로 저장한다고 책에서 읽은 부분이랑 헷깔린 것 같다. 애초에 C코드를 이렇게 짜면 xp의 데이터 값이 변경되기 때문에 zp에 값이 제대로 저장되지 않는 문제가 발생한다.
| 명령어 | 동작 설명 |
|---|---|
pushq S | rsp -= 8; M[rsp] = S |
popq D | D = M[rsp]; rsp += 8 |
pushq %rax ; %rax 값을 스택에 저장 → rsp는 8 감소
popq %rdx ; 스택 최상단 값을 %rdx에 저장 → rsp는 8 증가
pushq %rsp는 특별한 경우니까 처리 순서 유의해야 해 (스택 포인터를 저장할 때 주의 필요!)(%rbp), 8(%rbp), -8(%rbp)movb, movw, movl, movq로 각각 1, 2, 4, 8 바이트 데이터를 옮긴다.%rax는 64비트, %eax는 하위 32비트, %ax는 하위 16비트, %al은 하위 8비트.movl은 상위 32비트를 자동으로 0으로 초기화하여 예측 가능성을 높인다.%rsp는 스택 최상단 주소를 가리키며, push 시 8바이트 감소, pop 시 8바이트 증가:pushq %rax → rsp -= 8; M[rsp] = %raxpopq %rax → %rax = M[rsp]; rsp += 8%rbp는 현재 함수의 기준점이며, 함수 호출 시 %rsp의 값을 저장해서 기준으로 사용한다.offset(reg) 형태는 메모리 주소 지정 방식:12(%rbp) → %rbp + 12 바이트 떨어진 주소를 참조int arr[2100000]; 는 8MB를 초과하므로 Segmentation Fault 발생.%esp: 32비트 스택 포인터 (레거시)%rsp: 64비트 스택 포인터 (현대 x86-64에서 사용)%esp는 %rsp의 하위 32비트이며, 32비트 연산 시 상위 32비트가 0으로 초기화된다.int는 4바이트 정렬, long/pointer는 8바이트 정렬 필요char는 1바이트만 사용하지만 정렬을 위해 나머지를 padding으로 둘 수 있다.int는 4바이트 → 2,097,152개 이상 선언 시 스택 초과int big[2100000]; → Stack Overflow 발생