初めに
C言語で乗算の処理を書くとlea命令が生成されることがあります。
コンパイル
test.c
int main() {
int a = 1, b = 2, c = 3, d = 4;
int x = a + b*3 + c*5 + d;
return x;
}
bcc(WindowsXP)
bcc32 -S test.c
test.asm
.386p
model flat
ifndef ??version
?debug macro
endm
endif
?debug S "test.c"
?debug T "test.c"
_TEXT segment dword public use32 'CODE'
_TEXT ends
_DATA segment dword public use32 'DATA'
_DATA ends
_BSS segment dword public use32 'BSS'
_BSS ends
DGROUP group _BSS,_DATA
_TEXT segment dword public use32 'CODE'
_main proc near
?live1@0:
;
; int main(){
;
push ebp
mov ebp,esp
push ebx
;
; int a=1,b=2,c=3,d=4;
;
@1:
mov eax,1
mov edx,2
mov ecx,3
mov ebx,4
;
; int x=a+b*3+c*5+d;
;
?live1@32: ; EAX = a, EDX = b, ECX = c, EBX = d
lea edx,dword ptr [edx+2*edx]
add eax,edx
lea ecx,dword ptr [ecx+4*ecx]
add eax,ecx
add ebx,eax
mov eax,ebx
;
; return x;
;
?live1@48: ; EAX = x
;
; }
;
?live1@64: ;
@3:
@2:
pop ebx
pop ebp
ret
_main endp
_TEXT ends
public _main
?debug D "test.c" 23649 25327
end
b*3はlea edx,dword ptr [edx+2*edx]
c*5はlea ecx,dword ptr [ecx+4*ecx]
となっている。
gcc(Ubuntu)
こちらは予想したコードが生成されませんでした。
gcc -m32 -S -O0 -masm=intel \
-fno-asynchronous-unwind-tables \
-fno-unwind-tables \
-fno-stack-protector \
-fno-pic \
-fno-pie \
test.c
test.s
.file "test.c"
.intel_syntax noprefix
.text
.globl main
.type main, @function
main:
push ebp
mov ebp, esp
sub esp, 32
mov DWORD PTR [ebp-4], 1
mov DWORD PTR [ebp-8], 2
mov DWORD PTR [ebp-12], 3
mov DWORD PTR [ebp-16], 4
mov edx, DWORD PTR [ebp-8]
mov eax, edx
add eax, eax
add edx, eax
mov eax, DWORD PTR [ebp-4]
lea ecx, [edx+eax]
mov edx, DWORD PTR [ebp-12]
mov eax, edx
sal eax, 2
add eax, edx
lea edx, [ecx+eax]
mov eax, DWORD PTR [ebp-16]
add eax, edx
mov DWORD PTR [ebp-20], eax
mov eax, DWORD PTR [ebp-20]
leave
ret
.size main, .-main
.ident "GCC: (Ubuntu 11.4.0-1ubuntu1~22.04.2) 11.4.0"
.section .note.GNU-stack,"",@progbits
leaで乗算する仕組み
num1 * num2は
mov reg,num1
lea reg,dword ptr [reg+(num2-1)*reg]
で処理できます。
使用できるnum2の数には制限があります。
使える数と使えない数に関しては筆者もまだ理解できていません。
試しにアセンブリでプログラムを書いて確かめます。
test.asm
BITS 32
section .text
global _start
_start:
;3*8
mov edx, 3
lea edx, dword [edx+7*edx]
;2*5
mov ecx,2
lea ecx, dword [ecx+4*ecx]
int 3 ; //ここで止め、GDBからEAXの値を確認
; exit(0)
mov eax, 1 ; sys_exit
mov ebx, 0
int 0x80
nasm -f elf32 test.asm
ld -m elf_i386 -o test test.o
gdb ./test
run
info registers eax
GDBで止めて乗算が正常に計算されているかを確認します。
0x08049015 in _start ()
(gdb) info registers edx ecx
edx 0x18 24
ecx 0xa 10
3*8=24,2*5=10よって正しく計算されていることが分かります。
lea本来の使い方
test.asm
; info registers eax ebx
BITS 32
section .text
global _start
main:
push ebp
mov ebp,esp
sub esp,4
; int val=5;
mov eax,5
mov dword [ebp-4],eax
; eax=&val
lea eax, dword [ebp-4]
; ebx=val
mov ebx, dword [ebp-4]
int 3 ; ここで止めてレジスタを確認
mov esp,ebp
pop ebp
ret
_start:
call main
; exit(0)
mov eax, 1 ; sys_exit
mov ebx, 0
int 0x80
leaで取得されるのはアドレス、movで取得されるのはそのアドレスに保存された値となります
0x08049015 in main ()
(gdb) info registers eax ebx
eax 0xffffd294 -11628
ebx 0x5 5