آموزش کامل اسمبلی x86-64 از صفر تا صد (NASM + Linux)
⚡ خلاصه سریع — اصل کاریها
اینها مهمترین چیزهایی هستن که اگه هیچی ندونی، فقط اینا رو بدونی کافیه:
- اسمبلی زبان قابل خواندن برای CPU است؛ هر دستور تقریباً مستقیم به یک کد ماشین تبدیل میشود.
- رجیسترها حافظههای خیلی سریع داخل CPU هستند؛ مهمترینها:
rax، rbx، rcx، rdx، rsi، rdi، rsp، rbp، r8-r15. - سینتکس NASM اینتل است:
mov rax, 1یعنی مقدار ۱ را در رجیسترraxقرار بده. - آدرسدهی حافظه با
[base + index*scale + displacement]انجام میشود. - فراخوانی سیستم لینوکس روی x86-64 با دستور
syscallانجام میشود: شماره درraxو آرگومانها درrdi، rsi، rdx، r10، r8، r9. - سکشنها
.dataبرای دادههای مقداردار،.bssبرای دادههای صفرنشده و.textبرای کد است. - پشته با
pushوpopمدیریت میشود؛ مکانیزم فراخوانی توابع همcallوretاست. - خروج از برنامه بدون syscall شماره ۶۰ باعث Segmentation Fault میشود.
۳ اشتباه رایج که باید ازشون پرهیز کنی:
- فراموش کردن syscall خروج (
mov rax, 60+syscall). - استفاده از
movبرای انتقال مستقیم حافظه به حافظه (mov [a], [b]غیرمجاز است). - قاطی کردن سینتکس AT&T و Intel — در NASM مقصد اول است.
اگر فقط ۵ دقیقه وقت داری، اینها رو یاد بگیر:
movبرای جابهجایی داده بین رجیستر/حافظه/مقدار ثابت.syscallبرای صحبت با سیستمعامل و خروج از برنامه.- ساختار
_startو نحوه مونت/لینک کردن باnasmوld.
۱. مقدمه
اسمبلی چیست؟
اسمبلی نزدیکترین زبان برنامهنویسی به زبان ماشین است. هر دستور اسمبلی معمولاً دقیقاً یک کار ساده انجام میدهد:
mov rax, 42
یعنی عدد ۴۲ را داخل رجیستر rax بگذار. همین یک خط توسط مونتکننده (Assembler) به چند بایت کد ماشین تبدیل میشود که CPU آن را اجرا میکند.
چرا باید اسمبلی یاد بگیریم؟
- درک عمیق سختافزار: میفهمی CPU چطور داده را جابهجا میکند، مقایسه میکند و تصمیم میگیرد.
- مهندسی معکوس و امنیت: بدافزارها، کرک نرمافزار، تحلیل باینری و اکسپلویتنویسی.
- سیستمعامل و هستهنویسی: بوتلودرها، درایورها و بخشهای سطح پایین.
- بهینهسازی: کامپایلرها را بهتر میفهمی و میتوانی کد C/C++ بهتری بنویسی.
- ابزارسازی: دیباگر، شبیهساز، ماشین مجازی و کتابخانههای runtime.
کجا استفاده میشود؟
- بوتلودرها و firmware
- کرنل لینوکس و ویندوز
- درایورهای سختافزاری
- موتورهای بازی برای بهینهسازی مسیرهای داغ
- ابزارهای مهندسی معکوس
- بدافزار و آنتیویروس
- سیستمهای نهفته (Embedded) با منابع محدود
۲. پیشنیازها
چیزهایی که بهتر است از قبل بدانی
- مفاهیم پایه برنامهنویسی: متغیر، حلقه، شرط، تابع
- آشنایی ابتدایی با سیستمعامل لینوکس و ترمینال
- عدد دودویی و هگزادسیمال
- اینکه CPU چیست و حافظه یعنی چه
ابزارهای لازم
| ابزار | کاربرد |
|---|---|
| NASM | مونتکننده x86-64 |
| ld | لینککننده GNU |
| gdb | دیباگر |
| objdump | مشاهده disassembly |
| readelf | مشاهده ساختار فایل ELF |
| strace | ردیابی syscallها |
| ویرایشگر متن | Vim، VS Code، nano |
۳. نصب و راهاندازی
نصب روی دبیان/اوبونتو
sudo apt update
sudo apt install -y nasm binutils gdb
نصب روی آرچ
sudo pacman -S nasm binutils gdb
بررسی نصب
nasm -v
ld -v
gdb --version
اولین پروژه: چاپ Hello Assembly
فایل hello.asm را بساز:
section .data
msg db "Hello, Assembly!", 10
len equ $ - msg
section .text
global _start
_start:
mov rax, 1 ; sys_write
mov rdi, 1 ; stdout
mov rsi, msg ; آدرس رشته
mov rdx, len ; طول رشته
syscall ; فراخوانی کرنل
mov rax, 60 ; sys_exit
xor rdi, rdi ; کد خروج ۰
syscall
مونت و لینک
nasm -f elf64 hello.asm -o hello.o
ld hello.o -o hello
./hello
خروجی:
Hello, Assembly!
بررسی کد خروج:
echo $?
خروجی:
0
۴. مفاهیم پایه
۴.۱ رجیسترها
رجیسترها حافظههای کوچک اما فوقسریع داخل پردازنده هستند. در x86-64 این رجیسترهای عمومی مهماند:
| رجیستر ۶۴ بیتی | رجیستر ۳۲ بیتی | رجیستر ۱۶ بیتی | رجیستر ۸ بیتی (پایین) | کاربرد رایج |
|---|---|---|---|---|
| rax | eax | ax | al | مقدار بازگشتی توابع، جمع/ضرب |
| rbx | ebx | bx | bl | پایه داده، callee-saved |
| rcx | ecx | cx | cl | شمارنده حلقه |
| rdx | edx | dx | dl | داده، باقیمانده تقسیم |
| rsi | esi | si | sil | آرگومان دوم، ایندکس مبدا |
| rdi | edi | di | dil | آرگومان اول، ایندکس مقصد |
| rbp | ebp | bp | bpl | Base Pointer در stack frame |
| rsp | esp | sp | spl | Stack Pointer |
| r8-r15 | r8d-r15d | r8w-r15w | r8b-r15b | رجیسترهای عمومی اضافه |
۴.۲ سکشنها
section .data
; دادههای مقداردار فقطخواندنی/خواندنی-نوشتنی
section .bss
; دادههای بدون مقدار اولیه که صفر میشوند
section .text
; کد اجرایی
مثال:
section .data
name db "Sara", 0
age dq 30
section .bss
buffer resb 128
array resq 10
section .text
global _start
_start:
mov al, [name]
mov rax, [age]
; ...
۴.۳ فرمت دستورها
دستورهای NASM معمولاً این شکلی هستند:
mnemonic destination, source
مثال:
mov rax, 5
add rax, rbx
cmp rax, 10
۴.۴ اعداد: باینری، هگز، دسیمال
mov rax, 10 ; دهدهی
mov rax, 0b1010 ; باینری
mov rax, 0xA ; هگزادسیمال
mov rax, 0o12 ; اکتال
۴.۵ دایرکتیوهای مهم NASM
| دایرکتیو | معنی | مثال |
|---|---|---|
db |
بایت | db 0x41 |
dw |
word دو بایتی | dw 0x1234 |
dd |
double word چهار بایتی | dd 0xDEADBEEF |
dq |
quad word هشت بایتی | dq 0x123456789 |
resb |
رزرو بایت در .bss |
buffer resb 64 |
resq |
رزرو qword در .bss |
arr resq 5 |
equ |
تعریف ثابت | len equ $ - msg |
times |
تکرار | times 5 db 0 |
align |
تراز آدرس | align 16 |
۴.۶ آدرسدهی حافظه
فرمول کلی:
[base + index*scale + displacement]
مثال:
mov rax, [rbx] ; فقط base
mov rax, [rbx + 8] ; base + offset
mov rax, [rbx + rcx*4] ; base + index*scale
mov rax, [rbx + rcx*4 + 16] ; کاملترین فرم
scaleفقط ۱، ۲، ۴ یا ۸ میتواند باشد.rspنمیتواند بهعنوان index استفاده شود.
۴.۷ فلگها
CPU یک رجیستر به نام rflags دارد که بعد از بسیاری از دستورها بهروز میشود:
| فلگ | معنی |
|---|---|
CF |
Carry Flag — بیت نقلی |
ZF |
Zero Flag — نتیجه صفر |
SF |
Sign Flag — بیت علامت |
OF |
Overflow Flag — سرریز علامتدار |
PF |
Parity Flag — زوج بودن تعداد بیتهای ۱ |
AF |
Auxiliary Carry — نقلی در BCD |
۵. مثالهای کد ساده
۵.۱ خروج با کد دلخواه
section .text
global _start
_start:
mov rdi, 7
mov rax, 60
syscall
اجرا:
nasm -f elf64 exit.asm -o exit.o
ld exit.o -o exit
./exit
echo $?
خروجی:
7
۵.۲ جمع دو عدد و خروج با نتیجه
section .text
global _start
_start:
mov rax, 20
add rax, 22
mov rdi, rax
mov rax, 60
syscall
خروجی echo $?:
42
۵.۳ حلقه ساده از ۵ به ۱
section .text
global _start
_start:
mov rcx, 5
.loop:
dec rcx
jnz .loop
xor rdi, rdi
mov rax, 60
syscall
۵.۴ شرط و پرش
section .text
global _start
_start:
mov rax, 10
cmp rax, 10
je .equal
mov rdi, 1
jmp .done
.equal:
xor rdi, rdi
.done:
mov rax, 60
syscall
خروجی:
0
۵.۵ تابع ساده با call و ret
section .text
global _start
_start:
call add_three
mov rdi, rax
mov rax, 60
syscall
add_three:
mov rax, 2
add rax, 3
ret
خروجی:
5
۵.۶ جمع اعضای آرایه
section .data
nums dq 1, 2, 3, 4, 5
len equ 5
section .text
global _start
_start:
xor rax, rax
xor rcx, rcx
.loop:
add rax, [nums + rcx*8]
inc rcx
cmp rcx, len
jl .loop
mov rdi, rax
mov rax, 60
syscall
خروجی echo $?:
15
۶. مثال واقعی و کاربردی
پروژه: محاسبه فاکتوریل ۵ و چاپ نتیجه
در این پروژه عدد ۵ را فاکتوریل میکنیم و عدد ۱۲۰ را بهصورت رشته در خروجی چاپ میکنیم.
section .data
msg db "Factorial of 5 is: "
msg_len equ $ - msg
newline db 10
section .bss
num_buf resb 32
section .text
global _start
print_number:
; ورودی: rax = عدد
; خروجی: عدد بهصورت متن روی stdout
mov rdi, num_buf
add rdi, 31
mov byte [rdi], 0
mov rbx, 10
xor rcx, rcx
.reverse:
xor rdx, rdx
div rbx
add dl, '0'
dec rdi
mov [rdi], dl
inc rcx
test rax, rax
jnz .reverse
mov rdx, rcx
mov rsi, rdi
mov rax, 1
mov rdi, 1
syscall
ret
_start:
; محاسبه ۵! = ۱۲۰
mov rax, 1
mov rcx, 1
.factorial_loop:
imul rax, rcx
inc rcx
cmp rcx, 5
jle .factorial_loop
; چاپ پیام
mov rax, 1
mov rdi, 1
mov rsi, msg
mov rdx, msg_len
syscall
; چاپ عدد
call print_number
; چاپ خط جدید
mov rax, 1
mov rdi, 1
mov rsi, newline
mov rdx, 1
syscall
; خروج
mov rax, 60
xor rdi, rdi
syscall
اجرا:
nasm -f elf64 factorial.asm -o factorial.o
ld factorial.o -o factorial
./factorial
خروجی:
Factorial of 5 is: 120
۷. مباحث پیشرفته
۷.۱ Calling Convention در x86-64 System V
وقتی از C یا زبانی دیگر با اسمبلی ارتباط میگیری، این قرارداد را رعایت کن:
| مورد | رجیستر |
|---|---|
| آرگومان اول | rdi |
| آرگومان دوم | rsi |
| آرگومان سوم | rdx |
| آرگومان چهارم | rcx |
| آرگومان پنجم | r8 |
| آرگومان ششم | r9 |
| مقدار بازگشتی | rax |
| آرگومانهای بیشتر | روی پشته |
| callee-saved | rbx، rbp، r12-r15 |
| caller-saved | rax، rcx، rdx، rsi، rdi، r8-r11 |
۷.۲ Stack Frame استاندارد
my_function:
push rbp
mov rbp, rsp
sub rsp, 32 ; فضای متغیرهای محلی
mov [rbp-8], rax ; متغیر محلی
; ...
leave ; mov rsp, rbp + pop rbp
ret
۷.۳ ماکروها در NASM
%macro sys_write 2
mov rax, 1
mov rdi, 1
mov rsi, %1
mov rdx, %2
syscall
%endmacro
section .data
msg db "Hello", 5
section .text
global _start
_start:
sys_write msg, 5
mov rax, 60
xor rdi, rdi
syscall
۷.۴ اتصال اسمبلی و C
فایل add.asm:
global add_nums
section .text
add_nums:
lea rax, [rdi + rsi]
ret
فایل main.c:
#include <stdio.h>
extern long add_nums(long, long);
int main(void) {
printf("%ld\n", add_nums(3, 4));
return 0;
}
کامپایل:
nasm -f elf64 add.asm -o add.o
gcc -no-pie main.c add.o -o program
./program
خروجی:
7
۷.۵ دیباگ با GDB
nasm -f elf64 -g -F dwarf hello.asm -o hello.o
ld hello.o -o hello
gdb ./hello
در gdb:
break _start
run
info registers
layout asm
layout regs
stepi
nexti
x/10gx $rsp
disassemble /r _start
۷.۶ نگاهی به SIMD
x86-64 رجیسترهای ۱۲۸/۲۵۶ بیتی xmm0-xmm15 و ymm0-ymm15 دارد:
movdqa xmm0, [source]
paddd xmm0, xmm1
این دستورها چند داده را همزمان پردازش میکنند.
۸. اشتباهات رایج
۸.۱ فراموش کردن خروج از برنامه
; ❌ اشتباه
_start:
mov rax, 1
mov rdi, 1
syscall
; ✅ درست
_start:
mov rax, 1
mov rdi, 1
syscall
mov rax, 60
xor rdi, rdi
syscall
۸.۲ انتقال حافظه به حافظه
; ❌ اشتباه
mov [a], [b]
; ✅ درست
mov rax, [b]
mov [a], rax
۸.۳ ناسازگاری اندازه
; ❌ اشتباه
mov rax, [small_byte]
; ✅ درست
mov al, [small_byte]
; یا
movzx rax, byte [small_byte]
۸.۴ فراموش کردن صفر کردن rdx قبل از تقسیم
; ❌ اشتباه
mov rax, 17
mov rcx, 5
div rcx
; ✅ درست
mov rax, 17
xor rdx, rdx
mov rcx, 5
div rcx
۸.۵ استفاده از پرش علامتدار بهجای بدونعلامت
; وقتی اعداد بدونعلامت هستند
; ❌ استفاده از jg
cmp rax, rbx
jg .label
; ✅ استفاده از ja
cmp rax, rbx
ja .label
۸.۶ فرض صفر بودن رجیسترها در شروع
رجیسترها در شروع برنامه یا بعد از syscall مقدار نامعلوم دارند. همیشه مقداردهی صریح کن.
۸.۷ بهمریختن پشته
; ❌ push بدون pop
foo:
push rax
ret
; ✅ متعادل
foo:
push rax
pop rax
ret
۸.۸ فراموش کردن global _start
بدون global _start لینککننده نقطه شروع پیدا نمیکند.
۹. بهترین روشها (Best Practices)
۹.۱ صفر کردن رجیستر
; ❌ کندتر و طولانیتر
mov rax, 0
; ✅ سریعتر و بهینهتر
xor rax, rax
۹.۲ استفاده از equ برای ثابتها
STDOUT equ 1
SYS_EXIT equ 60
۹.۳ استفاده از label های محلی
.loop:
dec rcx
jnz .loop
۹.۴ توضیحگذاری منطقی
mov rsi, msg ; آرگومان دوم: اشارهگر به متن
mov rdx, len ; آرگومان سوم: طول متن
۹.۵ استفاده از lea برای محاسبه آدرس
; بهجای
mov rax, rbx
add rax, rcx
shl rax, 2
; استفاده کن از
lea rax, [rbx + rcx*4]
۹.۶ رعایت قرارداد توابع
در توابعی که قرار است با C link شوند، رجیسترهای rbx, rbp, r12-r15 را حفظ کن.
۹.۷ جدول syscall کنار دستت باشد
شمارههای مهم:
| syscall | شماره |
|---|---|
| read | 0 |
| write | 1 |
| open | 2 |
| close | 3 |
| mmap | 9 |
| exit | 60 |
۹.۸ از دیباگر استفاده کن
نه فقط برای رفع خطا، بلکه برای دیدن واقعی رفتار CPU.
۱۰. منابع و ادامه مسیر
کتابها
- Programming from the Ground Up — Jonathan Bartlett
- Assembly Language Step-by-Step — Jeff Duntemann
- The Art of 64-bit Assembly — Randall Hyde
سایتها و مستندات
- Intel SDM: مستندات رسمی پردازندههای اینتل
- AMD Manuals: مستندات رسمی AMD
- syscall.sh: جدول syscall لینوکس
- Compiler Explorer (godbolt.org): مشاهده خروجی اسمبلی کامپایلرها
- OSDev Wiki: آموزش توسعه سیستمعامل و اسمبلی سطح پایین
ابزارهای سطح بعد
- gdb + pwndbg: دیباگ امنیتی و اکسپلویت
- Ghidra / IDA: مهندسی معکوس
- radare2 / rizin: تحلیل باینری
- objdump و readelf: بررسی فایلهای ELF
- strace: ردیابی syscallهای برنامه
مسیرهای ادامه
- لینک کردن اسمبلی با C/C++
- نوشتن bootloader
- یادگیری SIMD و AVX
- مهندسی معکوس و اکسپلویتنویسی
- توسعه کرنل و درایور
- بهینهسازی حلقههای حساس با اسمبلی
۱۱. مرجع کامل توابع و متدها (API Deep Dive)
در اسمبلی مفهوم متد نداریم؛ اما هر دستور مثل یک تابع کوچک CPU است. برای درک دقیق، مهمترین دستورها را با ساختار متد بررسی میکنیم.
📌 نام متد: mov()
امضای متد (Signature):
mov destination, source
ورودیها (Parameters):
| نام پارامتر | نوع | اجباری؟ | توضیح دقیق | مثال مقدار |
|---|---|---|---|---|
| destination | register یا memory | بله | محل مقصد | rax, [rbx] |
| source | register یا immediate یا memory | بله | مقدار منبع | 0x10, rcx, [var] |
مقدار برگشتی (Return Value):
- نوع: ندارد
- توضیح: فقط مقدار منبع را در مقصد مینویسد
- فلگها را تغییر نمیدهد
کاری که انجام میده (گام به گام):
۱. ترکیب عملوندها را اعتبارسنجی میکند.
۲. مقدار منبع را میخواند.
۳. اگر منبع رجیستر/حافظه باشد، مقدار آن را میگیرد.
۴. مقدار را در مقصد مینویسد.
۵. به دستور بعدی میرود.
مثال ساده و قابل اجرا:
section .text
global _start
_start:
mov rax, 42
mov rdi, rax
mov rax, 60
syscall
خروجی echo $?:
42
مثال واقعی و کاربردی:
section .data
status dq 5
section .text
global _start
_start:
mov rax, [status] ; خواندن وضعیت از حافظه
mov rdi, rax
mov rax, 60
syscall
خطاها و Exceptions احتمالی:
| Exception | زمان رخ دادن | راهحل |
|---|---|---|
#PF |
آدرس حافظه نامعتبر | اشارهگر را بررسی کن |
#GP |
ترکیب عملوند نامعتبر مثل mov [a], [b] |
از رجیستر واسط استفاده کن |
#UD |
کد ماشین نامعتبر | سینتکس را تصحیح کن |
Overloadها:
mov rax, rbx ;; register -> register
mov rax, 0x1234 ;; immediate -> register
mov rax, [var] ;; memory -> register
mov [var], rax ;; register -> memory
mov byte [var], 0x12 ;; immediate -> memory با ذکر اندازه
mov rax, qword [var] ;; explicit size
اشتباهات رایج:
- ❌
mov [a], [b]→ ✅mov rax, [b]+mov [a], rax - ❌
mov rax, byte_varبدون hematching size → ✅mov al, [byte_var]
متدهای مرتبط:
lea— محاسبه آدرس بدون دسترسی به حافظهmovzx— انتقال با توسعه صفرmovsx— انتقال با توسعه علامت
📌 نام متد: add()
امضای متد (Signature):
add destination, source
ورودیها (Parameters):
| نام پارامتر | نوع | اجباری؟ | توضیح دقیق | مثال مقدار |
|---|---|---|---|---|
| destination | register یا memory | بله | عدد مقصد که با منبع جمع میشود | rax, [counter] |
| source | register یا immediate یا memory | بله | عدد منبع | 5, rbx |
مقدار برگشتی (Return Value):
- نوع: ندارد
- توضیح: نتیجه جمع در مقصد ذخیره میشود
- فلگهای
CF,ZF,SF,OF,PF,AFبهروز میشوند
کاری که انجام میده (گام به گام):
۱. مقدار منبع را میخواند.
۲. مقدار مقصد را میخواند.
۳. جمع میکند.
۴. نتیجه را در مقصد مینویسد.
۵. فلگها را تنظیم میکند.
مثال ساده و قابل اجرا:
section .text
global _start
_start:
mov rax, 20
add rax, 22
mov rdi, rax
mov rax, 60
syscall
خروجی echo $?:
42
مثال واقعی و کاربردی:
add rax, [rcx + rdi*4] ; جمع عضو آرایه با رجیستر accumulator
خطاها و Exceptions احتمالی:
| Exception | زمان رخ دادن | راهحل |
|---|---|---|
#PF |
دسترسی به حافظه نامعتبر | بررسی آدرس |
#GP |
سایز عملوند نامعتبر | تعیین اندازه عملوند |
Overloadها:
add rax, rbx ;; reg <- reg
add rax, 10 ;; reg <- imm
add rax, [var] ;; reg <- mem
add [var], rax ;; mem <- reg
add byte [var], 1 ;; mem <- imm
اشتباهات رایج:
- ❌
add [a], [b]→ ✅ انتقال یک طرف به رجیستر
متدهای مرتبط:
sub— تفریقinc— افزایش یک واحدیadc— جمع با بیت نقلی
📌 نام متد: sub()
امضای متد (Signature):
sub destination, source
ورودیها (Parameters):
| نام پارامتر | نوع | اجباری؟ | توضیح دقیق | مثال مقدار |
|---|---|---|---|---|
| destination | register یا memory | بله | عددی که از آن کم میشود | rbx |
| source | register یا immediate یا memory | بله | مقدار کسرشونده | 10 |
مقدار برگشتی (Return Value):
- نوع: ندارد
- توضیح: نتیجه
destination = destination - source - فلگهای
CF,ZF,SF,OF,PF,AFبهروز میشوند
کاری که انجام میده (گام به گام):
۱. مقدار منبع را میخواند.
۲. مقدار مقصد را میخواند.
۳. تفریق میکند.
۴. نتیجه را در مقصد مینویسد.
۵. فلگها را تنظیم میکند.
مثال ساده و قابل اجرا:
section .text
global _start
_start:
mov rax, 50
sub rax, 8
mov rdi, rax
mov rax, 60
syscall
خروجی echo $?:
42
مثال واقعی و کاربردی:
sub rcx, 1 ; شمارنده را کم کن
jnz .loop
خطاها و Exceptions احتمالی:
| Exception | زمان رخ دادن | راهحل |
|---|---|---|
#PF |
آدرس حافظه نامعتبر | بررسی اشارهگر |
#GP |
ترکیب عملوند نامعتبر | اصلاح سایز/ترکیب |
Overloadها:
sub rax, rbx
sub rax, 10
sub rax, [var]
sub [var], rax
sub word [var], 2
اشتباهات رایج:
- ❌
sub [a], [b]→ ✅ از رجیستر واسط استفاده کن
متدهای مرتبط:
add— جمعdec— کاهش یک واحدیsbb— تفریق با بیت نقلی
📌 نام متد: cmp()
امضای متد (Signature):
cmp left, right
ورودیها (Parameters):
| نام پارامتر | نوع | اجباری؟ | توضیح دقیق | مثال مقدار |
|---|---|---|---|---|
| left | register | بله | عملوند سمت چپ | rax |
| right | register یا immediate یا memory | بله | عملوند سمت راست | 10 |
مقدار برگشتی (Return Value):
- نوع: ندارد
- توضیح: نتیجه نوشته نمیشود؛ فقط فلگها تنظیم میشوند
- محاسبه
left - rightبدون ذخیره نتیجه
کاری که انجام میده (گام به گام):
۱. مقدار راست را از چپ کم میکند.
۲. حاصل را دور میریزد.
۳. فلگهای CF, ZF, SF, OF, PF, AF را بهروز میکند.
۴. دستور بعدی معمولاً یک پرش شرطی است.
مثال ساده و قابل اجرا:
section .text
global _start
_start:
mov rax, 10
cmp rax, 10
je .equal
mov rdi, 1
jmp .done
.equal:
xor rdi, rdi
.done:
mov rax, 60
syscall
خروجی echo $?:
0
مثال واقعی و کاربردی:
cmp byte [input], 'q'
je .quit
خطاها و Exceptions احتمالی:
| Exception | زمان رخ دادن | راهحل |
|---|---|---|
#PF |
آدرس حافظه نامعتبر | بررسی اشارهگر |
Overloadها:
cmp rax, rbx
cmp rax, 10
cmp rax, [var]
cmp [var], rax
cmp byte [var], 0
اشتباهات رایج:
- ❌ استفاده از
cmpبدون پرش بعدی و انتظار تغییر مقصد - ❌ فراموش کردن اینکه
cmpعملوندها را تغییر نمیدهد
متدهای مرتبط:
test— بررسی بیتی بدون ذخیره نتیجهsub— تفریق واقعیjcc— پرش بر اساس فلگها
📌 نام متد: Jcc()
امضای متد (Signature):
jcc target
که cc یکی از condition codeها است: e، ne، g، ge، l، le، a، ae، b، be، z، nz، s، ns، o، no.
ورودیها (Parameters):
| نام پارامتر | نوع | اجباری؟ | توضیح دقیق | مثال مقدار |
|---|---|---|---|---|
| condition code | بخشی از mnemonic | بله | شرط پرش | je, jg |
| target | label | بله | مقصد پرش | .loop |
مقدار برگشتی (Return Value):
- نوع: ندارد
- توضیح: اگر شرط برقرار باشد،
RIPبه مقصد تغییر میکند؛ وگرنه اجرا ادامه مییابد
کاری که انجام میده (گام به گام):
۱. فلگهای rflags را بررسی میکند.
۲. شرط cc را با جدول شرایط مقایسه میکند.
۳. اگر درست بود، آدرس دستور بعدی را با مقصد جایگزین میکند.
۴. اگر نادرست بود، هیچ تغییری در جریان اجرا نمیدهد.
مثال ساده و قابل اجرا:
section .text
global _start
_start:
mov rax, 5
cmp rax, 5
je .same
mov rdi, 1
jmp .done
.same:
xor rdi, rdi
.done:
mov rax, 60
syscall
خروجی echo $?:
0
مثال واقعی و کاربردی:
cmp rcx, len
jl .loop
خطاها و Exceptions احتمالی:
| Exception | زمان رخ دادن | راهحل |
|---|---|---|
#GP |
مقصد خارج از محدوده پرش | از label نزدیک استفاده کن |
Overloadها / جدول شرطها:
| دستور | معنی | شرط فلگ |
|---|---|---|
je / jz |
مساوی / صفر | ZF=1 |
jne / jnz |
نامساوی / غیرصفر | ZF=0 |
jg |
بزرگتر علامتدار | ZF=0 and SF=OF |
jge |
بزرگتر یا مساوی علامتدار | SF=OF |
jl |
کوچکتر علامتدار | SF!=OF |
jle |
کوچکتر یا مساوی علامتدار | ZF=1 or SF!=OF |
ja |
بزرگتر بدونعلامت | CF=0 and ZF=0 |
jb |
کوچکتر بدونعلامت | CF=1 |
js |
منفی | SF=1 |
jns |
غیرمنفی | SF=0 |
jo |
سرریز | OF=1 |
اشتباهات رایج:
- ❌ استفاده از
jgبرای اعداد بدونعلامت → ✅ja - ❌ استفاده از
jeبعد ازaddبهجایcmpبدون توجه به فلگ
متدهای مرتبط:
cmp— تنظیم فلگ برای پرشjmp— پرش بدون شرطtest— تنظیم فلگ با AND بیتی
📌 نام متد: call()
امضای متد (Signature):
call target
ورودیها (Parameters):
| نام پارامتر | نوع | اجباری؟ | توضیح دقیق | مثال مقدار |
|---|---|---|---|---|
| target | label/register/memory | بله | آدرس تابع یا label | my_func, rax, [func_ptr] |
مقدار برگشتی (Return Value):
- نوع: ندارد
- توضیح: آدرس بازگشت روی پشته push میشود و
RIPبه تابع مقصد میرود
کاری که انجام میده (گام به گام):
۱. آدرس دستور بعدی را محاسبه میکند.
۲. آن آدرس را روی پشته push میکند.
۳. آدرس مقصد را در RIP میگذارد.
۴. اجرا در تابع مقصد ادامه مییابد.
مثال ساده و قابل اجرا:
section .text
global _start
_start:
call foo
mov rdi, rax
mov rax, 60
syscall
foo:
mov rax, 7
ret
خروجی echo $?:
7
مثال واقعی و کاربردی:
call compute_total
mov [total], rax
خطاها و Exceptions احتمالی:
| Exception | زمان رخ دادن | راهحل |
|---|---|---|
#SS |
پشته پر یا آدرس نامعتبر | تنظیم stack |
#PF |
مقصد نامعتبر | بررسی آدرس تابع |
Overloadها:
call my_function ;; پرش نسبی به label
call rax ;; فراخوانی غیرمستقیم از رجیستر
call [function_pointer] ;; فراخوانی از حافظه
اشتباهات رایج:
- ❌ فراخوانی تابعی که
retندارد - ❌ بهمریختن alignment پشته در تعامل با C
متدهای مرتبط:
ret— بازگشت از تابعjmp— پرش بدون ذخیره آدرس بازگشتpush/pop— مدیریت پشته
📌 نام متد: ret()
امضای متد (Signature):
ret
ret imm16
ورودیها (Parameters):
| نام پارامتر | نوع | اجباری؟ | توضیح دقیق | مثال مقدار |
|---|---|---|---|---|
| immediate 16 بیتی | immediate | خیر | تعداد بایتهای پاکشونده از پشته پس از بازگشت | ret 16 |
مقدار برگشتی (Return Value):
- نوع: ندارد
- توضیح: آدرس بازگشت از پشته pop و به
RIPمنتقل میشود
کاری که انجام میده (گام به گام):
۱. مقدار بالای پشته را pop میکند.
۲. آن را در RIP قرار میدهد.
۳. اگر عملوند داشته باشد، مقدار آن را به RSP اضافه میکند.
مثال ساده و قابل اجرا:
section .text
global _start
_start:
call get_number
mov rdi, rax
mov rax, 60
syscall
get_number:
mov rax, 9
ret
خروجی echo $?:
9
مثال واقعی و کاربردی:
ret 16 ; بازگشت و پاکسازی ۱۶ بایت آرگومان از پشته
خطاها و Exceptions احتمالی:
| Exception | زمان رخ دادن | راهحل |
|---|---|---|
#SS |
پشته نامعتبر | بررسی RSP |
#PF |
آدرس بازگشت نامعتبر | بررسی call متعادل |
Overloadها:
ret
ret 16
اشتباهات رایج:
- ❌
retبدونcallقبلی → پریدن به آدرس نامعتبر - ❌ عدم پاکسازی آرگومانهای پشته در توابع stdcall-like
متدهای مرتبط:
call— فراخوانی تابعpush— ذخیره آدرس بازگشتpop— بازیابی آدرس
📌 نام متد: push()
امضای متد (Signature):
push source
ورودیها (Parameters):
| نام پارامتر | نوع | اجباری؟ | توضیح دقیق | مثال مقدار |
|---|---|---|---|---|
| source | register64 / memory64 / immediate32 | بله | مقداری که روی پشته میرود | rax, qword [var], 42 |
مقدار برگشتی (Return Value):
- نوع: ندارد
- توضیح:
RSPرا ۸ واحد کم میکند و مقدار را در آن آدرس مینویسد
کاری که انجام میده (گام به گام):
۱. RSP = RSP - 8
۲. مقدار منبع را در [RSP] مینویسد.
۳. در x86-64، اندازه عملوند ۶۴ بیتی است.
مثال ساده و قابل اجرا:
section .text
global _start
_start:
push 7
pop rdi
mov rax, 60
syscall
خروجی echo $?:
7
مثال واقعی و کاربردی:
push rax ; ذخیره موقت رجیستر قبل از call
push rbx
call function
pop rbx
pop rax
خطاها و Exceptions احتمالی:
| Exception | زمان رخ دادن | راهحل |
|---|---|---|
#SS |
سرریز پشته | محدودیت stack را بررسی کن |
#PF |
دسترسی غیرمجاز به صفحه پشته | آرایههای بزرگ روی heap ببر |
Overloadها:
push rax
push qword [var]
push 10 ;; imm32 sign-extended به 64 بیت
اشتباهات رایج:
- ❌
push eaxدر حالت ۶۴ بیتی → ✅push rax - ❌ push/pop نامتعادل
متدهای مرتبط:
pop— بازیابی از پشتهcall— push آدرس بازگشتret— pop آدرس بازگشت
📌 نام متد: pop()
امضای متد (Signature):
pop destination
ورودیها (Parameters):
| نام پارامتر | نوع | اجباری؟ | توضیح دقیق | مثال مقدار |
|---|---|---|---|---|
| destination | register64 یا memory64 | بله | محل ذخیره مقدار بالای پشته | rbx, [var] |
مقدار برگشتی (Return Value):
- نوع: ندارد
- توضیح: مقدار بالای پشته را در مقصد مینویسد و
RSPرا ۸ واحد زیاد میکند
کاری که انجام میده (گام به گام):
۱. مقدار [RSP] را میخواند.
۲. آن را در مقصد مینویسد.
۳. RSP = RSP + 8
مثال ساده و قابل اجرا:
section .text
global _start
_start:
push 42
pop rax
mov rdi, rax
mov rax, 60
syscall
خروجی echo $?:
42
مثال واقعی و کاربردی:
pop rbx ; بازیابی مقدار ذخیرهشده
خطاها و Exceptions احتمالی:
| Exception | زمان رخ دادن | راهحل |
|---|---|---|
#SS |
pop از پشته خالی | push/pop را متعادل کن |
#PF |
آدرس پشته نامعتبر | بررسی RSP |
Overloadها:
pop rax
pop qword [var]
اشتباهات رایج:
- ❌ pop بدون push قبلی
- ❌ چیدن نادرست ترتیب pop نسبت به push
متدهای مرتبط:
push— ذخیره روی پشتهcall/ret
📌 نام متد: lea()
امضای متد (Signature):
lea destination, [address_expression]
ورودیها (Parameters):
| نام پارامتر | نوع | اجباری؟ | توضیح دقیق | مثال مقدار |
|---|---|---|---|---|
| destination | register | بله | رجیستری که آدرس محاسبهشده را میگیرد | rax |
| address_expression | memory expression | بله | عبارتی که آدرساش محاسبه میشود | [rbx + rcx*4 + 8] |
مقدار برگشتی (Return Value):
- نوع: ندارد
- توضیح: آدرس مؤثر بدون دسترسی به حافظه در مقصد قرار میگیرد
کاری که انجام میده (گام به گام):
۱. آدرس مؤثر عبارت inside brackets را محاسبه میکند.
۲. فقط آدرس محاسبهشده را در مقصد مینویسد.
۳. هیچگونه dereference انجام نمیشود.
مثال ساده و قابل اجرا:
section .text
global _start
_start:
mov rsi, 42
lea rax, [rsi + 8]
mov rdi, rax
mov rax, 60
syscall
خروجی echo $?:
50
مثال واقعی و کاربردی:
lea rax, [arr + rcx*8] ; آدرس عنصر rcx ام آرایه را بگیر
mov rdi, [rax]
خطاها و Exceptions احتمالی:
| Exception | زمان رخ دادن | راهحل |
|---|---|---|
#GP |
ترکیب نامعتبر در آدرسدهی | بررسی scale/index |
Overloadها:
lea rax, [rbx]
lea rax, [rbx + 8]
lea rax, [rbx + rcx*4]
lea rax, [rbx + rcx*4 + 16]
اشتباهات رایج:
- ❌ فکر کردن اینکه
leaمقدار حافظه را میخواند - ❌ استفاده از
movبرای محاسبه آدرس و تحمل چند دستور اضافی
متدهای مرتبط:
mov— انتقال مقدار حافظه یا رجیسترadd— محاسبه آدرس دستسازshl— ضرب با توان دو
📌 نام متد: syscall()
امضای متد (Signature):
syscall
ورودیها (Parameters):
| نام پارامتر | رجیستر | نوع | اجباری؟ | توضیح دقیق | مثال مقدار |
|---|---|---|---|---|---|
| syscall number | rax | integer | بله | شماره سرویس کرنل | 60 |
| arg1 | rdi | integer/pointer | بستگی به سرویس | آرگومان اول | 1 |
| arg2 | rsi | integer/pointer | بستگی به سرویس | آرگومان دوم | msg |
| arg3 | rdx | integer/pointer | بستگی به سرویس | آرگومان سوم | len |
| arg4 | r10 | integer/pointer | بستگی به سرویس | آرگومان چهارم | 0 |
| arg5 | r8 | integer/pointer | بستگی به سرویس | آرگومان پنجم | 0 |
| arg6 | r9 | integer/pointer | بستگی به سرویس | آرگومان ششم | 0 |
مقدار برگشتی (Return Value):
- نوع: integer/pointer در
rax - توضیح: مقدار موفق یا
-errnoمنفی در خطا rcxوr11خراب میشوند
کاری که انجام میده (گام به گام):
۱. CPU وارد حالت کرنل میشود.
۲. کرنل شماره syscall را از rax میخواند.
۳. آرگومانها را از رجیسترها میگیرد.
۴. سرویس را اجرا میکند.
۵. نتیجه را در rax میگذارد.
۶. به user mode برمیگردد.
مثال ساده و قابل اجرا:
section .data
msg db "Hello", 10
len equ $ - msg
section .text
global _start
_start:
mov rax, 1
mov rdi, 1
mov rsi, msg
mov rdx, len
syscall
mov rax, 60
xor rdi, rdi
syscall
خروجی:
Hello
مثال واقعی و کاربردی:
; خواندن از stdin و چاپ همان داده
mov rax, 0
mov rdi, 0
mov rsi, buffer
mov rdx, 64
syscall
mov rdx, rax
mov rax, 1
mov rdi, 1
syscall
خطاها و Exceptions احتمالی:
| خطا/Exception | زمان رخ دادن | راهحل |
|---|---|---|
EFAULT |
اشارهگر نامعتبر به کرنل | آدرس را بررسی کن |
EINVAL |
آرگومان نامعتبر | syscall و آرگومان جدول را چک کن |
EBADF |
file descriptor نامعتبر | fd را چک کن |
#PF |
آدرس حافظه کاربر نامعتبر | به rsi/rdi اشارهگر درست بده |
Overloadها:
syscall ;; اجرای syscall بر اساس rax
int 0x80 ;; روش قدیمی ۳۲ بیتی در لینوکس
اشتباهات رایج:
- ❌ فراموش کردن تنظیم
raxقبل ازsyscall - ❌ جابهجا گذاشتن
r10بهجایrdxبرای آرگومان چهارم - ❌ فراموش کردن اینکه
syscallبعضی رجیسترها را خراب میکند
متدهای مرتبط:
sysret— بازگشت از syscall در سطح کرنلint 0x80— فراخوانی سیستمی ۳۲ بیتیcpuid— دسترسی به اطلاعات CPU
📌 نام متد: imul()
امضای متد (Signature):
imul reg, r/m
imul reg, r/m, imm32
; یا فرم یکعملوندی
imul r/m
ورودیها (Parameters):
| نام پارامتر | نوع | اجباری؟ | توضیح دقیق | مثال مقدار |
|---|---|---|---|---|
| destination | register | بله در فرم دو/سهعملوندی | رجیستر مقصد ضرب | rax |
| source | register/memory | بله | عملوند دوم ضرب | rbx |
| immediate | immediate | بله در فرم سهعملوندی | ضریب ثابت | 10 |
مقدار برگشتی (Return Value):
- نوع: integer
- توضیح: حاصلضرب علامتدار در مقصد ذخیره میشود
CFوOFدر صورت سرریز شدن از مقصد یک میشوند
کاری که انجام میده (گام به گام):
۱. مقدار منبع را میخواند.
۲. در مقصد ضرب میکند.
۳. اگر فرم سهعملوندی است، مقصد = منبع × عدد ثابت.
۴. نتیجه را مینویسد.
۵. فلگهای سرریز را تنظیم میکند.
مثال ساده و قابل اجرا:
section .text
global _start
_start:
mov rax, 6
mov rbx, 7
imul rax, rbx
mov rdi, rax
mov rax, 60
syscall
خروجی echo $?:
42
مثال واقعی و کاربردی:
imul rax, [quantity] ; rax = rax * quantity
imul rax, rbx, 10 ; rax = rbx * 10
خطاها و Exceptions احتمالی:
| Exception | زمان رخ دادن | راهحل |
|---|---|---|
#PF |
منبع حافظه نامعتبر | بررسی اشارهگر |
Overloadها:
imul rax, rbx ; rax = rax * rbx
imul rax, rbx, 10 ; rax = rbx * 10
imul rbx ; rdx:rax = rax * rbx (فرم یکعملوندی)
mul rbx ; ضرب بدونعلامت
اشتباهات رایج:
- ❌ استفاده از
mulبرای عدد علامتدار - ❌ فراموش کردن اینکه
imulتکعملوندی نتیجه را درrdx:raxمیگذارد
متدهای مرتبط:
mul— ضرب بدونعلامتdiv/idiv— تقسیمshl— ضرب سریع در توان دو
📌 نام متد: idiv() و div()
امضای متد (Signature):
idiv r/m64
div r/m64
ورودیها (Parameters):
| نام پارامتر | نوع | اجباری؟ | توضیح دقیق | مثال مقدار |
|---|---|---|---|---|
| divisor | register/memory | بله | مقسومعلیه | rcx, [divisor] |
| dividend (ضمنی) | rdx:rax | بله | مقسوم ۱۲۸ بیتی | high=rdx، low=rax |
مقدار برگشتی (Return Value):
- نوع: integer
- توضیح: خارجقسمت در
rax، باقیمانده درrdx - فلگها: وضعیت فلگها undefined است
کاری که انجام میده (گام به گام):
۱. مقدار rdx:rax را بهعنوان مقسوم میگیرد.
۲. آن را بر divisor تقسیم میکند.
۳. خارجقسمت را در rax مینویسد.
۴. باقیمانده را در rdx مینویسد.
مثال ساده و قابل اجرا:
section .text
global _start
_start:
mov rax, 17
xor rdx, rdx
mov rcx, 5
div rcx
mov rdi, rax
mov rax, 60
syscall
خروجی echo $?:
3
مثال واقعی و کاربردی:
; تبدیل عدد به رشته با تقسیم مکرر بر ۱۰
.reverse:
xor rdx, rdx
div rbx
add dl, '0'
dec rdi
mov [rdi], dl
inc rcx
test rax, rax
jnz .reverse
خطاها و Exceptions احتمالی:
| Exception | زمان رخ دادن | راهحل |
|---|---|---|
#DE |
مقسومعلیه صفر یا خارجقسمت بیرون از محدوده | divisor را چک کن، rdx را صفر/cqo کن |
Overloadها:
div rcx ; بدونعلامت
idiv rcx ; با علامت
idiv qword [var]
div r8b ; تقسیم بایتی
div r16 ; تقسیم ۱۶ بیتی
اشتباهات رایج:
- ❌ صفر نکردن
rdxقبل از تقسیم بدونعلامت - ❌ استفاده از
divبرای اعداد علامتدار - ❌ فراموش کردن
cqoبرایidivوقتی مقسوم علامتدار ۶۴ بیتی است
متدهای مرتبط:
cqo— sign-extend مقدارraxبهrdx:raxmul— ضرب بدونعلامتimul— ضرب علامتدار
📌 نام متد: xor()
امضای متد (Signature):
xor destination, source
ورودیها (Parameters):
| نام پارامتر | نوع | اجباری؟ | توضیح دقیق | مثال مقدار |
|---|---|---|---|---|
| destination | register یا memory | بله | محل نتیجه XOR | rax |
| source | register یا immediate یا memory | بله | مقدار ماسک | 0xFF |
مقدار برگشتی (Return Value):
- نوع: ندارد
- توضیح: نتیجه
destination XOR sourceدر مقصد نوشته میشود CFوOFصفر میشوند؛SF,ZF,PFبهروز میشوند
کاری که انجام میده (گام به گام):
۱. مقدار منبع و مقصد را میخواند.
۲. XOR بیتی محاسبه میکند.
۳. نتیجه را در مقصد مینویسد.
۴. فلگها را تنظیم میکند.
مثال ساده و قابل اجرا:
section .text
global _start
_start:
mov rax, 0b1100
xor rax, 0b1010
mov rdi, rax
mov rax, 60
syscall
خروجی echo $?:
6
مثال واقعی و کاربردی:
xor rax, rax ; سریعترین راه صفر کردن rax
xor rcx, rcx ; پاک کردن شمارنده
خطاها و Exceptions احتمالی:
| Exception | زمان رخ دادن | راهحل |
|---|---|---|
#PF |
آدرس حافظه نامعتبر | بررسی اشارهگر |
Overloadها:
xor rax, rbx
xor rax, 0xFFFF
xor rax, [var]
xor [var], rax
xor byte [var], 1
اشتباهات رایج:
- ❌ فرض اینکه
xorفقط برای صفر کردن است - ❌ فراموش کردن اینکه
xor rax, raxبالای ۶۴ بیت را هم صفر میکند ولیxor al, alفقطalرا صفر میکند
متدهای مرتبط:
and— AND بیتیor— OR بیتیnot— NOT بیتی
۱۲. مقایسه کلاسها/توابع مشابه
۱۲.۱ mov در مقابل lea
| ویژگی | mov rax, [rbx+8] |
lea rax, [rbx+8] |
|---|---|---|
| دسترسی به حافظه | بله | خیر |
| نتیجه | مقدار داخل آن آدرس | خود آدرس |
| سرعت | کندتر، به حافظه میرود | سریعتر، فقط محاسبه |
| کاربرد | خواندن داده | ساخت اشارهگر |
mov rax, [rbx+8] ; rax = مقدار داخل حافظه rbx+8
lea rax, [rbx+8] ; rax = rbx+8
۱۲.۲ sub در مقابل cmp
| ویژگی | sub |
cmp |
|---|---|---|
| نتیجه در مقصد | ذخیره میشود | ذخیره نمیشود |
| فلگها | بله | بله |
| کاربرد اصلی | تغییر عدد | فقط مقایسه برای پرش |
sub rax, 5 ; rax واقعاً کم میشود
cmp rax, 5 ; rax ثابت میماند
۱۲.۳ call در مقابل jmp
| ویژگی | call |
jmp |
|---|---|---|
| ذخیره آدرس بازگشت | بله | خیر |
| استفاده | فراخوانی تابع | شاخهدادن بدون بازگشت |
call my_func ; بعد از my_func به اینجا برمیگردد
jmp my_func ; دیگر برنمیگردد
۱۲.۴ shr در مقابل sar
| ویژگی | shr |
sar |
|---|---|---|
| معنی | shift به راست منطقی | shift به راست حسابی |
| بیت علامت | صفر میشود | حفظ میشود |
mov rax, -16
sar rax, 2 ; منفی میماند
mov rax, 0xF0
shr rax, 4 ; بیتهای خالی صفر میشوند
۱۲.۵ mul در مقابل imul
| ویژگی | mul |
imul |
|---|---|---|
| نوع | بدونعلامت | علامتدار |
| کاربرد | اعداد مثبت | اعداد مثبت/منفی |
mul rbx ; unsigned multiply
imul rbx ; signed multiply
۱۲.۶ div در مقابل idiv
| ویژگی | div |
idiv |
|---|---|---|
| نوع | بدونعلامت | علامتدار |
آمادهسازی rdx |
xor rdx, rdx |
cqo |
mov rax, 100
xor rdx, rdx
mov rcx, 7
div rcx ; unsigned
mov rax, -100
cqo
mov rcx, 7
idiv rcx ; signed
۱۲.۷ test در مقابل cmp
| ویژگی | test |
cmp |
|---|---|---|
| عملیات | AND بیتی | تفریق |
| کاربرد | بررسی صفر بودن/بیت خاص | مقایسه عددی |
test rax, rax ; آیا rax صفر است؟
cmp rax, 10 ; آیا rax برابر ۱۰ است؟
۱۲.۸ je در مقابل jg در مقابل ja
| دستور | معنی | عدد |
|---|---|---|
je |
برابر | هر دو |
jg |
بزرگتر علامتدار | signed |
ja |
بزرگتر بدونعلامت | unsigned |
cmp rax, rbx
jg .greater_signed
cmp rax, rbx
ja .greater_unsigned
۱۳. سناریوهای واقعی (Real-World Scenarios)
۱۳.۱ پیادهسازی strlen در اسمبلی
section .data
msg db "Hello", 0
section .text
global _start
_start:
mov rdi, msg
call strlen
mov rdi, rax
mov rax, 60
syscall
strlen:
xor eax, eax
.loop:
cmp byte [rdi+rax], 0
je .done
inc rax
jmp .loop
.done:
ret
خروجی echo $?:
5
۱۳.۲ دریافت Vendor ID پردازنده با CPUID
section .bss
vendor resb 13
section .text
global _start
_start:
mov eax, 0
cpuid
; vendor string در ebx, edx, ecx است
mov [vendor], ebx
mov [vendor+4], edx
mov [vendor+8], ecx
mov byte [vendor+12], 0
; اکنون vendor حاوی "GenuineIntel" یا "AuthenticAMD" است
; ...
۱۳.۳ استفاده از اسمبلی در پروژه C
این روش در پروژههای واقعی برای بهینهسازی توابع حساس استفاده میشود.
math_asm.asm:
global multiply
section .text
multiply:
mov rax, rdi
imul rax, rsi
ret
main.c:
#include <stdio.h>
extern long multiply(long, long);
int main(void) {
printf("%ld\n", multiply(6, 7));
return 0;
}
کامپایل:
nasm -f elf64 math_asm.asm -o math_asm.o
gcc -no-pie main.c math_asm.o -o program
./program
خروجی:
42
۱۳.۴ تحلیل باینری با objdump
objdump -d ./hello
نمونه خروجی:
0000000000401000 <_start>:
401000: b8 01 00 00 00 mov eax,0x1
401005: bf 01 00 00 00 mov edi,0x1
40100a: 48 be 00 20 40 00 00 movabs rsi,0x402000
401011: 00 00 00
401014: ba 0e 00 00 00 mov edx,0xe
401019: 0f 05 syscall
۱۴. کارایی و Performance
۱۴.۱ از رجیسترها بهجای حافظه استفاده کن
رجیسترها صدها برابر سریعتر از حافظه اصلی هستند.
; کندتر
mov rax, [counter]
add rax, 1
mov [counter], rax
; سریعتر
mov rcx, [counter]
inc rcx
; نگهداشتن در رجیستر تا وقتی ممکن است
۱۴.۲ صفر کردن با xor
xor rax, rax ; کوتاهتر و سریعتر
mov rax, 0 ; طولانیتر
۱۴.۳ استفاده از lea برای آدرسدهی
; سه دستور
mov rax, rbx
add rax, rcx
shl rax, 2
; یک دستور
lea rax, [rbx + rcx*4]
۱۴.۴ ضرب در توان دو با Shift
; ضرب در ۸
shl rax, 3
۱۴.۵ پرهیز از تقسیم
تقسیم یکی از کندترین دستورهای CPU است. وقتی ممکن است، از shift، ضریب معکوس یا LEA استفاده کن.
; تقسیم بر ۸ با shift
shr rax, 3
۱۴.۶ تراز دادهها
section .data
align 16
array dq 1, 2, 3, 4
۱۴.۷ کاهش پرشها و استفاده از پیشبینی شاخه
- حلقههای کوچک را مرتب نگه دار.
- شاخههای غیرقابل پیشبینی را کاهش بده.
- در صورت امکان از
cmovاستفاده کن.
۱۴.۸ استفاده از SIMD
بهجای پردازش یک عدد در هر دستور:
movdqu xmm0, [src]
paddd xmm0, xmm1
movdqu [dst], xmm0
۱۵. چکلیست یادگیری
سطح مبتدی
- میتوانی رجیسترهای اصلی را نام ببری.
- مفهوم
mov,add,subرا میدانی. - یک برنامه Hello World مینویسی.
- syscall خروج را فراموش نمیکنی.
- حلقه و پرش شرطی را میفهمی.
- تفاوت
.dataو.bssرا میدانی.
سطح متوسط
- با
callوretتابع مینویسی. - پشته و
push/popرا درست مدیریت میکنی. - آدرسدهی حافظه با
base+index*scale+offsetرا روان استفاده میکنی. -
cmpو جدولjccرا خوب میفهمی. - یک برنامه مثل فاکتوریل را کامل مینویسی.
- با
gdbفلگها و رجیسترها را بررسی میکنی.
سطح پیشرفته
- Calling Convention را رعایت میکنی.
- اسمبلی را با C link میکنی.
- مفاهیم signed/unsigned را در
mul/div/jccدرست اعمال میکنی. - macro مینویسی.
- از
objdumpبرای دیساسمبل کردن باینری استفاده میکنی. - یک تابع رشتهای مثل
strlenیاstrcpyرا از صفر مینویسی. - آماده یادگیری SIMD یا مهندسی معکوس هستی.