gcc 組合語言
x86 組合語言
- x86 組合語言 – http://www.int80h.org/
MinGW 程式
檔案: add.c
int add(int a, int b) {
int t = a, x=b;
return a+b;
}
檔案: main.c
#include <stdio.h>
extern int add(int a, int b);
int main() {
int t = add(5, 8);
printf("add(5, 8)=%d\n", t);
return 0;
}
執行
$ gcc main.c add.c -o add
$ ./add
add(5, 8)=13
$ gcc -fverbose-asm -S main.c -o
main.s
$ gcc -fverbose-asm -S add.c -o add.s
$ gcc main.s add.s -o add
$ ./add
add(5, 8)=13
組合語言 add.s
.file "add.c"
.text
.globl _add
.def _add; .scl 2; .type 32; .endef
_add:
pushl %ebp #
movl %esp, %ebp #,
subl $16, %esp #,
movl 8(%ebp), %eax # a, tmp89
movl %eax, -4(%ebp) # tmp89, t
movl 12(%ebp), %eax # b, tmp90
movl %eax, -8(%ebp) # tmp90, x
movl 8(%ebp), %edx # a, tmp91
movl 12(%ebp), %eax # b, tmp92
addl %edx, %eax # tmp91, D.1490
leave
ret
.ident "GCC: (tdm-1) 5.1.0"
組合語言 main.s
.def ___main; .scl 2; .type 32; .endef
.section .rdata,"dr"
LC0:
.ascii "add(5, 8)=%d\12\0"
.text
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
pushl %ebp #
movl %esp, %ebp #,
andl $-16, %esp #,
subl $32, %esp #,
call ___main #
movl $8, 4(%esp) #,
movl $5, (%esp) #,
call _add #
movl %eax, 28(%esp) # tmp89, t
movl 28(%esp), %eax # t, tmp90
movl %eax, 4(%esp) # tmp90,
movl $LC0, (%esp) #,
call _printf #
movl $0, %eax #, D.1933
leave
ret
.ident "GCC: (tdm-1) 5.1.0"
.def _add; .scl 2; .type 32; .endef
.def _printf; .scl 2; .type 32; .endef
解說
add.s
.file "add.c" // 原始碼是 add.c
.text // 程式段開始
.globl _add // _add 是個全域 Global 標記
.def _add; .scl 2; .type 32; .endef
// 32 位元,參考 https://stackoverflow.com/questions/1317081/gccs-assembly-output-of-an-empty-program-on-x86-win32
_add: // _add 標記
pushl %ebp # // 將 ebp 框架暫存器推入堆疊
movl %esp, %ebp #, // ebp = esp, 新的框架位址
movl 8(%ebp), %edx # a, tmp89 // 將 a 放入 edx
movl 12(%ebp), %eax # b, tmp90 // 將 b 放入 eax
addl %edx, %eax # tmp89, D.1488 // eax = a+b
popl %ebp # // 恢復呼叫前的 ebp
ret // 返回上一層
.ident "GCC: (tdm-1) 5.1.0"
繪圖說明 call/ret 的原理
leave
ret
相當於
movl %ebp, %esp
popl %ebp
popl %eip
主程式解說
.file "main.c"
.def ___main; .scl 2; .type 32; .endef
.section .rdata,"dr"
LC0:
.ascii "add(5, 8)=%d\12\0"
.text
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
pushl %ebp #
movl %esp, %ebp #,
andl $-16, %esp #,
subl $32, %esp #, // 框架大小分配 32
call ___main #
movl $8, 4(%esp) #, // 準備參數 b=8
movl $5, (%esp) #, // a=5
call _add # // 呼叫 add(a,b)
movl %eax, 28(%esp) # tmp89, t // t = eax
movl 28(%esp), %eax # t, tmp90
movl %eax, 4(%esp) # tmp90, // 將 t 放到堆疊
movl $LC0, (%esp) #, // 將 LC0 放入堆疊
call _printf # // 呼叫 _printf
movl $0, %eax #, D.1933 // 設定傳回值 eax 為 0
leave // 準備離開,恢復框架
ret
.ident "GCC: (tdm-1) 5.1.0"
.def _add; .scl 2; .type 32; .endef
.def _printf; .scl 2; .type 32; .endef
說明: https://stackoverflow.com/questions/7060970/substitutes-for-x86-assembly-call-instruction
遞迴範例
C 語言: fib.c
int fib(int n) {
if (n <= 1) return 1;
return fib(n-1) + fib(n-2);
}
組合語言: fib.s
.file "fib.c"
.text
.globl _fib
.def _fib; .scl 2; .type 32; .endef
_fib:
pushl %ebp #
movl %esp, %ebp #,
pushl %ebx #
subl $20, %esp #,
cmpl $1, 8(%ebp) #, n
jg L2 #,
movl $1, %eax #, D.1493
jmp L3 #
L2:
movl 8(%ebp), %eax # n, tmp93
subl $1, %eax #, D.1493
movl %eax, (%esp) # D.1493,
call _fib #
movl %eax, %ebx #, D.1493
movl 8(%ebp), %eax # n, tmp94
subl $2, %eax #, D.1493
movl %eax, (%esp) # D.1493,
call _fib #
addl %ebx, %eax # D.1493, D.1493
L3:
addl $20, %esp #,
popl %ebx #
popl %ebp #
ret
.ident "GCC: (tdm-1) 5.1.0"
影片
- x86 Assembly #9 - Calling Conventions | cdecl
- x86 Assembly #10 - CALL | Call Instruction (讚!)
- x86 Assembly #11 - RET | Return Instruction (讚!)
- x86 Assembly #27 - Local Variables and Arguments Passing | Part 2