Sıkça Sorulan Sorular

Aşağıda Linux sembolik makina dili ile programlamada sıkça sorulan sorular (cevapları ile) verilmiştir. Bazı soruar ve cevapları linux-assembly ileti listesinden alınmıştır.

3.8.1. Linux altında nasıl grafik programlama yaparım?
3.8.2. Saf (pure) sembolik makina kodunu Linux altında nasıl derlerim?
3.8.3. Başka faydalı araçlar var mı?
3.8.4. Linux'tan (BSD, BeOS, v.b.) BIOS işlevlerine nasıl erişebilirim?
3.8.5. Sembolik makina dilinde çekirdek modülleri yazmak mümkün mü?
3.8.6. Belleği dinamik olarak nasıl tahsis edebilirim?
3.8.7. select sistem çağrılarını nasıl kullanacağımı anlayamıyorum!


3.8.1. Linux altında nasıl grafik programlama yaparım?

Paul Furber'den bir cevap:

Pekala, Linux'ta grafik işleri için pekçok araç vardır. Hangisini
kullanacağınız ne yapmak istediğinize bağlıdır. Tüm bilgilere sahip
bir web sayfası yoktur ama işte bir kaç püf nokta:

SVGALib: Bu, konsoldan SVGA erişimi için C kütüphanesidir.
Artıları: öğrenmesi kolay, iyi kodlama örnekleri, DOS'taki gfx
kütüphanesinden pek de farklı değil, DOS'taki tüm etkiler az bir
değişiklikle dönüştürülebilir.
Eksileri: program doğrudan donanıma eriştiği için, çalışmak için root
erişim yetkilerine gerek duyar, her çip (chipset) ile çalışmaz,
X-Windows altında çalışmaz.
http://ftp.is.co.za'da svgalib-1.4.x diye aratın.

Framebuffer: SVGA ile ilgili kendinizce yapılabilen grafikler
Artıları: hızlı, doğrusal olarak haritalanmış video erişimi,
eğer isterseniz ASM kullanılabilir :)
Eksileri: çekirdek içinde derlenmeli, çipsete özgün özellikler,
çalışması için X kapatılmalı, iyi linux sistem çağrıları ve çekirdek
bilgisine dayanır, hata ayıklaması zordur
Örnekler: asmutils (http://www.linuxassembly.org), yaprak örneği,
framebuffer kodu ve asm ile ilgili ipuçları için benim sayfam
(http://ma.verick.co.za/linux4k/)

Xlib: XFree86 için uygulama ve geliştirme kütüphaneleri.
Artıları: X uygulamanız üzerinde tam bir kontrol
Eksileri: Öğrenmesi zor, çalışması korkunç ve az da olsa X'in düşük
seviyede nasıl çalıştığı bilgisini gerektirir
Tavsiye edilmez, ama onu için bu kadar yanıp tutuşuyorsanız durmayın.
Muhtemelen tüm başlık ve kütüphane  dosyaları yüklenmiştir, dolayısiyle
ihtiyacınız olana sahipsiniz.

Düşük seviyeli API'ler: PTC, SDL, GGI ve Clanlib'i içerir.
Artıları: çok esnek, X veya konsolda çalışır, video donanımını soyutlar
onun için düzgün doğrusal bir yüzey çizebilirsiniz, pekçok güzel örnek kod,
OpenGL ve ses kütüphaneleri gibi diğer API'lere bağlantı kurabilir
Microsoft DirectX sürümleri özgürdür.
Artıları: Kendi yaptığınız kadar hızlı değildir, gelişim sürecince bazen
sürümler çok sık değişir.
Örnekler: PTC ve GGI mükemmel demolara sahiptir, SDL ise oyunlar için,
sdlQuake, Myth II, Civ CTP ve ayrıca Clanlib'de kullanılmıştır.

Yüksek seviyeli API'ler: OpenGL - başka var mı?
Artıları: temiz API, yüzlerce işlevsellik ve örnek, endüstriyel
standart; bundan dolayı mesela SGI'dan öğrenilebilir
Eksileri: donanım hızlandırılması normalde bir zorunluluktur,
bazı sürümler ve platformlar arasında acayiplikler
Örnekler: çokça - bağlantılar bölümü altındaki www.mesa3d.org kısma bakınız.

Bakmayı sürdürmek için svgalib örneklerini inceleyin, ve aynı zamanda
SDL'yı yükleyin ve çalışır duruma getirin. Bundan sonrasında ise limit
gökyüzüdür.

3.8.2. Saf (pure) sembolik makina kodunu Linux altında nasıl derlerim?

Sembolik Makina Dili Hata Ayıklayıcısı'nın (Assembly Language Debugger), sembolik makina kodlarıyla çalışması için tasarlanmış eski bir sürümü vardır ve de Linux ve *BSD üzerinde çalışmabilmesi için yeterince taşınabilirdir. Halihazırda işlevseldir ve de doğru seçim olacaktır, bir bakın!

gdb'yi de deneyebilirsiniz ;). Kaynak kod hata ayıklayıcısı olmasına rağmen, saf sembolik makina kodlarını ayıklamak için de kullanılabilir, biraz hileyle gdb'ye istediğinizi yapmanızı söyleyebilirsiniz (maalesef nasm'ın g seçeneği gdb için yeterli bilgi üretmemektedir; sanırım bu nasm'ın bir açığı). Aşağıda Dmitry Bakhvalov 'dan bir cevap var:

Kişisel olarak, gdb'yi asm uygulamarınının hatalarını bulmak için
kullanırım. şunu deneyin:

1) Derlemek için aşağıdaki kodu kullanın:
   $ nasm -f elf -g smth.asm
   $ ld -o smth smth.o

2) gdb'yi çalıştırın
   $ gdb smth

3) gdb içinde:
   (gdb) disassemble _start
   at _start+1'e bir kesme koyun
    (eğer at _start konursa çalışmaz, nedenini bilmiyorum)
   (gdb) b *0x8048075

   kodu takip edebilmek için aşağıdaki kodu kullanırım
   (gdb)define n
   >ni
   >printf "eax=%x ebx=%x ...etc...",$eax,$ebx,...etc...
   >disassemble $pc $pc+15
   >end

   daha sonra programı r parametresiyle çalıştırıp, n ile hata ayıklayın

   Umarım yardımcı olmuştur.

???'dan ek bir bilgi:

  .gdbinit'imin içinde epeydir kullandığım bir makro var,
  ve eminim hayatı daha kolay hale getiriyor.
  Az farkla: "x /8i $pc"' kullanırım bu da belli sayıdaki çevrilmemiş
  koda müsaade eder.  Daha sonra iyi şekilde boyutu seçilmiş xterm'imle
  gdb çıktısı tazelenmiş olarak ve kaydırma gerektirmeden görünür.

Eğer kodunuza kesmeler koymak istiyorsanız, sadece int 3 ifadesini kesme olarak kullanabilirsiniz. (gdb içerisine elle adresi gömmek yerine).

Eğer gas kullanıyorsanız, gas ve gdp ile ilgili belgelere başvurmalısınız..

3.8.3. Başka faydalı araçlar var mı?

Elbette, strace size yardım edebilir (FreeBSD'de ktrace ve kdump), bu sistem çağrı ve sinyallerini takip etmek için kullanılır. Ayrıntılar için kılavuz sayfasını (man trace) ve strace --help komutunu deneyiniz.

3.8.4. Linux'tan (BSD, BeOS, v.b.) BIOS işlevlerine nasıl erişebilirim?

Kısa cevap: Hiç bir şekilde. Bu korumalı bir kiptir, yerine işletim sistemi servislerini kullanın. Tekrar ediyorum, int 0x10, int 0x13, v.b.'yi kullanamazsınız. Ne şans ki, hemen her şey sistem çağrıları ve kütüphane işlevleri ile ifade edilebilmektedir. En kötü durumda, port erişimini deneyebilir ve bir çekirdek yamasıyla istenileni gerçekleştirmeyi deneyebilirsiniz veya LRMI kütüphanesini kullanarak BIOS işlevlerine erişmeyi deneyin.

3.8.5. Sembolik makina dilinde çekirdek modülleri yazmak mümkün mü?

Evet, aslında mümkün. Her ne kadar genelde iyi bir fikir olmamasına rağmen (bir şeyleri çok zor hızlandıracaktır), böylesine bir yönteme ihtiyaç olabilir. Bir modülün kendisini yazmak o kadar da zor değildir - bir modülün kendisinin öntanımlı evrensel işlevleri olmalıdır, aynı zamanda bazı harici işlevleri de çekirdekten çağırması gerekebilir. Ayrıntılar için çekirdek kaynak koduna (bir modül olarak derlenebilenlere) bakınız.

Bu arada, işte size en basitinden bir çekirdek modülü (kaynak APJ #8'den mammon_'un örneğine dayanmaktadır):

section .text

        global init_module
        global cleanup_module
        global kernel_version

        extern printk

init_module:
        push    dword str1
        call    printk
        pop     eax
        xor     eax,eax
        ret

cleanup_module:
        push    dword str2
        call    printk
        pop     eax
        ret

str1            db      "init_module done",0xa,0
str2            db      "cleanup_module done",0xa,0

kernel_version  db      "2.2.18",0

Bu örneğin yaptığı tek şey yaptıklarını rapor etmekdir. kernel_version'unu sizinkine uygun halde değiştirin ve modülü şöyle derleyin:

$ nasm -f elf -o module.m module.asm
$ ld -r -o module.o module.m

Artık onunla insmod/rmmod/lsmod (root yetkileri gerekir) kullanarak oynayabilirsiniz; çok eğlenceli, değil mi?

3.8.6. Belleği dinamik olarak nasıl tahsis edebilirim?

H-Peter Recktenwald'dan özlü bir cevap:

ebx := 0        (in fact, any value below .bss seems to do)
sys_brk
eax := current top (of .bss section)

ebx := [ current top < ebx < (esp - 16K) ]
sys_brk
eax := new top of .bss

Tiago Gasiba'dan daha gelişmiş bir cevap:

section .bss

var1    resb    1

section .text

;
;allocate memory
;

%define LIMIT   0x4000000          ; yaklaşık 100Megs

        mov     ebx,0              ; data bölümünün en alt kısmını elde et
        call    sys_brk

        cmp     eax,-1             ; tamam mı?
        je      erro1

        add     eax,LIMIT          ; +LIMIT bellek kısmını tahsis et
        mov     ebx,eax
        call    sys_brk

        cmp     eax,-1             ; tamam mı?
        je      erro1

        cmp     eax,var1+1         ; data bölümü büyüdü mü?
        je      erro1

;
;tahsis edilmiş alanı kullan
;
                                   ; şimdi eax data bölümünün alt kısmını
                                   ; barındırır
        mov     ebx,eax            ; alt kısmı kaydet
        mov     eax,var1           ; eax=data bölümün başlangıç kısmı
repeat:
        mov     word    [eax],1    ; 1'lerle doldur
        inc     eax
        cmp     ebx,eax            ; şu anki pozisyon = en alt?
        jne     repeat

;
;belleği serbest bırak
;

        mov     ebx,var1           ; belleği geri ver
        call    sys_brk            ; başlangıcını=var1 yaparak

        cmp     eax,-1             ; tamam mı?
        je      erro2

3.8.7. select sistem çağrılarını nasıl kullanacağımı anlayamıyorum!

Patrick Mochel'den bir cevap

sys_open'ı çağırdığınız zaman, sürecinizle ilgili açık olan tüm
dosya tanıtıcılarının olduğu bir tablodan bir indis olarak, bir
dosya tanıtıcı döndürür. stdin, stdout, ve stderr için sırasıyla
0, 1 ve 2'dir, çünkü bunlar süreciniz için her zaman açık durur.
Aynı zamanda ilk açtığınız dosya tanıtıcısının 3 olocağını ve
artacağını unutmayın.

İndislemeyi anlamak select'in ne yaptığını anlamanızı sağlar.
select'i çağırdığınız zaman, okumak için, yazmak için, istisnai
durumları belirlemek için belli bir dosya tanıtıcısını beklediğinizi
belirtiyorsunuzdur. Süreciniz 1024 açık dosya tanıtıcısına sahip olabilir,
dolayısiyle fd_set sadece bir bit maskesi gibi çalışarak hangi
dosya tanıtıcısının hangi işlem için geçerli olduğunu belirtir.
Bir şeyler ifade etti mi?

Her açtığınız fd birer indis olduğu için, her bir fd_set için on
veya off olmaya ihtiyacı vardır, sadece 1024 bitlik bir fd_set yapısına
ihtiyacınız vardır. Yapıyı belirtmek için 1024 / 32 = 32 long gereklidir.

Şimdi basit bir örnekle açıklayalım.
Farzedelimki dosya tanıtıcıyı okumaya çalışıyorsunuz (zamanaşımı olmadan).

- fd_set'e belleği tahsis et

.data

my_fds: times 32 dd 0

- okumak istediğiniz dosya tanıtıcıyı açın

- fd_set yapısındaki bitini ayarlayın

    Öncelikle, 32 dwords'ün hangisinde bitin olduğunu belirlemelisiniz.

    Daha sonra, bts'yi kullanarak bu dword içindeki biti ayarlayın,
    bts biti 32'ye bölümden kalana göre ayarlayacaktır.
    Bu da önce hangi dword ile çalışmaya başlamanızı belirlemenin sebebidir.

    mov edx, 0
    mov ebx, 32
    div ebx

    lea ebx, my_fds
    bts ebx[eax * 4], edx

- son adımı okumak istediğiniz dosya tanıtıcılar için yineleyin

- belli bir eylem beklediğiniz diğer iki fd_set'in herbiri için de
  tüm örneği tekrarlayın

Bundan, geriye denklemin diğer iki parçası kalır - n parametresi ve
zaman aşımı parametresi. Zaman aşımı parametresini okuyucuya örnek
olarak bırakıyorum (evet, tembelim), fakat kısaca n parametresiyle
ilgili konuşacağım.

Bu, seçtiğiniz dosya tanıtıcıları içerisindeki en büyük değere sahip
olanın değeri (herhangi bir fd_set'ten) artı birdir. Neden artı bir?
Çünkü, bu değerden maskeyi belirlemek kolaydır. Farzedin ki x dosya
göstericisi üzerinde bir veri var, fakat sizin ilgilendiğiniz en yüksek
olan (n-1). fd_set sadece bir bit maskesi olduğundan, çekirdeğin
select'en geri dönmesi veya dönememesi için verimli bir yola ihtiyacı
vardır. Dolayısiyle, bu, ilgilendiğiniz bitleri maske dışında bırakır,
halihazırda atanmış bitlerde herhangi bir değer var mı diye kontrol eder,
eğer varsa geri döner. Aslında, fantastik olduğunu söylemek düşündüğüm kadar
kolay değil. Çekirdeğin bu maskeyi nasıl belirlediğini görmek için,
çekirdek kaynak ağacındaki fs/select.c'ye bakınız.

Herneyse, bu numarayı bilmeye ihtiyacınız vardır, en kolay yol da açılmış
olan en son dosya tanıtıcının numarasını bir yerlere kaydetmektir.

Evet, bildiklerim bunlar. Yukarıdaki kodla ilgili uyarı (her zaman ki gibi)
şu: test edilmemiştir. Sanırım çalışır, eğer çalışmazsa beni haberdar edin.
Fakat, eğer evrensel bir nükleer felakete sebep olursa, o zaman aramayın. ;)

Şimdilik hepsi bu kadar..