LEA(Load Effective Address) Instruction을 만날 때마다 잘 와닿지 않았던 이유중 하나는 Effective Address가 대체 뭘 말하는 지를 알지 못했기 때문이었던 것 같습니다. 저 Effective Address라는 것이 뭘까… 하고 조사를 해보니 이제 조금은 알 것 같네요.
1. Effective Address
먼저 IA-32 Instruction에서 메모리를 참조하는 피연산자(operand)는 다음과 같은 구조를 가지게 됩니다.
1.1 Segment Selector
Memory Operand를 구성하는 첫번째 요소인 Segment Selector는 현 Operand를 포함하고 있는 세그먼트를 가리키게 됩니다. 32비트 모드냐 64비트 모드냐에 따라서 약간 차이가 있습니다.
32비트 protected 모드의 경우에는, 이전 스터디에서 보았던 것처럼 GDT에 Segment Descriptor들을 정의해 놓고 이곳에서의 13bit의 index와 3bit의 flag로 구성되게 됩니다.
하지만 64비트 모드에서는 Segmentation이 거의 사용되지 않는다고 하네요.1 프로세서가 CS, DS, ES, SS를 0으로써 처리한다고 합니다.(그러나 FS와 GS는 예외라고 하네요). 실제로 이번에 분석중인 head_64.S에서도 ds/es/ss/fs/gs를 명시적으로 0으로 만드는 부분도 있었습니다.
1.2 Offset
Memory Operand를 구성하는 두번째 요소인 Offset은 다음의 네가지 부분으로 구성됩니다.
- Displacement
- 8/16/32비트 값
- Base
- 임의의 레지스터 안에 있는 값
- Index
- 임의의 레지스터 안에 있는 값
- Scale factor
- Index에 곱해질 2, 4 또는 8
네가지를 가지고 위와 같이 계산된 결과 offset을 바로 Effective Address라고 부른다고 하네요. 다 아셨던 부분일 수도 있지만.. Intel 메뉴얼에 이렇게 정의되어있을 줄은 몰랐네요.
The offset which results from adding these components is called an effective address.2
1.2.1 Offset(Effective Address) in GNU Assembler
우리가 분석하고 있는 Linux에서 사용되는 GNU Assembler에서 이 Offset(Effective Address)를 어떻게 정의할 수 있는지 알아봅시다. GAS 메뉴얼에서 x86 파트를 한번 찾아보았습니다.3
An Intel syntax indirect memory reference of the form
section:[base + index*scale + disp]
is translated into the AT&T syntax
section:disp(base, index, scale)
저희는 AT&T 스타일을 쓰고 있기 때문에4 아래와 같이 네 부분을 괄호와 쉼표로 구성하게 됩니다. 왜 갑자기 linux 분석에 들어와서 "[]"로 메모리 접근하는 연산이 없어졌나 했더니, 문법이 두 종류 였었던 것이네요.
2. MOV vs LEA
결론부터 말씀드리면, MOV로 위의 4개의 인자를 계산하여 Immediate Operand로써 대입시키나, LEA로 한번에 계산하나 똑같다고 할 수 있습니다. 다만 한번에 하는게 훨씬 좋겠죠? 하지만 Memory Operand로써 Mov로 쓰게 되면, Memory Operand가 참조하고 있는 메모리의 값을 Destination Operand로 대입시키게 됩니다.
.
결론에서 말씀 하신건과 같이 lea eax,[ebp-8] 은 다음과 같이 mov로 변환 할 수 있습니다.
mov eax, ebp
sub eax, 8
결국 eax에는 ebp-8의 주소값이 저장이 되게 됩니다.
참고로 mov eax, [ebp-8]은 ebp-8이 가르키는 곳의 값이 eax에 저장되는 것입니다.