Bu X86_64 Assembly'a merhaba deyin 'in beşinci bölümü ve burada makrolara bakacağız.X86_64 ile ilgili blog yazısı olmayacak, özellikle de nasm assembler ve preprocessor ile ilgili olacak. Eğer ilginizi çekiyor ise okumaya devam edin.
NASM iki makro türünü destekler:
tek-satır
çoksatır
Tüm tek satırlı makrolar % tanım yönergesiyle başlamalıdır.Form aşağıdaki gibidir:
%define makro_adi(parametre) değer
Nasm makrosunun davranışı ve görünümü C'dekine çok benzerdir. Örneğin, aşağıdaki tek satırlı makroyu oluşturabiliriz:
%define argc rsp + 8
%define cliArg1 rsp + 24
ve kod için de kullanımı:
; argc, rsp + 8 değerine genişletilecek
mov rax, [argc]
cmp rax, 3
jne .mustBe3args
Multiline makro,%macro nasm yönergesiyle başlar ve %endmacro ile biter. Bu genel form aşağıdaki gibidir:
%macro parametre_sayısı
komut
komut
komut
%endmacro
Örneğin:
%macro bootstrap 1
push ebp
mov ebp,esp
%endmacro
Ve onu kullanabiliriz:
_start:
bootstrap
Örneğin, PRINT makrosuna bakalım:
%macro PRINT 1
pusha
pushf
jmp %%astr
%%str db %1, 0
%%strln equ $-%%str
%%astr: _syscall_write %%str, %%strln
popf
popa
%endmacro
%macro _syscall_write 2
mov rax, 1
mov rdi, 1
mov rsi, %%str
mov rdx, %%strln
syscall
%endmacro
Makro inceleyelim ve nasıl çalıştığını anlayalım: İlk satırda PRINT makrosunu bir parametre ile tanımladık.Daha sonra tüm genel registerları (pusha komutuyla ) ve flag registerları (pushf komutuyla) pushlayalım.Bundan sonra %% astr etiketine atladık.Makroda tanımlanan tüm etiketlerin %% ile başlaması gerektiğine dikkat edin.Şimdi 2 parametreyle __syscall __ makrosuna geçiyoruz. __syscall_write uygulamasına bakalım. Stdout'a stringi yazdırmak için önceki yazılarda write system call kullandığımızı hatırlayabilirsiniz. Şöyle görünüyor:
; write syscall sayısı
mov rax, 1
; file descriptor(dosya tanıtıcı), standard output(çıkış)
mov rdi, 1
; mesaj adresi
mov rsi, msg
; mesajın uzunluğu
mov rdx, 14
; write syscall'u çağır
syscall
__ syscall_write makromuzda, rax (write system call numarası) ve rdi (stdout file descriptor) 1'i koymak için ilk iki komutu tanımlarız.Daha sonra %% str değerini rsi registerına koyarız (pointer to string), burada %% str, PRINT makrosunun ilk parametresi olan local labeldir. (makro parametresinin $ parameter_number ile erişimine dikkat edin) ve 0 ile bitirin. (her string sıfır ile bitmelidir).Ve string'in uzunluğunu hesaplayan %% strlen. Bundan sonra sistem çağrısını sistem çağrısı komutu olarak adlandırıyoruz ve hepsi bu kadar.
Şimdi kullanabiliriz:
label: PRINT "Hello World!"
NASM, aşağıdaki standart makroları desteklemektedir:
Veri yapısı tanımı için STRUC ve END STRUC kullanabilirsiniz. Örneğin:
struc person
name: resb 10
age: resb 1
endstruc
Ve şimdi yapımızın örneğini yapabiliriz:
section .data
p: istruc person
at name db "name"
at age db 25
iend
section .text
_start:
mov rax, [p + person.name]
Diğer assembly dosyalarını ekleyebilir ve oraya atlayabilir veya % include direktifiyle fonksiyonları çağırabiliriz.