X86_64 Assembly'a merhaba deyin 'in altıncı bölümü ve burada AT&T assembler sözdizimine bakacağız.Daha önce tüm bölümlerde nasm assembler kullanıyorduk ama burada fasm,yasm ve diğerleri gibi söz dizimleri farklı olan assembly derleyicileri var.Yukarıda yazdığım gibi gas'a(GNU assembler) bakacağız ve sözdiziminin nasm ile arasındaki farkı göreceğiz.GCC, GNU assembler kullanılır eğer basit bir Merhaba Dünya assembler çıktısına bakarsanız:
#include <unistd.h>
int main(void) {
write(1, "Hello World\n", 15);
return 0;
}
Aşağıdaki çıktıyı göreceksiniz:
.file "test.c"
.section .rodata
.LC0:
.string "Hello World\n"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $15, %edx
movl $.LC0, %esi
movl $1, %edi
call write
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 4.9.1-16ubuntu6) 4.9.1"
.section .note.GNU-stack,"",@progbits
Nasm Merhaba dünya'sından farklı görünüyor bazı farklılıklara bakalım.
Nasıl olduğunuzu bilmiyorum, ama bir assembly programı yazmaya başladığımda genellikle bölüm(section) tanımından başlıyorum. Basit bir örneğe bakalım:
.data
//
// ilk değer atanmış verilerin tanımı
//
.text
.global _start
_start:
//
// ana program
//
Burada iki küçük farklılığı not edebilirsiniz:
• Bölüm tanımı . sembolü ile başlar.
• main bölümü, nasm'de yaptığımız gibi global yerine .globl ile tanımlanır.
Ayrıca gas veri tanımlaması için başka direktifler kullanır:
.section .data
// 1 bayt
var1: .byte 10
// 2 bayt
var2: .word 10
// 4 bayt
var3: .int 10
// 8 bayt
var4: .quad 10
// 16 v
var5: .octa 10
// assembles each string (with no automatic trailing zero byte) into consecutive addresses
str1: .asci "Hello world"
// tıpkı .ascii gibi ama her string sıfır baytı izler.
str2: .asciz "Hello world"
// String içindeki karakterleri nesne(object) dosyasına kopyala.
str3: .string "Hello world"
Operand(işlenen) sırası nasm ile assembler programı yazarken data manipulation için genel sözdizimini izleriz:
mov hedef, kaynak
GNU assembler ile sırasını değiştirdik:
mov kaynak, hedef
Örneğin:
; nasm sözdizimi
mov rax, rcx
; gas sözdizimi
mov %rcx, %rax
Ayrıca, burada registerlar % sembolüyle başlamaz. Doğrudan operand(Direct operand) kullanıyorsanız $ sembolünü kullanmanız gerekir.
movb $10, %rax
Bazen hafızanın bir bölümünü almamız gerektiğinde, örneğin 64 register'ın ilk baytı, aşağıdaki sözdizimini kullandık:
mov ax, word [rsi]
Gas'da bu tür işlemlerin başka bir yolu var. Operand boyutu tanımlamıyoruz ancak komutlarda:
movw (%rsi), %ax
GNU assembler, işlemler için 6 postfix'e sahiptir:
b - 1 bayt operand
w - 2 bayt operand
l - 4 bayt operand
q - 8 bayt operand
t - 10 bayt operand
o - 16 bayt operand
Bu kural sadece mov komutu için geçerli değildir.Addl, xorb, cmpw ve benzerileri içinde durum aynıdır.
Nasm örneğinde [] yerine önceki örnekte () köşeli parantez kullandığımızı not edebilirsiniz.Parantez içindeki değerleri ayırmak için GAS kullanılır: (% rax), örneğin:
movq -8(%rbp),%rdi
movq 8(%rbp),%rdi
GNU assembler uzak fonksiyon çağrısı ve atlamaları için aşağıdaki operatörleri destekler:
lcall $section, $offset
Far jump(uzağa atlama) - Geçerli kod bölümünden farklı bir segmentte bulunan ancak aynı düzeyde ayrıcalığa olan bir segmente atlamak bazen bir ara atlama olarak adlandırılır.
GNU assembler 3 yorum türünü destekler:
# - tek satır yorumlar
// - tek satır yorumlar
/* */ - çok satırlı yorumlar